Add PictureID and NonReference to codec information

The PictureID and NonReference information is now routed from the
encoder to the RTP packetizer through CodecSpecificInfo and 
RTPVideoHeaderVP8.
Review URL: http://webrtc-codereview.appspot.com/51003

git-svn-id: http://webrtc.googlecode.com/svn/trunk@155 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
hlundin@google.com 2011-07-07 08:04:23 +00:00
parent dcdf311b6c
commit f0a476bf76
11 changed files with 523 additions and 506 deletions

View File

@ -41,10 +41,14 @@ struct RTPVideoHeaderH263
bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the
// first byte of this packet
};
enum {kNoPictureId = -1};
struct RTPVideoHeaderVP8
{
bool startBit; // Start of partition
bool stopBit; // Stop of partition
bool startBit; // Start of partition.
bool stopBit; // Stop of partition.
WebRtc_Word16 pictureId; // Picture ID index, 15 bits;
// kNoPictureId if PictureID does not exist.
bool nonReference; // Frame is discardable.
};
union RTPVideoTypeHeader
{

View File

@ -11,7 +11,6 @@
#include "rtp_format_vp8.h"
#include <cassert> // assert
#include <math.h> // ceil
#include <string.h> // memcpy
namespace webrtc {
@ -20,13 +19,14 @@ namespace webrtc {
// Modes are: kStrict, kAggregate, kSloppy.
const RtpFormatVp8::AggregationMode RtpFormatVp8::aggr_modes_[kNumModes] =
{ kAggrNone, kAggrPartitions, kAggrFragments };
const bool RtpFormatVp8::bal_modes_[kNumModes] =
const bool RtpFormatVp8::balance_modes_[kNumModes] =
{ true, false, false };
const bool RtpFormatVp8::sep_first_modes_[kNumModes] =
const bool RtpFormatVp8::separate_first_modes_[kNumModes] =
{ true, false, false };
RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size,
const RTPVideoHeaderVP8& hdr_info,
const RTPFragmentationHeader& fragmentation,
VP8PacketizerMode mode)
: payload_data_(payload_data),
@ -37,14 +37,16 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
first_fragment_(true),
vp8_header_bytes_(1),
aggr_mode_(aggr_modes_[mode]),
balance_(bal_modes_[mode]),
separate_first_(sep_first_modes_[mode])
balance_(balance_modes_[mode]),
separate_first_(separate_first_modes_[mode]),
hdr_info_(hdr_info)
{
part_info_ = fragmentation;
}
RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size)
WebRtc_UWord32 payload_size,
const RTPVideoHeaderVP8& hdr_info)
: payload_data_(payload_data),
payload_size_(static_cast<int>(payload_size)),
part_info_(),
@ -54,8 +56,9 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
first_fragment_(true),
vp8_header_bytes_(1),
aggr_mode_(aggr_modes_[kSloppy]),
balance_(bal_modes_[kSloppy]),
separate_first_(sep_first_modes_[kSloppy])
balance_(balance_modes_[kSloppy]),
separate_first_(separate_first_modes_[kSloppy]),
hdr_info_(hdr_info)
{
part_info_.VerifyAndAllocateFragmentationHeader(1);
part_info_.fragmentationLength[0] = payload_size;
@ -79,8 +82,7 @@ int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes,
// Balance payload sizes to produce (almost) equal size
// fragments.
// Number of fragments for remaining_bytes:
int num_frags = ceil(
static_cast<double>(remaining_bytes) / max_payload_len);
int num_frags = remaining_bytes / max_payload_len + 1;
// Number of bytes in this fragment:
return static_cast<int>(static_cast<double>(remaining_bytes)
/ num_frags + 0.5);
@ -98,8 +100,9 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
const int num_partitions = part_info_.fragmentationVectorSize;
int send_bytes = 0; // How much data to send in this packet.
bool split_payload = true; // Splitting of partitions is initially allowed.
int remaining_in_partition = part_info_.fragmentationOffset[part_ix_]
- payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_];
int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] -
payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] +
FirstHeaderExtraLength(); // Add header extra length to payload length.
int rem_payload_len = max_payload_len - vp8_header_bytes_;
while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition,
@ -137,9 +140,12 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
++part_ix_; // Advance to next partition.
}
send_bytes -= FirstHeaderExtraLength(); // Remove the extra length again.
assert(send_bytes > 0);
const bool end_of_fragment = (remaining_in_partition == 0);
// 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, end_of_fragment, buffer,
max_payload_len);
if (*bytes_to_send < 0)
{
return -1;
@ -150,46 +156,99 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
return 0;
}
int RtpFormatVp8::WriteHeaderAndPayload(int send_bytes,
int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes,
bool end_of_fragment,
WebRtc_UWord8* buffer)
WebRtc_UWord8* buffer,
int buffer_length)
{
// Write the VP8 payload header.
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// | RSV |I|N|FI |B|
// +-+-+-+-+-+-+-+-+
// 0 1 2
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RSV |I|N|FI |B| PictureID (1 or 2 octets) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
if (send_bytes < 0)
if (payload_bytes < 0)
{
return -1;
}
if (payload_bytes_sent_ + send_bytes > payload_size_)
if (payload_bytes_sent_ + payload_bytes > payload_size_)
{
return -1;
}
// PictureID always present in first packet
const int picture_id_present = beginning_;
// TODO(hlundin): must pipe this info from VP8 encoder
const int kNonrefFrame = 0;
buffer[0] = 0;
if (picture_id_present) buffer[0] |= (0x01 << 4); // I
if (kNonrefFrame) buffer[0] |= (0x01 << 3); // N
if (!first_fragment_) buffer[0] |= (0x01 << 2); // FI
if (!end_of_fragment) buffer[0] |= (0x01 << 1); // FI
if (beginning_) buffer[0] |= 0x01; // B
if (hdr_info_.nonReference) buffer[0] |= (0x01 << 3); // N
if (!first_fragment_) buffer[0] |= (0x01 << 2); // FI
if (!end_of_fragment) buffer[0] |= (0x01 << 1); // FI
if (beginning_) buffer[0] |= 0x01; // B
memcpy(&buffer[vp8_header_bytes_], &payload_data_[payload_bytes_sent_],
send_bytes);
int pic_id_len = WritePictureID(&buffer[vp8_header_bytes_],
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)
{
return -1;
}
memcpy(&buffer[vp8_header_bytes_ + pic_id_len],
&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_ += send_bytes;
payload_bytes_sent_ += payload_bytes;
// Return total length of written data.
return send_bytes + vp8_header_bytes_;
return payload_bytes + vp8_header_bytes_ + pic_id_len;
}
int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
{
const WebRtc_UWord16 pic_id =
static_cast<WebRtc_UWord16> (hdr_info_.pictureId);
int picture_id_len = PictureIdLength();
if (picture_id_len > buffer_length) return -1; // error
if (picture_id_len == 2)
{
buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F);
buffer[1] = pic_id & 0xFF;
}
else if (picture_id_len == 1)
{
buffer[0] = pic_id & 0x7F;
}
return picture_id_len;
}
int RtpFormatVp8::FirstHeaderExtraLength() const
{
if (!beginning_)
{
return 0;
}
int length = 0;
length += PictureIdLength();
return length;
}
int RtpFormatVp8::PictureIdLength() const
{
if (!beginning_ || hdr_info_.pictureId == kNoPictureId)
{
return 0;
}
if (hdr_info_.pictureId <= 0x7F)
{
return 1;
}
else
{
return 2;
}
}
} // namespace webrtc

