Implement VP8 packetizer and unit tests

Implemented a new VP8 packetizer with three modes. The packetizer
class needs access to the fragmentation information, which is
now created in the codec wrapper and passed through the callback
chain to the RTPSenderVideo::SendVP8().

A unit test for the VP8 packetizer was also implemented. It tests the
three different modes. The tests could definitely be more elaborate.
Review URL: http://webrtc-codereview.appspot.com/34003

git-svn-id: http://webrtc.googlecode.com/svn/trunk@48 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
hlundin@google.com 2011-06-07 12:23:14 +00:00
parent 8dcd789d2b
commit d2c7bff3a1
6 changed files with 209 additions and 692 deletions

View File

@ -8,10 +8,6 @@
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* This file contains the implementation of the VP8 packetizer class.
*/
#include "rtp_format_vp8.h"
#include <cassert> // assert
@ -19,18 +15,17 @@
namespace webrtc {
RTPFormatVP8::RTPFormatVP8(const WebRtc_UWord8* payload_data,
const WebRtc_UWord32 payload_size,
RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size,
const RTPFragmentationHeader* fragmentation,
const VP8PacketizerMode mode)
:
payload_data_(payload_data),
payload_size_(payload_size),
bytes_sent_(0),
mode_(mode),
beginning_(true),
first_fragment_(true),
vp8_header_bytes_(1)
VP8PacketizerMode mode)
: payload_data_(payload_data),
payload_size_(payload_size),
payload_bytes_sent_(0),
mode_(mode),
beginning_(true),
first_fragment_(true),
vp8_header_bytes_(1)
{
if (fragmentation == NULL)
{
@ -44,23 +39,38 @@ RTPFormatVP8::RTPFormatVP8(const WebRtc_UWord8* payload_data,
}
}
bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send)
{
// Convenience variables
const int num_fragments = frag_info_.fragmentationVectorSize;
RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size)
: payload_data_(payload_data),
payload_size_(payload_size),
frag_info_(),
payload_bytes_sent_(0),
mode_(kSloppy),
beginning_(true),
first_fragment_(true),
vp8_header_bytes_(1)
{}
int RtpFormatVp8::GetFragIdx()
{
// Which fragment are we in?
int frag_ix = 0;
while ((frag_ix + 1 < num_fragments) &&
(bytes_sent_ >= frag_info_.fragmentationOffset[frag_ix + 1]))
while ((frag_ix + 1 < frag_info_.fragmentationVectorSize) &&
(payload_bytes_sent_ >= frag_info_.fragmentationOffset[frag_ix + 1]))
{
++frag_ix;
}
return frag_ix;
}
// How much data to send in this packet?
int send_bytes = 0;
bool last_fragment = false;
int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send, bool* last_packet)
{
// Convenience variables
const int num_fragments = frag_info_.fragmentationVectorSize;
int frag_ix = GetFragIdx(); //TODO (hlundin): Store frag_ix as a member?
int send_bytes = 0; // How much data to send in this packet.
bool end_of_fragment = false;
switch (mode_)
{
@ -75,16 +85,17 @@ bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
{
// Pack as many whole partitions we can into this packet;
// don't fragment.
while (send_bytes + vp8_header_bytes_
while ((frag_ix < num_fragments) &&
(send_bytes + vp8_header_bytes_
+ frag_info_.fragmentationLength[frag_ix]
<= max_payload_len)
<= max_payload_len))
{
send_bytes += frag_info_.fragmentationLength[frag_ix];
++frag_ix;
}
// This packet ends on a complete fragment.
last_fragment = true;
end_of_fragment = true;
break; // Jump out of case statement.
}
}
@ -99,7 +110,7 @@ bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
{
// Find out how much is left to send in the current partition.
const int remaining_bytes = frag_info_.fragmentationOffset[frag_ix]
- bytes_sent_ + frag_info_.fragmentationLength[frag_ix];
- payload_bytes_sent_ + frag_info_.fragmentationLength[frag_ix];
assert(remaining_bytes > 0);
assert(remaining_bytes <= frag_info_.fragmentationLength[frag_ix]);
@ -112,7 +123,7 @@ bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
{
// last packet from this partition
send_bytes = remaining_bytes;
last_fragment = true;
end_of_fragment = true;
}
break;
}
@ -120,17 +131,17 @@ bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
case kSloppy:
{
// Send a full packet, or what is left of the payload.
const int remaining_bytes = payload_size_ - bytes_sent_;
const int remaining_bytes = payload_size_ - payload_bytes_sent_;
if (remaining_bytes + vp8_header_bytes_ > max_payload_len)
{
send_bytes = max_payload_len - vp8_header_bytes_;
last_fragment = false;
end_of_fragment = false;
}
else
{
send_bytes = remaining_bytes;
last_fragment = true;
end_of_fragment = true;
}
break;
}
@ -138,20 +149,23 @@ bool RTPFormatVP8::NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
default:
// Should not end up here
assert(false);
return -1;
}
// Write the payload header and the payload to buffer.
*bytes_to_send = WriteHeaderAndPayload(send_bytes, last_fragment, buffer);
*bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer);
if (*bytes_to_send < 0)
{
return -1;
}
// Anything left to send?
if (bytes_sent_ < payload_size_)
return false;
else
return true;
*last_packet = payload_bytes_sent_ >= payload_size_;
assert(!*last_packet || (payload_bytes_sent_ == payload_size_));
return 0;
}
int RTPFormatVP8::WriteHeaderAndPayload(const int send_bytes,
const bool last_fragment,
int RtpFormatVp8::WriteHeaderAndPayload(int send_bytes,
bool end_of_fragment,
WebRtc_UWord8* buffer)
{
// Write the VP8 payload header.
@ -160,25 +174,34 @@ int RTPFormatVP8::WriteHeaderAndPayload(const int send_bytes,
// | RSV |I|N|FI |B|
// +-+-+-+-+-+-+-+-+
if (send_bytes < 0)
{
return -1;
}
if (payload_bytes_sent_ + send_bytes > payload_size_)
{
return -1;
}
// PictureID always present in first packet
const int pictureid_present = beginning_;
const int picture_id_present = beginning_;
// TODO(hlundin): must pipe this info from VP8 encoder
const int nonref_frame = 0;
const int kNonrefFrame = 0;
buffer[0] = 0 | (pictureid_present << 4) // I
| (nonref_frame << 3) // N
| (!first_fragment_ << 2) | (!last_fragment << 1) // FI
| (beginning_);
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
// Copy the payload.
assert(bytes_sent_ + send_bytes <= payload_size_);
memcpy(&buffer[vp8_header_bytes_], &payload_data_[bytes_sent_], send_bytes);
memcpy(&buffer[vp8_header_bytes_], &payload_data_[payload_bytes_sent_],
send_bytes);
// Update state variables.
beginning_ = false; // next packet cannot be first packet in frame
// next packet starts new fragment if this ended one
first_fragment_ = last_fragment;
bytes_sent_ += send_bytes;
first_fragment_ = end_of_fragment;
payload_bytes_sent_ += send_bytes;
// Return total length of written data.
return send_bytes + vp8_header_bytes_;

View File

@ -10,78 +10,16 @@
/*
* This file contains the declaration of the VP8 packetizer class.
*/
#ifndef WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_
#define WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_
#include "module_common_types.h"
#include "typedefs.h"
namespace webrtc
{
// VP8 packetization modes.
enum VP8PacketizerMode
{
kStrict = 0, // split partitions if too large; never aggregate partitions
kAggregate, // split partitions if too large; aggregate partitions/fragments
kSloppy, // split entire payload without considering partition boundaries
};
// Packetizer for VP8.
class RTPFormatVP8
{
public:
// Constructor.
// Initialize with payload from encoder and fragmentation info.
// If fragmentation info is NULL, mode will be forced to kSloppy.
RTPFormatVP8(const WebRtc_UWord8* payload_data,
const WebRtc_UWord32 payload_size,
const RTPFragmentationHeader* fragmentation,
const VP8PacketizerMode mode = kStrict);
// 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.
bool NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send);
private:
// Write the payload header and copy the payload to the buffer.
// 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
// partition.
int WriteHeaderAndPayload(const int send_bytes, const bool last_fragment,
WebRtc_UWord8* buffer);
const WebRtc_UWord8* payload_data_;
const WebRtc_UWord32 payload_size_;
RTPFragmentationHeader frag_info_;
int bytes_sent_;
VP8PacketizerMode mode_;
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
};
}
#endif /* WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_ */
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
* A packetizer object is created for each encoded video frame. The
* constructor is called with the payload data and size,
* together with the fragmentation information and a packetizer mode
* of choice. Alternatively, if no fragmentation info is available, the
* second constructor can be used with only payload data and size; in that
* case the mode kSloppy is used.
*
* 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 contains the declaration of the VP8 packetizer class.
* After creating the packetizer, the method NextPacket is called
* repeatedly to get all packets for the frame. The method returns
* false as long as there are more packets left to fetch.
*/
#ifndef WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_
@ -93,46 +31,54 @@ private:
namespace webrtc
{
// VP8 packetization modes.
enum VP8PacketizerMode
{
kStrict = 0, // split partitions if too large; never aggregate partitions
kAggregate, // split partitions if too large; aggregate partitions/fragments
kAggregate, // split partitions if too large; aggregate whole partitions
kSloppy, // split entire payload without considering partition boundaries
};
// Packetizer for VP8.
class RTPFormatVP8
class RtpFormatVp8
{
public:
// Constructor.
// Initialize with payload from encoder and fragmentation info.
// If fragmentation info is NULL, mode will be forced to kSloppy.
RTPFormatVP8(const WebRtc_UWord8* payload_data,
const WebRtc_UWord32 payload_size,
// The payload_data must be exactly one encoded VP8 frame.
RtpFormatVp8(const WebRtc_UWord8* payload_data,
WebRtc_UWord32 payload_size,
const RTPFragmentationHeader* fragmentation,
const VP8PacketizerMode mode = kStrict);
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);
// 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.
bool NextPacket(const int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send);
// Returns true for the last packet of the frame, false otherwise (i.e.,
// call the function again to get the next packet).
int NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
int* bytes_to_send, bool* last_packet);
private:
// Determine from which fragment the next byte to send will be taken.
int GetFragIdx();
// Write the payload header and copy the payload to the buffer.
// 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
// partition.
int WriteHeaderAndPayload(const int send_bytes, const bool last_fragment,
int WriteHeaderAndPayload(int send_bytes, bool end_of_fragment,
WebRtc_UWord8* buffer);
const WebRtc_UWord8* payload_data_;
const WebRtc_UWord32 payload_size_;
RTPFragmentationHeader frag_info_;
int bytes_sent_;
int payload_bytes_sent_;
VP8PacketizerMode mode_;
bool beginning_; // first partition in this frame
bool first_fragment_; // first fragment of a partition

View File

@ -1106,14 +1106,17 @@ RTPSenderVideo::SendVP8(const FrameType frameType,
WebRtc_UWord16 maxPayloadLengthVP8 = _rtpSender.MaxPayloadLength()
- FECPacketOverhead() - rtpHeaderLength;
RTPFormatVP8 packetizer(data, payloadBytesToSend, fragmentation, kStrict);
RtpFormatVp8 packetizer(data, payloadBytesToSend, fragmentation, kStrict);
bool last = false;
while (!last)
{
// Write VP8 Payload Descriptor and VP8 payload.
last = packetizer.NextPacket(maxPayloadLengthVP8,
&dataBuffer[rtpHeaderLength], &payloadBytesInPacket);
if (packetizer.NextPacket(maxPayloadLengthVP8,
&dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last) < 0)
{
return -1;
}
// Write RTP header.
// Set marker bit true if this is the last packet in frame.

View File

@ -18,65 +18,13 @@
'../../source/rtp_rtcp.gyp:rtp_rtcp',
'../../../../../testing/gtest.gyp:gtest',
'../../../../../testing/gtest.gyp:gtest_main',
# '../../../signal_processing_library/main/source/spl.gyp:spl',
],
'include_dirs': [
'../../source',
],
# 'direct_dependent_settings': {
# 'include_dirs': [
# '../interface',
# ],
# },
'sources': [
'unit_test.h',
'unit_test.cc',
'../../source/rtp_format_vp8.h',
'../../source/rtp_format_vp8.cc',
],
},
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:
# 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.
{
'includes': [
'../../../../common_settings.gypi', # Common settings
],
'targets': [
{
'target_name': 'test_rtp_format_vp8',
'type': 'executable',
'dependencies': [
'../../source/rtp_rtcp.gyp:rtp_rtcp',
'../../../../../testing/gtest.gyp:gtest',
'../../../../../testing/gtest.gyp:gtest_main',
# '../../../signal_processing_library/main/source/spl.gyp:spl',
],
'include_dirs': [
'../../source',
],
# 'direct_dependent_settings': {
# 'include_dirs': [
# '../interface',
# ],
# },
'sources': [
'unit_test.h',
'unit_test.cc',
'../../source/rtp_format_vp8.h',
'../../source/rtp_format_vp8.cc',
],
},

View File

@ -13,19 +13,30 @@
* This file includes unit tests for the VP8 packetizer.
*/
#include "unit_test.h"
#include <gtest/gtest.h>
#include "typedefs.h"
#include "rtp_format_vp8.h"
namespace webrtc {
namespace {
RTPFormatVP8Test::RTPFormatVP8Test()
{
}
using webrtc::RTPFragmentationHeader;
using webrtc::RtpFormatVp8;
void RTPFormatVP8Test::SetUp() {
payload_data = new WebRtc_UWord8[30];
payload_size = 30;
for (int i = 0; i < payload_size; i++)
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
}
@ -39,7 +50,7 @@ void RTPFormatVP8Test::SetUp() {
fragmentation->fragmentationOffset[2] = 20;
}
void RTPFormatVP8Test::TearDown() {
void RtpFormatVp8Test::TearDown() {
delete [] payload_data;
delete fragmentation;
}
@ -57,15 +68,18 @@ void RTPFormatVP8Test::TearDown() {
#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0))
TEST_F(RTPFormatVP8Test, TestStrictMode)
TEST_F(RtpFormatVp8Test, TestStrictMode)
{
WebRtc_UWord8 buffer[20];
int send_bytes = 0;
bool last;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, fragmentation, kStrict);
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
fragmentation, webrtc::kStrict);
// get first packet
EXPECT_FALSE(packetizer.NextPacket(8, buffer, &send_bytes));
EXPECT_EQ(0, packetizer.NextPacket(8, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,8);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 1);
@ -78,7 +92,8 @@ TEST_F(RTPFormatVP8Test, TestStrictMode)
}
// get second packet
EXPECT_FALSE(packetizer.NextPacket(8, buffer, &send_bytes));
EXPECT_EQ(0, packetizer.NextPacket(8, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,4); // 3 remaining from partition, 1 header
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
@ -92,7 +107,8 @@ TEST_F(RTPFormatVP8Test, TestStrictMode)
// Second partition
// Get first (and only) packet
EXPECT_FALSE(packetizer.NextPacket(20, buffer, &send_bytes));
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);
@ -106,7 +122,8 @@ TEST_F(RTPFormatVP8Test, TestStrictMode)
// Third partition
// Get first packet (of three)
EXPECT_FALSE(packetizer.NextPacket(5, buffer, &send_bytes));
EXPECT_EQ(0, packetizer.NextPacket(5, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,5);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
@ -119,7 +136,8 @@ TEST_F(RTPFormatVP8Test, TestStrictMode)
}
// Get second packet (of three)
EXPECT_FALSE(packetizer.NextPacket(5, buffer, &send_bytes));
EXPECT_EQ(0, packetizer.NextPacket(5, buffer, &send_bytes, &last));
EXPECT_FALSE(last);
EXPECT_EQ(send_bytes,5);
EXPECT_RSV_ZERO(buffer[0]);
EXPECT_BIT_I_EQ(buffer[0], 0);
@ -132,7 +150,8 @@ TEST_F(RTPFormatVP8Test, TestStrictMode)
}
// Get third and last packet
EXPECT_TRUE(packetizer.NextPacket(5, buffer, &send_bytes)); // last packet in frame
EXPECT_EQ(0, packetizer.NextPacket(5, 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);
@ -146,72 +165,78 @@ TEST_F(RTPFormatVP8Test, TestStrictMode)
}
TEST_F(RTPFormatVP8Test, TestAggregateMode)
TEST_F(RtpFormatVp8Test, TestAggregateMode)
{
WebRtc_UWord8 buffer[30];
WebRtc_UWord8 buffer[kPayloadSize];
int send_bytes = 0;
bool last;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, fragmentation,
kAggregate);
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
fragmentation, webrtc::kAggregate);
// get first packet
// first two partitions aggregated
EXPECT_FALSE(packetizer.NextPacket(25, buffer, &send_bytes));
EXPECT_EQ(send_bytes,21); // Two 10-byte partitions and 1 byte header
// 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], 0x00);
EXPECT_FI_EQ(buffer[0], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 1);
for (int i = 1; i < 11; i++)
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 0);
}
for (int i = 11; i < 21; i++)
{
EXPECT_EQ(buffer[i], 1);
}
// get second packet
// first half of last partition
EXPECT_FALSE(packetizer.NextPacket(6, buffer, &send_bytes));
EXPECT_EQ(send_bytes,6); // First 5 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], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// get third packet
// second half of last partition
EXPECT_TRUE(packetizer.NextPacket(6, buffer, &send_bytes)); // last packet
EXPECT_EQ(send_bytes,6); // Last 5 from last partition, 1 header
// 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)
TEST_F(RtpFormatVp8Test, TestSloppyMode)
{
WebRtc_UWord8 buffer[30];
WebRtc_UWord8 buffer[kPayloadSize];
int send_bytes = 0;
bool last;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, fragmentation,
kSloppy);
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
fragmentation, webrtc::kSloppy);
// get first packet
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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);
@ -225,7 +250,8 @@ TEST_F(RTPFormatVP8Test, TestSloppyMode)
// get second packet
// fragments of first and second partitions
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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);
@ -243,7 +269,8 @@ TEST_F(RTPFormatVP8Test, TestSloppyMode)
// get third packet
// fragments of second and third partitions
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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);
@ -261,7 +288,8 @@ TEST_F(RTPFormatVP8Test, TestSloppyMode)
// get fourth packet
// second half of last partition
EXPECT_TRUE(packetizer.NextPacket(9, buffer, &send_bytes)); // last packet
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);
@ -276,16 +304,18 @@ TEST_F(RTPFormatVP8Test, TestSloppyMode)
}
// Verify that sloppy mode is forced if fragmentation info is missing.
TEST_F(RTPFormatVP8Test, TestSloppyModeFallback)
TEST_F(RtpFormatVp8Test, TestSloppyModeFallback)
{
WebRtc_UWord8 buffer[30];
WebRtc_UWord8 buffer[kPayloadSize];
int send_bytes = 0;
bool last;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, NULL /*fragmentation*/,
kStrict); // should be changed to kSlopy
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize,
NULL /*fragInfo*/, webrtc::kStrict); // should be changed to kSloppy
// get first packet
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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);
@ -299,7 +329,8 @@ TEST_F(RTPFormatVP8Test, TestSloppyModeFallback)
// get second packet
// fragments of first and second partitions
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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);
@ -317,7 +348,8 @@ TEST_F(RTPFormatVP8Test, TestSloppyModeFallback)
// get third packet
// fragments of second and third partitions
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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);
@ -335,7 +367,8 @@ TEST_F(RTPFormatVP8Test, TestSloppyModeFallback)
// get fourth packet
// second half of last partition
EXPECT_TRUE(packetizer.NextPacket(9, buffer, &send_bytes)); // last packet
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);
@ -355,362 +388,4 @@ int main(int argc, char** argv) {
return RUN_ALL_TESTS();
}
}
/*
* 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 "unit_test.h"
#include "rtp_format_vp8.h"
namespace webrtc {
RTPFormatVP8Test::RTPFormatVP8Test()
{
}
void RTPFormatVP8Test::SetUp() {
payload_data = new WebRtc_UWord8[30];
payload_size = 30;
for (int i = 0; i < payload_size; 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;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, fragmentation, kStrict);
// get first packet
EXPECT_FALSE(packetizer.NextPacket(8, buffer, &send_bytes));
EXPECT_EQ(send_bytes,8);
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 < 8; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// get second packet
EXPECT_FALSE(packetizer.NextPacket(8, buffer, &send_bytes));
EXPECT_EQ(send_bytes,4); // 3 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 < 4; i++)
{
EXPECT_EQ(buffer[i], 0);
}
// Second partition
// Get first (and only) packet
EXPECT_FALSE(packetizer.NextPacket(20, buffer, &send_bytes));
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 three)
EXPECT_FALSE(packetizer.NextPacket(5, buffer, &send_bytes));
EXPECT_EQ(send_bytes,5);
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 < 5; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// Get second packet (of three)
EXPECT_FALSE(packetizer.NextPacket(5, buffer, &send_bytes));
EXPECT_EQ(send_bytes,5);
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 < 5; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// Get third and last packet
EXPECT_TRUE(packetizer.NextPacket(5, buffer, &send_bytes)); // 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[30];
int send_bytes = 0;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, fragmentation,
kAggregate);
// get first packet
// first two partitions aggregated
EXPECT_FALSE(packetizer.NextPacket(25, buffer, &send_bytes));
EXPECT_EQ(send_bytes,21); // Two 10-byte partitions 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], 0x00);
EXPECT_BIT_B_EQ(buffer[0], 1);
for (int i = 1; i < 11; i++)
{
EXPECT_EQ(buffer[i], 0);
}
for (int i = 11; i < 21; i++)
{
EXPECT_EQ(buffer[i], 1);
}
// get second packet
// first half of last partition
EXPECT_FALSE(packetizer.NextPacket(6, buffer, &send_bytes));
EXPECT_EQ(send_bytes,6); // First 5 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], 0x01);
EXPECT_BIT_B_EQ(buffer[0], 0);
for (int i = 1; i < 6; i++)
{
EXPECT_EQ(buffer[i], 2);
}
// get third packet
// second half of last partition
EXPECT_TRUE(packetizer.NextPacket(6, buffer, &send_bytes)); // last packet
EXPECT_EQ(send_bytes,6); // Last 5 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 < 6; i++)
{
EXPECT_EQ(buffer[i], 2);
}
}
TEST_F(RTPFormatVP8Test, TestSloppyMode)
{
WebRtc_UWord8 buffer[30];
int send_bytes = 0;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, fragmentation,
kSloppy);
// get first packet
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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_TRUE(packetizer.NextPacket(9, buffer, &send_bytes)); // 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[30];
int send_bytes = 0;
RTPFormatVP8 packetizer = RTPFormatVP8(payload_data, payload_size, NULL /*fragmentation*/,
kStrict); // should be changed to kSlopy
// get first packet
EXPECT_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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_FALSE(packetizer.NextPacket(9, buffer, &send_bytes));
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_TRUE(packetizer.NextPacket(9, buffer, &send_bytes)); // 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

