Qt音視訊開發1-vlc解碼播放
阿新 • • 發佈:2020-08-02
一、前言
最開始接觸視訊監控這塊的時候,用的就是vlc作為解碼的核心,主要是因為vlc使用簡單方便,直接傳入一個控制代碼即可,簡單幾行程式碼就可以實現一個視訊流播放,很適合初學者使用,也推薦初學者用qt+vlc來做播放器,提供的介面還是非常友好的,而且門類特別多,想要獲取媒體檔案的各種資訊比如寬高,設定寬高比等,直接呼叫介面函式傳入引數就能設定。
所有用vlc做視訊監控解碼的人都會遇到一個問題,那就是滑鼠事件被接管攔截了,不能識別滑鼠事件,比如雙擊最大化等,這就很憋屈了,明明很好用的一個東西,怎麼突然之間滑鼠事件也識別不到了呢,網上一搜一大把,主要有三個解決辦法。
- 修改vlc原始碼,重新編譯,替換動態庫檔案。
- 全域性滑鼠鉤子攔截滑鼠訊息進行處理。
- 設定控制代碼以後直接將控制元件/接受視訊渲染的控制元件禁用掉。
最終採用方法3,實現起來簡單快速,修改vlc原始碼的編譯工作量太大了,畢竟vlc依賴一大堆的外掛,用vlc的人一般都是初學者半吊子,哪裡有能力去編譯一遍vlc哦。
二、功能特點
- 多執行緒實時播放視訊流和本地視訊。
- 支援windows+linux+mac,支援vlc2和vlc3。
- 多執行緒顯示影象,不卡主介面。
- 自動重連網路攝像頭。
- 可設定邊框大小即偏移量和邊框顏色。
- 可設定是否繪製OSD標籤即標籤文字或圖片和標籤位置。
- 可設定兩種OSD位置和風格。
- 可設定是否儲存到檔案以及檔名。
- 可直接拖曳檔案到vlcwidget控制元件播放。
- 支援h265視訊流+rtmp等常見視訊流。
- 可暫停播放和繼續播放。
- 支援回撥模式和控制代碼兩種模式。
- 支援執行緒讀取進度等資訊和事件回撥兩種處理模式。
- 自動將當前播放位置和音量大小是否靜音以訊號發出去。
- 提供介面設定播放位置和音量及設定靜音。
- 支援儲存單個視訊檔案和定時儲存視訊檔案。
- 自定義頂部懸浮條,傳送單擊訊號通知,可設定是否啟用。
三、效果圖
四、相關站點
- 國內站點: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
五、核心程式碼
bool VlcThread::init()
{
//判斷該攝像機是否能聯通
if (checkConn && isRtsp) {
if (!checkUrl(url, checkTime)) {
return false;
}
}
QFileInfo info(url);
suffix = info.suffix();
//設定拓展名
if (url.startsWith("dshow://")) {
suffix = "dshow";
} else if (url.startsWith("rtsp")) {
suffix = "rtsp";
} else if (url.startsWith("rtmp")) {
suffix = "rtmp";
} else if (url.startsWith("http")) {
suffix = "http";
}
const char *tempArg = "";
if (suffix == "h264") {
tempArg = "--demux=h264";
} else if (suffix == "h265") {
tempArg = "--demux=h265";
}
const char *vlc_args[9] = {"-I", "dummy", "--no-osd", "--no-stats", "--ignore-config", "--no-video-on-top", "--no-video-title-show", "--no-snapshot-preview", tempArg};
vlcInst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
if (vlcInst == NULL) {
return false;
}
if (isRtsp || suffix == "dshow") {
vlcMedia = libvlc_media_new_location(vlcInst, url.toUtf8().constData());
} else {
//windows上需要替換檔案路徑
QString url = this->url;
url = QDir::toNativeSeparators(url);
vlcMedia = libvlc_media_new_path(vlcInst, url.toUtf8().constData());
}
if (vlcMedia == NULL) {
return false;
}
//媒體播放物件
vlcPlayer = libvlc_media_player_new_from_media(vlcMedia);
if (vlcPlayer == NULL) {
return false;
}
//建立事件管理器
if (callbackevent) {
vlcEvent = libvlc_media_player_event_manager(vlcPlayer);
libvlc_event_new(vlcEvent, this);
}
//回撥方式和控制代碼方式兩種分別處理
if (callback) {
callbackData = new CallbackData;
callbackData->thread = this;
callbackData->pixels = new uchar[bufferWidth * bufferHeight * 4];
memset(callbackData->pixels, 0, bufferWidth * bufferHeight * 4);
int width = callbackData->thread->getBufferWidth();
int height = callbackData->thread->getBufferHeight();
//設定回撥拿到每幀資料
libvlc_video_set_callbacks(vlcPlayer, lock, unlock, display, callbackData);
//設定每幀格式 RV32-Format_RGB32 RGBA-Format_RGBA8888 YUYV I420
libvlc_video_set_format(vlcPlayer, "RV32", width, height, width * 4);
} else {
//設定播放控制代碼
if (playWidget == NULL) {
return false;
}
#if defined(Q_OS_WIN)
libvlc_media_player_set_hwnd(vlcPlayer, (void *)playWidget->winId());
#elif defined(Q_OS_LINUX)
libvlc_media_player_set_xwindow(vlcPlayer, playWidget->winId());
#elif defined(Q_OS_MAC)
libvlc_media_player_set_nsobject(vlcPlayer, (void *)playWidget->winId());
#endif
//禁用控制代碼滑鼠訊息
QTimer::singleShot(1000, this, SLOT(disableHwnd()));
}
//設定硬體加速 none auto any d3d11va dxva2
setOption(QString(":avcodec-hw=%1").arg(hardware));
//設定通訊協議 tcp udp
setOption(QString(":rtsp-%1").arg(transport));
//設定快取時間 預設500毫秒
setOption(QString(":network-caching=%1").arg(caching));
//:rtsp-frame-buffer-size=1000000
//設定寬度高度,本地USB攝像頭需要單獨設定
if (suffix == "dshow") {
setOption(QString(":dshow-size=%1*%2").arg(videoWidth).arg(videoHeight));
} else {
setSize(videoWidth, videoHeight);
}
//設定儲存檔案
this->initSave();
//開啟播放
libvlc_media_player_play(vlcPlayer);
//qDebug() << TIMEMS << "init vlc finsh";
return true;
}