View File

@ -47,21 +47,23 @@ public:
// The payload_data must be exactly one encoded VP8 frame.
RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size,
const RTPVideoHeaderVP8& hdr_info,
const RTPFragmentationHeader& fragmentation,
VP8PacketizerMode mode);
// Initialize without fragmentation info. Mode kSloppy will be used.
// The payload_data must be exactly one encoded VP8 frame.
RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size);
WebRtc_UWord32 payload_size,
const RTPVideoHeaderVP8& hdr_info);
// Get the next payload with VP8 payload header.
// max_payload_len limits the sum length of payload and VP8 payload header.
// buffer is a pointer to where the output will be written.
// bytes_to_send is an output variable that will contain number of bytes
// written to buffer.
// Returns true for the last packet of the frame, false otherwise (i.e.,
// call the function again to get the next packet).
// written to buffer. Parameter last_packet is true for the last packet of
// the frame, false otherwise (i.e., call the function again to get the
// next packet). Returns negative on error, zero otherwise.
int NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send, bool* last_packet);
@ -74,8 +76,8 @@ private:
};
static const AggregationMode aggr_modes_[kNumModes];
static const bool bal_modes_[kNumModes];
static const bool sep_first_modes_[kNumModes];
static const bool balance_modes_[kNumModes];
static const bool separate_first_modes_[kNumModes];
// Calculate size of next chunk to send. Returns 0 if none can be sent.
int CalcNextSize(int max_payload_len, int remaining_bytes,
@ -86,7 +88,20 @@ private:
// last_fragment indicates that this packet ends with the last byte of a
// partition.
int WriteHeaderAndPayload(int send_bytes, bool end_of_fragment,
WebRtc_UWord8* buffer);
WebRtc_UWord8* buffer, int buffer_length);
// Write the PictureID from codec_specific_info_ to buffer. One or two
// bytes are written, depending on magnitude of PictureID. The function
// returns the number of bytes written.
int WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const;
// Calculate and return length (octets) of the variable header fields in
// the next header (i.e., header length in addition to vp8_header_bytes_).
int FirstHeaderExtraLength() const;
// Calculate and return length (octets) of PictureID field in the next
// header. Can be 0, 1, or 2.
int PictureIdLength() const;
const WebRtc_UWord8* payload_data_;
const int payload_size_;
@ -95,10 +110,11 @@ private:
int part_ix_;
bool beginning_; // first partition in this frame
bool first_fragment_; // first fragment of a partition
const int vp8_header_bytes_; // length of VP8 payload header
const int vp8_header_bytes_; // length of VP8 payload header's fixed part
AggregationMode aggr_mode_;
bool balance_;
bool separate_first_;
const RTPVideoHeaderVP8 hdr_info_;
};
}

