Qt/C++音視頻開發49-推流到各種流媒體服務程序

一、前言

最近將推流程序完善了很多功能,尤其是增加了對多種流媒體服務程序的支持,目前支持mediamtx、LiveQing、EasyDarwin、nginx-rtmp、ZLMediaKit、srs、ABLMediaServer等,其中經過大量的對比測試,個人比較建議使用mediamtx和ZLMediaKit,因爲這兩者支持的格式衆多,不僅同時支持rtsp/rtmp推流,還支持各種格式rtsp/rtmp/hls/flv/ws-flv/webrtc等拉流,涵蓋面非常全,而且拉流的畫面非常流暢,在局域網沒有出現花屏的現象,對視頻文件、視頻流支持都非常友好。

爲了增強程序的拓展性,以便適應後期增加其他流媒體服務器程序,特意將流媒體服務程序的信息用配置文件存取來,可以自行增刪改,推流和拉流對應的端口都可以自行修改,這樣非常適用於一臺電腦多種流媒體服務,通過配置不同的端口來保證同時推流到多個流媒體服務程序,比如windows系統554端口很可能被系統的進程佔用,所以需要更改爲其他端口,在流媒體服務程序對應的配置文件更改後,還需要在推流程序對應的配置文件中修改,這樣後期如果增加了其他的流媒體服務程序,只需要在配置文件增加即可,程序會自動讀取並加載到下拉框。

二、效果圖








三、體驗地址

  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等)。

五、相關代碼

QList<QString> VideoPushUrl::listPushType = QList<QString>();
QList<QString> VideoPushUrl::listPullType = QList<QString>();
QList<int> VideoPushUrl::listPullPort = QList<int>();

void VideoPushUrl::initServerInfo()
{
    listPushType.clear();
    listPullType.clear();
    listPullPort.clear();

    listPushType << "mediamtx" << "mediamtx" << "mediamtx" << "mediamtx";
    listPullType << "rtsp" << "rtmp" << "hls" << "webrtc";
    listPullPort << 8554 << 1935 << 8888 << 8889;

    listPushType << "LiveQing" << "LiveQing" << "LiveQing" << "LiveQing" << "LiveQing";
    listPullType << "rtmp" << "hls" << "flv" << "ws-flv" << "webrtc";
    listPullPort << 10085 << 18000 << 18000 << 18000 << 18000;

    listPushType << "EasyDarwin";
    listPullType << "rtsp";
    listPullPort << 5541;

    listPushType << "nginx-rtmp";
    listPullType << "rtmp";
    listPullPort << 1935;

    listPushType << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit" << "ZLMediaKit";
    listPullType << "rtsp" << "rtmp" << "hls" << "flv" << "ws-flv" << "webrtc";
    listPullPort << 554 << 1935 << 80 << 80 << 80 << 80;

    listPushType << "srs" << "srs" << "srs" << "srs";
    listPullType << "rtmp" << "hls" << "flv" << "webrtc";
    listPullPort << 1935 << 8080 << 8080 << 8080;

    listPushType << "ABLMediaServer" << "ABLMediaServer" << "ABLMediaServer" << "ABLMediaServer" << "ABLMediaServer";
    listPullType << "rtsp" << "rtmp" << "hls" << "flv" << "ws-flv";
    listPullPort << 554 << 1935 << 9088 << 8088 << 6088;
}

void VideoPushUrl::initServerInfo(const QString &fileName)
{
    listPushType.clear();
    listPullType.clear();
    listPullPort.clear();

    QFile file(fileName);
    if (file.open(QFile::ReadOnly | QFile::Text)) {
        while (!file.atEnd()) {
            QString content = file.readLine();
            content.replace("\r", "");
            content.replace("\n", "");
            if (content.isEmpty()) {
                continue;
            }

            QStringList list = content.split(",");
            if (list.count() == 3) {
                listPushType << list.at(0);
                listPullType << list.at(1);
                listPullPort << list.at(2).toInt();
            }
        }
    }
}

QStringList VideoPushUrl::getPushType()
{
    QStringList types;
    foreach (QString type, listPushType) {
        if (!types.contains(type)) {
            types << type;
        }
    }
    return types;
}

QString VideoPushUrl::getPushPath(const QString &pushUrl)
{
    //("rtsp:", "", "127.0.0.1:5541") ("rtsp:", "", "127.0.0.1:5541", "live") ("rtsp:", "", "127.0.0.1:5541", "live/test")
    QString path = "/";
    QStringList list = pushUrl.split("/");
    int count = list.count();
    //從第三位開始後面所有的都是資源目錄
    for (int i = 3; i < count; ++i) {
        path = path + list.at(i) + "/";
    }

    //末尾的斜槓去掉
    return path.mid(0, path.length() - 1);
}

int VideoPushUrl::getPullPort(const QString &pushType, const QString &pullType)
{
    int port = 80;
    int count = listPushType.count();
    for (int i = 0; i < count; ++i) {
        if (listPushType.at(i) == pushType && listPullType.at(i) == pullType) {
            port = listPullPort.at(i);
            break;
        }
    }
    return port;
}

