一、前言
錄像功能是視頻監控系統的常用功能,就是將打開的視頻流或者視頻文件重新保存成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來模擬單擊了錄像,停止錄像只需要再次執行一次即可,所以要存儲成多個視頻文件,只需要動態改變錄像文件存儲路徑這個變量即可。
二、功能特點
- 多線程實時播放視頻流和本地視頻。
- 支持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
五、核心代碼
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);
}