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:
parent
dcdf311b6c
commit
f0a476bf76
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
353
modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc
Normal file
353
modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc
Normal 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
|
@ -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',
|
||||
],
|
||||
},
|
||||
],
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user