View File

@ -0,0 +1,353 @@
/*
* 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 VP8 packetizer.
*/
#include <gtest/gtest.h>
#include "typedefs.h"
#include "rtp_format_vp8.h"
namespace {
using webrtc::RTPFragmentationHeader;
using webrtc::RtpFormatVp8;
using webrtc::RTPVideoHeaderVP8;
const WebRtc_UWord32 kPayloadSize = 30;
class RtpFormatVp8Test : public ::testing::Test {
protected:
RtpFormatVp8Test() {};
virtual void SetUp();
virtual void TearDown();
void CheckHeader(bool first_in_frame, bool frag_start, bool frag_end);
void CheckPayload(int payload_end);
void CheckLast(bool last) const;
void CheckPacket(int send_bytes, int expect_bytes, bool last,
bool first_in_frame, bool frag_start, bool frag_end);
WebRtc_UWord8 payload_data_[kPayloadSize];
WebRtc_UWord8 buffer_[kPayloadSize];
WebRtc_UWord8 *data_ptr_;
RTPFragmentationHeader* fragmentation_;
RTPVideoHeaderVP8 hdr_info_;
int payload_start_;
};
void RtpFormatVp8Test::SetUp() {
for (int i = 0; i < kPayloadSize; i++)
{
payload_data_[i] = i / 10; // integer division
}
data_ptr_ = payload_data_;
fragmentation_ = new RTPFragmentationHeader;
fragmentation_->VerifyAndAllocateFragmentationHeader(3);
fragmentation_->fragmentationLength[0] = 10;
fragmentation_->fragmentationLength[1] = 10;
fragmentation_->fragmentationLength[2] = 10;
fragmentation_->fragmentationOffset[0] = 0;
fragmentation_->fragmentationOffset[1] = 10;
fragmentation_->fragmentationOffset[2] = 20;
hdr_info_.pictureId = 0;
hdr_info_.nonReference = false;
}
void RtpFormatVp8Test::TearDown() {
delete fragmentation_;
}
#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_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0))
#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_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a)
#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0))
void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start,
bool frag_end)
{
payload_start_ = 1;
EXPECT_RSV_ZERO(buffer_[0]);
if (first_in_frame & hdr_info_.pictureId != webrtc::kNoPictureId)
{
EXPECT_BIT_I_EQ(buffer_[0], 1);
if (hdr_info_.pictureId > 0x7F)
{
EXPECT_BIT_EQ(buffer_[1], 7, 1);
EXPECT_EQ(buffer_[1] & 0x7F,
(hdr_info_.pictureId >> 8) & 0x7F);
EXPECT_EQ(buffer_[2], hdr_info_.pictureId & 0xFF);
payload_start_ += 2;
}
else
{
EXPECT_BIT_EQ(buffer_[1], 7, 0);
EXPECT_EQ(buffer_[1] & 0x7F,
(hdr_info_.pictureId) & 0x7F);
payload_start_ += 1;
}
}
EXPECT_BIT_N_EQ(buffer_[0], 0);
WebRtc_UWord8 fi = 0x03;
if (frag_start) fi = fi & 0x01;
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::CheckPayload(int payload_end)
{
for (int i = payload_start_; i < payload_end; i++, data_ptr_++)
EXPECT_EQ(buffer_[i], *data_ptr_);
}
void RtpFormatVp8Test::CheckLast(bool last) const
{
EXPECT_EQ(last, data_ptr_ == payload_data_ + kPayloadSize);
}
void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last,
bool first_in_frame, bool frag_start, bool frag_end)
{
EXPECT_EQ(send_bytes, expect_bytes);
CheckHeader(first_in_frame, frag_start, frag_end);
CheckPayload(send_bytes);
CheckLast(last);
}
TEST_F(RtpFormatVp8Test, TestStrictMode)
{
int send_bytes = 0;
bool last;
bool first_in_frame = true;
hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_, *fragmentation_, webrtc::kStrict);
// get first packet, expect balanced size = same as second packet
EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ false);
first_in_frame = false;
// get second packet
EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ true);
// Second partition
// Get first (and only) packet
EXPECT_EQ(0, packetizer.NextPacket(20, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 11, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ true);
// Third partition
// Get first packet (of four)
EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 4, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ false);
// Get second packet (of four)
EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 3, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ false);
// Get third packet (of four)
EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 4, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ false);
// Get fourth and last packet
EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 3, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ true);
}
TEST_F(RtpFormatVp8Test, TestAggregateMode)
{
int send_bytes = 0;
bool last;
bool first_in_frame = true;
hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_, *fragmentation_, webrtc::kAggregate);
// get first packet
// first half of first partition
EXPECT_EQ(0, packetizer.NextPacket(6, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 6, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ false);
first_in_frame = false;
// get second packet
// second half of first partition
EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ true);
// get third packet
// last two partitions aggregated
EXPECT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 21, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ true);
}
TEST_F(RtpFormatVp8Test, TestSloppyMode)
{
int send_bytes = 0;
bool last;
bool first_in_frame = true;
hdr_info_.pictureId = webrtc::kNoPictureId; // no PictureID
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_, *fragmentation_, webrtc::kSloppy);
// get first packet
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ false);
first_in_frame = false;
// get second packet
// fragments of first and second partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ false);
// get third packet
// fragments of second and third partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ false);
// get fourth packet
// second half of last partition
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 7, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ true);
}
// Verify that sloppy mode is forced if fragmentation info is missing.
TEST_F(RtpFormatVp8Test, TestSloppyModeFallback)
{
int send_bytes = 0;
bool last;
bool first_in_frame = true;
hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_);
// get first packet
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ true,
/* frag_end */ false);
first_in_frame = false;
// get second packet
// fragments of first and second partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ false);
// get third packet
// fragments of second and third partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ false);
// get fourth packet
// second half of last partition
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
CheckPacket(send_bytes, 9, last,
first_in_frame,
/* frag_start */ false,
/* frag_end */ true);
}
// Verify that non-reference bit is set.
TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
int send_bytes = 0;
bool last;
bool first_in_frame = true;
hdr_info_.nonReference = true;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
hdr_info_);
// get first packet
ASSERT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last));
ASSERT_FALSE(last);
EXPECT_BIT_N_EQ(buffer_[0], 1);
// get second packet
ASSERT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last));
ASSERT_TRUE(last);
EXPECT_BIT_N_EQ(buffer_[0], 1);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace

