Updating to new VP8 rtp format

The VP8 packetizer and tests have been updated to the new
RTP draft (http://tools.ietf.org/html/draft-ietf-payload-vp8-01).
The receive-side parser is also updated, and a new unit test
is implemented for it. Finally, some data traversing work to
get the parsed information into the decoder.
Review URL: http://webrtc-codereview.appspot.com/116011

git-svn-id: http://webrtc.googlecode.com/svn/trunk@482 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org
2011-08-29 15:37:12 +00:00
parent 09734086c6
commit 8571af7be6
12 changed files with 922 additions and 251 deletions

View File

@ -37,18 +37,30 @@ struct RTPAudioHeader
struct RTPVideoHeaderH263 struct RTPVideoHeaderH263
{ {
void InitRTPVideoHeaderH263() {};
bool independentlyDecodable; // H.263-1998 if no P bit it's not independently decodable bool independentlyDecodable; // H.263-1998 if no P bit it's not independently decodable
bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the
// first byte of this packet // first byte of this packet
}; };
enum {kNoPictureId = -1}; enum {kNoPictureId = -1};
enum {kNoTl0PicIdx = -1};
enum {kNoTemporalIdx = -1};
struct RTPVideoHeaderVP8 struct RTPVideoHeaderVP8
{ {
bool startBit; // Start of partition. void InitRTPVideoHeaderVP8()
bool stopBit; // Stop of partition. {
nonReference = false;
pictureId = kNoPictureId;
tl0PicIdx = kNoTl0PicIdx;
temporalIdx = kNoTemporalIdx;
}
bool nonReference; // Frame is discardable.
WebRtc_Word16 pictureId; // Picture ID index, 15 bits; WebRtc_Word16 pictureId; // Picture ID index, 15 bits;
// kNoPictureId if PictureID does not exist. // kNoPictureId if PictureID does not exist.
bool nonReference; // Frame is discardable. WebRtc_Word16 tl0PicIdx; // TL0PIC_IDX, 8 bits;
// kNoTl0PicIdx means no value provided.
WebRtc_Word8 temporalIdx; // Temporal layer index, or kNoTemporalIdx.
}; };
union RTPVideoTypeHeader union RTPVideoTypeHeader
{ {

View File

@ -35,11 +35,12 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
part_ix_(0), part_ix_(0),
beginning_(true), beginning_(true),
first_fragment_(true), first_fragment_(true),
vp8_header_bytes_(1), vp8_fixed_payload_descriptor_bytes_(1),
aggr_mode_(aggr_modes_[mode]), aggr_mode_(aggr_modes_[mode]),
balance_(balance_modes_[mode]), balance_(balance_modes_[mode]),
separate_first_(separate_first_modes_[mode]), separate_first_(separate_first_modes_[mode]),
hdr_info_(hdr_info) hdr_info_(hdr_info),
first_partition_in_packet_(0)
{ {
part_info_ = fragmentation; part_info_ = fragmentation;
} }
@ -54,11 +55,12 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
part_ix_(0), part_ix_(0),
beginning_(true), beginning_(true),
first_fragment_(true), first_fragment_(true),
vp8_header_bytes_(1), vp8_fixed_payload_descriptor_bytes_(1),
aggr_mode_(aggr_modes_[kSloppy]), aggr_mode_(aggr_modes_[kSloppy]),
balance_(balance_modes_[kSloppy]), balance_(balance_modes_[kSloppy]),
separate_first_(separate_first_modes_[kSloppy]), separate_first_(separate_first_modes_[kSloppy]),
hdr_info_(hdr_info) hdr_info_(hdr_info),
first_partition_in_packet_(0)
{ {
part_info_.VerifyAndAllocateFragmentationHeader(1); part_info_.VerifyAndAllocateFragmentationHeader(1);
part_info_.fragmentationLength[0] = payload_size; part_info_.fragmentationLength[0] = payload_size;
@ -97,14 +99,22 @@ int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes,
int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer, int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send, bool* last_packet) int* bytes_to_send, bool* last_packet)
{ {
if (max_payload_len < vp8_fixed_payload_descriptor_bytes_
+ PayloadDescriptorExtraLength() + 1)
{
// The provided payload length is not long enough for the payload
// descriptor and one payload byte. Return an error.
return -1;
}
const int num_partitions = part_info_.fragmentationVectorSize; const int num_partitions = part_info_.fragmentationVectorSize;
int send_bytes = 0; // How much data to send in this packet. int send_bytes = 0; // How much data to send in this packet.
bool split_payload = true; // Splitting of partitions is initially allowed. bool split_payload = true; // Splitting of partitions is initially allowed.
int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] - int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] -
payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] + payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] +
FirstHeaderExtraLength(); // Add header extra length to payload length. PayloadDescriptorExtraLength();
int rem_payload_len = max_payload_len - vp8_header_bytes_; int rem_payload_len = max_payload_len - vp8_fixed_payload_descriptor_bytes_;
const int first_partition_in_packet = part_ix_; first_partition_in_packet_ = part_ix_;
if (first_partition_in_packet_ > 8) return -1;
while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition, while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition,
split_payload)) split_payload))
@ -141,67 +151,117 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
++part_ix_; // Advance to next partition. ++part_ix_; // Advance to next partition.
} }
send_bytes -= FirstHeaderExtraLength(); // Remove the extra length again. send_bytes -= PayloadDescriptorExtraLength(); // Remove extra length again.
assert(send_bytes > 0); assert(send_bytes > 0);
const bool end_of_fragment = (remaining_in_partition == 0);
// Write the payload header and the payload to buffer. // Write the payload header and the payload to buffer.
*bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer, *bytes_to_send = WriteHeaderAndPayload(send_bytes, buffer, max_payload_len);
max_payload_len);
if (*bytes_to_send < 0) if (*bytes_to_send < 0)
{ {
return -1; return -1;
} }
beginning_ = false; // Next packet cannot be first packet in frame.
// Next packet starts new fragment if this ended one.
first_fragment_ = (remaining_in_partition == 0);
*last_packet = (payload_bytes_sent_ >= payload_size_); *last_packet = (payload_bytes_sent_ >= payload_size_);
assert(!*last_packet || (payload_bytes_sent_ == payload_size_)); assert(!*last_packet || (payload_bytes_sent_ == payload_size_));
return first_partition_in_packet; return first_partition_in_packet_;
} }
int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes, int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes,
bool end_of_fragment,
WebRtc_UWord8* buffer, WebRtc_UWord8* buffer,
int buffer_length) int buffer_length)
{ {
// Write the VP8 payload header. // Write the VP8 payload descriptor.
// 0 1 2 // 0
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 // 0 1 2 3 4 5 6 7 8
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+
// | RSV |I|N|FI |B| PictureID (1 or 2 octets) | // |X| |N|S| PART_ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+
// X: |I|L|T| | (mandatory if any of the below are used)
// +-+-+-+-+-+-+-+-+-+
// I: |PictureID (8/16b)| (optional)
// +-+-+-+-+-+-+-+-+-+
// L: | TL0PIC_IDX | (optional)
// +-+-+-+-+-+-+-+-+-+
// T: | TID | | (optional)
// +-+-+-+-+-+-+-+-+-+
if (payload_bytes < 0) assert(payload_bytes > 0);
{ assert(payload_bytes_sent_ + payload_bytes <= payload_size_);
return -1; assert(vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength()
} + payload_bytes <= buffer_length);
if (payload_bytes_sent_ + payload_bytes > payload_size_)
{
return -1;
}
buffer[0] = 0; buffer[0] = 0;
if (hdr_info_.nonReference) buffer[0] |= (0x01 << 3); // N if (XFieldPresent()) buffer[0] |= kXBit;
buffer[0] |= (GetFIFlag(end_of_fragment) << 1); // FI if (hdr_info_.nonReference) buffer[0] |= kNBit;
if (beginning_) buffer[0] |= 0x01; // B if (first_fragment_) buffer[0] |= kSBit;
buffer[0] |= (first_partition_in_packet_ & kPartIdField);
int pic_id_len = WritePictureID(&buffer[vp8_header_bytes_], const int extension_length = WriteExtensionFields(buffer, buffer_length);
buffer_length - vp8_header_bytes_);
if (pic_id_len < 0) return pic_id_len; // error
if (pic_id_len > 0) buffer[0] |= (0x01 << 4); // I
if (vp8_header_bytes_ + pic_id_len + payload_bytes > buffer_length) memcpy(&buffer[vp8_fixed_payload_descriptor_bytes_ + extension_length],
{
return -1;
}
memcpy(&buffer[vp8_header_bytes_ + pic_id_len],
&payload_data_[payload_bytes_sent_], payload_bytes); &payload_data_[payload_bytes_sent_], payload_bytes);
beginning_ = false; // next packet cannot be first packet in frame
// next packet starts new fragment if this ended one
first_fragment_ = end_of_fragment;
payload_bytes_sent_ += payload_bytes; payload_bytes_sent_ += payload_bytes;
// Return total length of written data. // Return total length of written data.
return payload_bytes + vp8_header_bytes_ + pic_id_len; return payload_bytes + vp8_fixed_payload_descriptor_bytes_
+ extension_length;
}
int RtpFormatVp8::WriteExtensionFields(WebRtc_UWord8* buffer, int buffer_length)
const
{
int extension_length = 0;
if (XFieldPresent())
{
WebRtc_UWord8* x_field = buffer + vp8_fixed_payload_descriptor_bytes_;
*x_field = 0;
extension_length = 1; // One octet for the X field.
if (PictureIdPresent())
{
if (WritePictureIDFields(x_field, buffer, buffer_length,
&extension_length) < 0)
{
return -1;
}
}
if (TL0PicIdxFieldPresent())
{
if (WriteTl0PicIdxFields(x_field, buffer, buffer_length,
&extension_length) < 0)
{
return -1;
}
}
if (TIDFieldPresent())
{
if (WriteTIDFields(x_field, buffer, buffer_length,
&extension_length) < 0)
{
return -1;
}
}
assert(extension_length == PayloadDescriptorExtraLength());
}
return extension_length;
}
int RtpFormatVp8::WritePictureIDFields(WebRtc_UWord8* x_field,
WebRtc_UWord8* buffer,
int buffer_length,
int* extension_length) const
{
*x_field |= kIBit;
const int pic_id_length = WritePictureID(
buffer + vp8_fixed_payload_descriptor_bytes_ + *extension_length,
buffer_length - vp8_fixed_payload_descriptor_bytes_
- *extension_length);
if (pic_id_length < 0) return -1;
*extension_length += pic_id_length;
return 0;
} }
int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
@ -209,7 +269,7 @@ int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
const WebRtc_UWord16 pic_id = const WebRtc_UWord16 pic_id =
static_cast<WebRtc_UWord16> (hdr_info_.pictureId); static_cast<WebRtc_UWord16> (hdr_info_.pictureId);
int picture_id_len = PictureIdLength(); int picture_id_len = PictureIdLength();
if (picture_id_len > buffer_length) return -1; // error if (picture_id_len > buffer_length) return -1;
if (picture_id_len == 2) if (picture_id_len == 2)
{ {
buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F); buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F);
@ -222,17 +282,51 @@ int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
return picture_id_len; return picture_id_len;
} }
int RtpFormatVp8::FirstHeaderExtraLength() const int RtpFormatVp8::WriteTl0PicIdxFields(WebRtc_UWord8* x_field,
WebRtc_UWord8* buffer,
int buffer_length,
int* extension_length) const
{
if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length
+ 1)
{
return -1;
}
*x_field |= kLBit;
buffer[vp8_fixed_payload_descriptor_bytes_
+ *extension_length] = hdr_info_.tl0PicIdx;
++*extension_length;
return 0;
}
int RtpFormatVp8::WriteTIDFields(WebRtc_UWord8* x_field,
WebRtc_UWord8* buffer,
int buffer_length,
int* extension_length) const
{
if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length
+ 1)
{
return -1;
}
*x_field |= kTBit;
buffer[vp8_fixed_payload_descriptor_bytes_ + *extension_length]
= hdr_info_.temporalIdx << 5;
++*extension_length;
return 0;
}
int RtpFormatVp8::PayloadDescriptorExtraLength() const
{ {
if (!beginning_) if (!beginning_)
{ {
return 0; return 0;
} }
int length = 0; int length_bytes = PictureIdLength();
if (TL0PicIdxFieldPresent()) ++length_bytes;
length += PictureIdLength(); if (TIDFieldPresent()) ++length_bytes;
if (length_bytes > 0) ++length_bytes; // Include the extension field.
return length; return length_bytes;
} }
int RtpFormatVp8::PictureIdLength() const int RtpFormatVp8::PictureIdLength() const
@ -251,19 +345,19 @@ int RtpFormatVp8::PictureIdLength() const
} }
} }
int RtpFormatVp8::GetFIFlag(bool end_of_fragment) const bool RtpFormatVp8::XFieldPresent() const
{ {
if (first_fragment_ && end_of_fragment) { return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent());
return 0x0; }
}
if (first_fragment_ && !end_of_fragment) { bool RtpFormatVp8::TIDFieldPresent() const
return 0x1; {
} return (hdr_info_.temporalIdx != kNoTemporalIdx);
if (!first_fragment_ && !end_of_fragment) { }
return 0x2;
} bool RtpFormatVp8::TL0PicIdxFieldPresent() const
// if (!first_fragment_ && end_of_fragment) {
return 0x3; return (hdr_info_.tl0PicIdx != kNoTl0PicIdx);
} }
} // namespace webrtc } // namespace webrtc

