一、前言
採集電腦屏幕桌面並推流一般是用來做共享桌面、遠程協助、投屏之類的應用,最簡單入門的做法可能會採用開個定時器或者線程抓圖,將整個屏幕截圖下來,然後將圖片傳出去,這種方式很簡單但是性能要低不少,一般採用ffmpeg來做桌面推流的居多,畢竟如果不採用代碼直接ffmpeg一行命令即可(ffmpeg -f gdigrab -r 30 -i desktop -vcodec libx264 -preset:v ultrafast -tune:v zerolatency -f rtsp -g 5 -an rtsp://192.168.0.110:6907/stream), 最起碼這個還沒開始寫代碼直接就可以體驗起來這個感覺很好。很多開源項目,就因爲無法保證直接編譯就能跑起來,導致熄火,都跑不起來何來的看下去的興趣,尤其是初學者而言更是如此。
採集大致步驟:
- 查找格式 av_find_input_format,參數 gdigrab/x11grab/avfoundation
- 打開桌面 avformat_open_input,參數 desktop
- 查找視頻流 av_find_best_stream
- 查找解碼器 avcodec_find_decoder
- 打開解碼器 avcodec_open2
- 循環讀取 av_read_frame
- 解碼視頻 avcodec_send_packet/avcodec_receive_frame
- 關閉釋放 avcodec_free_context/avformat_close_input
推流大致步驟:
- 創建輸出 avformat_alloc_output_context2
- 創建視頻流 avformat_new_stream
- 打開輸出 avio_open,參數填推流完整地址
- 寫入開始符 avformat_write_header
- 寫入幀數據 av_interleaved_write_frame
- 關閉釋放 avio_close/avformat_free_context
二、效果圖
三、體驗地址
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 體驗地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_push。
四、相關代碼
void FFmpegThread::initInputFormat()
{
//本地攝像頭/桌面錄屏
if (videoType == VideoType_Camera) {
#if defined(Q_OS_WIN)
//ifmt = av_find_input_format("vfwcap");
ifmt = av_find_input_format("dshow");
#elif defined(Q_OS_LINUX)
//可以打開cheese程序查看本地攝像頭(如果是在虛擬機中需要設置usb選項3.1)
//ifmt = av_find_input_format("v4l2");
ifmt = av_find_input_format("video4linux2");
#elif defined(Q_OS_MAC)
ifmt = av_find_input_format("avfoundation");
#endif
} else if (videoType == VideoType_Desktop) {
#if defined(Q_OS_WIN)
ifmt = av_find_input_format("gdigrab");
#elif defined(Q_OS_LINUX)
ifmt = av_find_input_format("x11grab");
#elif defined(Q_OS_MAC)
ifmt = av_find_input_format("avfoundation");
#endif
}
}
bool FFmpegThread::initInput()
{
//實例化格式處理上下文
formatCtx = avformat_alloc_context();
//設置超時回調(有些不存在的地址或者網絡不好的情況下要卡很久)
formatCtx->interrupt_callback.callback = FFmpegHelper::avinterruptCallBackFun;
formatCtx->interrupt_callback.opaque = this;
//打開輸入(通過標誌位控制回調那邊做超時判斷)
//其他地方調用 formatCtx->url formatCtx->filename 可以拿到設置的地址(兩個變量值一樣)
tryOpen = true;
QByteArray urlData = VideoHelper::getRightUrl(videoType, videoUrl).toUtf8();
int result = avformat_open_input(&formatCtx, urlData.data(), ifmt, &options);
tryOpen = false;
if (result < 0) {
debug("打開出錯", "錯誤: " + FFmpegHelper::getError(result));
return false;
}
//根據自己項目需要開啓下面部分代碼加快視頻流打開速度
//開啓後由於值太小可能會出現部分視頻流獲取不到分辨率
if (decodeType == DecodeType_Fastest && videoType == VideoType_Rtsp) {
//接口內部讀取的最大數據量(從源文件中讀取的最大字節數)
//默認值5000000導致這裏卡很久最耗時(可以調小來加快打開速度)
formatCtx->probesize = 50000;
//從文件中讀取的最大時長(單位爲 AV_TIME_BASE units)
formatCtx->max_analyze_duration = 5 * AV_TIME_BASE;
//內部讀取的數據包不放入緩衝區
//formatCtx->flags |= AVFMT_FLAG_NOBUFFER;
//設置解碼錯誤驗證過濾花屏
//formatCtx->error_recognition |= AV_EF_EXPLODE;
}
//獲取流信息
result = avformat_find_stream_info(formatCtx, NULL);
if (result < 0) {
debug("找流失敗", "錯誤: " + FFmpegHelper::getError(result));
return false;
}
//解碼格式
formatName = formatCtx->iformat->name;
//某些格式比如視頻流不做音視頻同步(響應速度快)
if (formatName == "rtsp" || videoUrl.endsWith(".sdp")) {
useSync = false;
}
//設置了最快速度則不啓用音視頻同步
if (decodeType == DecodeType_Fastest) {
useSync = false;
}
//有些格式不支持硬解碼
if (formatName.contains("rm") || formatName.contains("avi") || formatName.contains("webm")) {
hardware = "none";
}
//本地攝像頭設備解碼出來的直接就是yuv顯示不需要硬解碼
if (videoType == VideoType_Camera || videoType == VideoType_Desktop) {
useSync = false;
hardware = "none";
}
//過低版本不支持硬解碼
#if (FFMPEG_VERSION_MAJOR < 3)
hardware = "none";
#endif
//獲取文件時長(這裏獲取到的是秒)
double length = (double)formatCtx->duration / AV_TIME_BASE;
duration = length * 1000;
this->checkVideoType();
//有時候網絡地址也可能是純音頻
if (videoType == VideoType_FileHttp) {
onlyAudio = VideoHelper::getOnlyAudio(videoUrl, formatName);
}
if (getIsFile()) {
//文件必須要音視頻同步
useSync = true;
//發送文件時長信號
emit receiveDuration(duration > 0 ? duration : 0);
}
QString msg = QString("格式: %1 時長: %2 秒 加速: %3").arg(formatName).arg(duration / 1000).arg(hardware);
debug("媒體信息", msg);
return true;
}
五、功能特點
5.1 文件推流
- 指定網卡和監聽端口,接收網絡請求推送音視頻等各種文件。
- 實時統計顯示每個文件對應的訪問數量、總訪問數量、不同IP地址訪問數量。
- 可指定多種模式,0-直接播放、1-下載播放。
- 實時打印顯示各種收發請求和應答數據。
- 每個文件對應MD5加密的唯一標識符,用於請求地址後綴區分訪問哪個文件。
- 支持各種瀏覽器(谷歌chromium/微軟edge/火狐firefox等)、各種播放器(vlc/mpv/ffplay/potplayer/mpchc等)打開請求。
- 播放過程中可以任意切換播放進度,支持倍速播放。
- 需要推流的文件名稱歷史記錄自動存儲和打開加載應用。
- 切換文件獲取訪問地址,自動拷貝地址到剪切板方便直接粘貼測試使用。
- 極低CPU佔用,128路1080P同時推流不到1%CPU佔用,異步發送數據機制。
- 純QTcpSocket通信,不依賴流媒體服務程序,核心源碼不到500行,註釋詳細,功能完整。
- 支持Qt4/Qt5/Qt6任意版本,支持任意系統(windows/linux/macos/android/嵌入式linux等)。
5.2 網絡推流
- 支持各種本地視頻文件和網絡視頻文件。
- 支持各種網絡視頻流,網絡攝像頭,協議包括rtsp、rtmp、http。
- 支持將本地攝像頭設備推流,可指定分辨率和幀率等。
- 支持將本地桌面推流,可指定屏幕區域和幀率等。
- 自動啓動流媒體服務程序,默認mediamtx(原rtsp-simple-server),可選用srs、EasyDarwin、LiveQing、ZLMediaKit等。
- 可實時切換預覽視頻文件。
- 推流的清晰度和質量可調。
- 可動態添加文件、目錄、地址。
- 視頻文件自動循環推流,如果視頻源是視頻流,在掉線後會自動重連。
- 網絡視頻流自動重連,重連成功自動繼續推流。
- 網絡視頻流實時性極高,延遲極低,延遲時間大概在100ms左右。
- 推流後除了用rtmp地址訪問以外,還支持直接hls/webrtc訪問,可以直接瀏覽器打開看實時畫面。
- 支持Qt4/Qt5/Qt6任意版本,支持任意系統(windows/linux/macos/android/嵌入式linux等)。