View File

@ -8,24 +8,23 @@
{
'includes': [
'../../../../common_settings.gypi', # Common settings
'../../../common_settings.gypi', # Common settings
],
'targets': [
{
'target_name': 'test_rtp_format_vp8',
'target_name': 'rtp_format_vp8_unittest',
'type': 'executable',
'dependencies': [
'../../source/rtp_rtcp.gyp:rtp_rtcp',
'../../../../../testing/gtest.gyp:gtest',
'../../../../../testing/gtest.gyp:gtest_main',
'rtp_rtcp.gyp:rtp_rtcp',
'../../../../testing/gtest.gyp:gtest',
'../../../../testing/gtest.gyp:gtest_main',
],
'include_dirs': [
'../../source',
'.',
],
'sources': [
'unit_test.h',
'unit_test.cc',
'../../source/rtp_format_vp8.cc',
'rtp_format_vp8_unittest.h',
'rtp_format_vp8_unittest.cc',
],
},
],

View File

@ -1195,17 +1195,6 @@ RTPSenderVideo::SendH263MBs(const FrameType frameType,
return 0;
}
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RSV |I|N|FI |B| PictureID (integer #bytes) |
+-+-+-+-+-+-+-+-+ |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| : (VP8 data or VP8 payload header; byte aligned)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
WebRtc_Word32
RTPSenderVideo::SendVP8(const FrameType frameType,
const WebRtc_Word8 payloadType,
@ -1227,7 +1216,9 @@ RTPSenderVideo::SendVP8(const FrameType frameType,
WebRtc_UWord16 maxPayloadLengthVP8 = _rtpSender.MaxPayloadLength()
- FECPacketOverhead() - rtpHeaderLength;
RtpFormatVp8 packetizer(data, payloadBytesToSend, *fragmentation, kStrict);
assert(rtpTypeHdr);
RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8,
*fragmentation, kStrict);
bool last = false;
while (!last)

View File

@ -120,8 +120,7 @@ private:
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize,
const RTPFragmentationHeader* fragmentation,
const RTPVideoTypeHeader* /*rtpTypeHdr*/);
// TODO(hlundin): Remove comments once we start using rtpTypeHdr.
const RTPVideoTypeHeader* rtpTypeHdr);
// MPEG 4
WebRtc_Word32 FindMPEG4NALU(const WebRtc_UWord8* inData ,WebRtc_Word32 MaxPayloadLength);

View File

@ -1,404 +0,0 @@
/*
* 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 VP8 packetizer.
*/
#include <gtest/gtest.h>
#include "typedefs.h"
#include "rtp_format_vp8.h"
namespace {
using webrtc::RTPFragmentationHeader;
using webrtc::RtpFormatVp8;
const WebRtc_UWord32 kPayloadSize = 30;
class RtpFormatVp8Test : public ::testing::Test {
protected:
RtpFormatVp8Test() {};
virtual void SetUp();
virtual void TearDown();
WebRtc_UWord8* payload_data;
RTPFragmentationHeader* fragmentation;
};
void RtpFormatVp8Test::SetUp() {
payload_data = new WebRtc_UWord8[kPayloadSize];
for (int i = 0; i < kPayloadSize; i++)
{
payload_data[i] = i / 10; // integer division
}
fragmentation = new RTPFragmentationHeader;
fragmentation->VerifyAndAllocateFragmentationHeader(3);
fragmentation->fragmentationLength[0] = 10;
fragmentation->fragmentationLength[1] = 10;
fragmentation->fragmentationLength[2] = 10;
fragmentation->fragmentationOffset[0] = 0;
fragmentation->fragmentationOffset[1] = 10;
fragmentation->fragmentationOffset[2] = 20;
}
void RtpFormatVp8Test::TearDown() {
delete [] payload_data;
delete fragmentation;
}
#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_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0))
#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_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a)
#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0))
TEST_F(RtpFormatVp8Test, TestStrictMode)
{
WebRtc_UWord8 buffer[20];
int send_bytes = 0;
bool last;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
*fragmentation, webrtc::kStrict);
// get first packet, expect balanced size = same as second packet
EXPECT_EQ(0, packetizer.NextPacket(8, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,6);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 1);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 1);
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// get second packet
EXPECT_EQ(0, packetizer.NextPacket(8, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,6); // 5 remaining from partition, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x02);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// Second partition
// Get first (and only) packet
EXPECT_EQ(0, packetizer.NextPacket(20, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,11);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x00);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 11; i++)
{
EXPECT_EQ(buffer[i], 1);
}
// Third partition
// Get first packet (of four)
EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,4);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x01); // first fragment
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 4; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// Get second packet (of four)
EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,3);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x03); // middle fragment
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 3; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// Get third packet (of four)
EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,4);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x03); // middle fragment
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 4; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// Get fourth and last packet
EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last));
EXPECT_TRUE(last); // last packet in frame
EXPECT_EQ(send_bytes,3); // 2 bytes payload left, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x02); // last fragment
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 3; i++)
{
EXPECT_EQ(buffer[i], 2);
}
}
TEST_F(RtpFormatVp8Test, TestAggregateMode)
{
WebRtc_UWord8 buffer[kPayloadSize];
int send_bytes = 0;
bool last;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
*fragmentation, webrtc::kAggregate);
// get first packet
// first half of first partition
EXPECT_EQ(0, packetizer.NextPacket(6, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,6); // First 5 from first partition, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 1);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 1);
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// get second packet
// second half of first partition
EXPECT_EQ(0, packetizer.NextPacket(10, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,6); // Last 5 from first partition, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x02);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// get third packet
// last two partitions aggregated
EXPECT_EQ(0, packetizer.NextPacket(25, buffer, &send_bytes, &last));
EXPECT_TRUE(last); // last packet
EXPECT_EQ(send_bytes,21); // Two 10-byte partitions and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x00);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 11; i++)
{
EXPECT_EQ(buffer[i], 1);
}
for (int i = 11; i < 21; i++)
{
EXPECT_EQ(buffer[i], 2);
}
}
TEST_F(RtpFormatVp8Test, TestSloppyMode)
{
WebRtc_UWord8 buffer[kPayloadSize];
int send_bytes = 0;
bool last;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
*fragmentation, webrtc::kSloppy);
// get first packet
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,9); // 8 bytes payload and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 1);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 1);
for (int i = 1; i < 9; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// get second packet
// fragments of first and second partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,9); // 8 bytes (2+6) payload and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x03);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i <= 2; i++)
{
EXPECT_EQ(buffer[i], 0);
}
for (int i = 3; i < 9; i++)
{
EXPECT_EQ(buffer[i], 1);
}
// get third packet
// fragments of second and third partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,9); // 8 bytes (4+4) payload and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x03);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i <= 4; i++)
{
EXPECT_EQ(buffer[i], 1);
}
for (int i = 5; i < 9; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// get fourth packet
// second half of last partition
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_TRUE(last); // last packet
EXPECT_EQ(send_bytes,7); // Last 6 from last partition, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x02);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 5; i++)
{
EXPECT_EQ(buffer[i], 2);
}
}
// Verify that sloppy mode is forced if fragmentation info is missing.
TEST_F(RtpFormatVp8Test, TestSloppyModeFallback)
{
WebRtc_UWord8 buffer[kPayloadSize];
int send_bytes = 0;
bool last;
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize);
// get first packet
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,9); // 8 bytes payload and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 1);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 1);
for (int i = 1; i < 9; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// get second packet
// fragments of first and second partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,9); // 8 bytes (2+6) payload and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x03);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i <= 2; i++)
{
EXPECT_EQ(buffer[i], 0);
}
for (int i = 3; i < 9; i++)
{
EXPECT_EQ(buffer[i], 1);
}
// get third packet
// fragments of second and third partitions
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,9); // 8 bytes (4+4) payload and 1 byte header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x03);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i <= 4; i++)
{
EXPECT_EQ(buffer[i], 1);
}
for (int i = 5; i < 9; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// get fourth packet
// second half of last partition
EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last));
EXPECT_TRUE(last); // last packet
EXPECT_EQ(send_bytes,7); // Last 6 from last partition, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
EXPECT_BIT_N_EQ(buffer[0], 0);
EXPECT_FI_EQ(buffer[0], 0x02);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 5; i++)
{
EXPECT_EQ(buffer[i], 2);
}
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace

