From eeb2693f22488a9764599fdf86b3cfbba41c6188 Mon Sep 17 00:00:00 2001 From: ickeep Date: Thu, 4 Jun 2026 16:03:06 +0800 Subject: [PATCH] Fix RTMP proxy audio forwarding (#4745) ## Summary This fixes RTMP playback audio corruption and VLC playback compatibility when proxying some upstream RTMP streams through `addStreamProxy`. In the reproduced case, HTTP-FLV playback was normal, while RTMP playback had missing audio or occasional sharp noise in VLC. ## Root cause - RTMP aggregate message parsing copied the 4-byte `PreviousTagSize` field into the sub-message payload. This shifts the payload boundary and can corrupt subsequent audio packets. - `RtmpSession::onSendMedia` forwarded proxied audio/video packets with the packet's original chunk stream id. This can make audio packets use an unsuitable chunk id for RTMP playback. - The RTMP play response emitted non-essential `MSG_DATA` notifications (`|RtmpSampleAccess` and `NetStream.Data.Start`). FFmpeg tolerates these, but VLC may expose them as an extra `Data: none` stream and handle RTMP playback poorly. ## Changes - Exclude `PreviousTagSize` from aggregate sub-message payloads while still skipping it when advancing the parser cursor. - Send audio packets on `CHUNK_AUDIO` and video packets on `CHUNK_VIDEO` from `STREAM_MEDIA`; keep the original values for other packet types. - Avoid sending the non-essential RTMP play data notifications before metadata/config/media packets. ## Validation - Built `MediaServer` locally in Debug mode. - Proxied four live RTMP test streams via `addStreamProxy`. - Verified local RTMP and HTTP-FLV playback expose AAC audio tracks. - Decoded RTMP audio with `ffmpeg -xerror`; all tested streams completed without AAC decode errors. - Verified the VLC compatibility change removes the extra `Data: none` stream from RTMP output while preserving Video + Audio streams. - Built and deployed a Docker image locally with docker compose and re-tested RTMP/FLV audio playback. --- src/Rtmp/RtmpProtocol.cpp | 7 ++----- src/Rtmp/RtmpSession.cpp | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Rtmp/RtmpProtocol.cpp b/src/Rtmp/RtmpProtocol.cpp index daf43564..672f506f 100644 --- a/src/Rtmp/RtmpProtocol.cpp +++ b/src/Rtmp/RtmpProtocol.cpp @@ -910,10 +910,7 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { ts |= (*ptr << 24); ptr += 1; ptr += 3; - // 参考FFmpeg多拷贝了4个字节 [AUTO-TRANSLATED:d8aae4ae] - // Reference FFmpeg copied 4 more bytes - size += 4; - if (ptr + size > ptr_tail) { + if (ptr + size + 4 > ptr_tail) { break; } if (!first_message) { @@ -930,7 +927,7 @@ void RtmpProtocol::handle_chunk(RtmpPacket::Ptr packet) { sub_packet.stream_index = chunk_data.stream_index; sub_packet.chunk_id = chunk_data.chunk_id; handle_chunk(std::move(sub_packet_ptr)); - ptr += size; + ptr += size + 4; } break; } diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 89e36f87..67219aa0 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -273,17 +273,7 @@ void RtmpSession::sendPlayResponse(const string &err, const RtmpMediaSource::Ptr "details", _media_info.stream, "clientid", "0"}); - // |RtmpSampleAccess(true, true) AMFEncoder invoke; - invoke << "|RtmpSampleAccess" << true << true; - sendResponse(MSG_DATA, invoke.data()); - - //onStatus(NetStream.Data.Start) - invoke.clear(); - AMFValue obj(AMF_OBJECT); - obj.set("code", "NetStream.Data.Start"); - invoke << "onStatus" << obj; - sendResponse(MSG_DATA, invoke.data()); //onStatus(NetStream.Play.PublishNotify) sendStatus({ "level", "status", @@ -587,7 +577,17 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) { } void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) { - sendRtmp(pkt->type_id, pkt->stream_index, pkt, pkt->time_stamp, pkt->chunk_id); + switch (pkt->type_id) { + case MSG_AUDIO: + sendRtmp(pkt->type_id, STREAM_MEDIA, pkt, pkt->time_stamp, CHUNK_AUDIO); + break; + case MSG_VIDEO: + sendRtmp(pkt->type_id, STREAM_MEDIA, pkt, pkt->time_stamp, CHUNK_VIDEO); + break; + default: + sendRtmp(pkt->type_id, pkt->stream_index, pkt, pkt->time_stamp, pkt->chunk_id); + break; + } } bool RtmpSession::close(MediaSource &sender) {