一、前言
之前已經實現了rtsp/rtmp推流,rtsp/rtmp/hls/flv/ws-flv/webrtc等拉流,這種一般都需要依賴一個獨立的流媒體服務程序,有沒有一種更便捷的方式不需要這種依賴,然後又能實現推拉流呢,當然有的那就是udpp推流,其中udp推流還可以是組播或者單播推流,組播一般會選擇224.0.0.1這個地址,單播的話就是指定到唯一的IP地址比如192.168.0.8,組播的話相當於同一個交換機內所有局域網設備都會收到該數據,優點是推一次所有地方都可以拉流取到數據,缺點是極有可能產生組播風暴,因爲每個局域網的設備都可能收到大量的視頻數據包,這些完全是多餘的。
在之前的ffmpeg推流代碼基礎上,只需要增加一行代碼即可實現udp推流,那就是在調用avformat_alloc_output_context2填入對應的格式需要填入mpegts,其實也可以是h264,但是個人推薦用mpegts,用ffmpeg命令行推流是 ffmpeg -re -stream_loop -1 -i f:/mp4/push/1.mp4 -c copy -f mpegts udp://127.0.0.1:1234 ,可以看到要指定對應的端口號,一個推流佔用一個端口號,如果要推10個視頻文件就要佔用10個端口。
二、效果圖
三、體驗地址
- 國內站點: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。
四、功能特點
- 支持各種本地視頻文件和網絡視頻文件。
- 支持各種網絡視頻流,網絡攝像頭,協議包括rtsp、rtmp、http。
- 支持將本地攝像頭設備推流,可指定分辨率和幀率等。
- 支持將本地桌面推流,可指定屏幕區域和幀率等。
- 自動啓動流媒體服務程序,默認mediamtx(原rtsp-simple-server),可選用srs、EasyDarwin、LiveQing、ZLMediaKit等。
- 可實時切換預覽視頻文件,可切換視頻文件播放進度,切換到哪裏就推流到哪裏。
- 推流的清晰度和質量可調。
- 可動態添加文件、目錄、地址。
- 視頻文件自動循環推流,如果視頻源是視頻流,在掉線後會自動重連。
- 網絡視頻流自動重連,重連成功自動繼續推流。
- 網絡視頻流實時性極高,延遲極低,延遲時間大概在100ms左右。
- 極低CPU佔用,4路主碼流推流只需要佔用0.2%CPU。理論上常規普通PC機器推100路毫無壓力,主要性能瓶頸在網絡。
- 推流可選推流到rtsp/rtmp兩種,推流後的數據支持直接rtsp/rtmp/hls/webrtc四種方式訪問,可以直接瀏覽器打開看實時畫面。
- 可以推流到外網服務器,然後通過手機、電腦、平板等設備播放對應的視頻流。
- 每個推流都可以手動指定唯一標識符(方便拉流/用戶無需記憶複雜的地址),沒有指定則按照策略隨機生成hash值。
- 自動生成測試網頁直接打開播放,可以看到實時效果,自動按照數量對應宮格顯示。
- 推流過程中可以在表格中切換對應推流項,實時預覽正在推流的視頻,並可以切換視頻文件的播放進度。
- 音視頻同步推流,符合264/265/aac格式的自動原數據推流,不符合的自動轉碼再推流(會佔用一定CPU)。
- 轉碼策略支持三種,自動處理(符合要求的原數據/不符合的轉碼),僅限文件(文件類型的轉碼視頻),所有轉碼。
- 表格中實時顯示每一路推流的分辨率和音視頻數據狀態,灰色表示沒有輸入流,黑色表示沒有輸出流,綠色表示原數據推流,紅色表示轉碼後的數據推流。
- 自動重連視頻源,自動重連流媒體服務器,保證啓動後,推流地址和打開地址都實時重連,只要恢復後立即連上繼續採集和推流。
- 提供循環推流示例,一個視頻源同時推流到多個流媒體服務器,比如打開一個視頻同時推流到抖音/快手/B站等,可以作爲錄播推流,列表循環,非常方便實用。
- 根據不同的流媒體服務器類型,自動生成對應的rtsp/rtmp/hls/flv/ws-flv/webrtc地址,用戶可以直接複製該地址到播放器或者網頁中預覽查看。
- 編碼視頻格式可以選擇自動處理(源頭是264就264/源頭是265就265),轉H264(強制轉264),轉H265(強制轉265)。
- 支持Qt4/Qt5/Qt6任意版本,支持任意系統(windows/linux/macos/android/嵌入式linux等)。
五、相關代碼
void FFmpegPushClient::initOsd()
{
QList<OsdInfo> osds;
OsdInfo osd;
//日期時間
osd.name = "datetime";
osd.color = "#FFFFFF";
osd.fontSize = 30;
osd.format = OsdFormat_DateTime;
osd.position = OsdPosition_LeftTop;
osds << osd;
//圖片
osd.name = "osd.png";
osd.image = QImage(":/image/bg_novideo.png");
osd.format = OsdFormat_Image;
osd.position = OsdPosition_LeftBottom;
//設置唯一名稱標識並將圖片保存(濾鏡基本上都是支持指定圖片文件)
osd.name = "osd.png";
QString file = QString("./%1").arg(osd.name);
osd.image.save(file, "png");
osds << osd;
ffmpegThread->setOsdInfo(osds);
}
void FFmpegPushClient::start()
{
if (ffmpegThread || videoUrl.isEmpty() || pushUrl.isEmpty()) {
return;
}
//實例化視頻採集線程
ffmpegThread = new FFmpegThread;
//關聯播放開始信號用來啓動推流
connect(ffmpegThread, SIGNAL(receivePlayStart(int)), this, SLOT(receivePlayStart(int)));
//關聯錄製信號變化用來判斷是否推流成功
connect(ffmpegThread, SIGNAL(recorderStateChanged(RecorderState, QString)), this, SLOT(recorderStateChanged(RecorderState, QString)));
//設置播放地址
ffmpegThread->setVideoUrl(videoUrl);
//設置解碼內核
ffmpegThread->setVideoCore(VideoCore_FFmpeg);
//設置視頻模式
ffmpegThread->setVideoMode(VideoMode_Opengl);
//設置硬解碼(和推流無關/只是爲了加速顯示/推流只和硬編碼有關)
//ffmpegThread->setHardware("dxva2");
//設置解碼策略(推流的地址再拉流建議開啓最快速度)
//ffmpegThread->setDecodeType(DecodeType_Fastest);
//設置讀取超時時間超時後會自動重連
ffmpegThread->setReadTimeout(5 * 1000);
//設置連接超時時間(0表示一直連)
ffmpegThread->setConnectTimeout(0);
//設置重複播放相當於循環推流
ffmpegThread->setPlayRepeat(true);
//設置默認不播放音頻(界面上切換到哪一路就開啓)
ffmpegThread->setPlayAudio(false);
//設置默認不預覽視頻(界面上切換到哪一路就開啓)
ffmpegThread->setPushPreview(false);
//設置保存視頻類將數據包信號發出來用於保存文件
FFmpegSave *saveFile = ffmpegThread->getSaveFile();
saveFile->setSendPacket(!fileName.isEmpty(), false);
connect(saveFile, SIGNAL(receivePacket(AVPacket *)), this, SLOT(receivePacket(AVPacket *)));
connect(saveFile, SIGNAL(receiveSaveStart()), this, SLOT(receiveSaveStart()));
connect(saveFile, SIGNAL(receiveSaveFinsh()), this, SLOT(receiveSaveFinsh()));
connect(saveFile, SIGNAL(receiveSaveError(int)), this, SLOT(receiveSaveError(int)));
//如果是本地設備或者桌面錄屏要取出其他參數
VideoHelper::initVideoPara(ffmpegThread, videoUrl, encodeVideoScale);
//設置編碼策略/視頻編碼格式/視頻壓縮比率/視頻縮放比例
ffmpegThread->setEncodeType((EncodeType)encodeType);
ffmpegThread->setVideoFormat((VideoFormat)videoFormat);
ffmpegThread->setEncodeVideoRatio(encodeVideoRatio);
ffmpegThread->setEncodeVideoScale(encodeVideoScale);
//啓動播放
ffmpegThread->play();
}
void FFmpegPushClient::stop()
{
//停止推流和採集並徹底釋放對象
if (ffmpegThread) {
ffmpegThread->recordStop();
ffmpegThread->stop();
ffmpegThread->deleteLater();
ffmpegThread = NULL;
}
//停止錄製
if (ffmpegSave) {
ffmpegSave->stop();
ffmpegSave->deleteLater();
ffmpegSave = NULL;
}
}