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.
This commit is contained in:
ickeep
2026-06-04 16:03:06 +08:00
committed by GitHub
parent 4a2c4d0e98
commit eeb2693f22
2 changed files with 13 additions and 16 deletions

View File

@@ -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;
}

View File

@@ -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) {
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) {