View File

@ -80,6 +80,13 @@ private:
static const AggregationMode aggr_modes_[kNumModes]; static const AggregationMode aggr_modes_[kNumModes];
static const bool balance_modes_[kNumModes]; static const bool balance_modes_[kNumModes];
static const bool separate_first_modes_[kNumModes]; static const bool separate_first_modes_[kNumModes];
static const int kXBit = 0x80;
static const int kNBit = 0x20;
static const int kSBit = 0x10;
static const int kPartIdField = 0x0F;
static const int kIBit = 0x80;
static const int kLBit = 0x40;
static const int kTBit = 0x20;
// Calculate size of next chunk to send. Returns 0 if none can be sent. // Calculate size of next chunk to send. Returns 0 if none can be sent.
int CalcNextSize(int max_payload_len, int remaining_bytes, int CalcNextSize(int max_payload_len, int remaining_bytes,
@ -89,8 +96,29 @@ private:
// Will copy send_bytes bytes from the current position on the payload data. // Will copy send_bytes bytes from the current position on the payload data.
// last_fragment indicates that this packet ends with the last byte of a // last_fragment indicates that this packet ends with the last byte of a
// partition. // partition.
int WriteHeaderAndPayload(int send_bytes, bool end_of_fragment, int WriteHeaderAndPayload(int send_bytes, WebRtc_UWord8* buffer,
WebRtc_UWord8* buffer, int buffer_length); int buffer_length);
// Write the X field and the appropriate extension fields to buffer.
// The function returns the extension length (including X field), or -1
// on error.
int WriteExtensionFields(WebRtc_UWord8* buffer, int buffer_length) const;
// Set the I bit in the x_field, and write PictureID to the appropriate
// position in buffer. The function returns 0 on success, -1 otherwise.
int WritePictureIDFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer,
int buffer_length, int* extension_length) const;
// Set the L bit in the x_field, and write Tl0PicIdx to the appropriate
// position in buffer. The function returns 0 on success, -1 otherwise.
int WriteTl0PicIdxFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer,
int buffer_length, int* extension_length) const;
// Set the T bit in the x_field, and write TID to the appropriate
// position in buffer. The function returns 0 on success, -1 otherwise.
int WriteTIDFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer,
int buffer_length, int* extension_length) const;
// Write the PictureID from codec_specific_info_ to buffer. One or two // Write the PictureID from codec_specific_info_ to buffer. One or two
// bytes are written, depending on magnitude of PictureID. The function // bytes are written, depending on magnitude of PictureID. The function
@ -99,13 +127,17 @@ private:
// Calculate and return length (octets) of the variable header fields in // Calculate and return length (octets) of the variable header fields in
// the next header (i.e., header length in addition to vp8_header_bytes_). // the next header (i.e., header length in addition to vp8_header_bytes_).
int FirstHeaderExtraLength() const; int PayloadDescriptorExtraLength() const;
// Calculate and return length (octets) of PictureID field in the next // Calculate and return length (octets) of PictureID field in the next
// header. Can be 0, 1, or 2. // header. Can be 0, 1, or 2.
int PictureIdLength() const; int PictureIdLength() const;
int GetFIFlag(bool end_of_fragment) const; // Check whether each of the optional fields will be included in the header.
bool XFieldPresent() const;
bool TIDFieldPresent() const;
bool TL0PicIdxFieldPresent() const;
bool PictureIdPresent() const { return (PictureIdLength() > 0); }
const WebRtc_UWord8* payload_data_; const WebRtc_UWord8* payload_data_;
const int payload_size_; const int payload_size_;
@ -114,11 +146,13 @@ private:
int part_ix_; int part_ix_;
bool beginning_; // first partition in this frame bool beginning_; // first partition in this frame
bool first_fragment_; // first fragment of a partition bool first_fragment_; // first fragment of a partition
const int vp8_header_bytes_; // length of VP8 payload header's fixed part const int vp8_fixed_payload_descriptor_bytes_; // length of VP8 payload
// descriptors's fixed part
AggregationMode aggr_mode_; AggregationMode aggr_mode_;
bool balance_; bool balance_;
bool separate_first_; bool separate_first_;
const RTPVideoHeaderVP8 hdr_info_; const RTPVideoHeaderVP8 hdr_info_;
int first_partition_in_packet_;
}; };
} }

