Qt音視頻開發3-vlc錄像存儲


一、前言

錄像功能是視頻監控系統的常用功能,就是將打開的視頻流或者視頻文件重新保存成MP4文件,當然也可以保存成其他格式,一般默認用MP4比較好,比較標准一些,MP4格式的兼容性最好,基本上沒有說那台電腦不能播放MP4文件,所以就保存成這種最常用的視頻文件格式就好了。

vlc的錄像功能是內置封裝好的,在打開文件的前面設置相應的命令參數即可,如果只是要求整個過程保存成一個視頻文件,這個很好辦,網上方法一大堆,只要調用libvlc_media_add_option函數設置:sout=#duplicate{dst=file{dst=d:/1.mp4},dst=display}即可,最開始用的是:sout=#stream_out_duplicate{dst=display,dst=std{access=file,mux=%1,dst=%2}}參數,后面換成vlc3以后發現不支持了,查閱相關資料后發現要用duplicate,可能vlc3開始不支持stream_out_duplicate只支持duplicate吧。

保存成單個視頻文件,這個沒有任何問題和難度,但是視頻監控領域中經常需要的是定時保存成單個文件,比如30分鍾一個視頻文件,這樣方便檢索,而且也不會看起來一個視頻文件很大很大,畢竟視頻監控是7*24小時運行的,那這個文件不知道多大,vlc要動態保存多個文件,這就需要模擬執行錄像、停止錄像的功能來實現,主要的流程就是通過var_CreateGetString函數拿到錄像文件存儲路徑變量,然后var_SetString設置該變量,最后調用var_ToggleBool來模擬單擊了錄像,停止錄像只需要再次執行一次即可,所以要存儲成多個視頻文件,只需要動態改變錄像文件存儲路徑這個變量即可。

二、功能特點

  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

五、核心代碼

void VlcThread::initSave()
{
    if (!saveFile) {
        return;
    }

    if (saveInterval == 0) {
        saveOne(fileName);
    }
}

void VlcThread::saveOne(const QString &fileName)
{
    QString temp = url.toUpper();
    QString mux = "ts";
    if (temp.endsWith("MP4") || temp.endsWith("MOV")) {
        mux = "mp4";
    }

    //舊格式 :sout=#stream_out_duplicate{dst=display,dst=std{access=file,mux=%1,dst=%2}}
    //新格式 :sout=#duplicate{dst=file{dst=d:/1.mp4},dst=display}
    //vlc3開始不支持stream_out_duplicate只支持duplicate
    QString option = QString(":sout=#duplicate{dst=display,dst=std{access=file,mux=%1,dst=%2}}").arg(mux).arg(fileName);
    setOption(option);
}

void VlcThread::saveVideo()
{
    //只有啟用了保存文件才保存,這里不要加拓展名,會自動生成
    //文件會在到了間隔后生成
    QMutexLocker locker(&mutex);
    if (saveFile) {
        //重新設置文件名稱
        QString dirName = QString("%1/%2").arg(savePath).arg(QDATE);
        newDir(dirName);
        fileName = QString("%1/%2_%3").arg(dirName).arg(fileFlag).arg(STRDATETIME);
        saveVideo(fileName);
    }
}

void VlcThread::saveVideo(const QString &fileName)
{
    //除了第一次不要執行外,其他都執行,因為第一次需要先啟動存儲
    if (!first) {
        stopSave(vlcPlayer);
    }

    first = false;
    startSave(vlcPlayer, fileName);
}

//錄像用函數
static input_thread_t *libvlc_get_input_thread(libvlc_media_player_t *vlcPlayer)
{
    input_thread_t *input = NULL;
    if (vlcPlayer != NULL) {
        input = vlcPlayer->input.p_thread;
        if (input) {
            vlc_object_hold(input);
        }
    }

    return input;
}

//開始錄像
static void startSave(libvlc_media_player_t *vlcPlayer, const QString &fileName = "")
{
    input_thread_t *input = libvlc_get_input_thread(vlcPlayer);
    if (input == NULL) {
        return;
    }

#ifdef vlc3
    // 傳過來的是帶文件名的路徑,需要去掉后面的文件名
    QStringList list = fileName.split("/");
    QStringList paths;
    int count = list.count() - 1;
    for (int i = 0; i < count; i++) {
        paths << list.at(i);
    }

    QString path = paths.join("/");
    QString name = list.last();

    var_CreateGetString(input, "input-record-path");
    var_SetString(input, "input-record-path", path.toUtf8().data());
    //var_CreateGetString(input, "sout-record-dst-prefix");
    //var_SetString(input, "sout-record-dst-prefix", name.toUtf8().data());
    //var_CreateGetString(input, "record-video-name");
    //var_SetString(input, "record-video-name", name.toUtf8().data());
#else
    var_CreateGetString(input, "input-record-path");
    var_SetString(input, "input-record-path", fileName.toUtf8().constData());
    //var_SetString(input, "sout-record-dst-prefix", fileName.toUtf8().constData());
#endif

    var_ToggleBool(input, "record");
    vlc_object_release(input);
}

//停止錄像
static void stopSave(libvlc_media_player_t *vlcPlayer)
{
    input_thread_t *input = libvlc_get_input_thread(vlcPlayer);
    if (input == NULL) {
        return;
    }

    var_ToggleBool(input, "record");
    vlc_object_release(input);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM