Qt 基于FFmpeg的視頻轉換器 - 轉GIF動圖

Qt 基于FFmpeg的視頻轉換器 - 轉GIF動圖

  • 引言
  • 一、設計思路
  • 二、核心源碼
  • 三、參考鏈接

引言

在這里插入圖片描述 在這里插入圖片描述

gif格式的動圖可以通過連續播放一系列圖像或視頻片段來展示動態效果,使信息更加生動形象,可以很方便的嵌入到網頁或者ppt中。上圖展示了視頻的前幾幀轉為gif動圖的效果 (轉了7%直接取消了)。

之前寫過一個基于python的 MP4視頻轉GIF動圖,速度略慢且不容易打包 (體積很大),故基于c++寫一個小程序,方便日常使用. (這里推薦幾個gif生成的小工具 - GifCamScreenGif.exeLICEcap.exe等等 or 直接使用ffmpeg提供的小工具)

  • 本文思路:基于FFmpeg進行視頻的讀取解碼成一張張圖片,調用gif.h將圖片寫入gif

gif-h官方git地址:https://github.com/charlietangora/gif-h

一、設計思路

可參考之前的博客:Qt 基于FFmpeg的視頻播放器 - QtFFmpegPlayer

    1. 和之前的視頻播放器play()函數類似,實現savetoGif()函數,將視頻的一幀解碼成圖片后,立即寫入gif文件
       GifWriteFrame(&writer, image.bits(),static_cast<uint32_t>(avcodec_context->width),static_cast<uint32_t>(avcodec_context->height),static_cast<uint32_t>(100/this->m_fps),        // 單位是1/100秒,即10ms8, true);frame_id++;qDebug()<<QString("當前轉換第 %1 幀").arg(frame_id);emit sig_SendFrameNum(frame_id);
    1. 創建新的FFmpegVideo類和新的處理線程,避免與播放線程沖突
m_FFmpegProcessing = new FFmpegVideo();
m_ProcessingThread = new QThread(this);
m_FFmpegProcessing->moveToThread(m_ProcessingThread);  // 移動到線程中
    1. 創建非模態的進度條,發送sig_SendFrameNum幀數信號設置進度條進度 同時判斷是否點擊了進度條的按鈕 (穩妥起見此連接設置為Qt::BlockingQueuedConnection - 確定同步執行對m_stopProcessing 及時賦值)
    // 進度條progressDialog = new QProgressDialog();progressDialog->setMinimumWidth(300);               // 設置最小寬度progressDialog->setWindowModality(Qt::NonModal);    // 非模態,其它窗口正常交互  Qt::WindowModal 模態progressDialog->setMinimumDuration(0);              // 等待0秒后顯示progressDialog->setWindowTitle(tr("進度條框"));      // 標題名progressDialog->setLabelText(tr("正在轉換"));        // 標簽的progressDialog->setCancelButtonText(tr("放棄"));    // 取消按鈕progressDialog->setRange(0, static_cast<int>(m_FFmpegProcessing->m_frame_num));    // 考慮是否移換種方式顯示進度條進度... 不使用幀數
// 進度條綁定
connect(m_FFmpegProcessing, &FFmpegVideo::sig_SendFrameNum, this, [&](int num){if(progressDialog->wasCanceled()){    // 彈窗的取消按鈕m_FFmpegProcessing->m_stopProcessing = true;return;}progressDialog->setValue(num);}, Qt::BlockingQueuedConnection);  // 發送信號后,先執行此內容 再繼續執行線程,保證線程可以及時推出

使用lambda表達式接收信號,需要注意其默認參數… 建議寫完整防止奇奇怪怪的問題
Qt使用connect連接信號與lambda表達式需要注意:https://blog.csdn.net/qq_17769915/article/details/132609165
qt 如何使用 lamda 表達式接收線程中發射的數據,并在里面更新 UI ?https://www.cnblogs.com/cheungxiongwei/p/10895172.html

    1. 子線程中會判斷m_stopProcessing - 是否點擊了進度條的退出按鈕. 如果點擊了按鈕,最后也會執行GifEnd生成一個不完整的gif
while(this->m_stopProcessing == false)
GifEnd(&writer);   // 取消之后是否需要保存不完整的gif?  暫時保存

使用事件循環,信號,stop變量,sleep阻塞,QWaitCondition+QMutex條件變量,退出子線程工作:https://blog.csdn.net/u012999461/article/details/127204493

    1. 進度條在函數中new的,子線程結束之后需釋放deleteLater。 還有一些小問題… 比如點兩次另存為gif,可以同時彈出兩個進度條等等 - 進度條沒必要每次都new… 后續繼續改進
// 開始轉換  在這里連接需注意Qt::UniqueConnection 使得連接唯一
connect(m_ProcessingThread, SIGNAL(started()), m_FFmpegProcessing, SLOT(savetoGif()), Qt::UniqueConnection);
connect(m_ProcessingThread, &QThread::finished, progressDialog, &QProgressDialog::deleteLater, Qt::UniqueConnection);
m_ProcessingThread->start();
m_ProcessingThread->quit();

二、核心源碼

其他源碼可參考我之前的博客:Qt 基于FFmpeg的視頻播放器 - QtFFmpegPlayer

  1. FFmpegVideo::savetoGif()
void FFmpegVideo::savetoGif()
{qDebug()<<"savetoGif";//avformat_seek_file()GifWriter writer = {};GifBegin(&writer, this->m_outfilename.toStdString().c_str(),static_cast<uint32_t>(avcodec_context->width),static_cast<uint32_t>(avcodec_context->height),static_cast<uint32_t>(100/this->m_fps),        // 單位是1/100秒,即10ms8, true );// 初始化臨時變量AVPacket* av_packet = static_cast<AVPacket*>(av_malloc(sizeof(AVPacket)));AVFrame *pFramein = av_frame_alloc();   //輸入和輸出的幀數據AVFrame *pFrameRGB = av_frame_alloc();uint8_t * pOutbuffer = static_cast<uint8_t *>(av_malloc(      //緩沖區分配內存static_cast<quint64>(av_image_get_buffer_size(AV_PIX_FMT_RGBA,avcodec_context->width,avcodec_context->height,1))));// 初始化緩沖區av_image_fill_arrays(pFrameRGB->data,pFrameRGB->linesize,pOutbuffer,AV_PIX_FMT_RGB32,avcodec_context->width, avcodec_context->height, 1);// 格式轉換SwsContext* pSwsContext = sws_getContext(avcodec_context->width,    // 輸入寬avcodec_context->height,   // 輸入高avcodec_context->pix_fmt,  // 輸入格式avcodec_context->width,    // 輸出寬avcodec_context->height,   // 輸出高AV_PIX_FMT_RGBA,           // 輸出格式SWS_BICUBIC,               ///todonullptr,nullptr,nullptr);int ret=0;int frame_id = 0;this->m_stopProcessing = false;// 開始循環while(this->m_stopProcessing == false){if (av_read_frame(avformat_context, av_packet) >= 0){if (av_packet->stream_index == av_stream_index){avcodec_send_packet(avcodec_context, av_packet);        // 解碼ret = avcodec_receive_frame(avcodec_context, pFramein); // 獲取解碼輸出if (ret == 0){sws_scale(pSwsContext,  //圖片格式的轉換static_cast<const uint8_t* const*>(pFramein->data),pFramein->linesize, 0, avcodec_context->height,pFrameRGB->data,  pFrameRGB->linesize);QImage  *tmpImg  = new QImage(static_cast<uchar *>(pOutbuffer),avcodec_context->width,avcodec_context->height,QImage::Format_RGBA8888);QImage image = tmpImg->copy();GifWriteFrame(&writer, image.bits(),static_cast<uint32_t>(avcodec_context->width),static_cast<uint32_t>(avcodec_context->height),static_cast<uint32_t>(100/this->m_fps),        // 單位是1/100秒,即10ms8, true);frame_id++;qDebug()<<QString("當前轉換第 %1 幀").arg(frame_id);emit sig_SendFrameNum(frame_id);//break;}}}}GifEnd(&writer);   // 取消之后是否需要保存不完整的gif?  暫時保存av_packet_unref(av_packet);
}
    1. MainWindow::saveVideo()
void MainWindow::saveVideo()
{if(!m_FFmpegVideo){return;}m_FFmpegProcessing->loadVideoFile(m_FFmpegVideo->m_filename);  // 讀取視頻QFileInfo fileInfo(m_FFmpegProcessing->m_filename);QString filePath = QFileDialog::getSaveFileName(this, QObject::tr("Open File"),fileInfo.completeBaseName() + ".gif",QObject::tr("gif (*.gif) ;; All Files (*)"));m_FFmpegProcessing->m_outfilename = filePath; // 輸出文件fileInfo.setFile(filePath);// 轉GIF ------------int ret = fileInfo.suffix().compare(QString("gif"), Qt::CaseInsensitive);// 進度條progressDialog = new QProgressDialog();progressDialog->setMinimumWidth(300);               // 設置最小寬度progressDialog->setWindowModality(Qt::NonModal);    // 非模態,其它窗口正常交互  Qt::WindowModal 模態progressDialog->setMinimumDuration(0);              // 等待0秒后顯示progressDialog->setWindowTitle(tr("進度條框"));      // 標題名progressDialog->setLabelText(tr("正在轉換"));        // 標簽的progressDialog->setCancelButtonText(tr("放棄"));    // 取消按鈕progressDialog->setRange(0, static_cast<int>(m_FFmpegProcessing->m_frame_num));    // 考慮是否移換種方式顯示進度條進度... 不使用幀數// 轉換if(ret == 0){// 進度條綁定connect(m_FFmpegProcessing, &FFmpegVideo::sig_SendFrameNum, this, [&](int num){if(progressDialog->wasCanceled()){    // 彈窗的取消按鈕m_FFmpegProcessing->m_stopProcessing = true;return;}progressDialog->setValue(num);}, Qt::BlockingQueuedConnection);  // 發送信號后,先執行此內容 再繼續執行線程,保證線程可以及時推出// 開始轉換  在這里連接需注意Qt::UniqueConnection 使得連接唯一connect(m_ProcessingThread, SIGNAL(started()), m_FFmpegProcessing, SLOT(savetoGif()), Qt::UniqueConnection);connect(m_ProcessingThread, &QThread::finished, progressDialog, &QProgressDialog::deleteLater, Qt::UniqueConnection);m_ProcessingThread->start();m_ProcessingThread->quit();}
}

三、參考鏈接

    1. 直接調用工具:

用ffmpeg提供的工具將視頻轉成gif動圖:https://blog.csdn.net/xindoo/article/details/127603896
Android錄屏并利用FFmpeg轉換成gif:https://blog.csdn.net/MingHuang2017/article/details/79186527

    1. 代碼實現:

Qt項目中,實現屏幕截圖并生成gif的詳細示例:https://www.zhihu.com/tardis/bd/art/194303756
Qt編寫自定義控件35-GIF錄屏控件:https://developer.aliyun.com/article/712842
ffmpeg生成gif動圖:https://www.jianshu.com/p/d9652fc2e3fd
FFmpeg進階: 截取視頻生成gif動圖:https://zhuanlan.zhihu.com/p/628705382

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19359.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19359.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19359.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

隨身WIFI 路由器等嵌入式Linux 編程器固件解包打包

一、固件提取 (1)直接使用CH341A編程器提取全量包,適合于無adb場景 (2)使用adb 、dd工具提取rootfs分區,通常是mtd4,一般我們也只需要修改rootfs 二、firmware 分析 Ubuntu18.04及以上,低版本需解決很多依賴問題 安裝python3和pyhton3-pip 1.下載工具 ?git clo…

基于Paraformer的alpha-token強制對齊

1. 基本原理 CIF 作為Parafoemr的核心模塊&#xff0c;用于預測字數和生成聲學向量&#xff0c;從而實現了單輪非自回歸解碼。其中字數的預測主要通過encoder輸出系數alpha的累計得分&#xff0c;滿足通關閾值β1.0即可產生一個token&#xff0c;其中alpha曲線在一定程度上呈現…

Android12.0 SIM卡語言自適應

文章目錄 需求語言設定Settings中語言切換流程檢測到SIM卡&#xff0c;更新系統語言最終修改 需求 要求系統語言跟隨SIM卡的語言變化。 語言設定 (1)系統預置語言, 即在makefile中指定的語言 (2)重啟, 如果未插卡, 則系統語言為預置的語言 (3)重啟插入SIM卡開機, 會自適應為…

【前端】vue+element項目中select下拉框label想要顯示多個值多個字段

Vue Element項目中select下拉框label想要顯示多個值 <el-selectv-model"form.plantId"collapse-tagsfilterableplaceholder"請選擇品種種類"style"width: 270px;"><el-optionv-for"item in plantIdArray":key"item.id&…

前端首屏加載速度慢問題?怎么解決

前端首屏加載速度慢是用戶體驗中的一個關鍵問題&#xff0c;它直接影響用戶對網站的第一印象以及用戶留存率。首屏加載時間是指從用戶輸入網址到頁面首屏內容完全呈現在用戶面前所需的時間。這個指標對于搜索引擎優化&#xff08;SEO&#xff09;和用戶體驗都至關重要。下面將探…

CSS:浮動

? 文檔流&#xff1a; 由于網頁默認是一個二維平面&#xff0c;當我們在網頁中一行行擺放標簽時&#xff0c;塊標簽會獨占一行&#xff0c;行標簽則只占自身大小&#xff0c;這種情況下要實現網頁布局就很麻煩了&#xff0c;所以我們就需要通過一些方法來改變這種默認的布局方…

centos7離線安裝pthon3.8

centos7離線安裝pthon3.8 因服務器無外網環境&#xff0c;所以事先需要把所有離線的依賴都準備好。 安裝前的準備 先在有外網環境的機器上準備依賴 安裝 centos-release-scl 第三方yum源 yum install centos-release-scl安裝 yum 依賴下載插件 yum install yum-plugin-do…

Javascript 位運算符(,|,^,<<,>>,>>>)

文章目錄 一、什么是位運算&#xff1f;二、如何使用1. 位與&#xff08;AND&#xff09;&#xff1a;&用途&#xff08;1&#xff09;數據清零&#xff08;2&#xff09;判斷奇偶 2. 位或&#xff08;OR&#xff09;&#xff1a;|用途&#xff08;1&#xff09;向下取整 3…

GO語言 gin框架 簡述

原文地址 基本路由 Go語言中文文檔 一、簡介 Gin是一個golang的輕量級web框架&#xff0c;性能不錯&#xff0c;API友好。 Gin支持Restful風格的API&#xff0c;可以直接從URL路徑上接收api參數或者URL參數&#xff0c;也可是使用json或者表單 數據綁定的方式接收參數。 Gin響…

【傳知代碼】BERT論文解讀及情感分類實戰-論文復現

文章目錄 概述原理介紹BERT模型架構任務1 Masked LM&#xff08;MLM&#xff09;任務2 Next Sentence Prediction (NSP)模型輸入下游任務微調GLUE數據集SQuAD v1.1 和 v2.0NER 情感分類實戰IMDB影評情感數據集數據集構建模型構建 核心代碼超參數設置訓練結果注意事項 小結 本文…

AIOps在線評測基準首階段建設完成,面向社區發布真實運維數據!

本文根據必示科技算法研究員、產品總監聶曉輝博士在2024 CCF國際AIOps挑戰賽線下宣講會上的演講整理成文。 2024年1月份OpenAIOps社區成立&#xff0c;隨著越來越多的社區成員加入&#xff0c;各項工作在有條不紊的推進中。在線評測基準系統&#xff08;AIOps Live Benchmark&a…

積鼎CFDPro水文水動力模型,專為中小流域洪水“四預”研發的流體仿真技術

水動力模型與水文模型是水利工程與水文學研究中不可或缺的兩大工具。水動力模型著重于流體運動的動力學機制&#xff0c;通過一系列方程組捕捉水流的時空變化&#xff0c;而概念性水文模型則側重于流域尺度的水文循環過程&#xff0c;利用物理概念與經驗關系進行近似模擬。兩者…

Windows系統部署YOLOv5 v6.1版本的訓練與推理環境保姆級教程

文章目錄 一 概述二 依賴環境(prerequisites)2.1 硬件環境2.2 軟件環境 三 環境安裝3.1 創建并激活虛擬環境3.2 安裝Pytorch與torchvision3.3 校驗Pytorch安裝3.4 下載 YOLOv5 v6.1 源碼3.5 安裝 YOLOv5 依賴3.6 下載預訓練模型3.7 安裝其他依賴3.8 測試環境安裝3.9 測試訓練流…

jupyter notebook更改位置

1.找到jupyer的配置文件 一般在c盤用戶的.jupter文件夾下 2. 用記事本打開這個配置文件&#xff0c;定位到c.NotebookApp.notebook_dir /path_to_your_directory 替換你的位置 3.找到jupyer圖標的位置&#xff0c;打開屬性 添加要存放的位置在目標文件的末尾&#xff0c;重新…

python | spacy,一個神奇的 Python 庫!

本文來源公眾號“python”&#xff0c;僅用于學術分享&#xff0c;侵權刪&#xff0c;干貨滿滿。 原文鏈接&#xff1a;spacy&#xff0c;一個神奇的 Python 庫&#xff01; 大家好&#xff0c;今天為大家分享一個神奇的 Python 庫 - spacy。 Github地址&#xff1a;https:/…

一個全面了解Xilinx FPGA IP核的窗口:《Xilinx系列FPGA芯片IP核詳解》(可下載)

隨著摩爾定律的逐漸放緩&#xff0c;傳統的芯片設計方法面臨著越來越多的挑戰。而FPGA以其并行處理能力和可編程性&#xff0c;為解決復雜問題提供了新的途徑。它允許設計者在同一個芯片上實現多種不同的功能模塊&#xff0c;極大地提高了資源的利用率和系統的綜合性能。 FPGA…

領域數據模型建設步驟

領域數據模型建設步驟 以某音樂app為例: 1.數據調研和業務調研&#xff0c;識別業務過程&#xff0c;實體&#xff0c;關鍵指標 業務過程&#xff1a;播放&#xff0c;收藏&#xff0c;下載&#xff0c;點擊&#xff0c;購買&#xff0c;支付 實體&#xff1a;音樂&#xff0c…

HCIA-ARP

ARP的由來 ARP這一種協議它會是在我們HCIA中第一個需要完全掌握的一個協議&#xff0c;不然對于數據通訊來說大家都會一直覺得很繞圈 協議棧&#xff0c;網線&#xff0c;網卡&#xff0c;它們組成了我們最小的數據通信的小脈絡注&#xff1a;可以了解ARP攻擊&#xff08;冒充訪…

使用Java和MyBatis獲取表頭與數據

使用Java和MyBatis獲取表頭與數據 在數據處理與展示中&#xff0c;經常需要將數據庫查詢結果中的表頭&#xff08;列名&#xff09;與實際數據提取出來。本文將介紹如何通過Java的JDBC和MyBatis來實現這一需求。 1. 使用JDBC獲取表頭與數據 在JDBC中&#xff0c;可以使用Res…

文獻解讀-群體基因組第二期|《中國人群中PAX2新生突變的檢測及表型分析:一項單中心研究》

關鍵詞&#xff1a;應用遺傳流行病學&#xff1b;群體測序&#xff1b;群體基因組&#xff1b;基因組變異檢測&#xff1b; 文獻簡介 標題&#xff08;英文&#xff09;&#xff1a;Detection of De Novo PAX2 Variants and Phenotypes in Chinese Population: A Single-Cente…