1. 程式人生 > 實用技巧 >Qt音視訊開發1-vlc解碼播放

Qt音視訊開發1-vlc解碼播放

一、前言

最開始接觸視訊監控這塊的時候,用的就是vlc作為解碼的核心,主要是因為vlc使用簡單方便,直接傳入一個控制代碼即可,簡單幾行程式碼就可以實現一個視訊流播放,很適合初學者使用,也推薦初學者用qt+vlc來做播放器,提供的介面還是非常友好的,而且門類特別多,想要獲取媒體檔案的各種資訊比如寬高,設定寬高比等,直接呼叫介面函式傳入引數就能設定。

所有用vlc做視訊監控解碼的人都會遇到一個問題,那就是滑鼠事件被接管攔截了,不能識別滑鼠事件,比如雙擊最大化等,這就很憋屈了,明明很好用的一個東西,怎麼突然之間滑鼠事件也識別不到了呢,網上一搜一大把,主要有三個解決辦法。

  1. 修改vlc原始碼,重新編譯,替換動態庫檔案。
  2. 全域性滑鼠鉤子攔截滑鼠訊息進行處理。
  3. 設定控制代碼以後直接將控制元件/接受視訊渲染的控制元件禁用掉。

最終採用方法3,實現起來簡單快速,修改vlc原始碼的編譯工作量太大了,畢竟vlc依賴一大堆的外掛,用vlc的人一般都是初學者半吊子,哪裡有能力去編譯一遍vlc哦。

二、功能特點

  1. 多執行緒實時播放視訊流和本地視訊。
  2. 支援windows+linux+mac,支援vlc2和vlc3。
  3. 多執行緒顯示影象,不卡主介面。
  4. 自動重連網路攝像頭。
  5. 可設定邊框大小即偏移量和邊框顏色。
  6. 可設定是否繪製OSD標籤即標籤文字或圖片和標籤位置。
  7. 可設定兩種OSD位置和風格。
  8. 可設定是否儲存到檔案以及檔名。
  9. 可直接拖曳檔案到vlcwidget控制元件播放。
  10. 支援h265視訊流+rtmp等常見視訊流。
  11. 可暫停播放和繼續播放。
  12. 支援回撥模式和控制代碼兩種模式。
  13. 支援執行緒讀取進度等資訊和事件回撥兩種處理模式。
  14. 自動將當前播放位置和音量大小是否靜音以訊號發出去。
  15. 提供介面設定播放位置和音量及設定靜音。
  16. 支援儲存單個視訊檔案和定時儲存視訊檔案。
  17. 自定義頂部懸浮條,傳送單擊訊號通知,可設定是否啟用。

三、效果圖

四、相關站點

  1. 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
  3. 個人主頁:https://blog.csdn.net/feiyangqingyun
  4. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
  5. 體驗地址: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;
}