FFmpeg開發筆記(二十七)解決APP無法訪問ZLMediaKit的直播連結問題

aqi00發表於2024-06-08
上一篇文章介紹瞭如何透過ZLMediaKit實現影片推拉流,並使用VLC播放器驗證影片直播地址。即使不用VLC播放器,直接在Qt工程的C++程式碼中呼叫FFmpeg的API,也能訪問ZLMediaKit的直播地址,並正常渲染影片畫面。關於如何在Qt工程中引入FFmpeg,可參考《FFmpeg開發實戰:從零基礎到短影片上線》一書的“第11章 FFmpeg的桌面開發”。

《FFmpeg開發實戰:從零基礎到短影片上線》一書的“第12章 FFmpeg的移動開發”介紹瞭如何在手機APP上整合FFmpeg。根據該書的操作步驟,在APP工程的JNI程式碼中呼叫FFmpeg的API,也能正常播放ZLMediaKit的直播畫面。
但是如果手機APP不走FFmpeg,透過其他途徑訪問直播地址之時,卻發現無法播放ZLMediaKit的HLS直播地址http://124.***.***.***:8080/live/test/hls.m3u8。無論採用谷歌官方的ExoPlayer,還是採用微信小程式的video標籤,都播放不了ZLMediaKit的HLS影片。檢視APP的報錯日誌,發現ExoPlayer扔出以下的錯誤資訊:

E/ExoPlayerImplInternal: Playback error
      com.google.android.exoplayer2.ExoPlaybackException: Source error
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:641)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:613)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loopOnce(Looper.java:206)
        at android.os.Looper.loop(Looper.java:296)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: com.google.android.exoplayer2.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 401
        at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:396)
        at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:258)
        at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:84)
        at com.google.android.exoplayer2.source.hls.HlsMediaChunk.prepareExtraction(HlsMediaChunk.java:495)
        at com.google.android.exoplayer2.source.hls.HlsMediaChunk.feedDataToExtractor(HlsMediaChunk.java:468)
        at com.google.android.exoplayer2.source.hls.HlsMediaChunk.loadMedia(HlsMediaChunk.java:437)
        at com.google.android.exoplayer2.source.hls.HlsMediaChunk.load(HlsMediaChunk.java:394)
        at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:412)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:930)

原來是流媒體服務端丟出了401錯誤,意思是沒有許可權拒絕訪問。這個問題著實難搞,尋尋覓覓、反反覆覆,總也找不到為啥會沒有許可權,分明使用VLC播放器是可以正常播放的呀。
於是檢查ZLMediaKit的原始碼,發現ZLMediaKit內部對於HTTP地址增加了Cookie校驗,原來這套校驗規則適配了FFmpeg,卻尚未適配ExoPlayer,也未適配小程式。問題程式碼位於ZLMediaKit原始碼的src/Http/HttpFileManager.cpp裡的accessFile函式,程式碼片段如下:

auto strongSession = weakSession.lock();
if (!strongSession) {
    // http客戶端已經斷開,不需要回復
    return;
}
if (!err_msg.empty()) {
    //檔案鑑權失敗
    StrCaseMap headerOut;
    if (cookie) {
        headerOut["Set-Cookie"] = cookie->getCookie(cookie->getAttach<HttpCookieAttachment>()._path);
    }
    cb(401, "text/html", headerOut, std::make_shared<HttpStringBody>(err_msg));
    return;
}

原來accessFile函式內部對於HTTP連結的Cookie校驗失敗時會固定返回401錯誤。那麼修改HttpFileManager.cpp裡的accessFile函式,把這裡的401鑑權程式碼註釋掉,並將修改後的程式碼檔案上傳到Linux伺服器。
然後回到build目錄執行make和make install命令重新編譯安裝ZLMediaKit,也就是依次執行下面命令。

kill -s 9 `ps -aux | grep MediaServer | awk '{print $2}'`
cd /usr/local/src/ZLMediaKit/build
make
make install

編譯安裝完畢,執行下面命令,重新啟動MediaServer服務。

cd /usr/local/src/ZLMediaKit/release/linux/Debug
./MediaServer -d &

執行以下命令,將本地影片推流給ZLMediaKit。

ffmpeg -re -stream_loop -1 -i "/usr/local/src/test/2018s.mp4" -vcodec h264 -f rtsp rtsp://127.0.0.1/live/test

然後在APP程式碼中使用ExoPlayer播放HLS地址http://124.***.***.***:8080/live/test/hls.m3u8,發現可以正常播放HLS影片了。接著使用真機除錯微信小程式,發現透過video標籤也能正常播放HLS影片了。更多詳細的FFmpeg開發知識參見《FFmpeg開發實戰:從零基礎到短影片上線》一書。

相關文章