一、前言
用ffmpeg來實作自己的播放器,這是一直以來的一個目標,之前的難點卡在音視頻同步以及如何播放聲音這兩點(盡管之前已經進行過不少的嘗試和探索,但是問題還是挺多,比如音視頻同步不完美,有些檔案正常而有些檔案不準,聲音播放采用的sdl總感覺多了個依賴怪怪的,而且很多初學者也反映希望采用Qt自身的類來播放),近期正好把這兩個難點一一攻破了,音視頻同步采用的外部時鐘同步,聲音播放采用的Qt自帶的QAudioOutput(并沒有采用sdl,省去學習sdl開源庫的成本),播放器的demo如期進行,有時候做專案,如果將各個難點擊破以后,接下來都是順理成章水到渠成的事情,速度會非常快,這也是我經常用的策略,
最簡單基本播放器具備的功能:
- 播放、關閉、暫停、繼續,
- 音量調節、靜音設定,
- 進度調節、定位播放,
- 總時長、已播放時長,
- 音頻、視頻、本地檔案、視頻流,
前面幾篇文章寫了音視頻同步、音頻播放、音量設定、靜音設定,這里就差一個進度調節、定位播放的處理了,ffmpeg內置了av_seek_frame函式負責定位播放幀,總共4個引數,含義分別如下:
- 引數1 AVFormatContext *s 表示處理媒體物件的背景關系,
- 引數2 int stream_index 表示流的索引,填-1表示自動默認流索引,
- 引數3 int64_t timestamp 表示要定位的時間,單位是微妙,如果傳入的是秒則需要 * AV_TIME_BASE,
- 引數4 int flags 表示如何定位和查找使用的策略,建議選擇AVSEEK_FLAG_BACKWARD,其余引數容易花屏,
- 回傳值 >= 0 表示成功,
二、功能特點
- 多執行緒實時播放視頻流+本地視頻+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
五、核心代碼
uint FFmpegThread::getLength()
{
return duration * 1000;
}
uint FFmpegThread::getPosition()
{
return 0;
}
void FFmpegThread::setPosition(int position)
{
if (this->isRunning() && !isRtsp && !isUsbCamera) {
pause();
QThread::msleep(100);
videoSync->clear();
audioSync->clear();
int64_t timestamp = ((double)position / 1000.0) * AV_TIME_BASE;
av_seek_frame(formatCtx, -1, timestamp, AVSEEK_FLAG_BACKWARD);
next();
}
}
void FFmpegThread::play()
{
//通過標志位讓執行緒執行初始化
isPlay = true;
isPause = false;
}
void FFmpegThread::pause()
{
//只對本地檔案起作用
playAudio = false;
if (!isRtsp && !isUsbCamera && !isPause) {
isPause = true;
}
}
void FFmpegThread::next()
{
//只對本地檔案起作用
playAudio = true;
if (!isRtsp && !isUsbCamera && isPause) {
isPause = false;
videoSync->reset();
audioSync->reset();
}
}
void FFmpegThread::stop()
{
//通過標志位讓執行緒停止
stopped = true;
}
void FFmpegThread::snap()
{
//通過標志位來截圖 句柄模式才需要
if (!callback) {
isSnap = true;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/135732.html
標籤:其他
上一篇:一往無前的背后
下一篇:Haclon中相機標定的步驟
