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
This commit is contained in:
henrik.lundin@webrtc.org 2011-12-13 14:11:06 +00:00
parent 4aae0e489f
commit eda86dc76b
16 changed files with 72 additions and 28 deletions

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

@ -167,6 +167,7 @@ namespace ModuleRTPUtility
int pictureID;
int tl0PicIdx;
int tID;
bool layerSync;
int keyIdx;
int frameWidth;
int frameHeight;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,6 +58,7 @@ public:
int PictureId() const;
int TemporalId() const;
bool LayerSync() const;
int Tl0PicId() const;
bool NonReference() const;

View File

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

View File

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

View File

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