From 9fa131d71b5e0e9e73dfc73e639bb4425bcc4012 Mon Sep 17 00:00:00 2001 From: YuLi Date: Thu, 30 Jul 2026 21:46:49 -0700 Subject: [PATCH] document VP9 frame payload requirement --- ext-codec/VP9Rtp.cpp | 4 ++++ tests/test_vp9_rtp.cpp | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ext-codec/VP9Rtp.cpp b/ext-codec/VP9Rtp.cpp index def02127..bec8659d 100644 --- a/ext-codec/VP9Rtp.cpp +++ b/ext-codec/VP9Rtp.cpp @@ -208,6 +208,8 @@ int RTPPayloadVP9::parse(const unsigned char *data, int dataLength) { } } } + // Downstream VP9 frame inspection reads the first frame-data byte, so a + // payload descriptor without any frame data must not enter the frame path. if (remaining == 0) return -1; return dataPtr - data; } @@ -245,6 +247,8 @@ bool VP9RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) { RTPPayloadVP9 info; int offset = info.parse(payload, payload_size); + // Keep this independent of the parser's internal checks: a successful + // descriptor must still leave at least one byte of VP9 frame payload. if (offset < 0 || static_cast(offset) >= payload_size) { WarnL << "VP9 RTP payload parse failed, seq:" << seq; _frame_drop = true; diff --git a/tests/test_vp9_rtp.cpp b/tests/test_vp9_rtp.cpp index 27c1be80..2d2ecb63 100644 --- a/tests/test_vp9_rtp.cpp +++ b/tests/test_vp9_rtp.cpp @@ -59,7 +59,7 @@ void runCase(const ParseCase &test) { } } -void testParseFailureDropsCurrentFrame() { +void testParseFailureDropsCurrentFrame(const char *name, const std::vector &invalid_payload) { VP9RtpDecoder decoder; std::vector frames; decoder.addDelegate([&](const Frame::Ptr &frame) { @@ -70,18 +70,19 @@ void testParseFailureDropsCurrentFrame() { RtpInfo rtp_info(0x12345678, 1400, 90000, 98, 0, 0); auto input = [&](const std::vector &payload, bool mark) { auto rtp = rtp_info.makeRtp(TrackVideo, payload.data(), payload.size(), mark, 100); - require(rtp != nullptr, "failed to create RTP packet for parse-failure recovery test"); + require(rtp != nullptr, std::string(name) + ": failed to create RTP packet"); decoder.inputRtp(rtp, false); }; input({ 0x08, 0x80, 0x11 }, false); // B: begin assembling a frame. - input({ 0x50, 0x03 }, false); // Truncated P_DIFF chain. + input(invalid_payload, false); input({ 0x04, 0x22 }, true); // E: must not emit the incomplete frame. - require(frames.empty(), "parse failure emitted a partial VP9 frame"); + require(frames.empty(), std::string(name) + ": parse failure emitted a partial VP9 frame"); input({ 0x0C, 0x80, 0x33 }, true); // A new complete frame must recover normally. - require(frames.size() == 1, "decoder did not recover after a VP9 parse failure"); - require(frames[0] == std::string("\x80\x33", 2), "recovered VP9 frame payload does not match"); + require(frames.size() == 1, std::string(name) + ": decoder did not recover after a parse failure"); + require(frames[0] == std::string("\x80\x33", 2), + std::string(name) + ": recovered VP9 frame payload does not match"); } } // namespace @@ -117,8 +118,9 @@ int main() { runCase({ "GOF exact boundary", { 0x02, 0x08, 0x02, 0x04, 0x11, 0x08, 0x22, 0x33, 0xAA }, 8 }); runCase({ "GOF truncated references", { 0x02, 0x08, 0x01, 0x08, 0x22 }, -1 }); - runCase({ "descriptor only", { 0x00 }, -1 }); - runCase({ "layer descriptor only", { 0x30, 0x00 }, -1 }); - testParseFailureDropsCurrentFrame(); + runCase({ "reject descriptor-only payload", { 0x00 }, -1 }); + runCase({ "reject layer-descriptor-only payload", { 0x30, 0x00 }, -1 }); + testParseFailureDropsCurrentFrame("truncated P_DIFF", { 0x50, 0x03 }); + testParseFailureDropsCurrentFrame("descriptor-only middle packet", { 0x00 }); return 0; }