//各種拉流協議分析 https://www.cnblogs.com/xi-jie/p/14031604.html
QString VideoPushUrl::getPullUrl(const QString &pushUrl, const QString &pushType, const QString &pullType, const QString &ip, const QString &flag)
{
    //找到對應服務器類型和拉流類型的端口
    int port = getPullPort(pushType, pullType);
    //資源目錄(可以爲空)
    QString path = getPushPath(pushUrl);
    //去掉特殊字符比如?
    QString name = flag.split("?").first();

    //根據服務器類型獲取對應的地址
    QString url = QString("://%1:%2%3/%4").arg(ip).arg(port).arg(path).arg(name);
    if (pushType == "mediamtx") {
        //同時支持rtsp/rtmp推拉流(非常棒)
        if (pullType == "rtsp") {
            url = "rtsp" + url;
        } else if (pullType == "rtmp") {
            url = "rtmp" + url;
        } else if (pullType == "hls") {
            url = "http" + url;
        } else if (pullType == "webrtc") {
            url = "http" + url;
        }
    } else if (pushType == "LiveQing") {
        //只支持rtmp推流
        if (pullType == "rtmp") {
            url = QString("rtmp://%1:%2/hls/%3").arg(ip).arg(port).arg(name);
        } else if (pullType == "hls") {
            url = QString("http://%1:%2/hls/%3/%3_live.m3u8").arg(ip).arg(port).arg(name);
        } else if (pullType == "flv") {
            url = QString("http://%1:%2/flv/hls/%3.flv").arg(ip).arg(port).arg(name);
        } else if (pullType == "ws-flv") {
            url = QString("ws://%1:%2/ws-flv/hls/%3.flv").arg(ip).arg(port).arg(name);
        } else if (pullType == "webrtc") {
            url = QString("webrtc://%1:%2/rtc/hls/%3").arg(ip).arg(port).arg(name);
        }
    } else if (pushType == "EasyDarwin") {
        //只支持rtsp推流拉流
        if (pullType == "rtsp") {
            url = "rtsp" + url;
        }
    } else if (pushType == "nginx-rtmp") {
        //只支持rtmp推流拉流
        if (pullType == "rtmp") {
            url = "rtmp" + url;
        }
    } else if (pushType == "ZLMediaKit") {
        //同時支持rtsp/rtmp推拉流(名氣最大/用戶最多)
        if (pullType == "rtsp") {
            url = "rtsp" + url;
        } else if (pullType == "rtmp") {
            url = "rtmp" + url;
        } else if (pullType == "hls") {
            url = "http" + url + "/hls.m3u8";
        } else if (pullType == "flv") {
            url = "http" + url + ".live.flv";
        } else if (pullType == "ws-flv") {
            url = "ws" + url + ".live.flv";
        } else if (pullType == "webrtc") {

        }
    } else if (pushType == "srs") {
        //不支持rtsp推流拉流(以前支持/後面都移除了)
        if (pullType == "rtmp") {
            url = "rtmp" + url;
        } else if (pullType == "hls") {
            url = "http" + url + ".m3u8";
        } else if (pullType == "flv") {
            url = "http" + url + ".flv";
        } else if (pullType == "webrtc") {
            url = "webrtc" + url;
        }
    } else if (pushType == "ABLMediaServer") {
        //支持rtsp/rtmp推流拉流(目前還不穩定/兼容性不夠好)
        if (pullType == "rtsp") {
            url = "rtsp" + url;
        } else if (pullType == "rtmp") {
            url = "rtmp" + url;
        } else if (pullType == "hls") {
            url = "http" + url + ".m3u8";
        } else if (pullType == "flv") {
            url = "http" + url + ".flv";
        } else if (pullType == "ws-flv") {
            url = "ws" + url + ".flv";
        }
    } else if (pushType == "Monibuca") {
        //支持rtsp/rtmp推流拉流(拉流格式衆多/各種插件/性能很強勁/具體有待驗證)
        if (pullType == "rtsp") {
            url = "rtsp" + url;
        } else if (pullType == "rtmp") {
            url = "rtmp" + url;
        } else if (pullType == "hls") {
            url = QString("http://%1:%2/hls%3/%4.m3u8").arg(ip).arg(port).arg(path).arg(name);
        } else if (pullType == "flv") {
            url = QString("http://%1:%2/hdl%3/%4.flv").arg(ip).arg(port).arg(path).arg(name);
        } else if (pullType == "ws-flv") {
            url = QString("ws://%1:%2/jessica%3/%4.flv").arg(ip).arg(port).arg(path).arg(name);
        } else if (pullType == "webrtc") {
            url = QString("webrtc://%1:%2/webrtc/play%3/%4").arg(ip).arg(port).arg(path).arg(name);
        }
    }

    return url;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章