From eda86dc76b1c08cc9c32bce431c6eab78dd2a9c2 Mon Sep 17 00:00:00 2001 From: "henrik.lundin@webrtc.org" Date: Tue, 13 Dec 2011 14:11:06 +0000 Subject: [PATCH] Adding a LayerSync bit to VP8 RTP header Updated RtpFormatVp8, ModuleRTPUtility, VP8Encoder and VP8Decoder to support a new LayerSync ("Y") bit. Note, in VP8Encoder the bit must be used together with a non-negative value for temporalIdx. Fixing the plumbing between RTP module and and from VP8 wrapper. Updating unit tests; all pass. The new bit is yet to be used by the VP8 wrapper. Review URL: http://webrtc-codereview.appspot.com/323008 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1169 4adac7df-926f-26a2-2b94-8c16560cd09d --- src/modules/interface/module_common_types.h | 23 +++++++++++-------- src/modules/rtp_rtcp/source/rtp_format_vp8.cc | 8 +++++-- src/modules/rtp_rtcp/source/rtp_format_vp8.h | 3 ++- .../source/rtp_format_vp8_unittest.cc | 9 ++++++-- .../rtp_rtcp/source/rtp_receiver_video.cc | 9 ++++++-- src/modules/rtp_rtcp/source/rtp_utility.cc | 6 +++-- src/modules/rtp_rtcp/source/rtp_utility.h | 1 + .../rtp_rtcp/source/rtp_utility_test.cc | 13 +++++++---- .../codecs/interface/video_codec_interface.h | 1 + .../codecs/vp8/main/source/vp8.cc | 1 + .../video_coding/main/source/encoded_frame.cc | 3 +++ .../video_coding/main/source/frame_buffer.cc | 4 ++++ .../video_coding/main/source/frame_buffer.h | 1 + .../main/source/generic_encoder.cc | 10 ++++---- .../video_coding/main/source/session_info.cc | 7 ++++++ .../video_coding/main/source/session_info.h | 1 + 16 files changed, 72 insertions(+), 28 deletions(-) diff --git a/src/modules/interface/module_common_types.h b/src/modules/interface/module_common_types.h index 6cb19481bb..253b8a065f 100644 --- a/src/modules/interface/module_common_types.h +++ b/src/modules/interface/module_common_types.h @@ -57,6 +57,7 @@ struct RTPVideoHeaderVP8 pictureId = kNoPictureId; tl0PicIdx = kNoTl0PicIdx; temporalIdx = kNoTemporalIdx; + layerSync = false; keyIdx = kNoKeyIdx; partitionId = 0; beginningOfPartition = false; @@ -64,18 +65,20 @@ struct RTPVideoHeaderVP8 frameHeight = 0; } - bool nonReference; // Frame is discardable. - WebRtc_Word16 pictureId; // Picture ID index, 15 bits; - // kNoPictureId if PictureID does not exist. - WebRtc_Word16 tl0PicIdx; // TL0PIC_IDX, 8 bits; - // kNoTl0PicIdx means no value provided. - WebRtc_Word8 temporalIdx; // Temporal layer index, or kNoTemporalIdx. - int keyIdx; // 5 bits; kNoKeyIdx means not used. - int partitionId; // VP8 partition ID + bool nonReference; // Frame is discardable. + WebRtc_Word16 pictureId; // Picture ID index, 15 bits; + // kNoPictureId if PictureID does not exist. + WebRtc_Word16 tl0PicIdx; // TL0PIC_IDX, 8 bits; + // kNoTl0PicIdx means no value provided. + WebRtc_Word8 temporalIdx; // Temporal layer index, or kNoTemporalIdx. + bool layerSync; // This frame is a layer sync frame. + // Disabled if temporalIdx == kNoTemporalIdx. + int keyIdx; // 5 bits; kNoKeyIdx means not used. + int partitionId; // VP8 partition ID bool beginningOfPartition; // True if this packet is the first // in a VP8 partition. Otherwise false - int frameWidth; // Exists for key frames. - int frameHeight; // Exists for key frames. + int frameWidth; // Exists for key frames. + int frameHeight; // Exists for key frames. }; union RTPVideoTypeHeader { diff --git a/src/modules/rtp_rtcp/source/rtp_format_vp8.cc b/src/modules/rtp_rtcp/source/rtp_format_vp8.cc index afaecbf08d..5f5153d248 100644 --- a/src/modules/rtp_rtcp/source/rtp_format_vp8.cc +++ b/src/modules/rtp_rtcp/source/rtp_format_vp8.cc @@ -167,7 +167,7 @@ int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes, // +-+-+-+-+-+-+-+-+-+ // L: | TL0PIC_IDX | (optional) // +-+-+-+-+-+-+-+-+-+ - // T/K: | TID | KEYIDX | (optional) + // T/K: |TID:Y| KEYIDX | (optional) // +-+-+-+-+-+-+-+-+-+ assert(payload_bytes > 0); @@ -280,7 +280,9 @@ int RtpFormatVp8::WriteTIDAndKeyIdxFields(WebRtc_UWord8* x_field, *data_field = 0; if (TIDFieldPresent()) { *x_field |= kTBit; - *data_field |= hdr_info_.temporalIdx << 5; + assert(hdr_info_.temporalIdx >= 0 && hdr_info_.temporalIdx <= 3); + *data_field |= hdr_info_.temporalIdx << 6; + *data_field |= hdr_info_.layerSync ? kYBit : 0; } if (KeyIdxFieldPresent()) { *x_field |= kKBit; @@ -314,6 +316,8 @@ bool RtpFormatVp8::XFieldPresent() const { } bool RtpFormatVp8::TIDFieldPresent() const { + assert((hdr_info_.layerSync == false) || + (hdr_info_.temporalIdx != kNoTemporalIdx)); return (hdr_info_.temporalIdx != kNoTemporalIdx); } diff --git a/src/modules/rtp_rtcp/source/rtp_format_vp8.h b/src/modules/rtp_rtcp/source/rtp_format_vp8.h index 39c151ebe7..cd0bc7c8cc 100644 --- a/src/modules/rtp_rtcp/source/rtp_format_vp8.h +++ b/src/modules/rtp_rtcp/source/rtp_format_vp8.h @@ -86,6 +86,7 @@ class RtpFormatVp8 { static const int kLBit = 0x40; static const int kTBit = 0x20; static const int kKBit = 0x10; + static const int kYBit = 0x20; // Calculate size of next chunk to send. Returns 0 if none can be sent. int CalcNextSize(int max_payload_len, int remaining_bytes, @@ -114,7 +115,7 @@ class RtpFormatVp8 { int WriteTl0PicIdxFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer, int buffer_length, int* extension_length) const; - // Set the T and K bits in the x_field, and write TID and KeyIdx to the + // Set the T and K bits in the x_field, and write TID, Y and KeyIdx to the // appropriate position in buffer. The function returns 0 on success, // -1 otherwise. int WriteTIDAndKeyIdxFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer, diff --git a/src/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc b/src/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc index 6599ef842c..25633c9891 100644 --- a/src/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc +++ b/src/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc @@ -94,7 +94,9 @@ void RtpFormatVp8Test::TearDown() { #define EXPECT_BIT_K_EQ(x, a) EXPECT_BIT_EQ(x, 4, a) -#define EXPECT_TID_EQ(x, a) EXPECT_EQ((((x) & 0xE0) >> 5), a) +#define EXPECT_TID_EQ(x, a) EXPECT_EQ((((x) & 0xC0) >> 6), a) + +#define EXPECT_BIT_Y_EQ(x, a) EXPECT_BIT_EQ(x, 5, a) #define EXPECT_KEYIDX_EQ(x, a) EXPECT_EQ(((x) & 0x1F), a) @@ -170,9 +172,11 @@ void RtpFormatVp8Test::CheckTIDAndKeyIdx() { if (hdr_info_.temporalIdx != kNoTemporalIdx) { EXPECT_BIT_T_EQ(buffer_[1], 1); EXPECT_TID_EQ(buffer_[payload_start_], hdr_info_.temporalIdx); + EXPECT_BIT_Y_EQ(buffer_[payload_start_], hdr_info_.layerSync); } else { EXPECT_BIT_T_EQ(buffer_[1], 0); EXPECT_TID_EQ(buffer_[payload_start_], 0); + EXPECT_BIT_Y_EQ(buffer_[payload_start_], false); } if (hdr_info_.keyIdx != kNoKeyIdx) { EXPECT_BIT_K_EQ(buffer_[1], 1); @@ -404,13 +408,14 @@ TEST_F(RtpFormatVp8Test, TestNonReferenceBit) { EXPECT_BIT_N_EQ(buffer_[0], 1); } -// Verify Tl0PicIdx and TID fields +// Verify Tl0PicIdx and TID fields, and layerSync bit. TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) { int send_bytes = 0; bool last; hdr_info_.tl0PicIdx = 117; hdr_info_.temporalIdx = 2; + hdr_info_.layerSync = true; RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, hdr_info_, *fragmentation_, kAggregate); diff --git a/src/modules/rtp_rtcp/source/rtp_receiver_video.cc b/src/modules/rtp_rtcp/source/rtp_receiver_video.cc index 6dd756b75c..a681760954 100644 --- a/src/modules/rtp_rtcp/source/rtp_receiver_video.cc +++ b/src/modules/rtp_rtcp/source/rtp_receiver_video.cc @@ -644,8 +644,13 @@ RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtpHeader, kNoPictureId; toHeader->tl0PicIdx = fromHeader->hasTl0PicIdx ? fromHeader->tl0PicIdx : kNoTl0PicIdx; - toHeader->temporalIdx = fromHeader->hasTID ? fromHeader->tID : - kNoTemporalIdx; + if (fromHeader->hasTID) { + toHeader->temporalIdx = fromHeader->tID; + toHeader->layerSync = fromHeader->layerSync; + } else { + toHeader->temporalIdx = kNoTemporalIdx; + toHeader->layerSync = false; + } toHeader->keyIdx = fromHeader->hasKeyIdx ? fromHeader->keyIdx : kNoKeyIdx; toHeader->frameWidth = fromHeader->frameWidth; diff --git a/src/modules/rtp_rtcp/source/rtp_utility.cc b/src/modules/rtp_rtcp/source/rtp_utility.cc index 2191af7e99..8e419c9869 100644 --- a/src/modules/rtp_rtcp/source/rtp_utility.cc +++ b/src/modules/rtp_rtcp/source/rtp_utility.cc @@ -434,6 +434,7 @@ ModuleRTPUtility::RTPPayload::SetType(RtpVideoCodecTypes videoType) info.VP8.pictureID = -1; info.VP8.tl0PicIdx = -1; info.VP8.tID = -1; + info.VP8.layerSync = false; info.VP8.frameWidth = 0; info.VP8.frameHeight = 0; break; @@ -867,7 +868,7 @@ ModuleRTPUtility::RTPPayloadParser::ParseMPEG4( // +-+-+-+-+-+-+-+-+ // L: | TL0PICIDX | (OPTIONAL) // +-+-+-+-+-+-+-+-+ -// T/K: | TID | KEYIDX | (OPTIONAL) +// T/K: |TID:Y| KEYIDX | (OPTIONAL) // +-+-+-+-+-+-+-+-+ // // Payload header (considered part of the actual payload, sent to decoder) @@ -1041,7 +1042,8 @@ int ModuleRTPUtility::RTPPayloadParser::ParseVP8TIDAndKeyIdx( if (*dataLength <= 0) return -1; if (vp8->hasTID) { - vp8->tID = ((**dataPtr >> 5) & 0x07); + vp8->tID = ((**dataPtr >> 6) & 0x03); + vp8->layerSync = (**dataPtr & 0x20) ? true : false; // Y bit } if (vp8->hasKeyIdx) { diff --git a/src/modules/rtp_rtcp/source/rtp_utility.h b/src/modules/rtp_rtcp/source/rtp_utility.h index 729ddccc05..929b3296aa 100644 --- a/src/modules/rtp_rtcp/source/rtp_utility.h +++ b/src/modules/rtp_rtcp/source/rtp_utility.h @@ -167,6 +167,7 @@ namespace ModuleRTPUtility int pictureID; int tl0PicIdx; int tID; + bool layerSync; int keyIdx; int frameWidth; int frameHeight; diff --git a/src/modules/rtp_rtcp/source/rtp_utility_test.cc b/src/modules/rtp_rtcp/source/rtp_utility_test.cc index e382f0f5e6..41cdae7419 100644 --- a/src/modules/rtp_rtcp/source/rtp_utility_test.cc +++ b/src/modules/rtp_rtcp/source/rtp_utility_test.cc @@ -35,7 +35,7 @@ using ModuleRTPUtility::RTPPayloadVP8; // +-+-+-+-+-+-+-+-+ // L: | TL0PICIDX | (OPTIONAL) // +-+-+-+-+-+-+-+-+ -// T/K: | TID | KEYIDX | (OPTIONAL) +// T/K: |TID:Y| KEYIDX | (OPTIONAL) // +-+-+-+-+-+-+-+-+ // // Payload header @@ -153,11 +153,11 @@ TEST(ParseVP8Test, Tl0PicIdx) { EXPECT_EQ(13 - 3, parsedPacket.info.VP8.dataLength); } -TEST(ParseVP8Test, TID) { +TEST(ParseVP8Test, TIDAndLayerSync) { WebRtc_UWord8 payload[10] = {0}; payload[0] = 0x88; payload[1] = 0x20; - payload[2] = 0x40; + payload[2] = 0x80; // TID(2) + LayerSync(false) RTPPayloadParser rtpPayloadParser(kRtpVp8Video, payload, 10, 0); @@ -171,6 +171,7 @@ TEST(ParseVP8Test, TID) { VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 1 /*T*/, 0 /*K*/); EXPECT_EQ(2, parsedPacket.info.VP8.tID); + EXPECT_FALSE(parsedPacket.info.VP8.layerSync); EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data); EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength); @@ -206,7 +207,7 @@ TEST(ParseVP8Test, MultipleExtensions) { payload[2] = 0x80 | 17; // PictureID, high 7 bits. payload[3] = 17; // PictureID, low 8 bits. payload[4] = 42; // Tl0PicIdx. - payload[5] = 0x40 | 0x11; // TID + KEYIDX. + payload[5] = 0x40 | 0x20 | 0x11; // TID(1) + LayerSync(true) + KEYIDX(17). RTPPayloadParser rtpPayloadParser(kRtpVp8Video, payload, 10, 0); @@ -221,7 +222,7 @@ TEST(ParseVP8Test, MultipleExtensions) { EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID); EXPECT_EQ(42, parsedPacket.info.VP8.tl0PicIdx); - EXPECT_EQ(2, parsedPacket.info.VP8.tID); + EXPECT_EQ(1, parsedPacket.info.VP8.tID); EXPECT_EQ(17, parsedPacket.info.VP8.keyIdx); EXPECT_EQ(payload + 6, parsedPacket.info.VP8.data); @@ -248,6 +249,7 @@ TEST(ParseVP8Test, TestWithPacketizer) { inputHeader.nonReference = true; inputHeader.pictureId = 300; inputHeader.temporalIdx = 1; + inputHeader.layerSync = false; inputHeader.tl0PicIdx = kNoTl0PicIdx; // Disable. inputHeader.keyIdx = 31; RtpFormatVp8 packetizer = RtpFormatVp8(payload, 10, inputHeader); @@ -276,6 +278,7 @@ TEST(ParseVP8Test, TestWithPacketizer) { EXPECT_EQ(inputHeader.pictureId, parsedPacket.info.VP8.pictureID); EXPECT_EQ(inputHeader.temporalIdx, parsedPacket.info.VP8.tID); + EXPECT_EQ(inputHeader.layerSync, parsedPacket.info.VP8.layerSync); EXPECT_EQ(inputHeader.keyIdx, parsedPacket.info.VP8.keyIdx); EXPECT_EQ(packet + 5, parsedPacket.info.VP8.data); diff --git a/src/modules/video_coding/codecs/interface/video_codec_interface.h b/src/modules/video_coding/codecs/interface/video_codec_interface.h index 64bf4e9a6a..7b7f8223cc 100644 --- a/src/modules/video_coding/codecs/interface/video_codec_interface.h +++ b/src/modules/video_coding/codecs/interface/video_codec_interface.h @@ -33,6 +33,7 @@ struct CodecSpecificInfoVP8 bool nonReference; WebRtc_UWord8 simulcastIdx; WebRtc_UWord8 temporalIdx; + bool layerSync; int tl0PicIdx; // Negative value to skip tl0PicIdx WebRtc_Word8 keyIdx; // negative value to skip keyIdx }; diff --git a/src/modules/video_coding/codecs/vp8/main/source/vp8.cc b/src/modules/video_coding/codecs/vp8/main/source/vp8.cc index 0473d31fd8..24b5a24ead 100644 --- a/src/modules/video_coding/codecs/vp8/main/source/vp8.cc +++ b/src/modules/video_coding/codecs/vp8/main/source/vp8.cc @@ -509,6 +509,7 @@ void VP8Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, } else { #endif vp8Info->temporalIdx = kNoTemporalIdx; + vp8Info->layerSync = false; vp8Info->tl0PicIdx = kNoTl0PicIdx; #if WEBRTC_LIBVPX_VERSION >= 971 } diff --git a/src/modules/video_coding/main/source/encoded_frame.cc b/src/modules/video_coding/main/source/encoded_frame.cc index 360849c6b9..8e5d745488 100644 --- a/src/modules/video_coding/main/source/encoded_frame.cc +++ b/src/modules/video_coding/main/source/encoded_frame.cc @@ -112,6 +112,7 @@ void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header) // This is the first packet for this frame. _codecSpecificInfo.codecSpecific.VP8.pictureId = -1; _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0; + _codecSpecificInfo.codecSpecific.VP8.layerSync = false; _codecSpecificInfo.codecSpecific.VP8.keyIdx = -1; _codecSpecificInfo.codecType = kVideoCodecVP8; } @@ -126,6 +127,8 @@ void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header) { _codecSpecificInfo.codecSpecific.VP8.temporalIdx = header->codecHeader.VP8.temporalIdx; + _codecSpecificInfo.codecSpecific.VP8.layerSync = + header->codecHeader.VP8.layerSync; } if (header->codecHeader.VP8.keyIdx != kNoKeyIdx) { diff --git a/src/modules/video_coding/main/source/frame_buffer.cc b/src/modules/video_coding/main/source/frame_buffer.cc index 4fa0c971e8..4be89928e9 100644 --- a/src/modules/video_coding/main/source/frame_buffer.cc +++ b/src/modules/video_coding/main/source/frame_buffer.cc @@ -76,6 +76,10 @@ int VCMFrameBuffer::TemporalId() const { return _sessionInfo.TemporalId(); } +bool VCMFrameBuffer::LayerSync() const { + return _sessionInfo.LayerSync(); +} + int VCMFrameBuffer::Tl0PicId() const { return _sessionInfo.Tl0PicId(); } diff --git a/src/modules/video_coding/main/source/frame_buffer.h b/src/modules/video_coding/main/source/frame_buffer.h index f74ac9d628..3901f58fdd 100644 --- a/src/modules/video_coding/main/source/frame_buffer.h +++ b/src/modules/video_coding/main/source/frame_buffer.h @@ -58,6 +58,7 @@ public: int PictureId() const; int TemporalId() const; + bool LayerSync() const; int Tl0PicId() const; bool NonReference() const; diff --git a/src/modules/video_coding/main/source/generic_encoder.cc b/src/modules/video_coding/main/source/generic_encoder.cc index 2838a86f79..7c8ff9077f 100644 --- a/src/modules/video_coding/main/source/generic_encoder.cc +++ b/src/modules/video_coding/main/source/generic_encoder.cc @@ -262,15 +262,17 @@ void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info, case kVideoCodecVP8: { (*rtp)->codecHeader.VP8.InitRTPVideoHeaderVP8(); (*rtp)->codecHeader.VP8.pictureId = - info.codecSpecific.VP8.pictureId; + info.codecSpecific.VP8.pictureId; (*rtp)->codecHeader.VP8.nonReference = - info.codecSpecific.VP8.nonReference; + info.codecSpecific.VP8.nonReference; (*rtp)->codecHeader.VP8.temporalIdx = - info.codecSpecific.VP8.temporalIdx; + info.codecSpecific.VP8.temporalIdx; + (*rtp)->codecHeader.VP8.layerSync = + info.codecSpecific.VP8.layerSync; (*rtp)->codecHeader.VP8.tl0PicIdx = info.codecSpecific.VP8.tl0PicIdx; (*rtp)->codecHeader.VP8.keyIdx = - info.codecSpecific.VP8.keyIdx; + info.codecSpecific.VP8.keyIdx; (*rtp)->simulcastIdx = info.codecSpecific.VP8.simulcastIdx; return; } diff --git a/src/modules/video_coding/main/source/session_info.cc b/src/modules/video_coding/main/source/session_info.cc index 173f0fefe9..e7091e2bd2 100644 --- a/src/modules/video_coding/main/source/session_info.cc +++ b/src/modules/video_coding/main/source/session_info.cc @@ -59,6 +59,13 @@ int VCMSessionInfo::TemporalId() const { return packets_.front().codecSpecificHeader.codecHeader.VP8.temporalIdx; } +bool VCMSessionInfo::LayerSync() const { + if (packets_.empty() || + packets_.front().codecSpecificHeader.codec != kRTPVideoVP8) + return false; + return packets_.front().codecSpecificHeader.codecHeader.VP8.layerSync; +} + int VCMSessionInfo::Tl0PicId() const { if (packets_.empty() || packets_.front().codecSpecificHeader.codec != kRTPVideoVP8) diff --git a/src/modules/video_coding/main/source/session_info.h b/src/modules/video_coding/main/source/session_info.h index 6e645b40ec..555afb8e6d 100644 --- a/src/modules/video_coding/main/source/session_info.h +++ b/src/modules/video_coding/main/source/session_info.h @@ -63,6 +63,7 @@ class VCMSessionInfo { int HighSequenceNumber() const; int PictureId() const; int TemporalId() const; + bool LayerSync() const; int Tl0PicId() const; bool NonReference() const; int PrepareForDecode(uint8_t* frame_buffer);