View File

@ -24,20 +24,26 @@ using webrtc::RTPFragmentationHeader;
using webrtc::RtpFormatVp8; using webrtc::RtpFormatVp8;
using webrtc::RTPVideoHeaderVP8; using webrtc::RTPVideoHeaderVP8;
const WebRtc_UWord32 kPayloadSize = 30; const int kPayloadSize = 30;
const int kBufferSize = kPayloadSize + 6; // Add space for payload descriptor.
class RtpFormatVp8Test : public ::testing::Test { class RtpFormatVp8Test : public ::testing::Test {
protected: protected:
RtpFormatVp8Test() {}; RtpFormatVp8Test() {};
virtual void SetUp(); virtual void SetUp();
virtual void TearDown(); virtual void TearDown();
void CheckHeader(bool first_in_frame, bool frag_start, bool frag_end); void CheckHeader(bool first_in_frame, bool frag_start, int part_id);
void CheckPictureID();
void CheckTl0PicIdx();
void CheckTID();
void CheckPayload(int payload_end); void CheckPayload(int payload_end);
void CheckLast(bool last) const; void CheckLast(bool last) const;
void CheckPacket(int send_bytes, int expect_bytes, bool last, void CheckPacket(int send_bytes, int expect_bytes, bool last,
bool first_in_frame, bool frag_start, bool frag_end); bool first_in_frame, bool frag_start);
void CheckPacketZeroPartId(int send_bytes, int expect_bytes, bool last,
bool first_in_frame, bool frag_start);
WebRtc_UWord8 payload_data_[kPayloadSize]; WebRtc_UWord8 payload_data_[kPayloadSize];
WebRtc_UWord8 buffer_[kPayloadSize]; WebRtc_UWord8 buffer_[kBufferSize];
WebRtc_UWord8 *data_ptr_; WebRtc_UWord8 *data_ptr_;
RTPFragmentationHeader* fragmentation_; RTPFragmentationHeader* fragmentation_;
RTPVideoHeaderVP8 hdr_info_; RTPVideoHeaderVP8 hdr_info_;
@ -60,57 +66,130 @@ void RtpFormatVp8Test::SetUp() {
fragmentation_->fragmentationOffset[1] = 10; fragmentation_->fragmentationOffset[1] = 10;
fragmentation_->fragmentationOffset[2] = 20; fragmentation_->fragmentationOffset[2] = 20;
hdr_info_.pictureId = 0; hdr_info_.pictureId = webrtc::kNoPictureId;
hdr_info_.nonReference = false; hdr_info_.nonReference = false;
hdr_info_.temporalIdx = webrtc::kNoTemporalIdx;
hdr_info_.tl0PicIdx = webrtc::kNoTl0PicIdx;
} }
void RtpFormatVp8Test::TearDown() { void RtpFormatVp8Test::TearDown() {
delete fragmentation_; delete fragmentation_;
} }
// First octet tests
#define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a) #define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a)
#define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0) #define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0)
//#define EXPECT_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0)) #define EXPECT_BIT_X_EQ(x,a) EXPECT_BIT_EQ(x, 7, a)
#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 4, a)
#define EXPECT_BIT_N_EQ(x,a) EXPECT_EQ((((x)&0x08) > 0), (a > 0)) #define EXPECT_BIT_N_EQ(x,a) EXPECT_BIT_EQ(x, 5, a)
#define EXPECT_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a) #define EXPECT_BIT_S_EQ(x,a) EXPECT_BIT_EQ(x, 4, a)
#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0)) #define EXPECT_PART_ID_EQ(x, a) EXPECT_EQ(((x)&0x0F), a)
// Extension fields tests
#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 7, a)
#define EXPECT_BIT_L_EQ(x,a) EXPECT_BIT_EQ(x, 6, a)
#define EXPECT_BIT_T_EQ(x,a) EXPECT_BIT_EQ(x, 5, a)
#define EXPECT_TID_EQ(x,a) EXPECT_EQ((((x)&0xE0) >> 5), a)
void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start, void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start,
bool frag_end) int part_id)
{ {
payload_start_ = 1; payload_start_ = 1;
EXPECT_RSV_ZERO(buffer_[0]); EXPECT_BIT_EQ(buffer_[0], 6, 0); // check reserved bit
if (first_in_frame & hdr_info_.pictureId != webrtc::kNoPictureId)
if (first_in_frame &&
(hdr_info_.pictureId != webrtc::kNoPictureId ||
hdr_info_.temporalIdx != webrtc::kNoTemporalIdx ||
hdr_info_.tl0PicIdx != webrtc::kNoTl0PicIdx))
{ {
EXPECT_BIT_I_EQ(buffer_[0], 1); EXPECT_BIT_X_EQ(buffer_[0], 1);
++payload_start_;
CheckPictureID();
CheckTl0PicIdx();
CheckTID();
}
else
{
EXPECT_BIT_X_EQ(buffer_[0], 0);
}
EXPECT_BIT_N_EQ(buffer_[0], 0);
EXPECT_BIT_S_EQ(buffer_[0], frag_start);
// Check partition index.
if (part_id < 0)
{
// (Payload data is the same as the partition index.)
EXPECT_EQ(buffer_[0] & 0x0F, buffer_[payload_start_]);
}
else
{
EXPECT_EQ(buffer_[0] & 0x0F, part_id);
}
}
void RtpFormatVp8Test::CheckPictureID()
{
if (hdr_info_.pictureId != webrtc::kNoPictureId)
{
EXPECT_BIT_I_EQ(buffer_[1], 1);
if (hdr_info_.pictureId > 0x7F) if (hdr_info_.pictureId > 0x7F)
{ {
EXPECT_BIT_EQ(buffer_[1], 7, 1); EXPECT_BIT_EQ(buffer_[payload_start_], 7, 1);
EXPECT_EQ(buffer_[1] & 0x7F, EXPECT_EQ(buffer_[payload_start_] & 0x7F,
(hdr_info_.pictureId >> 8) & 0x7F); (hdr_info_.pictureId >> 8) & 0x7F);
EXPECT_EQ(buffer_[2], hdr_info_.pictureId & 0xFF); EXPECT_EQ(buffer_[payload_start_ + 1],
hdr_info_.pictureId & 0xFF);
payload_start_ += 2; payload_start_ += 2;
} }
else else
{ {
EXPECT_BIT_EQ(buffer_[1], 7, 0); EXPECT_BIT_EQ(buffer_[payload_start_], 7, 0);
EXPECT_EQ(buffer_[1] & 0x7F, EXPECT_EQ(buffer_[payload_start_] & 0x7F,
(hdr_info_.pictureId) & 0x7F); (hdr_info_.pictureId) & 0x7F);
payload_start_ += 1; payload_start_ += 1;
} }
} }
EXPECT_BIT_N_EQ(buffer_[0], 0); else
WebRtc_UWord8 fi = 0x03; {
if (frag_start) fi = fi & 0x01; EXPECT_BIT_I_EQ(buffer_[1], 0);
if (frag_end) fi = fi & 0x02; }
EXPECT_FI_EQ(buffer_[0], fi); }
if (first_in_frame) EXPECT_BIT_B_EQ(buffer_[0], 1);
void RtpFormatVp8Test::CheckTl0PicIdx()
{
if (hdr_info_.tl0PicIdx != webrtc::kNoTl0PicIdx)
{
EXPECT_BIT_L_EQ(buffer_[1], 1);
EXPECT_EQ(buffer_[payload_start_], hdr_info_.tl0PicIdx);
++payload_start_;
}
else
{
EXPECT_BIT_L_EQ(buffer_[1], 0);
}
}
void RtpFormatVp8Test::CheckTID()
{
if (hdr_info_.temporalIdx != webrtc::kNoTemporalIdx)
{
EXPECT_BIT_T_EQ(buffer_[1], 1);
EXPECT_TID_EQ(buffer_[payload_start_], hdr_info_.temporalIdx);
EXPECT_EQ(buffer_[payload_start_] & 0x1F, 0);
++payload_start_;
}
else
{
EXPECT_BIT_T_EQ(buffer_[1], 0);
}
} }
void RtpFormatVp8Test::CheckPayload(int payload_end) void RtpFormatVp8Test::CheckPayload(int payload_end)
@ -125,10 +204,22 @@ void RtpFormatVp8Test::CheckLast(bool last) const
} }
void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last, void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last,
bool first_in_frame, bool frag_start, bool frag_end) bool first_in_frame, bool frag_start)
{ {
EXPECT_EQ(send_bytes, expect_bytes); EXPECT_EQ(send_bytes, expect_bytes);
CheckHeader(first_in_frame, frag_start, frag_end); CheckHeader(first_in_frame, frag_start, -1);
CheckPayload(send_bytes);
CheckLast(last);
}
void RtpFormatVp8Test::CheckPacketZeroPartId(int send_bytes,
int expect_bytes,
bool last,
bool first_in_frame,
bool frag_start)
{
EXPECT_EQ(send_bytes, expect_bytes);
CheckHeader(first_in_frame, frag_start, 0);
CheckPayload(send_bytes); CheckPayload(send_bytes);
CheckLast(last); CheckLast(last);
} }
@ -143,57 +234,51 @@ TEST_F(RtpFormatVp8Test, TestStrictMode)
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_, *fragmentation_, webrtc::kStrict); hdr_info_, *fragmentation_, webrtc::kStrict);
// get first packet, expect balanced size = same as second packet // get first packet, expect balanced size ~= same as second packet
EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last, CheckPacket(send_bytes, 8, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ false);
first_in_frame = false; first_in_frame = false;
// get second packet // get second packet
EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last, CheckPacket(send_bytes, 7, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ true);
// Second partition // Second partition
// Get first (and only) packet // Get first (and only) packet
EXPECT_EQ(1, packetizer.NextPacket(20, buffer_, &send_bytes, &last)); EXPECT_EQ(1, packetizer.NextPacket(20, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 11, last, CheckPacket(send_bytes, 11, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ true);
// Third partition // Third partition
// Get first packet (of four) // Get first packet (of four)
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 4, last, CheckPacket(send_bytes, 4, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ false);
// Get second packet (of four) // Get second packet (of four)
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 3, last, CheckPacket(send_bytes, 3, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ false);
// Get third packet (of four) // Get third packet (of four)
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 4, last, CheckPacket(send_bytes, 4, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ false);
// Get fourth and last packet // Get fourth and last packet
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 3, last, CheckPacket(send_bytes, 3, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ true);
} }
TEST_F(RtpFormatVp8Test, TestAggregateMode) TEST_F(RtpFormatVp8Test, TestAggregateMode)
@ -207,29 +292,34 @@ TEST_F(RtpFormatVp8Test, TestAggregateMode)
hdr_info_, *fragmentation_, webrtc::kAggregate); hdr_info_, *fragmentation_, webrtc::kAggregate);
// get first packet // get first packet
// first half of first partition // first part of first partition (balanced fragments are expected)
EXPECT_EQ(0, packetizer.NextPacket(6, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 6, last, CheckPacket(send_bytes, 5, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ false);
first_in_frame = false; first_in_frame = false;
// get second packet // get second packet
// second half of first partition // second fragment of first partition
EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last, CheckPacket(send_bytes, 5, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ true);
// get third packet // get third packet
// third fragment of first partition
EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 5, last,
first_in_frame,
/* frag_start */ false);
// get fourth packet
// last two partitions aggregated // last two partitions aggregated
EXPECT_EQ(1, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); EXPECT_EQ(1, packetizer.NextPacket(25, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 21, last, CheckPacket(send_bytes, 21, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ true);
} }
TEST_F(RtpFormatVp8Test, TestSloppyMode) TEST_F(RtpFormatVp8Test, TestSloppyMode)
@ -246,8 +336,7 @@ TEST_F(RtpFormatVp8Test, TestSloppyMode)
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacket(send_bytes, 9, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ false);
first_in_frame = false; first_in_frame = false;
// get second packet // get second packet
@ -255,24 +344,22 @@ TEST_F(RtpFormatVp8Test, TestSloppyMode)
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacket(send_bytes, 9, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ false);
// get third packet // get third packet
// fragments of second and third partitions // fragments of second and third partitions
EXPECT_EQ(1, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(1, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacket(send_bytes, 9, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ false);
// get fourth packet // get fourth packet
// second half of last partition // second half of last partition
EXPECT_EQ(2, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(2, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last, CheckPacket(send_bytes, 7, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ true);
} }
// Verify that sloppy mode is forced if fragmentation info is missing. // Verify that sloppy mode is forced if fragmentation info is missing.
@ -287,43 +374,39 @@ TEST_F(RtpFormatVp8Test, TestSloppyModeFallback)
hdr_info_); hdr_info_);
// get first packet // get first packet
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacketZeroPartId(send_bytes, 10, last,
first_in_frame, first_in_frame,
/* frag_start */ true, /* frag_start */ true);
/* frag_end */ false);
first_in_frame = false; first_in_frame = false;
// get second packet // get second packet
// fragments of first and second partitions // fragments of first and second partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacketZeroPartId(send_bytes, 10, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ false);
// get third packet // get third packet
// fragments of second and third partitions // fragments of second and third partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacketZeroPartId(send_bytes, 10, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ false);
// get fourth packet // get fourth packet
// second half of last partition // second half of last partition
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last, CheckPacketZeroPartId(send_bytes, 7, last,
first_in_frame, first_in_frame,
/* frag_start */ false, /* frag_start */ false);
/* frag_end */ true);
} }
// Verify that non-reference bit is set. // Verify that non-reference bit is set.
TEST_F(RtpFormatVp8Test, TestNonReferenceBit) { TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
int send_bytes = 0; int send_bytes = 0;
bool last; bool last;
bool first_in_frame = true;
hdr_info_.nonReference = true; hdr_info_.nonReference = true;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
@ -340,6 +423,25 @@ TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
EXPECT_BIT_N_EQ(buffer_[0], 1); EXPECT_BIT_N_EQ(buffer_[0], 1);
} }
// Verify Tl0PicIdx and TID fields
TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) {
int send_bytes = 0;
bool last;
hdr_info_.tl0PicIdx = 117;
hdr_info_.temporalIdx = 2;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_, *fragmentation_, webrtc::kAggregate);
// get first and only packet
EXPECT_EQ(0, packetizer.NextPacket(kBufferSize, buffer_, &send_bytes,
&last));
bool first_in_frame = true;
CheckPacket(send_bytes, kPayloadSize + 4, last,
first_in_frame,
/* frag_start */ true);
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);