View File

@ -27,6 +27,8 @@ struct CodecSpecificInfoVP8
WebRtc_UWord8 pictureIdSLI;
bool hasReceivedRPSI;
WebRtc_UWord64 pictureIdRPSI;
WebRtc_Word16 pictureId; // negative value to skip pictureId
bool nonReference;
};
union CodecSpecificInfoUnion

View File

@ -241,7 +241,7 @@ VP8Encoder::InitEncode(const VideoCodec* inst,
_height = inst->height;
// random start 16 bits is enough
_pictureID = (WebRtc_UWord16)rand();
_pictureID = ((WebRtc_UWord16)rand()) % 0x7FFF;
// allocate memory for encoded image
if (_encodedImage._buffer != NULL)
@ -535,26 +535,16 @@ VP8Encoder::Encode(const RawImage& inputImage,
}
else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
{
// attach Picture ID
// we use 14 bits generating 1 or 2 bytes
// TODO(hlundin): update to follow latest RTP spec
WebRtc_UWord8 pictureIdSize = 2;
// TODO(hlundin): we should refactor this so that the pictureID is
// signaled through a codec specific struct and added in the RTP module.
if (_pictureID > 0x7f)
{
// more than 7 bits
_encodedImage._buffer[0] = 0x80 | (WebRtc_UWord8)(_pictureID >> 7);
_encodedImage._buffer[1] = (WebRtc_UWord8)(_pictureID & 0x7f);
}
else
{
_encodedImage._buffer[0] = (WebRtc_UWord8)_pictureID;
pictureIdSize = 1;
}
CodecSpecificInfo codecSpecific;
codecSpecific.codecType = kVideoCodecVP8;
CodecSpecificInfoVP8 *vp8Info = &(codecSpecific.codecSpecific.VP8);
memcpy(_encodedImage._buffer+pictureIdSize, pkt->data.frame.buf, pkt->data.frame.sz);
_encodedImage._length = WebRtc_UWord32(pkt->data.frame.sz) + pictureIdSize;
vp8Info->pictureId = _pictureID;
vp8Info->nonReference
= (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE);
memcpy(_encodedImage._buffer, pkt->data.frame.buf, pkt->data.frame.sz);
_encodedImage._length = WebRtc_UWord32(pkt->data.frame.sz);
_encodedImage._encodedHeight = _raw->h;
_encodedImage._encodedWidth = _raw->w;
@ -574,12 +564,10 @@ VP8Encoder::Encode(const RawImage& inputImage,
// First partition
fragInfo.fragmentationOffset[0] = 0;
WebRtc_UWord8 *firstByte = &_encodedImage._buffer[pictureIdSize];
WebRtc_UWord8 *firstByte = _encodedImage._buffer;
WebRtc_UWord32 tmpSize = (firstByte[2] << 16) | (firstByte[1] << 8)
| firstByte[0];
fragInfo.fragmentationLength[0] = (tmpSize >> 5) & 0x7FFFF;
// Let the PictureID belong to the first partition.
fragInfo.fragmentationLength[0] += pictureIdSize;
fragInfo.fragmentationPlType[0] = 0; // not known here
fragInfo.fragmentationTimeDiff[0] = 0;
@ -590,10 +578,11 @@ VP8Encoder::Encode(const RawImage& inputImage,
fragInfo.fragmentationPlType[1] = 0; // not known here
fragInfo.fragmentationTimeDiff[1] = 0;
_encodedCompleteCallback->Encoded(_encodedImage, NULL, &fragInfo);
_encodedCompleteCallback->Encoded(_encodedImage, &codecSpecific,
&fragInfo);
}
_pictureID++; // prepare next
_pictureID = (_pictureID + 1) % 0x7FFF; // prepare next
return WEBRTC_VIDEO_CODEC_OK;
}
return WEBRTC_VIDEO_CODEC_ERROR;
@ -732,18 +721,16 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
}
vpx_dec_iter_t _iter = NULL;
vpx_image_t* img;
WebRtc_UWord64 pictureID = 0;
// scan for number of bytes used for picture ID
WebRtc_UWord8 numberOfBytes;
for (numberOfBytes = 0;(inputImage._buffer[numberOfBytes] & 0x80 ) &&
numberOfBytes < 8; numberOfBytes++)
WebRtc_UWord64 pictureID = inputImage._buffer[0] & 0x7F;
WebRtc_UWord8 numberOfBytes = 1;
if (inputImage._buffer[0] & 0x80)
{
pictureID += inputImage._buffer[numberOfBytes] & 0x7f;
pictureID <<= 7;
pictureID <<= 8;
pictureID += inputImage._buffer[1];
++numberOfBytes;
}
pictureID += inputImage._buffer[numberOfBytes] & 0x7f;
numberOfBytes++;
// check for missing frames
if (missingFrames)

View File

@ -247,7 +247,18 @@ void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info,
RTPVideoTypeHeader** rtp) {
switch (info.codecType)
{
//TODO(hlundin): Implement case for kVideoCodecVP8.
case kVideoCodecVP8: {
if (info.codecSpecific.VP8.pictureId < 0)
{
(*rtp)->VP8.pictureId = kNoPictureId;
}
else
{
(*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId;
}
(*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference;
return;
}
default: {
// No codec specific info. Change RTP header pointer to NULL.
*rtp = NULL;