Qt/C++音視頻開發56-udp推流和拉流/組播和單播推流

一、前言

之前已經實現了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個端口。

二、效果圖


三、體驗地址

  1. 國內站點:https://gitee.com/feiyangqingyun
  2. 國際站點:https://github.com/feiyangqingyun
  3. 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 體驗地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_push。

四、功能特點

  1. 支持各種本地視頻文件和網絡視頻文件。
  2. 支持各種網絡視頻流,網絡攝像頭,協議包括rtsp、rtmp、http。
  3. 支持將本地攝像頭設備推流,可指定分辨率和幀率等。
  4. 支持將本地桌面推流,可指定屏幕區域和幀率等。
  5. 自動啓動流媒體服務程序,默認mediamtx(原rtsp-simple-server),可選用srs、EasyDarwin、LiveQing、ZLMediaKit等。
  6. 可實時切換預覽視頻文件,可切換視頻文件播放進度,切換到哪裏就推流到哪裏。
  7. 推流的清晰度和質量可調。
  8. 可動態添加文件、目錄、地址。
  9. 視頻文件自動循環推流,如果視頻源是視頻流,在掉線後會自動重連。
  10. 網絡視頻流自動重連,重連成功自動繼續推流。
  11. 網絡視頻流實時性極高,延遲極低,延遲時間大概在100ms左右。
  12. 極低CPU佔用,4路主碼流推流只需要佔用0.2%CPU。理論上常規普通PC機器推100路毫無壓力,主要性能瓶頸在網絡。
  13. 推流可選推流到rtsp/rtmp兩種,推流後的數據支持直接rtsp/rtmp/hls/webrtc四種方式訪問,可以直接瀏覽器打開看實時畫面。
  14. 可以推流到外網服務器,然後通過手機、電腦、平板等設備播放對應的視頻流。
  15. 每個推流都可以手動指定唯一標識符(方便拉流/用戶無需記憶複雜的地址),沒有指定則按照策略隨機生成hash值。
  16. 自動生成測試網頁直接打開播放,可以看到實時效果,自動按照數量對應宮格顯示。
  17. 推流過程中可以在表格中切換對應推流項,實時預覽正在推流的視頻,並可以切換視頻文件的播放進度。
  18. 音視頻同步推流,符合264/265/aac格式的自動原數據推流,不符合的自動轉碼再推流(會佔用一定CPU)。
  19. 轉碼策略支持三種,自動處理(符合要求的原數據/不符合的轉碼),僅限文件(文件類型的轉碼視頻),所有轉碼。
  20. 表格中實時顯示每一路推流的分辨率和音視頻數據狀態,灰色表示沒有輸入流,黑色表示沒有輸出流,綠色表示原數據推流,紅色表示轉碼後的數據推流。
  21. 自動重連視頻源,自動重連流媒體服務器,保證啓動後,推流地址和打開地址都實時重連,只要恢復後立即連上繼續採集和推流。
  22. 提供循環推流示例,一個視頻源同時推流到多個流媒體服務器,比如打開一個視頻同時推流到抖音/快手/B站等,可以作爲錄播推流,列表循環,非常方便實用。
  23. 根據不同的流媒體服務器類型,自動生成對應的rtsp/rtmp/hls/flv/ws-flv/webrtc地址,用戶可以直接複製該地址到播放器或者網頁中預覽查看。
  24. 編碼視頻格式可以選擇自動處理(源頭是264就264/源頭是265就265),轉H264(強制轉264),轉H265(強制轉265)。
  25. 支持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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章