一、前言
基本上各種播放器提供的錄制視頻接口,都是只有開始錄制和結(jié)束錄制兩個(gè),當(dāng)然一般用的最多的也是這兩個(gè)接口,但是實(shí)際使用過程中,還有一種可能需要中途暫停錄制,暫停以后再次繼續(xù)錄制,將中間部分視頻不需要錄制,跳過這部分不需要的視頻,而且錄制的視頻文件必須是能夠正常播放的連續(xù)的。vlc和mpv都只提供了開始錄制和停止錄制接口,ffmpeg既然是自己解碼,所以錄制完全自己控制,存儲的時(shí)候,每一幀的數(shù)據(jù)都要寫入pts和dts,每次重新計(jì)算時(shí)間基準(zhǔn)保證時(shí)間正確,不然不是連續(xù)的幀保存后會(huì)跳。
打通了視頻暫停錄制功,還有個(gè)應(yīng)用場景就可以迎難而解,就是多個(gè)通道的視頻,不同時(shí)段分開存入同一個(gè)視頻文件,類似于將輪詢的過程中展示的視頻挨個(gè)存儲到同一個(gè)視頻文件,當(dāng)然分辨率必須保持一致,不一致可能存儲會(huì)出問題,這種場景還是比較常見的,比如視頻通道輪詢過程中,指定某個(gè)位置的視頻存儲的同一個(gè)視頻文件,將輪詢的整個(gè)過程錄制下來,后期以便回放查閱視頻。在當(dāng)前封裝的組件中,除了打開和關(guān)閉錄制,中途只需要將視頻幀傳入即可,會(huì)自動(dòng)計(jì)算換算成正確的pts/dts存儲到MP4文件中。文章來源:http://www.zghlxwxcb.cn/news/detail-419724.html
二、效果圖
文章來源地址http://www.zghlxwxcb.cn/news/detail-419724.html
三、體驗(yàn)地址
- 國內(nèi)站點(diǎn):https://gitee.com/feiyangqingyun
- 國際站點(diǎn):https://github.com/feiyangqingyun
- 個(gè)人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 體驗(yàn)地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_demo/bin_linux_video。
四、相關(guān)代碼
void FFmpegThread::recordStart(const QString &fileName)
{
#ifdef videosave
AbstractVideoThread::recordStart(fileName);
if ((saveVideoType > 1) && !onlyAudio) {
this->setFileName(fileName);
//處于暫停階段則切換暫停標(biāo)志位(暫停后再次恢復(fù)說明又重新開始錄制)
if (saveFile->getIsPause()) {
isRecord = true;
saveFile->pause();
emit recorderStateChanged(RecorderState_Recording, fileName);
} else {
saveFile->setPara(saveVideoType, videoWidth, videoHeight, frameRate, formatCtx->streams[videoIndex], (videoType == VideoType_Camera));
saveFile->open(fileName);
if (saveFile->getIsOk()) {
isRecord = true;
emit recorderStateChanged(RecorderState_Recording, fileName);
}
}
}
#endif
}
void FFmpegThread::recordPause()
{
#ifdef videosave
AbstractVideoThread::recordPause();
if ((saveVideoType > 1) && !onlyAudio) {
if (saveFile->getIsOk()) {
isRecord = false;
saveFile->pause();
emit recorderStateChanged(RecorderState_Paused, fileName);
}
}
#endif
}
void FFmpegThread::recordStop()
{
#ifdef videosave
AbstractVideoThread::recordStop();
if ((saveVideoType > 1) && !onlyAudio) {
if (saveFile->getIsOk()) {
isRecord = false;
saveFile->stop();
//執(zhí)行過轉(zhuǎn)換合并的不用再發(fā)信號
if (!saveFile->isConvertMerge) {
emit recorderStateChanged(RecorderState_Stopped, fileName);
}
}
}
#endif
}
void FFmpegSave::save()
{
//從隊(duì)列中取出數(shù)據(jù)處理
//qDebug() << TIMEMS << videoFrames.count() << videoPackets.count();
if (videoFrames.count() > 0) {
mutex.lock();
AVFrame *frame = videoFrames.takeFirst();
mutex.unlock();
FFmpegHelper::encode(this, videoCodecCtx, videoPacket, frame, true);
FFmpegHelper::freeFrame(frame);
}
if (videoPackets.count() > 0) {
mutex.lock();
AVPacket *packet = videoPackets.takeFirst();
mutex.unlock();
this->writePacket(packet);
FFmpegHelper::freePacket(packet);
}
}
void FFmpegSave::close()
{
//寫入過開始符才能寫入文件結(jié)束符(沒有這個(gè)判斷會(huì)報(bào)錯(cuò))
if (packetCount > 0 && saveVideoType == SaveVideoType_Mp4) {
av_write_trailer(formatCtx);
}
//清空隊(duì)列中的數(shù)據(jù)
foreach (AVFrame *frame, videoFrames) {
FFmpegHelper::freeFrame(frame);
}
foreach (AVPacket *packet, videoPackets) {
FFmpegHelper::freePacket(packet);
}
packetCount = 0;
videoFrames.clear();
videoPackets.clear();
//釋放臨時(shí)數(shù)據(jù)包
if (videoPacket) {
FFmpegHelper::freePacket(videoPacket);
videoPacket = NULL;
}
//關(guān)閉編碼器上下文并釋放對象
if (videoCodecCtx) {
avcodec_free_context(&videoCodecCtx);
videoCodec = NULL;
videoCodecCtx = NULL;
}
//關(guān)閉文件流并釋放對象
if (formatCtx) {
avio_close(formatCtx->pb);
avformat_free_context(formatCtx);
formatCtx = NULL;
videoStreamOut = NULL;
videoStreamIn = NULL;
}
//執(zhí)行轉(zhuǎn)換合并音頻文件到一個(gè)文件
isConvertMerge = false;
if (convertMerge) {
if (saveVideoType == SaveVideoType_H264) {
isConvertMerge = FFmpegRun::aacAndH264ToMp4(fileName);
} else if (saveVideoType == SaveVideoType_Mp4) {
isConvertMerge = FFmpegRun::aacAndMp4ToMp4(fileName);
}
}
}
void FFmpegSave::setPara(const SaveVideoType &saveVideoType, int videoWidth, int videoHeight, int frameRate, AVStream *videoStreamIn, bool camera)
{
this->saveVideoType = saveVideoType;
this->videoWidth = videoWidth;
this->videoHeight = videoHeight;
if (camera) {
this->frameRate = frameRate > 15 ? 16.621 : frameRate;
} else {
this->frameRate = frameRate > 25 ? 25 : frameRate;
}
this->videoStreamIn = videoStreamIn;
}
void FFmpegSave::writeVideo(AVFrame *frame)
{
//沒打開或者暫停階段不處理
if (!isOk || isPause) {
return;
}
//可以直接寫入到文件也可以排隊(duì)處理
if (directSave) {
FFmpegHelper::encode(this, videoCodecCtx, videoPacket, frame, true);
} else {
mutex.lock();
videoFrames << av_frame_clone(frame);
mutex.unlock();
}
}
void FFmpegSave::writeVideo(AVPacket *packet)
{
//沒打開或者暫停階段不處理
if (!isOk || isPause) {
return;
}
//可以直接寫入到文件也可以排隊(duì)處理
if (directSave) {
this->writePacket(packet);
} else {
mutex.lock();
videoPackets << FFmpegHelper::creatPacket(packet);
mutex.unlock();
}
}
五、功能特點(diǎn)
5.1 基礎(chǔ)功能
- 支持各種音頻視頻文件格式,比如mp3、wav、mp4、asf、rm、rmvb、mkv等。
- 支持本地?cái)z像頭設(shè)備,可指定分辨率、幀率。
- 支持各種視頻流格式,比如rtp、rtsp、rtmp、http等。
- 本地音視頻文件和網(wǎng)絡(luò)音視頻文件,自動(dòng)識別文件長度、播放進(jìn)度、音量大小、靜音狀態(tài)等。
- 文件可以指定播放位置、調(diào)節(jié)音量大小、設(shè)置靜音狀態(tài)等。
- 支持倍速播放文件,可選0.5倍、1.0倍、2.5倍、5.0倍等速度,相當(dāng)于慢放和快放。
- 支持開始播放、停止播放、暫停播放、繼續(xù)播放。
- 支持抓拍截圖,可指定文件路徑,可選抓拍完成是否自動(dòng)顯示預(yù)覽。
- 支持錄像存儲,手動(dòng)開始錄像、停止錄像,部分內(nèi)核支持暫停錄像后繼續(xù)錄像,跳過不需要錄像的部分。
- 支持無感知切換循環(huán)播放、自動(dòng)重連等機(jī)制。
- 提供播放成功、播放完成、收到解碼圖片、收到抓拍圖片、視頻尺寸變化、錄像狀態(tài)變化等信號。
- 多線程處理,一個(gè)解碼一個(gè)線程,不卡主界面。
5.2 特色功能
- 同時(shí)支持多種解碼內(nèi)核,包括qmedia內(nèi)核(Qt4/Qt5/Qt6)、ffmpeg內(nèi)核(ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5)、vlc內(nèi)核(vlc2/vlc3)、mpv內(nèi)核(mpv1/mp2)、海康sdk、easyplayer內(nèi)核等。
- 非常完善的多重基類設(shè)計(jì),新增一種解碼內(nèi)核只需要實(shí)現(xiàn)極少的代碼量,就可以應(yīng)用整套機(jī)制。
- 同時(shí)支持多種畫面顯示策略,自動(dòng)調(diào)整(原始分辨率小于顯示控件尺寸則按照原始分辨率大小顯示,否則等比例縮放)、等比例縮放(永遠(yuǎn)等比例縮放)、拉伸填充(永遠(yuǎn)拉伸填充)。所有內(nèi)核和所有視頻顯示模式下都支持三種畫面顯示策略。
- 同時(shí)支持多種視頻顯示模式,句柄模式(傳入控件句柄交給對方繪制控制)、繪制模式(回調(diào)拿到數(shù)據(jù)后轉(zhuǎn)成QImage用QPainter繪制)、GPU模式(回調(diào)拿到數(shù)據(jù)后轉(zhuǎn)成yuv用QOpenglWidget繪制)。
- 支持多種硬件加速類型,ffmpeg可選dxva2、d3d11va等,mpv可選auto、dxva2、d3d11va,vlc可選any、dxva2、d3d11va。不同的系統(tǒng)環(huán)境有不同的類型選擇,比如linux系統(tǒng)有vaapi、vdpau,macos系統(tǒng)有videotoolbox。
- 解碼線程和顯示窗體分離,可指定任意解碼內(nèi)核掛載到任意顯示窗體,動(dòng)態(tài)切換。
- 支持共享解碼線程,默認(rèn)開啟并且自動(dòng)處理,當(dāng)識別到相同的視頻地址,共享一個(gè)解碼線程,在網(wǎng)絡(luò)視頻環(huán)境中可以大大節(jié)約網(wǎng)絡(luò)流量以及對方設(shè)備的推流壓力。國內(nèi)頂尖視頻廠商均采用此策略。這樣只要拉一路視頻流就可以共享到幾十個(gè)幾百個(gè)通道展示。
- 自動(dòng)識別視頻旋轉(zhuǎn)角度并繪制,比如手機(jī)上拍攝的視頻一般是旋轉(zhuǎn)了90度的,播放的時(shí)候要自動(dòng)旋轉(zhuǎn)處理,不然默認(rèn)是倒著的。
- 自動(dòng)識別視頻流播放過程中分辨率的變化,在視頻控件上自動(dòng)調(diào)整尺寸。比如攝像機(jī)可以在使用過程中動(dòng)態(tài)配置分辨率,當(dāng)分辨率改動(dòng)后對應(yīng)視頻控件也要做出同步反應(yīng)。
- 音視頻文件無感知自動(dòng)切換循環(huán)播放,不會(huì)出現(xiàn)切換期間黑屏等肉眼可見的切換痕跡。
- 視頻控件同時(shí)支持任意解碼內(nèi)核、任意畫面顯示策略、任意視頻顯示模式。
- 視頻控件懸浮條同時(shí)支持句柄、繪制、GPU三種模式,非絕對坐標(biāo)移來移去。
- 本地?cái)z像頭設(shè)備支持指定設(shè)備名稱、分辨率、幀率進(jìn)行播放。
- 錄像文件同時(shí)支持打開的視頻文件、本地?cái)z像頭、網(wǎng)絡(luò)視頻流等。
- 瞬間響應(yīng)打開和關(guān)閉,無論是打開不存在的視頻或者網(wǎng)絡(luò)流,探測設(shè)備是否存在,讀取中的超時(shí)等待,收到關(guān)閉指令立即中斷之前的操作并響應(yīng)。
- 支持打開各種圖片文件,支持本地音視頻文件拖曳播放。
- 視頻控件懸浮條自帶開始和停止錄像切換、聲音靜音切換、抓拍截圖、關(guān)閉視頻等功能。
- 音頻組件支持聲音波形值數(shù)據(jù)解析,可以根據(jù)該值繪制波形曲線和柱狀聲音條,默認(rèn)提供了聲音振幅信號。
- 各組件中極其詳細(xì)的打印信息提示,尤其是報(bào)錯(cuò)信息提示,封裝的統(tǒng)一打印格式。針對現(xiàn)場復(fù)雜的設(shè)備環(huán)境測試極其方便有用,相當(dāng)于精確定位到具體哪個(gè)通道哪個(gè)步驟出錯(cuò)。
- 代碼框架和結(jié)構(gòu)優(yōu)化到最優(yōu),性能強(qiáng)悍,持續(xù)迭代更新升級。
- 源碼支持Qt4、Qt5、Qt6,兼容所有版本。
5.3 視頻控件
- 可動(dòng)態(tài)添加任意多個(gè)osd標(biāo)簽信息,標(biāo)簽信息包括名字、是否可見、字號大小、文本文字、文本顏色、標(biāo)簽圖片、標(biāo)簽坐標(biāo)、標(biāo)簽格式(文本、日期、時(shí)間、日期時(shí)間、圖片)、標(biāo)簽位置(左上角、左下角、右上角、右下角、居中、自定義坐標(biāo))。
- 可動(dòng)態(tài)添加任意多個(gè)圖形信息,這個(gè)非常有用,比如人工智能算法解析后的圖形區(qū)域信息直接發(fā)給視頻控件即可。圖形信息支持任意形狀,直接繪制在原始圖片上,采用絕對坐標(biāo)。
- 圖形信息包括名字、邊框大小、邊框顏色、背景顏色、矩形區(qū)域、路徑集合、點(diǎn)坐標(biāo)集合等。
- 每個(gè)圖形信息都可指定三種區(qū)域中的一種或者多種,指定了的都會(huì)繪制。
- 內(nèi)置懸浮條控件,懸浮條位置支持頂部、底部、左側(cè)、右側(cè)。
- 懸浮條控件參數(shù)包括邊距、間距、背景透明度、背景顏色、文本顏色、按下顏色、位置、按鈕圖標(biāo)代碼集合、按鈕名稱標(biāo)識集合、按鈕提示信息集合。
- 懸浮條控件一排工具按鈕可自定義,通過結(jié)構(gòu)體參數(shù)設(shè)置,圖標(biāo)可選圖形字體還是自定義圖片。
- 懸浮條按鈕內(nèi)部實(shí)現(xiàn)了錄像切換、抓拍截圖、靜音切換、關(guān)閉視頻等功能,也可以自行在源碼中增加自己對應(yīng)的功能。
- 懸浮條按鈕對應(yīng)實(shí)現(xiàn)了功能的按鈕,有對應(yīng)圖標(biāo)切換處理,比如錄像按鈕按下后會(huì)切換到正在錄像中的圖標(biāo),聲音按鈕切換后變成靜音圖標(biāo),再次切換還原。
- 懸浮條按鈕單擊后都用名稱唯一標(biāo)識作為信號發(fā)出,可以自行關(guān)聯(lián)響應(yīng)處理。
- 懸浮條空白區(qū)域可以顯示提示信息,默認(rèn)顯示當(dāng)前視頻分辨率大小,可以增加幀率、碼流大小等信息。
- 視頻控件參數(shù)包括邊框大小、邊框顏色、焦點(diǎn)顏色、背景顏色(默認(rèn)透明)、文字顏色(默認(rèn)全局文字顏色)、填充顏色(視頻外的空白處填充黑色)、背景文字、背景圖片(如果設(shè)置了圖片優(yōu)先取圖片)、是否拷貝圖片、縮放顯示模式(自動(dòng)調(diào)整、等比例縮放、拉伸填充)、視頻顯示模式(句柄、繪制、GPU)、啟用懸浮條、懸浮條尺寸(橫向?yàn)楦叨?、縱向?yàn)閷挾龋腋l位置(頂部、底部、左側(cè)、右側(cè))。
到了這里,關(guān)于Qt音視頻開發(fā)38-ffmpeg視頻暫停錄制的設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!