View File

@ -14,7 +14,6 @@
#include "rtp_receiver_video.h" #include "rtp_receiver_video.h"
#include "trace.h"
#include "critical_section_wrapper.h" #include "critical_section_wrapper.h"
#include "tick_util.h" #include "tick_util.h"
@ -466,7 +465,8 @@ RTPReceiverVideo::ReceiveH263Codec(WebRtcRTPHeader* rtpHeader,
{ {
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH263Video, ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH263Video,
payloadData, payloadData,
payloadDataLength); payloadDataLength,
_id);
ModuleRTPUtility::RTPPayload parsedPacket; ModuleRTPUtility::RTPPayload parsedPacket;
const bool success = rtpPayloadParser.Parse(parsedPacket); const bool success = rtpPayloadParser.Parse(parsedPacket);
@ -491,7 +491,8 @@ RTPReceiverVideo::ReceiveH2631998Codec(WebRtcRTPHeader* rtpHeader,
{ {
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH2631998Video, ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH2631998Video,
payloadData, payloadData,
payloadDataLength); payloadDataLength,
_id);
ModuleRTPUtility::RTPPayload parsedPacket; ModuleRTPUtility::RTPPayload parsedPacket;
const bool success = rtpPayloadParser.Parse(parsedPacket); const bool success = rtpPayloadParser.Parse(parsedPacket);
@ -581,7 +582,8 @@ RTPReceiverVideo::ReceiveMPEG4Codec(WebRtcRTPHeader* rtpHeader,
{ {
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpMpeg4Video, ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpMpeg4Video,
payloadData, payloadData,
payloadDataLength); payloadDataLength,
_id);
ModuleRTPUtility::RTPPayload parsedPacket; ModuleRTPUtility::RTPPayload parsedPacket;
const bool success = rtpPayloadParser.Parse(parsedPacket); const bool success = rtpPayloadParser.Parse(parsedPacket);
@ -612,7 +614,8 @@ RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtpHeader,
{ {
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpVp8Video, ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpVp8Video,
payloadData, payloadData,
payloadDataLength); payloadDataLength,
_id);
ModuleRTPUtility::RTPPayload parsedPacket; ModuleRTPUtility::RTPPayload parsedPacket;
const bool success = rtpPayloadParser.Parse(parsedPacket); const bool success = rtpPayloadParser.Parse(parsedPacket);
@ -631,10 +634,17 @@ RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtpHeader,
} }
rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta; rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta;
rtpHeader->type.Video.codecHeader.VP8.startBit = parsedPacket.info.VP8.startFragment; // Start of partition RTPVideoHeaderVP8 *toHeader = &rtpHeader->type.Video.codecHeader.VP8;
rtpHeader->type.Video.codecHeader.VP8.stopBit= parsedPacket.info.VP8.stopFragment; // Stop of partition ModuleRTPUtility::RTPPayloadVP8 *fromHeader = &parsedPacket.info.VP8;
rtpHeader->type.Video.isFirstPacket = parsedPacket.info.VP8.beginningOfFrame; rtpHeader->type.Video.isFirstPacket = fromHeader->beginningOfPartition
&& (fromHeader->partitionID == 0);
toHeader->pictureId = fromHeader->hasPictureID ? fromHeader->pictureID :
kNoPictureId;
toHeader->tl0PicIdx = fromHeader->hasTl0PicIdx ? fromHeader->tl0PicIdx :
kNoTl0PicIdx;
toHeader->temporalIdx = fromHeader->hasTID ? fromHeader->tID :
kNoTemporalIdx;
if(CallbackOfReceivedPayloadData(parsedPacket.info.VP8.data, if(CallbackOfReceivedPayloadData(parsedPacket.info.VP8.data,
parsedPacket.info.VP8.dataLength, parsedPacket.info.VP8.dataLength,

View File

@ -23,10 +23,25 @@
'.', '.',
], ],
'sources': [ 'sources': [
'rtp_format_vp8_unittest.h',
'rtp_format_vp8_unittest.cc', 'rtp_format_vp8_unittest.cc',
], ],
}, },
{
'target_name': 'rtp_utility_test',
'type': 'executable',
'dependencies': [
'rtp_rtcp.gyp:rtp_rtcp',
'../../../../testing/gtest.gyp:gtest',
'../../../../testing/gtest.gyp:gtest_main',
],
'include_dirs': [
'.',
],
'sources': [
'rtp_utility_test.cc',
],
},
], ],
} }

