ffmpeg rtsp转hls_Qt音视频开发24-ffmpeg音视频同步
## 一、前言
用ffmpeg來做音視頻同步,個(gè)人認(rèn)為這個(gè)是ffmpeg基礎(chǔ)處理中最難的一個(gè),無數(shù)人就卡在這里,怎么也不準(zhǔn),本人也是嘗試過網(wǎng)上各種demo,基本上都是渣渣,要么僅僅支持極其少量的視頻文件比如收到的數(shù)據(jù)包是一幀視頻一幀音頻的,要么根本沒法同步歪七八糟的,要么進(jìn)度跳過去直接蹦蹦蹦崩潰的,其實(shí)最完美的音視頻同步處理demo就是ffplay,我親測(cè)過幾十種各種各樣的音視頻本地文件,數(shù)十種視頻流文件,都是非常完美,當(dāng)然啦這是親生的啦,不完美還玩?zhèn)€屁。
如果僅僅是播放視頻流(不帶音頻流),可能不需要音視頻同步,所以最開始只做rtsp視頻流播放的時(shí)候根本沒有考慮同步的問題,因?yàn)闆]遇到也不需要,等到后期發(fā)現(xiàn)各種rtmp、http、m3u8這種視頻流的時(shí)候,問題大了去了,他是hls格式的視頻流文件一次性過來的,一個(gè)個(gè)小視頻文件過來的,如果沒有同步的話,意味著突然之間刷刷刷的圖片過去很多,下一次來的又是刷刷的,這就需要自己計(jì)算同步了,上次接收到的數(shù)據(jù)包放入隊(duì)列,到了需要顯示的時(shí)候就顯示。
常用的音視頻同步方法:
1. 通過fps來控制,fps表示一秒鐘播放多少幀,比如25幀,可以自行計(jì)算一幀解碼用掉的時(shí)間,一幀占用(1000/25=40毫秒),通過延時(shí)來處理,這其實(shí)是最渣渣的辦法。
2. 記住開始解碼的時(shí)間startTime,通過av_rescale_q計(jì)算pts時(shí)間,兩者的差值就是需要延時(shí)的時(shí)間,調(diào)用av_usleep來延時(shí),這種只有部分文件正常,很多時(shí)候不正常。
3. 音頻同步到視頻,視頻時(shí)鐘作為主時(shí)鐘,沒試過,網(wǎng)上很多人說這個(gè)辦法不好。
4. 視頻同步到音頻,音頻時(shí)鐘作為主時(shí)鐘,沒試過,據(jù)說大部分人采用的此辦法。
5. 音視頻同步到外部時(shí)鐘,外部時(shí)鐘作為主時(shí)鐘,最終采用的辦法,容易理解互不干擾,各自按照外部時(shí)鐘去同步自己。
6. ffplay自身內(nèi)置了三種同步策略,可以通過參數(shù)來控制采用何種策略,默認(rèn)是視頻同步到音頻。
## 二、功能特點(diǎn)
1. 多線程實(shí)時(shí)播放視頻流+本地視頻+USB攝像頭等。
2. 支持windows+linux+mac,支持ffmpeg3和ffmpeg4,支持32位和64位。
3. 多線程顯示圖像,不卡主界面。
4. 自動(dòng)重連網(wǎng)絡(luò)攝像頭。
5. 可設(shè)置邊框大小即偏移量和邊框顏色。
6. 可設(shè)置是否繪制OSD標(biāo)簽即標(biāo)簽文本或圖片和標(biāo)簽位置。
7. 可設(shè)置兩種OSD位置和風(fēng)格。
8. 可設(shè)置是否保存到文件以及文件名。
9. 可直接拖曳文件到ffmpegwidget控件播放。
10. 支持h265視頻流+rtmp等常見視頻流。
11. 可暫停播放和繼續(xù)播放。
12. 支持存儲(chǔ)單個(gè)視頻文件和定時(shí)存儲(chǔ)視頻文件。
13. 自定義頂部懸浮條,發(fā)送單擊信號(hào)通知,可設(shè)置是否啟用。
14. 可設(shè)置畫面拉伸填充或者等比例填充。
15. 可設(shè)置解碼是速度優(yōu)先、質(zhì)量優(yōu)先、均衡處理。
16. 可對(duì)視頻進(jìn)行截圖(原始圖片)和截屏。
17. 錄像文件存儲(chǔ)支持裸流和MP4文件。
18. 音視頻完美同步,采用外部時(shí)鐘同步策略。
19. 支持seek定位播放位置。
20. 支持qsv、dxva2、d3d11va等硬解碼。
21. 支持opengl繪制視頻數(shù)據(jù),極低CPU占用。
22. 支持安卓和嵌入式linux,交叉編譯即可。
## 三、效果圖
## 四、相關(guān)站點(diǎn)
1. 國內(nèi)站點(diǎn):[https://gitee.com/feiyangqingyun/QWidgetDemo](https://gitee.com/feiyangqingyun/QWidgetDemo)
2. 國際站點(diǎn):[https://github.com/feiyangqingyun/QWidgetDemo](https://github.com/feiyangqingyun/QWidgetDemo)
3. 個(gè)人主頁:[https://blog.csdn.net/feiyangqingyun](https://blog.csdn.net/feiyangqingyun)
4. 知乎主頁:[https://www.zhihu.com/people/feiyangqingyun/](https://www.zhihu.com/people/feiyangqingyun/)
5. 體驗(yàn)地址:[https://blog.csdn.net/feiyangqingyun/article/details/97565652](https://blog.csdn.net/feiyangqingyun/article/details/97565652)
## 五、核心代碼
void FFmpegSync::run(){ reset(); while (!stopped) { //暫停狀態(tài)或者隊(duì)列中沒有幀則不處理 if (!thread->isPause && packets.count() > 0) { mutex.lock(); AVPacket *packet = packets.first(); mutex.unlock(); //h264的裸流文件同步有問題,獲取不到pts和dts,暫時(shí)用最蠢的辦法延時(shí)解決 if (thread->formatName == "h264") { int sleepTime = (1000 / thread->videoFps) - 5; msleep(sleepTime); } //計(jì)算當(dāng)前幀顯示時(shí)間 外部時(shí)鐘同步 ptsTime = getPtsTime(thread->formatCtx, packet); if (!this->checkPtsTime()) { msleep(1); continue; } //顯示當(dāng)前的播放進(jìn)度 checkShowTime(); //0-表示音頻 1-表示視頻 if (type == 0) { thread->decodeAudio(packet); } else if (type == 1) { thread->decodeVideo(packet); } //釋放資源并移除 thread->free(packet); mutex.lock(); packets.removeFirst(); mutex.unlock(); } msleep(1); } clear(); stopped = false;}bool FFmpegSync::checkPtsTime(){ bool ok = false; if (ptsTime > 0) { if (ptsTime > offsetTime + 100000) { bufferTime = ptsTime - offsetTime + 100000; } int offset = (type == 0 ? 1000 : 5000); offsetTime = av_gettime() - startTime + bufferTime; if ((offsetTime <= ptsTime && ptsTime - offsetTime <= offset) || (offsetTime > ptsTime)) { ok = true; } } else { ok = true; } return ok;}總結(jié)
以上是生活随笔為你收集整理的ffmpeg rtsp转hls_Qt音视频开发24-ffmpeg音视频同步的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php截断上传,截断在文件包含和上传中的
- 下一篇: rfcv函数实现_OpenSSL AES