一、前言
用ffmpeg來做音視頻同步,個人認為這個是ffmpeg基礎處理中最難的一個,無數人就卡在這里,怎么也不準,本人也是嘗試過網上各種demo,基本上都是渣渣,要么僅僅支持極其少量的視頻檔案比如收到的資料包是一幀視頻一幀音頻的,要么根本沒法同步歪七八糟的,要么進度跳過去直接蹦蹦蹦崩潰的,其實最完美的音視頻同步處理demo就是ffplay,我親測過幾十種各種各樣的音視頻本地檔案,數十種視頻流檔案,都是非常完美,當然啦這是親生的啦,不完美還玩個屁,
如果僅僅是播放視頻流(不帶音頻流),可能不需要音視頻同步,所以最開始只做rtsp視頻流播放的時候根本沒有考慮同步的問題,因為沒遇到也不需要,等到后期發現各種rtmp、http、m3u8這種視頻流的時候,問題大了去了,他是hls格式的視頻流檔案一次性過來的,一個個小視頻檔案過來的,如果沒有同步的話,意味著突然之間刷刷刷的圖片過去很多,下一次來的又是刷刷的,這就需要自己計算同步了,上次接收到的資料包放入佇列,到了需要顯示的時候就顯示,
常用的音視頻同步方法:
- 通過fps來控制,fps表示一秒鐘播放多少幀,比如25幀,可以自行計算一幀解碼用掉的時間,一幀占用(1000/25=40毫秒),通過延時來處理,這其實是最渣渣的辦法,
- 記住開始解碼的時間startTime,通過av_rescale_q計算pts時間,兩者的差值就是需要延時的時間,呼叫av_usleep來延時,這種只有部分檔案正常,很多時候不正常,
- 音頻同步到視頻,視頻時鐘作為主時鐘,沒試過,網上很多人說這個辦法不好,
- 視頻同步到音頻,音頻時鐘作為主時鐘,沒試過,據說大部分人采用的此辦法,
- 音視頻同步到外部時鐘,外部時鐘作為主時鐘,最終采用的辦法,容易理解互不干擾,各自按照外部時鐘去同步自己,
- ffplay自身內置了三種同步策略,可以通過引數來控制采用何種策略,默認是視頻同步到音頻,
二、功能特點
- 多執行緒實時播放視頻流+本地視頻+USB攝像頭等,
- 支持windows+linux+mac,支持ffmpeg3和ffmpeg4,支持32位和64位,
- 多執行緒顯示影像,不卡主界面,
- 自動重連網路攝像頭,
- 可設定邊框大小即偏移量和邊框顏色,
- 可設定是否繪制OSD標簽即標簽文本或圖片和標簽位置,
- 可設定兩種OSD位置和風格,
- 可設定是否保存到檔案以及檔案名,
- 可直接拖曳檔案到ffmpegwidget控制元件播放,
- 支持h265視頻流+rtmp等常見視頻流,
- 可暫停播放和繼續播放,
- 支持存盤單個視頻檔案和定時存盤視頻檔案,
- 自定義頂部懸浮條,發送單擊信號通知,可設定是否啟用,
- 可設定畫面拉伸填充或者等比例填充,
- 可設定解碼是速度優先、質量優先、均衡處理,
- 可對視頻進行截圖(原始圖片)和截屏,
- 錄像檔案存盤支持裸流和MP4檔案,
- 音視頻完美同步,采用外部時鐘同步策略,
- 支持seek定位播放位置,
- 支持qsv、dxva2、d3d11va等硬解碼,
- 支持opengl繪制視頻資料,極低CPU占用,
- 支持安卓和嵌入式linux,交叉編譯即可,
三、效果圖

四、相關站點
- 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
- 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
- 個人主頁:https://blog.csdn.net/feiyangqingyun
- 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
- 體驗地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652
五、核心代碼
void FFmpegSync::run()
{
reset();
while (!stopped) {
//暫停狀態或者佇列中沒有幀則不處理
if (!thread->isPause && packets.count() > 0) {
mutex.lock();
AVPacket *packet = packets.first();
mutex.unlock();
//h264的裸流檔案同步有問題,獲取不到pts和dts,暫時用最蠢的辦法延時解決
if (thread->formatName == "h264") {
int sleepTime = (1000 / thread->videoFps) - 5;
msleep(sleepTime);
}
//計算當前幀顯示時間 外部時鐘同步
ptsTime = getPtsTime(thread->formatCtx, packet);
if (!this->checkPtsTime()) {
msleep(1);
continue;
}
//顯示當前的播放進度
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;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/128585.html
標籤:其他
上一篇:海康大華等攝像頭RTSP低延遲(1秒以內)網頁無插件播放解決方案
下一篇:【FLIR工業相機】一、環境配置:win10+VS2017+qt5+spinnaker+opencv+python