View File

@ -14,6 +14,8 @@
#include <cmath> // ceil #include <cmath> // ceil
#include <cassert> #include <cassert>
#include "trace.h"
#if defined(_WIN32) #if defined(_WIN32)
#include <Windows.h> // FILETIME #include <Windows.h> // FILETIME
#include <WinSock.h> // timeval #include <WinSock.h> // timeval
@ -374,12 +376,15 @@ ModuleRTPUtility::RTPPayload::SetType(RtpVideoCodecTypes videoType)
} }
case kRtpVp8Video: case kRtpVp8Video:
{ {
info.VP8.beginningOfFrame = false;
info.VP8.nonReferenceFrame = false; info.VP8.nonReferenceFrame = false;
info.VP8.beginningOfPartition = false;
info.VP8.partitionID = 0;
info.VP8.hasPictureID = false; info.VP8.hasPictureID = false;
info.VP8.fragments = false; info.VP8.hasTl0PicIdx = false;
info.VP8.startFragment = false; info.VP8.hasTID = false;
info.VP8.stopFragment = false; info.VP8.pictureID = -1;
info.VP8.tl0PicIdx = -1;
info.VP8.tID = -1;
break; break;
} }
default: default:
@ -581,10 +586,13 @@ ModuleRTPUtility::RTPHeaderParser::Parse(WebRtcRTPHeader& parsedPacket) const
} }
// RTP payload parser // RTP payload parser
ModuleRTPUtility::RTPPayloadParser::RTPPayloadParser(const RtpVideoCodecTypes videoType, ModuleRTPUtility::RTPPayloadParser::RTPPayloadParser(
const WebRtc_UWord8* payloadData, const RtpVideoCodecTypes videoType,
const WebRtc_UWord16 payloadDataLength) const WebRtc_UWord8* payloadData,
const WebRtc_UWord16 payloadDataLength,
const WebRtc_Word32 id)
: :
_id(id),
_dataPtr(payloadData), _dataPtr(payloadData),
_dataLength(payloadDataLength), _dataLength(payloadDataLength),
_videoType(videoType) _videoType(videoType)
@ -793,63 +801,176 @@ ModuleRTPUtility::RTPPayloadParser::ParseMPEG4(
return true; return true;
} }
//
// VP8 format:
//
// Payload descriptor
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |X|R|N|S|PartID | (REQUIRED)
// +-+-+-+-+-+-+-+-+
// X: |I|L|T| RSV-A | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// I: | PictureID | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// L: | TL0PICIDX | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// T: | TID | RSV-B | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
//
// Payload header (considered part of the actual payload, sent to decoder)
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |Size0|H| VER |P|
// +-+-+-+-+-+-+-+-+
// | ... |
// + +
bool bool
ModuleRTPUtility::RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const ModuleRTPUtility::RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const
{ {
parsedPacket.info.VP8.hasPictureID = (_dataPtr[0] & 0x10)?true:false; RTPPayloadVP8 *vp8 = &parsedPacket.info.VP8;
parsedPacket.info.VP8.nonReferenceFrame = (_dataPtr[0] & 0x08)?true:false; const WebRtc_UWord8 *dataPtr = _dataPtr;
parsedPacket.info.VP8.fragments = (_dataPtr[0] & 0x06)?true:false; int dataLength = _dataLength;
parsedPacket.info.VP8.beginningOfFrame = (_dataPtr[0] & 0x01)?true:false;
if(parsedPacket.info.VP8.fragments) // Parse mandatory first byte of payload descriptor
bool extension = (*dataPtr & 0x80); // X bit
vp8->nonReferenceFrame = (*dataPtr & 0x20); // N bit
vp8->beginningOfPartition = (*dataPtr & 0x10); // S bit
vp8->partitionID = (*dataPtr & 0x0F); // PartID field
// Advance dataPtr and decrease remaining payload size
dataPtr++;
dataLength--;
if (extension)
{ {
WebRtc_UWord8 fragments = (_dataPtr[0] >> 1) & 0x03; const int parsedBytes = ParseVP8Extension(vp8, dataPtr, dataLength);
if( fragments == 1) if (parsedBytes < 0) return false;
{ dataPtr += parsedBytes;
parsedPacket.info.VP8.startFragment = true; dataLength -= parsedBytes;
parsedPacket.info.VP8.stopFragment = false;
} else if(fragments == 3)
{
parsedPacket.info.VP8.startFragment = false;
parsedPacket.info.VP8.stopFragment = true;
} else
{
parsedPacket.info.VP8.startFragment = false;
parsedPacket.info.VP8.stopFragment = false;
}
} else
{
parsedPacket.info.VP8.startFragment = true;
parsedPacket.info.VP8.stopFragment = true;
} }
if(parsedPacket.info.VP8.hasPictureID)
{
WebRtc_UWord8 numBytesPictureId = 1;
while(_dataPtr[numBytesPictureId] & 0x80)
{
numBytesPictureId++;
}
parsedPacket.frameType = (_dataPtr[1+numBytesPictureId] & 0x01) ? kPFrame : kIFrame; // first bit after picture id if (dataLength <= 0)
if(!parsedPacket.info.VP8.startFragment)
{
// if not start fragment parse away all picture IDs
parsedPacket.info.VP8.hasPictureID = false;
parsedPacket.info.VP8.data = _dataPtr+numBytesPictureId;
parsedPacket.info.VP8.dataLength = _dataLength-numBytesPictureId;
return true;
}
} else
{ {
parsedPacket.frameType = (_dataPtr[1] & 0x01) ? kPFrame : kIFrame; // first bit after picture id WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"Error parsing VP8 payload descriptor; payload too short");
return false;
} }
parsedPacket.info.VP8.data = _dataPtr+1;
parsedPacket.info.VP8.dataLength = _dataLength-1; // Read P bit from payload header (only at beginning of first partition)
if (dataLength > 0 && vp8->beginningOfPartition && vp8->partitionID == 0)
{
parsedPacket.frameType = (*dataPtr & 0x01) ? kPFrame : kIFrame;
}
else
{
parsedPacket.frameType = kPFrame;
}
parsedPacket.info.VP8.data = dataPtr;
parsedPacket.info.VP8.dataLength = dataLength;
return true; return true;
} }
int
ModuleRTPUtility::RTPPayloadParser::ParseVP8Extension(
RTPPayloadVP8 *vp8,
const WebRtc_UWord8 *dataPtr,
int dataLength) const
{
int parsedBytes = 0;
if (dataLength <= 0) return -1;
// Optional X field is present
vp8->hasPictureID = (*dataPtr & 0x80); // I bit
vp8->hasTl0PicIdx = (*dataPtr & 0x40); // L bit
vp8->hasTID = (*dataPtr & 0x20); // T bit
// Advance dataPtr and decrease remaining payload size
dataPtr++;
parsedBytes++;
dataLength--;
if (vp8->hasPictureID)
{
if (ParseVP8PictureID(vp8, &dataPtr, &dataLength, &parsedBytes) != 0)
{
return -1;
}
}
if (vp8->hasTl0PicIdx)
{
if (ParseVP8Tl0PicIdx(vp8, &dataPtr, &dataLength, &parsedBytes) != 0)
{
return -1;
}
}
if (vp8->hasTID)
{
if (ParseVP8TID(vp8, &dataPtr, &dataLength, &parsedBytes) != 0)
{
return -1;
}
}
return parsedBytes;
}
int
ModuleRTPUtility::RTPPayloadParser::ParseVP8PictureID(
RTPPayloadVP8 *vp8,
const WebRtc_UWord8 **dataPtr,
int *dataLength,
int *parsedBytes) const
{
if (*dataLength <= 0) return -1;
vp8->pictureID = (**dataPtr & 0x7F);
if (**dataPtr & 0x80)
{
(*dataPtr)++;
(*parsedBytes)++;
if (--(*dataLength) <= 0) return -1;
// PictureID is 15 bits
vp8->pictureID = (vp8->pictureID << 8) + **dataPtr;
}
(*dataPtr)++;
(*parsedBytes)++;
(*dataLength)--;
return 0;
}
int
ModuleRTPUtility::RTPPayloadParser::ParseVP8Tl0PicIdx(
RTPPayloadVP8 *vp8,
const WebRtc_UWord8 **dataPtr,
int *dataLength,
int *parsedBytes) const
{
if (*dataLength <= 0) return -1;
vp8->tl0PicIdx = **dataPtr;
(*dataPtr)++;
(*parsedBytes)++;
(*dataLength)--;
return 0;
}
int ModuleRTPUtility::RTPPayloadParser::ParseVP8TID(
RTPPayloadVP8 *vp8,
const WebRtc_UWord8 **dataPtr,
int *dataLength,
int *parsedBytes) const
{
if (*dataLength <= 0) return -1;
vp8->tID = ((**dataPtr >> 5) & 0x07);
(*dataPtr)++;
(*parsedBytes)++;
(*dataLength)--;
return 0;
}
bool bool
ModuleRTPUtility::RTPPayloadParser::H263PictureStartCode(const WebRtc_UWord8* data, const bool skipFirst2bytes) const ModuleRTPUtility::RTPPayloadParser::H263PictureStartCode(const WebRtc_UWord8* data, const bool skipFirst2bytes) const
{ {

View File

@ -139,12 +139,15 @@ namespace ModuleRTPUtility
}; };
struct RTPPayloadVP8 struct RTPPayloadVP8
{ {
bool beginningOfFrame;
bool nonReferenceFrame; bool nonReferenceFrame;
bool beginningOfPartition;
int partitionID;
bool hasPictureID; bool hasPictureID;
bool fragments; bool hasTl0PicIdx;
bool startFragment; bool hasTID;
bool stopFragment; int pictureID;
int tl0PicIdx;
int tID;
const WebRtc_UWord8* data; const WebRtc_UWord8* data;
WebRtc_UWord16 dataLength; WebRtc_UWord16 dataLength;
@ -172,7 +175,8 @@ namespace ModuleRTPUtility
public: public:
RTPPayloadParser(const RtpVideoCodecTypes payloadType, RTPPayloadParser(const RtpVideoCodecTypes payloadType,
const WebRtc_UWord8* payloadData, const WebRtc_UWord8* payloadData,
const WebRtc_UWord16 payloadDataLength); // Length w/o padding. const WebRtc_UWord16 payloadDataLength, // Length w/o padding.
const WebRtc_Word32 id);
~RTPPayloadParser(); ~RTPPayloadParser();
@ -188,6 +192,25 @@ namespace ModuleRTPUtility
bool ParseVP8(RTPPayload& parsedPacket) const; bool ParseVP8(RTPPayload& parsedPacket) const;
int ParseVP8Extension(RTPPayloadVP8 *vp8,
const WebRtc_UWord8 *dataPtr,
int dataLength) const;
int ParseVP8PictureID(RTPPayloadVP8 *vp8,
const WebRtc_UWord8 **dataPtr,
int *dataLength,
int *parsedBytes) const;
int ParseVP8Tl0PicIdx(RTPPayloadVP8 *vp8,
const WebRtc_UWord8 **dataPtr,
int *dataLength,
int *parsedBytes) const;
int ParseVP8TID(RTPPayloadVP8 *vp8,
const WebRtc_UWord8 **dataPtr,
int *dataLength,
int *parsedBytes) const;
// H.263 // H.263
bool H263PictureStartCode(const WebRtc_UWord8* data, bool H263PictureStartCode(const WebRtc_UWord8* data,
const bool skipFirst2bytes = false) const; const bool skipFirst2bytes = false) const;
@ -199,6 +222,7 @@ namespace ModuleRTPUtility
FrameTypes GetH263FrameType(const WebRtc_UWord8* inputVideoBuffer) const; FrameTypes GetH263FrameType(const WebRtc_UWord8* inputVideoBuffer) const;
private: private:
WebRtc_Word32 _id;
const WebRtc_UWord8* _dataPtr; const WebRtc_UWord8* _dataPtr;
const WebRtc_UWord16 _dataLength; const WebRtc_UWord16 _dataLength;
const RtpVideoCodecTypes _videoType; const RtpVideoCodecTypes _videoType;

View File

@ -0,0 +1,269 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* This file includes unit tests for the ModuleRTPUtility.
*/
#include <gtest/gtest.h>
#include "typedefs.h"
#include "rtp_utility.h"
#include "rtp_format_vp8.h"
namespace {
using webrtc::ModuleRTPUtility::RTPPayloadParser;
using webrtc::ModuleRTPUtility::RTPPayload;
using webrtc::ModuleRTPUtility::RTPPayloadVP8;
using webrtc::RtpVideoCodecTypes;
// Payload descriptor
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |X|R|N|S|PartID | (REQUIRED)
// +-+-+-+-+-+-+-+-+
// X: |I|L|T| RSV-A | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// I: | PictureID | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// L: | TL0PICIDX | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
// T: | TID | RSV-B | (OPTIONAL)
// +-+-+-+-+-+-+-+-+
//
// Payload header
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |Size0|H| VER |P|
// +-+-+-+-+-+-+-+-+
// | Size1 |
// +-+-+-+-+-+-+-+-+
// | Size2 |
// +-+-+-+-+-+-+-+-+
// | Bytes 4..N of |
// | VP8 payload |
// : :
// +-+-+-+-+-+-+-+-+
// | OPTIONAL RTP |
// | padding |
// : :
// +-+-+-+-+-+-+-+-+
void VerifyBasicHeader(const RTPPayloadVP8 &header, bool N, bool S, int PartID)
{
EXPECT_EQ(N, header.nonReferenceFrame);
EXPECT_EQ(S, header.beginningOfPartition);
EXPECT_EQ(PartID, header.partitionID);
}
void VerifyExtensions(const RTPPayloadVP8 &header, bool I, bool L, bool T)
{
EXPECT_EQ(I, header.hasPictureID);
EXPECT_EQ(L, header.hasTl0PicIdx);
EXPECT_EQ(T, header.hasTID);
}
TEST(ParseVP8Test, BasicHeader) {
WebRtc_UWord8 payload[4] = {0};
payload[0] = 0x14; // binary 0001 0100; S = 1, PartID = 4
payload[1] = 0x01; // P frame
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 4, 0);
RTPPayload parsedPacket;
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 4 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 0 /*T*/);
EXPECT_EQ(payload + 1, parsedPacket.info.VP8.data);
EXPECT_EQ(4 - 1, parsedPacket.info.VP8.dataLength);
}
TEST(ParseVP8Test, PictureID) {
WebRtc_UWord8 payload[10] = {0};
payload[0] = 0xA0;
payload[1] = 0x80;
payload[2] = 17;
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
RTPPayload parsedPacket;
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/);
EXPECT_EQ(17, parsedPacket.info.VP8.pictureID);
EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data);
EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength);
// Re-use payload, but change to long PictureID
payload[2] = 0x80 | 17;
payload[3] = 17;
RTPPayloadParser rtpPayloadParser2(webrtc::kRtpVp8Video, payload, 10, 0);
ASSERT_TRUE(rtpPayloadParser2.Parse(parsedPacket));
VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/);
EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID);
EXPECT_EQ(payload + 4, parsedPacket.info.VP8.data);
EXPECT_EQ(10 - 4, parsedPacket.info.VP8.dataLength);
}
TEST(ParseVP8Test, Tl0PicIdx) {
WebRtc_UWord8 payload[10] = {0};
payload[0] = 0x90;
payload[1] = 0x40;
payload[2] = 17;
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
RTPPayload parsedPacket;
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
EXPECT_EQ(webrtc::ModuleRTPUtility::kIFrame, parsedPacket.frameType);
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 0 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 1 /*L*/, 0 /*T*/);
EXPECT_EQ(17, parsedPacket.info.VP8.tl0PicIdx);
EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data);
EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength);
}
TEST(ParseVP8Test, TID) {
WebRtc_UWord8 payload[10] = {0};
payload[0] = 0x88;
payload[1] = 0x20;
payload[2] = 0x40;
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
RTPPayload parsedPacket;
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 1 /*T*/);
EXPECT_EQ(2, parsedPacket.info.VP8.tID);
EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data);
EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength);
}
TEST(ParseVP8Test, MultipleExtensions) {
WebRtc_UWord8 payload[10] = {0};
payload[0] = 0x88;
payload[1] = 0x80 | 0x40 | 0x20;
payload[2] = 0x80 | 17; // PictureID, high 7 bits
payload[3] = 17; // PictureID, low 8 bits
payload[4] = 42; // Tl0PicIdx
payload[5] = 0x40; // TID
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
RTPPayload parsedPacket;
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 1 /*L*/, 1 /*T*/);
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(payload + 6, parsedPacket.info.VP8.data);
EXPECT_EQ(10 - 6, parsedPacket.info.VP8.dataLength);
}
TEST(ParseVP8Test, TooShortHeader) {
WebRtc_UWord8 payload[4] = {0};
payload[0] = 0x88;
payload[1] = 0x80 | 0x40 | 0x20; // All extensions are enabled
payload[2] = 0x80 | 17; //... but only 2 bytes PictureID is provided
payload[3] = 17; // PictureID, low 8 bits
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 4, 0);
RTPPayload parsedPacket;
EXPECT_FALSE(rtpPayloadParser.Parse(parsedPacket));
}
using webrtc::RtpFormatVp8;
using webrtc::RTPVideoHeaderVP8;
TEST(ParseVP8Test, TestWithPacketizer) {
WebRtc_UWord8 payload[10] = {0};
WebRtc_UWord8 packet[20] = {0};
RTPVideoHeaderVP8 inputHeader;
inputHeader.nonReference = true;
inputHeader.pictureId = 300;
inputHeader.temporalIdx = 1;
inputHeader.tl0PicIdx = -1; // disable
RtpFormatVp8 packetizer = RtpFormatVp8(payload, 10, inputHeader);
bool last;
int send_bytes;
ASSERT_EQ(0, packetizer.NextPacket(20, packet, &send_bytes, &last));
ASSERT_TRUE(last);
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, packet, send_bytes,
0);
RTPPayload parsedPacket;
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
EXPECT_EQ(webrtc::ModuleRTPUtility::kIFrame, parsedPacket.frameType);
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
VerifyBasicHeader(parsedPacket.info.VP8,
inputHeader.nonReference /*N*/,
1 /*S*/,
0 /*PartID*/);
VerifyExtensions(parsedPacket.info.VP8,
1 /*I*/,
0 /*L*/,
1 /*T*/);
EXPECT_EQ(inputHeader.pictureId, parsedPacket.info.VP8.pictureID);
EXPECT_EQ(inputHeader.temporalIdx, parsedPacket.info.VP8.tID);
EXPECT_EQ(packet + 5, parsedPacket.info.VP8.data);
EXPECT_EQ(send_bytes - 5, parsedPacket.info.VP8.dataLength);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace

