mediakit 原始碼 輕微微 學習總結

KeBoom發表於2023-11-09

mediakit 原始碼 輕微微 學習總結

概要

專案地址:https://github.com/ZLMediaKit/ZLMediaKit

此專案我們把他做為一個流媒體伺服器,我們會有srt和rtsp的流推到mediakit,然後網頁使用webrtc播放。

還有使用一些web hook來支援按需推流、不中斷錄影等,更多功能不再贅述。

雖然我不是專業的流媒體開發人員,但是我們稍微學習一下這個專案,以下是我對這個專案的淺顯認識。

環境準備

windows11

wsl ubuntu22.04

vscode

專案結構

image-20231108142515362

第三方庫 3rdpart

他這個第三方庫是依賴很深的,tcp/udp伺服器,各種協議(rtmp,rtsp,hls等)對接都是他們做的,noticeCenter等。

對於這些伺服器,如果你要使用http請求,那麼你需要建立一個http伺服器。如果你要使用rtmp,那麼你就建立一個rtmp伺服器。只是監聽的埠不同。

對於http請求我們都很熟悉,是一請求一響應回答即可。

對於流媒體伺服器,比如說我們要實現一個最簡的rtmp伺服器,首先他仍然是一個tcp伺服器,我們監聽1935埠。假如我們使用ffmpeg將一個mp4檔案推rtmp流到我們rtmp伺服器,rtmp伺服器收到了資料包,然後我們是有庫去解析的,比如ffmpeg庫libavcodec,libavformat等等,這些庫能講資料解析成一個格式比如說叫AVFormat這樣一個類。接著無論是我們向將他轉成rtsp,hls或者其他的什麼格式的協議,都是有相應的庫去掉用的。比如我們想將他轉成rtsp協議推出去,rtspFormatData=rtspLibFunction(AVFormatData, url ) 基本上就是這樣一個函式. 這時有rtsp拉流請求,那麼rtsp伺服器監聽到後,看看有沒有這個url的資料檔案,有就給拉流的播放器。

以上是我的猜想。哈哈,下面是GPT的一個回答:

I ask: 
Now i have a rtmp server, server receive a rtmp stream, then convert to rtsp stream to push a url. How to do it ? Show me c++ code.
GPT answer: 
To design a streaming media server that receives an RTMP stream and converts it to an RTSP stream to push a URL, you can follow the steps below:

1. **Server Architecture**:
   - Create a central server application that listens for incoming RTMP connections.
   - Use a multi-threaded or asynchronous model to handle multiple RTMP connections simultaneously.
   - Consider using a library like libevent or Boost Asio for network operations.

2. **RTMP Server**:
   - Implement an RTMP server class, such as `RtmpServer`, to handle incoming RTMP connections.
   - Use a library like librtmp or RTMPDump to parse and process RTMP packets.
   - Handle RTMP handshake, metadata, and audio/video streams.

3. **RTSP Server**:
   - Implement an RTSP server class, such as `RtspServer`, to handle outgoing RTSP connections.
   - Use a library like live555 or GStreamer to generate RTSP streams.
   - Convert the incoming RTMP stream to an RTSP stream using appropriate codecs and formats.

Here's an example of how the code for the above design may look like in C++:

```cpp
#include <iostream>
#include <thread>
#include <boost/asio.hpp>

class RtmpServer {
public:
    void start() {
        // Start listening for RTMP connections
        // Handle incoming RTMP packets
        // Parse and process handshake, metadata, and audio/video streams
        // Convert RTMP stream to RTSP stream
    }
};

class RtspServer {
public:
    void start() {
        // Start listening for RTSP connections
        // Generate RTSP streams using appropriate codecs and formats
        // Push the RTSP stream to a URL
    }
};

int main() {
    // Create and start the RTMP server
    RtmpServer rtmpServer;
    std::thread rtmpThread([&]() {
        rtmpServer.start();
    });

    // Create and start the RTSP server
    RtspServer rtspServer;
    std::thread rtspThread([&]() {
        rtspServer.start();
    });

    // Wait for the servers to finish
    rtmpThread.join();
    rtspThread.join();

    return 0;
}
```

Please note that the above code is just a basic outline and you would need to implement the specific functionality for RTMP and RTSP handling, as well as handle threading, network operations, error handling, and other details as per your requirements.

It's important to consider that developing a full-fledged streaming media server involves much more complexity and considerations than can be covered in a simple code example. This code serves as a starting point and you may need to use additional libraries, implement error handling, logging, and other features to create a robust and reliable streaming media server.

你還可以接著問:
How to Implement an RTMP server?
And i have a media player , I want get rtmp stream from the rtmp server , how to implementing the RTMP server?
但是這裡就不貼回答了。大家可以自己去GPT~


上面說的是tcp伺服器和media-server,接著還有一個重要的是ZLToolKit中的noticeCenter,這個是訊息中心,比如有新的流推到mediakit或者推流停止、錄影開啟、錄影關閉等等,都會傳送通知到noticeCenter,然後這個noticeCenter再將訊息傳送的監聽此訊息的listener。

比如說有新的流推到mediakit,那麼會向noticeCenter傳送流注冊訊息,那麼有個web hook函式就是監聽此訊息的listener

api 資料夾

C API,文件說是給嵌入式裝置用的

docker

docker映象構建,構建之後的映象大小6百多MB,挺誇張的。

src

