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:
parent
4aae0e489f
commit
eda86dc76b
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -167,6 +167,7 @@ namespace ModuleRTPUtility
|
||||
int pictureID;
|
||||
int tl0PicIdx;
|
||||
int tID;
|
||||
bool layerSync;
|
||||
int keyIdx;
|
||||
int frameWidth;
|
||||
int frameHeight;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ public:
|
||||
|
||||
int PictureId() const;
|
||||
int TemporalId() const;
|
||||
bool LayerSync() const;
|
||||
int Tl0PicId() const;
|
||||
bool NonReference() const;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user