View File

@ -201,7 +201,7 @@ public:
// WEBRTC_VIDEO_CODEC_ERR_PARAMETER // WEBRTC_VIDEO_CODEC_ERR_PARAMETER
virtual WebRtc_Word32 Decode(const EncodedImage& inputImage, virtual WebRtc_Word32 Decode(const EncodedImage& inputImage,
bool missingFrames, bool missingFrames,
const CodecSpecificInfo* /*codecSpecificInfo*/, const CodecSpecificInfo* codecSpecificInfo,
WebRtc_Word64 /*renderTimeMs*/); WebRtc_Word64 /*renderTimeMs*/);
// Register a decode complete callback object. // Register a decode complete callback object.

View File

@ -694,7 +694,7 @@ VP8Decoder::InitDecode(const VideoCodec* inst,
WebRtc_Word32 WebRtc_Word32
VP8Decoder::Decode(const EncodedImage& inputImage, VP8Decoder::Decode(const EncodedImage& inputImage,
bool missingFrames, bool missingFrames,
const CodecSpecificInfo* /*codecSpecificInfo*/, const CodecSpecificInfo* codecSpecificInfo,
WebRtc_Word64 /*renderTimeMs*/) WebRtc_Word64 /*renderTimeMs*/)
{ {
if (!_inited) if (!_inited)
@ -727,16 +727,6 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
vpx_dec_iter_t _iter = NULL; vpx_dec_iter_t _iter = NULL;
vpx_image_t* img; vpx_image_t* img;
// scan for number of bytes used for picture ID
WebRtc_UWord64 pictureID = inputImage._buffer[0] & 0x7F;
WebRtc_UWord8 numberOfBytes = 1;
if (inputImage._buffer[0] & 0x80)
{
pictureID <<= 8;
pictureID += inputImage._buffer[1];
++numberOfBytes;
}
// check for missing frames // check for missing frames
if (missingFrames) if (missingFrames)
{ {
@ -749,8 +739,8 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
// we remove the picture ID here // we remove the picture ID here
if (vpx_codec_decode(_decoder, if (vpx_codec_decode(_decoder,
inputImage._buffer + numberOfBytes, inputImage._buffer,
inputImage._length - numberOfBytes, inputImage._length,
0, 0,
VPX_DL_REALTIME)) VPX_DL_REALTIME))
{ {
@ -761,7 +751,7 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
if (inputImage._frameType == kKeyFrame) if (inputImage._frameType == kKeyFrame)
{ {
// Reduce size due to PictureID that we won't copy. // Reduce size due to PictureID that we won't copy.
const WebRtc_UWord32 bytesToCopy = inputImage._length - numberOfBytes; const WebRtc_UWord32 bytesToCopy = inputImage._length;
if (_lastKeyFrame._size < bytesToCopy) if (_lastKeyFrame._size < bytesToCopy)
{ {
delete [] _lastKeyFrame._buffer; delete [] _lastKeyFrame._buffer;
@ -781,8 +771,7 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
_lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size]; _lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size];
} }
// Copy encoded frame. // Copy encoded frame.
memcpy(_lastKeyFrame._buffer, inputImage._buffer + numberOfBytes, memcpy(_lastKeyFrame._buffer, inputImage._buffer, bytesToCopy);
bytesToCopy);
_lastKeyFrame._length = bytesToCopy; _lastKeyFrame._length = bytesToCopy;
} }
@ -854,15 +843,22 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
// TODO(pw): how do we know it's a golden or alt reference frame? libvpx will // TODO(pw): how do we know it's a golden or alt reference frame? libvpx will
// provide an API for now I added it temporarily // provide an API for now I added it temporarily
if((lastRefUpdates & VP8_GOLD_FRAME) || (lastRefUpdates & VP8_ALTR_FRAME)) WebRtc_Word16 pictureId = codecSpecificInfo->codecSpecific.VP8.pictureId;
if (codecSpecificInfo && pictureId > -1)
{ {
if (!missingFrames && (inputImage._completeFrame == true)) if ((lastRefUpdates & VP8_GOLD_FRAME)
//if (!corrupted) // TODO(pw): Can we engage this line intead of the above? || (lastRefUpdates & VP8_ALTR_FRAME))
{ {
_decodeCompleteCallback->ReceivedDecodedReferenceFrame(pictureID); if (!missingFrames && (inputImage._completeFrame == true))
//if (!corrupted) // TODO(pw): Can we engage this line instead of
// the above?
{
_decodeCompleteCallback->ReceivedDecodedReferenceFrame(
pictureId);
}
} }
_decodeCompleteCallback->ReceivedDecodedFrame(pictureId);
} }
_decodeCompleteCallback->ReceivedDecodedFrame(pictureID);
#ifdef DEV_PIC_LOSS #ifdef DEV_PIC_LOSS
if (corrupted) if (corrupted)

View File

@ -248,14 +248,8 @@ void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info,
switch (info.codecType) switch (info.codecType)
{ {
case kVideoCodecVP8: { case kVideoCodecVP8: {
if (info.codecSpecific.VP8.pictureId < 0) (*rtp)->VP8.InitRTPVideoHeaderVP8();
{ (*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId;
(*rtp)->VP8.pictureId = kNoPictureId;
}
else
{
(*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId;
}
(*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference; (*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference;
return; return;
} }