@ -1,78 +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 header file includes declaration for unit tests for the VP8 packetizer.
*/
#ifndef WEBRTC_RTP_FORMAT_VP8_UNIT_TEST_H_
#define WEBRTC_RTP_FORMAT_VP8_UNIT_TEST_H_
#include <gtest/gtest.h>
#include "typedefs.h"
namespace webrtc {
class RTPFragmentationHeader;
class RTPFormatVP8Test : public ::testing::Test {
protected:
RTPFormatVP8Test();
virtual void SetUp();
virtual void TearDown();
WebRtc_UWord8* payload_data;
WebRtc_UWord32 payload_size;
RTPFragmentationHeader* fragmentation;
};
}
#endif // WEBRTC_RTP_FORMAT_VP8_UNIT_TEST_H_
/*
* 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 header file includes declaration for unit tests for the VP8 packetizer.
*/
#ifndef WEBRTC_RTP_FORMAT_VP8_UNIT_TEST_H_
#define WEBRTC_RTP_FORMAT_VP8_UNIT_TEST_H_
#include <gtest/gtest.h>
#include "typedefs.h"
namespace webrtc {
class RTPFragmentationHeader;
class RTPFormatVP8Test : public ::testing::Test {
protected:
RTPFormatVP8Test();
virtual void SetUp();
virtual void TearDown();
WebRtc_UWord8* payload_data;
WebRtc_UWord32 payload_size;
RTPFragmentationHeader* fragmentation;
};
}
#endif // WEBRTC_RTP_FORMAT_VP8_UNIT_TEST_H_