這裡面很多涉及音影片協議解析,看不太懂,我只debug了一下,大概說說我看到的。

我的切入點是這個流媒體伺服器是可以進行協議轉換的,我想看看他是怎麼轉換的,對於一條流要轉換成哪些協議,可以透過他的配置檔案進行配置,那麼沿著這個配置檔案的相關欄位,比如enable_rtmp, enable_rtsp等,找程式碼中用到的,最終只在MultiMediaSourceMuxer這個類的構造方法中發現if判斷。那我在此打斷點。

    if (option.enable_rtmp) {
        _rtmp = std::make_shared<RtmpMediaSourceMuxer>(_tuple, option, std::make_shared<TitleMeta>(dur_sec));
    }
    if (option.enable_rtsp) {
        _rtsp = std::make_shared<RtspMediaSourceMuxer>(_tuple, option, std::make_shared<TitleSdp>(dur_sec));
    }
    if (option.enable_hls) {
        _hls = dynamic_pointer_cast<HlsRecorder>(Recorder::createRecorder(Recorder::type_hls, _tuple, option));
    }
    if (option.enable_hls_fmp4) {
        _hls_fmp4 = dynamic_pointer_cast<HlsFMP4Recorder>(Recorder::createRecorder(Recorder::type_hls_fmp4, _tuple, option));
    }
    if (option.enable_mp4) {
        _mp4 = Recorder::createRecorder(Recorder::type_mp4, _tuple, option);
    }
    if (option.enable_ts) {
        _ts = dynamic_pointer_cast<TSMediaSourceMuxer>(Recorder::createRecorder(Recorder::type_ts, _tuple, option));
    }
    if (option.enable_fmp4) {
        _fmp4 = dynamic_pointer_cast<FMP4MediaSourceMuxer>(Recorder::createRecorder(Recorder::type_fmp4, _tuple, option));
    }

我用ffmpeg推送一條rtmp流到mediakit,然後程式碼執行到斷點處。然後我們從頭看呼叫棧,首先rtmp server監聽到請求,然後一路執行到Rtmpsession,他接受資料並快取快取到一個容器中。

第二個,他傳送一條訊息到noticeCenter,然後這個noticeCenter將訊息通知到listener。

第三個,就是我們打斷點的地方,這裡有個option,就是根據配置檔案來判斷是否建立相應協議的Muxer。

server

這裡是入口程式,檢視main檔案

根據程式啟動日誌,找到對應程式碼:

        try {
            auto secret = mINI::Instance()[API::kSecret];
            // if (secret == "035c73f7-bb6b-4889-a715-d9eb2d1925cc" || secret.empty()) {
            //     // 使用預設secret被禁止啟動
            //     throw std::invalid_argument("please modify the configuration named " + API::kSecret + " in " + g_ini_file);
            // }
            //rtsp伺服器,埠預設554
            if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort); }
            //rtsps伺服器,埠預設322
            if (rtspsPort) { rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort); }

            //rtmp伺服器,埠預設1935
            if (rtmpPort) { rtmpSrv->start<RtmpSession>(rtmpPort); }
            //rtmps伺服器,埠預設19350
            if (rtmpsPort) { rtmpsSrv->start<RtmpSessionWithSSL>(rtmpsPort); }

            //http伺服器,埠預設80
            if (httpPort) { httpSrv->start<HttpSession>(httpPort); }
            //https伺服器,埠預設443
            if (httpsPort) { httpsSrv->start<HttpsSession>(httpsPort); }

            //telnet遠端除錯伺服器
            if (shellPort) { shellSrv->start<ShellSession>(shellPort); }

#if defined(ENABLE_RTPPROXY)
            //建立rtp伺服器
            if (rtpPort) { rtpServer->start(rtpPort); }
#endif//defined(ENABLE_RTPPROXY)

#if defined(ENABLE_WEBRTC)
            //webrtc udp伺服器
            if (rtcPort) { rtcSrv_udp->start<WebRtcSession>(rtcPort);}

            if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort);}
             
#endif//defined(ENABLE_WEBRTC)

#if defined(ENABLE_SRT)
        // srt udp伺服器
        if(srtPort) { srtSrv->start<SRT::SrtSession>(srtPort); }
#endif//defined(ENABLE_SRT)

        } catch (std::exception &ex) {
            ErrorL << "Start server failed: " << ex.what();
            sleep(1);
#if !defined(_WIN32)
            if (pid != getpid() && kill_parent_if_failed) {
                //殺掉守護程式
                kill(pid, SIGINT);
            }
#endif
            return -1;
        }

        installWebApi();
        InfoL << "已啟動http api 介面";
        installWebHook();
        InfoL << "已啟動http hook 介面";

可以看到首先是根據埠建立各個伺服器。

接著installWebApi(); 這個就是註冊http api的地方,是一個map,然後key是url,value是一個函式,這裡就是這麼做的。如果向看一個http api的實現,就可以在webApi.cpp 檔案中找對應url,然後找對應的函式。

installWebHook(); 是新增listener到noticeCenter

CMakeLists.txt

他在這裡定義了一些宏,其中有一個Enable MP4這個宏定義,我咋都找不到怎麼實現的。希望c++的老師指點下。

option(ENABLE_MP4 "Enable MP4" ON)

ok,如果有音影片專業的c++,寫一篇更深入的mediakit的分析,帶我學習學習~~~