Updating to new VP8 rtp format
The VP8 packetizer and tests have been updated to the new RTP draft (http://tools.ietf.org/html/draft-ietf-payload-vp8-01). The receive-side parser is also updated, and a new unit test is implemented for it. Finally, some data traversing work to get the parsed information into the decoder. Review URL: http://webrtc-codereview.appspot.com/116011 git-svn-id: http://webrtc.googlecode.com/svn/trunk@482 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@ -37,18 +37,30 @@ struct RTPAudioHeader
|
|||||||
|
|
||||||
struct RTPVideoHeaderH263
|
struct RTPVideoHeaderH263
|
||||||
{
|
{
|
||||||
|
void InitRTPVideoHeaderH263() {};
|
||||||
bool independentlyDecodable; // H.263-1998 if no P bit it's not independently decodable
|
bool independentlyDecodable; // H.263-1998 if no P bit it's not independently decodable
|
||||||
bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the
|
bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the
|
||||||
// first byte of this packet
|
// first byte of this packet
|
||||||
};
|
};
|
||||||
enum {kNoPictureId = -1};
|
enum {kNoPictureId = -1};
|
||||||
|
enum {kNoTl0PicIdx = -1};
|
||||||
|
enum {kNoTemporalIdx = -1};
|
||||||
struct RTPVideoHeaderVP8
|
struct RTPVideoHeaderVP8
|
||||||
{
|
{
|
||||||
bool startBit; // Start of partition.
|
void InitRTPVideoHeaderVP8()
|
||||||
bool stopBit; // Stop of partition.
|
{
|
||||||
|
nonReference = false;
|
||||||
|
pictureId = kNoPictureId;
|
||||||
|
tl0PicIdx = kNoTl0PicIdx;
|
||||||
|
temporalIdx = kNoTemporalIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nonReference; // Frame is discardable.
|
||||||
WebRtc_Word16 pictureId; // Picture ID index, 15 bits;
|
WebRtc_Word16 pictureId; // Picture ID index, 15 bits;
|
||||||
// kNoPictureId if PictureID does not exist.
|
// kNoPictureId if PictureID does not exist.
|
||||||
bool nonReference; // Frame is discardable.
|
WebRtc_Word16 tl0PicIdx; // TL0PIC_IDX, 8 bits;
|
||||||
|
// kNoTl0PicIdx means no value provided.
|
||||||
|
WebRtc_Word8 temporalIdx; // Temporal layer index, or kNoTemporalIdx.
|
||||||
};
|
};
|
||||||
union RTPVideoTypeHeader
|
union RTPVideoTypeHeader
|
||||||
{
|
{
|
||||||
|
@ -35,11 +35,12 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
|
|||||||
part_ix_(0),
|
part_ix_(0),
|
||||||
beginning_(true),
|
beginning_(true),
|
||||||
first_fragment_(true),
|
first_fragment_(true),
|
||||||
vp8_header_bytes_(1),
|
vp8_fixed_payload_descriptor_bytes_(1),
|
||||||
aggr_mode_(aggr_modes_[mode]),
|
aggr_mode_(aggr_modes_[mode]),
|
||||||
balance_(balance_modes_[mode]),
|
balance_(balance_modes_[mode]),
|
||||||
separate_first_(separate_first_modes_[mode]),
|
separate_first_(separate_first_modes_[mode]),
|
||||||
hdr_info_(hdr_info)
|
hdr_info_(hdr_info),
|
||||||
|
first_partition_in_packet_(0)
|
||||||
{
|
{
|
||||||
part_info_ = fragmentation;
|
part_info_ = fragmentation;
|
||||||
}
|
}
|
||||||
@ -54,11 +55,12 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data,
|
|||||||
part_ix_(0),
|
part_ix_(0),
|
||||||
beginning_(true),
|
beginning_(true),
|
||||||
first_fragment_(true),
|
first_fragment_(true),
|
||||||
vp8_header_bytes_(1),
|
vp8_fixed_payload_descriptor_bytes_(1),
|
||||||
aggr_mode_(aggr_modes_[kSloppy]),
|
aggr_mode_(aggr_modes_[kSloppy]),
|
||||||
balance_(balance_modes_[kSloppy]),
|
balance_(balance_modes_[kSloppy]),
|
||||||
separate_first_(separate_first_modes_[kSloppy]),
|
separate_first_(separate_first_modes_[kSloppy]),
|
||||||
hdr_info_(hdr_info)
|
hdr_info_(hdr_info),
|
||||||
|
first_partition_in_packet_(0)
|
||||||
{
|
{
|
||||||
part_info_.VerifyAndAllocateFragmentationHeader(1);
|
part_info_.VerifyAndAllocateFragmentationHeader(1);
|
||||||
part_info_.fragmentationLength[0] = payload_size;
|
part_info_.fragmentationLength[0] = payload_size;
|
||||||
@ -97,14 +99,22 @@ int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes,
|
|||||||
int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
|
int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
|
||||||
int* bytes_to_send, bool* last_packet)
|
int* bytes_to_send, bool* last_packet)
|
||||||
{
|
{
|
||||||
|
if (max_payload_len < vp8_fixed_payload_descriptor_bytes_
|
||||||
|
+ PayloadDescriptorExtraLength() + 1)
|
||||||
|
{
|
||||||
|
// The provided payload length is not long enough for the payload
|
||||||
|
// descriptor and one payload byte. Return an error.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
const int num_partitions = part_info_.fragmentationVectorSize;
|
const int num_partitions = part_info_.fragmentationVectorSize;
|
||||||
int send_bytes = 0; // How much data to send in this packet.
|
int send_bytes = 0; // How much data to send in this packet.
|
||||||
bool split_payload = true; // Splitting of partitions is initially allowed.
|
bool split_payload = true; // Splitting of partitions is initially allowed.
|
||||||
int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] -
|
int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] -
|
||||||
payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] +
|
payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] +
|
||||||
FirstHeaderExtraLength(); // Add header extra length to payload length.
|
PayloadDescriptorExtraLength();
|
||||||
int rem_payload_len = max_payload_len - vp8_header_bytes_;
|
int rem_payload_len = max_payload_len - vp8_fixed_payload_descriptor_bytes_;
|
||||||
const int first_partition_in_packet = part_ix_;
|
first_partition_in_packet_ = part_ix_;
|
||||||
|
if (first_partition_in_packet_ > 8) return -1;
|
||||||
|
|
||||||
while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition,
|
while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition,
|
||||||
split_payload))
|
split_payload))
|
||||||
@ -141,67 +151,117 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer,
|
|||||||
++part_ix_; // Advance to next partition.
|
++part_ix_; // Advance to next partition.
|
||||||
}
|
}
|
||||||
|
|
||||||
send_bytes -= FirstHeaderExtraLength(); // Remove the extra length again.
|
send_bytes -= PayloadDescriptorExtraLength(); // Remove extra length again.
|
||||||
assert(send_bytes > 0);
|
assert(send_bytes > 0);
|
||||||
const bool end_of_fragment = (remaining_in_partition == 0);
|
|
||||||
// Write the payload header and the payload to buffer.
|
// Write the payload header and the payload to buffer.
|
||||||
*bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer,
|
*bytes_to_send = WriteHeaderAndPayload(send_bytes, buffer, max_payload_len);
|
||||||
max_payload_len);
|
|
||||||
if (*bytes_to_send < 0)
|
if (*bytes_to_send < 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beginning_ = false; // Next packet cannot be first packet in frame.
|
||||||
|
// Next packet starts new fragment if this ended one.
|
||||||
|
first_fragment_ = (remaining_in_partition == 0);
|
||||||
*last_packet = (payload_bytes_sent_ >= payload_size_);
|
*last_packet = (payload_bytes_sent_ >= payload_size_);
|
||||||
assert(!*last_packet || (payload_bytes_sent_ == payload_size_));
|
assert(!*last_packet || (payload_bytes_sent_ == payload_size_));
|
||||||
return first_partition_in_packet;
|
return first_partition_in_packet_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes,
|
int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes,
|
||||||
bool end_of_fragment,
|
|
||||||
WebRtc_UWord8* buffer,
|
WebRtc_UWord8* buffer,
|
||||||
int buffer_length)
|
int buffer_length)
|
||||||
{
|
{
|
||||||
// Write the VP8 payload header.
|
// Write the VP8 payload descriptor.
|
||||||
// 0 1 2
|
// 0
|
||||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
|
// 0 1 2 3 4 5 6 7 8
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+
|
||||||
// | RSV |I|N|FI |B| PictureID (1 or 2 octets) |
|
// |X| |N|S| PART_ID |
|
||||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+-+
|
||||||
|
// X: |I|L|T| | (mandatory if any of the below are used)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+
|
||||||
|
// I: |PictureID (8/16b)| (optional)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+
|
||||||
|
// L: | TL0PIC_IDX | (optional)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+
|
||||||
|
// T: | TID | | (optional)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
if (payload_bytes < 0)
|
assert(payload_bytes > 0);
|
||||||
{
|
assert(payload_bytes_sent_ + payload_bytes <= payload_size_);
|
||||||
return -1;
|
assert(vp8_fixed_payload_descriptor_bytes_ + PayloadDescriptorExtraLength()
|
||||||
}
|
+ payload_bytes <= buffer_length);
|
||||||
if (payload_bytes_sent_ + payload_bytes > payload_size_)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[0] = 0;
|
buffer[0] = 0;
|
||||||
if (hdr_info_.nonReference) buffer[0] |= (0x01 << 3); // N
|
if (XFieldPresent()) buffer[0] |= kXBit;
|
||||||
buffer[0] |= (GetFIFlag(end_of_fragment) << 1); // FI
|
if (hdr_info_.nonReference) buffer[0] |= kNBit;
|
||||||
if (beginning_) buffer[0] |= 0x01; // B
|
if (first_fragment_) buffer[0] |= kSBit;
|
||||||
|
buffer[0] |= (first_partition_in_packet_ & kPartIdField);
|
||||||
|
|
||||||
int pic_id_len = WritePictureID(&buffer[vp8_header_bytes_],
|
const int extension_length = WriteExtensionFields(buffer, buffer_length);
|
||||||
buffer_length - vp8_header_bytes_);
|
|
||||||
if (pic_id_len < 0) return pic_id_len; // error
|
|
||||||
if (pic_id_len > 0) buffer[0] |= (0x01 << 4); // I
|
|
||||||
|
|
||||||
if (vp8_header_bytes_ + pic_id_len + payload_bytes > buffer_length)
|
memcpy(&buffer[vp8_fixed_payload_descriptor_bytes_ + extension_length],
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(&buffer[vp8_header_bytes_ + pic_id_len],
|
|
||||||
&payload_data_[payload_bytes_sent_], payload_bytes);
|
&payload_data_[payload_bytes_sent_], payload_bytes);
|
||||||
|
|
||||||
beginning_ = false; // next packet cannot be first packet in frame
|
|
||||||
// next packet starts new fragment if this ended one
|
|
||||||
first_fragment_ = end_of_fragment;
|
|
||||||
payload_bytes_sent_ += payload_bytes;
|
payload_bytes_sent_ += payload_bytes;
|
||||||
|
|
||||||
// Return total length of written data.
|
// Return total length of written data.
|
||||||
return payload_bytes + vp8_header_bytes_ + pic_id_len;
|
return payload_bytes + vp8_fixed_payload_descriptor_bytes_
|
||||||
|
+ extension_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RtpFormatVp8::WriteExtensionFields(WebRtc_UWord8* buffer, int buffer_length)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
int extension_length = 0;
|
||||||
|
if (XFieldPresent())
|
||||||
|
{
|
||||||
|
WebRtc_UWord8* x_field = buffer + vp8_fixed_payload_descriptor_bytes_;
|
||||||
|
*x_field = 0;
|
||||||
|
extension_length = 1; // One octet for the X field.
|
||||||
|
if (PictureIdPresent())
|
||||||
|
{
|
||||||
|
if (WritePictureIDFields(x_field, buffer, buffer_length,
|
||||||
|
&extension_length) < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (TL0PicIdxFieldPresent())
|
||||||
|
{
|
||||||
|
if (WriteTl0PicIdxFields(x_field, buffer, buffer_length,
|
||||||
|
&extension_length) < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (TIDFieldPresent())
|
||||||
|
{
|
||||||
|
if (WriteTIDFields(x_field, buffer, buffer_length,
|
||||||
|
&extension_length) < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(extension_length == PayloadDescriptorExtraLength());
|
||||||
|
}
|
||||||
|
return extension_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RtpFormatVp8::WritePictureIDFields(WebRtc_UWord8* x_field,
|
||||||
|
WebRtc_UWord8* buffer,
|
||||||
|
int buffer_length,
|
||||||
|
int* extension_length) const
|
||||||
|
{
|
||||||
|
*x_field |= kIBit;
|
||||||
|
const int pic_id_length = WritePictureID(
|
||||||
|
buffer + vp8_fixed_payload_descriptor_bytes_ + *extension_length,
|
||||||
|
buffer_length - vp8_fixed_payload_descriptor_bytes_
|
||||||
|
- *extension_length);
|
||||||
|
if (pic_id_length < 0) return -1;
|
||||||
|
*extension_length += pic_id_length;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
|
int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
|
||||||
@ -209,7 +269,7 @@ int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
|
|||||||
const WebRtc_UWord16 pic_id =
|
const WebRtc_UWord16 pic_id =
|
||||||
static_cast<WebRtc_UWord16> (hdr_info_.pictureId);
|
static_cast<WebRtc_UWord16> (hdr_info_.pictureId);
|
||||||
int picture_id_len = PictureIdLength();
|
int picture_id_len = PictureIdLength();
|
||||||
if (picture_id_len > buffer_length) return -1; // error
|
if (picture_id_len > buffer_length) return -1;
|
||||||
if (picture_id_len == 2)
|
if (picture_id_len == 2)
|
||||||
{
|
{
|
||||||
buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F);
|
buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F);
|
||||||
@ -222,17 +282,51 @@ int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const
|
|||||||
return picture_id_len;
|
return picture_id_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpFormatVp8::FirstHeaderExtraLength() const
|
int RtpFormatVp8::WriteTl0PicIdxFields(WebRtc_UWord8* x_field,
|
||||||
|
WebRtc_UWord8* buffer,
|
||||||
|
int buffer_length,
|
||||||
|
int* extension_length) const
|
||||||
|
{
|
||||||
|
if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length
|
||||||
|
+ 1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*x_field |= kLBit;
|
||||||
|
buffer[vp8_fixed_payload_descriptor_bytes_
|
||||||
|
+ *extension_length] = hdr_info_.tl0PicIdx;
|
||||||
|
++*extension_length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RtpFormatVp8::WriteTIDFields(WebRtc_UWord8* x_field,
|
||||||
|
WebRtc_UWord8* buffer,
|
||||||
|
int buffer_length,
|
||||||
|
int* extension_length) const
|
||||||
|
{
|
||||||
|
if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length
|
||||||
|
+ 1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*x_field |= kTBit;
|
||||||
|
buffer[vp8_fixed_payload_descriptor_bytes_ + *extension_length]
|
||||||
|
= hdr_info_.temporalIdx << 5;
|
||||||
|
++*extension_length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RtpFormatVp8::PayloadDescriptorExtraLength() const
|
||||||
{
|
{
|
||||||
if (!beginning_)
|
if (!beginning_)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int length = 0;
|
int length_bytes = PictureIdLength();
|
||||||
|
if (TL0PicIdxFieldPresent()) ++length_bytes;
|
||||||
length += PictureIdLength();
|
if (TIDFieldPresent()) ++length_bytes;
|
||||||
|
if (length_bytes > 0) ++length_bytes; // Include the extension field.
|
||||||
return length;
|
return length_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpFormatVp8::PictureIdLength() const
|
int RtpFormatVp8::PictureIdLength() const
|
||||||
@ -251,19 +345,19 @@ int RtpFormatVp8::PictureIdLength() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpFormatVp8::GetFIFlag(bool end_of_fragment) const
|
bool RtpFormatVp8::XFieldPresent() const
|
||||||
{
|
{
|
||||||
if (first_fragment_ && end_of_fragment) {
|
return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent());
|
||||||
return 0x0;
|
}
|
||||||
}
|
|
||||||
if (first_fragment_ && !end_of_fragment) {
|
bool RtpFormatVp8::TIDFieldPresent() const
|
||||||
return 0x1;
|
{
|
||||||
}
|
return (hdr_info_.temporalIdx != kNoTemporalIdx);
|
||||||
if (!first_fragment_ && !end_of_fragment) {
|
}
|
||||||
return 0x2;
|
|
||||||
}
|
bool RtpFormatVp8::TL0PicIdxFieldPresent() const
|
||||||
// if (!first_fragment_ && end_of_fragment)
|
{
|
||||||
return 0x3;
|
return (hdr_info_.tl0PicIdx != kNoTl0PicIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -80,6 +80,13 @@ private:
|
|||||||
static const AggregationMode aggr_modes_[kNumModes];
|
static const AggregationMode aggr_modes_[kNumModes];
|
||||||
static const bool balance_modes_[kNumModes];
|
static const bool balance_modes_[kNumModes];
|
||||||
static const bool separate_first_modes_[kNumModes];
|
static const bool separate_first_modes_[kNumModes];
|
||||||
|
static const int kXBit = 0x80;
|
||||||
|
static const int kNBit = 0x20;
|
||||||
|
static const int kSBit = 0x10;
|
||||||
|
static const int kPartIdField = 0x0F;
|
||||||
|
static const int kIBit = 0x80;
|
||||||
|
static const int kLBit = 0x40;
|
||||||
|
static const int kTBit = 0x20;
|
||||||
|
|
||||||
// Calculate size of next chunk to send. Returns 0 if none can be sent.
|
// Calculate size of next chunk to send. Returns 0 if none can be sent.
|
||||||
int CalcNextSize(int max_payload_len, int remaining_bytes,
|
int CalcNextSize(int max_payload_len, int remaining_bytes,
|
||||||
@ -89,8 +96,29 @@ private:
|
|||||||
// Will copy send_bytes bytes from the current position on the payload data.
|
// Will copy send_bytes bytes from the current position on the payload data.
|
||||||
// last_fragment indicates that this packet ends with the last byte of a
|
// last_fragment indicates that this packet ends with the last byte of a
|
||||||
// partition.
|
// partition.
|
||||||
int WriteHeaderAndPayload(int send_bytes, bool end_of_fragment,
|
int WriteHeaderAndPayload(int send_bytes, WebRtc_UWord8* buffer,
|
||||||
WebRtc_UWord8* buffer, int buffer_length);
|
int buffer_length);
|
||||||
|
|
||||||
|
|
||||||
|
// Write the X field and the appropriate extension fields to buffer.
|
||||||
|
// The function returns the extension length (including X field), or -1
|
||||||
|
// on error.
|
||||||
|
int WriteExtensionFields(WebRtc_UWord8* buffer, int buffer_length) const;
|
||||||
|
|
||||||
|
// Set the I bit in the x_field, and write PictureID to the appropriate
|
||||||
|
// position in buffer. The function returns 0 on success, -1 otherwise.
|
||||||
|
int WritePictureIDFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer,
|
||||||
|
int buffer_length, int* extension_length) const;
|
||||||
|
|
||||||
|
// Set the L bit in the x_field, and write Tl0PicIdx to the appropriate
|
||||||
|
// position in buffer. The function returns 0 on success, -1 otherwise.
|
||||||
|
int WriteTl0PicIdxFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer,
|
||||||
|
int buffer_length, int* extension_length) const;
|
||||||
|
|
||||||
|
// Set the T bit in the x_field, and write TID to the appropriate
|
||||||
|
// position in buffer. The function returns 0 on success, -1 otherwise.
|
||||||
|
int WriteTIDFields(WebRtc_UWord8* x_field, WebRtc_UWord8* buffer,
|
||||||
|
int buffer_length, int* extension_length) const;
|
||||||
|
|
||||||
// Write the PictureID from codec_specific_info_ to buffer. One or two
|
// Write the PictureID from codec_specific_info_ to buffer. One or two
|
||||||
// bytes are written, depending on magnitude of PictureID. The function
|
// bytes are written, depending on magnitude of PictureID. The function
|
||||||
@ -99,13 +127,17 @@ private:
|
|||||||
|
|
||||||
// Calculate and return length (octets) of the variable header fields in
|
// Calculate and return length (octets) of the variable header fields in
|
||||||
// the next header (i.e., header length in addition to vp8_header_bytes_).
|
// the next header (i.e., header length in addition to vp8_header_bytes_).
|
||||||
int FirstHeaderExtraLength() const;
|
int PayloadDescriptorExtraLength() const;
|
||||||
|
|
||||||
// Calculate and return length (octets) of PictureID field in the next
|
// Calculate and return length (octets) of PictureID field in the next
|
||||||
// header. Can be 0, 1, or 2.
|
// header. Can be 0, 1, or 2.
|
||||||
int PictureIdLength() const;
|
int PictureIdLength() const;
|
||||||
|
|
||||||
int GetFIFlag(bool end_of_fragment) const;
|
// Check whether each of the optional fields will be included in the header.
|
||||||
|
bool XFieldPresent() const;
|
||||||
|
bool TIDFieldPresent() const;
|
||||||
|
bool TL0PicIdxFieldPresent() const;
|
||||||
|
bool PictureIdPresent() const { return (PictureIdLength() > 0); }
|
||||||
|
|
||||||
const WebRtc_UWord8* payload_data_;
|
const WebRtc_UWord8* payload_data_;
|
||||||
const int payload_size_;
|
const int payload_size_;
|
||||||
@ -114,11 +146,13 @@ private:
|
|||||||
int part_ix_;
|
int part_ix_;
|
||||||
bool beginning_; // first partition in this frame
|
bool beginning_; // first partition in this frame
|
||||||
bool first_fragment_; // first fragment of a partition
|
bool first_fragment_; // first fragment of a partition
|
||||||
const int vp8_header_bytes_; // length of VP8 payload header's fixed part
|
const int vp8_fixed_payload_descriptor_bytes_; // length of VP8 payload
|
||||||
|
// descriptors's fixed part
|
||||||
AggregationMode aggr_mode_;
|
AggregationMode aggr_mode_;
|
||||||
bool balance_;
|
bool balance_;
|
||||||
bool separate_first_;
|
bool separate_first_;
|
||||||
const RTPVideoHeaderVP8 hdr_info_;
|
const RTPVideoHeaderVP8 hdr_info_;
|
||||||
|
int first_partition_in_packet_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,20 +24,26 @@ using webrtc::RTPFragmentationHeader;
|
|||||||
using webrtc::RtpFormatVp8;
|
using webrtc::RtpFormatVp8;
|
||||||
using webrtc::RTPVideoHeaderVP8;
|
using webrtc::RTPVideoHeaderVP8;
|
||||||
|
|
||||||
const WebRtc_UWord32 kPayloadSize = 30;
|
const int kPayloadSize = 30;
|
||||||
|
const int kBufferSize = kPayloadSize + 6; // Add space for payload descriptor.
|
||||||
|
|
||||||
class RtpFormatVp8Test : public ::testing::Test {
|
class RtpFormatVp8Test : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
RtpFormatVp8Test() {};
|
RtpFormatVp8Test() {};
|
||||||
virtual void SetUp();
|
virtual void SetUp();
|
||||||
virtual void TearDown();
|
virtual void TearDown();
|
||||||
void CheckHeader(bool first_in_frame, bool frag_start, bool frag_end);
|
void CheckHeader(bool first_in_frame, bool frag_start, int part_id);
|
||||||
|
void CheckPictureID();
|
||||||
|
void CheckTl0PicIdx();
|
||||||
|
void CheckTID();
|
||||||
void CheckPayload(int payload_end);
|
void CheckPayload(int payload_end);
|
||||||
void CheckLast(bool last) const;
|
void CheckLast(bool last) const;
|
||||||
void CheckPacket(int send_bytes, int expect_bytes, bool last,
|
void CheckPacket(int send_bytes, int expect_bytes, bool last,
|
||||||
bool first_in_frame, bool frag_start, bool frag_end);
|
bool first_in_frame, bool frag_start);
|
||||||
|
void CheckPacketZeroPartId(int send_bytes, int expect_bytes, bool last,
|
||||||
|
bool first_in_frame, bool frag_start);
|
||||||
WebRtc_UWord8 payload_data_[kPayloadSize];
|
WebRtc_UWord8 payload_data_[kPayloadSize];
|
||||||
WebRtc_UWord8 buffer_[kPayloadSize];
|
WebRtc_UWord8 buffer_[kBufferSize];
|
||||||
WebRtc_UWord8 *data_ptr_;
|
WebRtc_UWord8 *data_ptr_;
|
||||||
RTPFragmentationHeader* fragmentation_;
|
RTPFragmentationHeader* fragmentation_;
|
||||||
RTPVideoHeaderVP8 hdr_info_;
|
RTPVideoHeaderVP8 hdr_info_;
|
||||||
@ -60,57 +66,130 @@ void RtpFormatVp8Test::SetUp() {
|
|||||||
fragmentation_->fragmentationOffset[1] = 10;
|
fragmentation_->fragmentationOffset[1] = 10;
|
||||||
fragmentation_->fragmentationOffset[2] = 20;
|
fragmentation_->fragmentationOffset[2] = 20;
|
||||||
|
|
||||||
hdr_info_.pictureId = 0;
|
hdr_info_.pictureId = webrtc::kNoPictureId;
|
||||||
hdr_info_.nonReference = false;
|
hdr_info_.nonReference = false;
|
||||||
|
hdr_info_.temporalIdx = webrtc::kNoTemporalIdx;
|
||||||
|
hdr_info_.tl0PicIdx = webrtc::kNoTl0PicIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpFormatVp8Test::TearDown() {
|
void RtpFormatVp8Test::TearDown() {
|
||||||
delete fragmentation_;
|
delete fragmentation_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First octet tests
|
||||||
#define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a)
|
#define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a)
|
||||||
|
|
||||||
#define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0)
|
#define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0)
|
||||||
|
|
||||||
//#define EXPECT_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0))
|
#define EXPECT_BIT_X_EQ(x,a) EXPECT_BIT_EQ(x, 7, a)
|
||||||
#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 4, a)
|
|
||||||
|
|
||||||
#define EXPECT_BIT_N_EQ(x,a) EXPECT_EQ((((x)&0x08) > 0), (a > 0))
|
#define EXPECT_BIT_N_EQ(x,a) EXPECT_BIT_EQ(x, 5, a)
|
||||||
|
|
||||||
#define EXPECT_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a)
|
#define EXPECT_BIT_S_EQ(x,a) EXPECT_BIT_EQ(x, 4, a)
|
||||||
|
|
||||||
#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0))
|
#define EXPECT_PART_ID_EQ(x, a) EXPECT_EQ(((x)&0x0F), a)
|
||||||
|
|
||||||
|
// Extension fields tests
|
||||||
|
#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 7, a)
|
||||||
|
|
||||||
|
#define EXPECT_BIT_L_EQ(x,a) EXPECT_BIT_EQ(x, 6, a)
|
||||||
|
|
||||||
|
#define EXPECT_BIT_T_EQ(x,a) EXPECT_BIT_EQ(x, 5, a)
|
||||||
|
|
||||||
|
#define EXPECT_TID_EQ(x,a) EXPECT_EQ((((x)&0xE0) >> 5), a)
|
||||||
|
|
||||||
void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start,
|
void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start,
|
||||||
bool frag_end)
|
int part_id)
|
||||||
{
|
{
|
||||||
payload_start_ = 1;
|
payload_start_ = 1;
|
||||||
EXPECT_RSV_ZERO(buffer_[0]);
|
EXPECT_BIT_EQ(buffer_[0], 6, 0); // check reserved bit
|
||||||
if (first_in_frame & hdr_info_.pictureId != webrtc::kNoPictureId)
|
|
||||||
|
if (first_in_frame &&
|
||||||
|
(hdr_info_.pictureId != webrtc::kNoPictureId ||
|
||||||
|
hdr_info_.temporalIdx != webrtc::kNoTemporalIdx ||
|
||||||
|
hdr_info_.tl0PicIdx != webrtc::kNoTl0PicIdx))
|
||||||
{
|
{
|
||||||
EXPECT_BIT_I_EQ(buffer_[0], 1);
|
EXPECT_BIT_X_EQ(buffer_[0], 1);
|
||||||
|
++payload_start_;
|
||||||
|
CheckPictureID();
|
||||||
|
CheckTl0PicIdx();
|
||||||
|
CheckTID();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_BIT_X_EQ(buffer_[0], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_BIT_N_EQ(buffer_[0], 0);
|
||||||
|
EXPECT_BIT_S_EQ(buffer_[0], frag_start);
|
||||||
|
|
||||||
|
// Check partition index.
|
||||||
|
if (part_id < 0)
|
||||||
|
{
|
||||||
|
// (Payload data is the same as the partition index.)
|
||||||
|
EXPECT_EQ(buffer_[0] & 0x0F, buffer_[payload_start_]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(buffer_[0] & 0x0F, part_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RtpFormatVp8Test::CheckPictureID()
|
||||||
|
{
|
||||||
|
if (hdr_info_.pictureId != webrtc::kNoPictureId)
|
||||||
|
{
|
||||||
|
EXPECT_BIT_I_EQ(buffer_[1], 1);
|
||||||
if (hdr_info_.pictureId > 0x7F)
|
if (hdr_info_.pictureId > 0x7F)
|
||||||
{
|
{
|
||||||
EXPECT_BIT_EQ(buffer_[1], 7, 1);
|
EXPECT_BIT_EQ(buffer_[payload_start_], 7, 1);
|
||||||
EXPECT_EQ(buffer_[1] & 0x7F,
|
EXPECT_EQ(buffer_[payload_start_] & 0x7F,
|
||||||
(hdr_info_.pictureId >> 8) & 0x7F);
|
(hdr_info_.pictureId >> 8) & 0x7F);
|
||||||
EXPECT_EQ(buffer_[2], hdr_info_.pictureId & 0xFF);
|
EXPECT_EQ(buffer_[payload_start_ + 1],
|
||||||
|
hdr_info_.pictureId & 0xFF);
|
||||||
payload_start_ += 2;
|
payload_start_ += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EXPECT_BIT_EQ(buffer_[1], 7, 0);
|
EXPECT_BIT_EQ(buffer_[payload_start_], 7, 0);
|
||||||
EXPECT_EQ(buffer_[1] & 0x7F,
|
EXPECT_EQ(buffer_[payload_start_] & 0x7F,
|
||||||
(hdr_info_.pictureId) & 0x7F);
|
(hdr_info_.pictureId) & 0x7F);
|
||||||
payload_start_ += 1;
|
payload_start_ += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_BIT_N_EQ(buffer_[0], 0);
|
else
|
||||||
WebRtc_UWord8 fi = 0x03;
|
{
|
||||||
if (frag_start) fi = fi & 0x01;
|
EXPECT_BIT_I_EQ(buffer_[1], 0);
|
||||||
if (frag_end) fi = fi & 0x02;
|
}
|
||||||
EXPECT_FI_EQ(buffer_[0], fi);
|
}
|
||||||
if (first_in_frame) EXPECT_BIT_B_EQ(buffer_[0], 1);
|
|
||||||
|
void RtpFormatVp8Test::CheckTl0PicIdx()
|
||||||
|
{
|
||||||
|
if (hdr_info_.tl0PicIdx != webrtc::kNoTl0PicIdx)
|
||||||
|
{
|
||||||
|
EXPECT_BIT_L_EQ(buffer_[1], 1);
|
||||||
|
EXPECT_EQ(buffer_[payload_start_], hdr_info_.tl0PicIdx);
|
||||||
|
++payload_start_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_BIT_L_EQ(buffer_[1], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RtpFormatVp8Test::CheckTID()
|
||||||
|
{
|
||||||
|
if (hdr_info_.temporalIdx != webrtc::kNoTemporalIdx)
|
||||||
|
{
|
||||||
|
EXPECT_BIT_T_EQ(buffer_[1], 1);
|
||||||
|
EXPECT_TID_EQ(buffer_[payload_start_], hdr_info_.temporalIdx);
|
||||||
|
EXPECT_EQ(buffer_[payload_start_] & 0x1F, 0);
|
||||||
|
++payload_start_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_BIT_T_EQ(buffer_[1], 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpFormatVp8Test::CheckPayload(int payload_end)
|
void RtpFormatVp8Test::CheckPayload(int payload_end)
|
||||||
@ -125,10 +204,22 @@ void RtpFormatVp8Test::CheckLast(bool last) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last,
|
void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last,
|
||||||
bool first_in_frame, bool frag_start, bool frag_end)
|
bool first_in_frame, bool frag_start)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(send_bytes, expect_bytes);
|
EXPECT_EQ(send_bytes, expect_bytes);
|
||||||
CheckHeader(first_in_frame, frag_start, frag_end);
|
CheckHeader(first_in_frame, frag_start, -1);
|
||||||
|
CheckPayload(send_bytes);
|
||||||
|
CheckLast(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RtpFormatVp8Test::CheckPacketZeroPartId(int send_bytes,
|
||||||
|
int expect_bytes,
|
||||||
|
bool last,
|
||||||
|
bool first_in_frame,
|
||||||
|
bool frag_start)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(send_bytes, expect_bytes);
|
||||||
|
CheckHeader(first_in_frame, frag_start, 0);
|
||||||
CheckPayload(send_bytes);
|
CheckPayload(send_bytes);
|
||||||
CheckLast(last);
|
CheckLast(last);
|
||||||
}
|
}
|
||||||
@ -143,57 +234,51 @@ TEST_F(RtpFormatVp8Test, TestStrictMode)
|
|||||||
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
|
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
|
||||||
hdr_info_, *fragmentation_, webrtc::kStrict);
|
hdr_info_, *fragmentation_, webrtc::kStrict);
|
||||||
|
|
||||||
// get first packet, expect balanced size = same as second packet
|
// get first packet, expect balanced size ~= same as second packet
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 7, last,
|
CheckPacket(send_bytes, 8, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ false);
|
|
||||||
first_in_frame = false;
|
first_in_frame = false;
|
||||||
|
|
||||||
// get second packet
|
// get second packet
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 7, last,
|
CheckPacket(send_bytes, 7, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ true);
|
|
||||||
|
|
||||||
// Second partition
|
// Second partition
|
||||||
// Get first (and only) packet
|
// Get first (and only) packet
|
||||||
EXPECT_EQ(1, packetizer.NextPacket(20, buffer_, &send_bytes, &last));
|
EXPECT_EQ(1, packetizer.NextPacket(20, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 11, last,
|
CheckPacket(send_bytes, 11, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ true);
|
|
||||||
|
|
||||||
// Third partition
|
// Third partition
|
||||||
// Get first packet (of four)
|
// Get first packet (of four)
|
||||||
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 4, last,
|
CheckPacket(send_bytes, 4, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// Get second packet (of four)
|
// Get second packet (of four)
|
||||||
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 3, last,
|
CheckPacket(send_bytes, 3, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// Get third packet (of four)
|
// Get third packet (of four)
|
||||||
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 4, last,
|
CheckPacket(send_bytes, 4, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// Get fourth and last packet
|
// Get fourth and last packet
|
||||||
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
EXPECT_EQ(2, packetizer.NextPacket(4, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 3, last,
|
CheckPacket(send_bytes, 3, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFormatVp8Test, TestAggregateMode)
|
TEST_F(RtpFormatVp8Test, TestAggregateMode)
|
||||||
@ -207,29 +292,34 @@ TEST_F(RtpFormatVp8Test, TestAggregateMode)
|
|||||||
hdr_info_, *fragmentation_, webrtc::kAggregate);
|
hdr_info_, *fragmentation_, webrtc::kAggregate);
|
||||||
|
|
||||||
// get first packet
|
// get first packet
|
||||||
// first half of first partition
|
// first part of first partition (balanced fragments are expected)
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(6, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 6, last,
|
CheckPacket(send_bytes, 5, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ false);
|
|
||||||
first_in_frame = false;
|
first_in_frame = false;
|
||||||
|
|
||||||
// get second packet
|
// get second packet
|
||||||
// second half of first partition
|
// second fragment of first partition
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 7, last,
|
CheckPacket(send_bytes, 5, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ true);
|
|
||||||
|
|
||||||
// get third packet
|
// get third packet
|
||||||
|
// third fragment of first partition
|
||||||
|
EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
|
||||||
|
CheckPacket(send_bytes, 5, last,
|
||||||
|
first_in_frame,
|
||||||
|
/* frag_start */ false);
|
||||||
|
|
||||||
|
// get fourth packet
|
||||||
// last two partitions aggregated
|
// last two partitions aggregated
|
||||||
EXPECT_EQ(1, packetizer.NextPacket(25, buffer_, &send_bytes, &last));
|
EXPECT_EQ(1, packetizer.NextPacket(25, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 21, last,
|
CheckPacket(send_bytes, 21, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpFormatVp8Test, TestSloppyMode)
|
TEST_F(RtpFormatVp8Test, TestSloppyMode)
|
||||||
@ -246,8 +336,7 @@ TEST_F(RtpFormatVp8Test, TestSloppyMode)
|
|||||||
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacket(send_bytes, 9, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ false);
|
|
||||||
first_in_frame = false;
|
first_in_frame = false;
|
||||||
|
|
||||||
// get second packet
|
// get second packet
|
||||||
@ -255,24 +344,22 @@ TEST_F(RtpFormatVp8Test, TestSloppyMode)
|
|||||||
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacket(send_bytes, 9, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// get third packet
|
// get third packet
|
||||||
// fragments of second and third partitions
|
// fragments of second and third partitions
|
||||||
EXPECT_EQ(1, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(1, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacket(send_bytes, 9, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// get fourth packet
|
// get fourth packet
|
||||||
// second half of last partition
|
// second half of last partition
|
||||||
EXPECT_EQ(2, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(2, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 7, last,
|
CheckPacket(send_bytes, 7, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that sloppy mode is forced if fragmentation info is missing.
|
// Verify that sloppy mode is forced if fragmentation info is missing.
|
||||||
@ -287,43 +374,39 @@ TEST_F(RtpFormatVp8Test, TestSloppyModeFallback)
|
|||||||
hdr_info_);
|
hdr_info_);
|
||||||
|
|
||||||
// get first packet
|
// get first packet
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacketZeroPartId(send_bytes, 10, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ true,
|
/* frag_start */ true);
|
||||||
/* frag_end */ false);
|
|
||||||
first_in_frame = false;
|
first_in_frame = false;
|
||||||
|
|
||||||
// get second packet
|
// get second packet
|
||||||
// fragments of first and second partitions
|
// fragments of first and second partitions
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacketZeroPartId(send_bytes, 10, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// get third packet
|
// get third packet
|
||||||
// fragments of second and third partitions
|
// fragments of second and third partitions
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacketZeroPartId(send_bytes, 10, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ false);
|
|
||||||
|
|
||||||
// get fourth packet
|
// get fourth packet
|
||||||
// second half of last partition
|
// second half of last partition
|
||||||
EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last));
|
EXPECT_EQ(0, packetizer.NextPacket(7, buffer_, &send_bytes, &last));
|
||||||
CheckPacket(send_bytes, 9, last,
|
CheckPacketZeroPartId(send_bytes, 7, last,
|
||||||
first_in_frame,
|
first_in_frame,
|
||||||
/* frag_start */ false,
|
/* frag_start */ false);
|
||||||
/* frag_end */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that non-reference bit is set.
|
// Verify that non-reference bit is set.
|
||||||
TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
|
TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
|
||||||
int send_bytes = 0;
|
int send_bytes = 0;
|
||||||
bool last;
|
bool last;
|
||||||
bool first_in_frame = true;
|
|
||||||
|
|
||||||
hdr_info_.nonReference = true;
|
hdr_info_.nonReference = true;
|
||||||
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
|
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
|
||||||
@ -340,6 +423,25 @@ TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
|
|||||||
EXPECT_BIT_N_EQ(buffer_[0], 1);
|
EXPECT_BIT_N_EQ(buffer_[0], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify Tl0PicIdx and TID fields
|
||||||
|
TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) {
|
||||||
|
int send_bytes = 0;
|
||||||
|
bool last;
|
||||||
|
|
||||||
|
hdr_info_.tl0PicIdx = 117;
|
||||||
|
hdr_info_.temporalIdx = 2;
|
||||||
|
RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize,
|
||||||
|
hdr_info_, *fragmentation_, webrtc::kAggregate);
|
||||||
|
|
||||||
|
// get first and only packet
|
||||||
|
EXPECT_EQ(0, packetizer.NextPacket(kBufferSize, buffer_, &send_bytes,
|
||||||
|
&last));
|
||||||
|
bool first_in_frame = true;
|
||||||
|
CheckPacket(send_bytes, kPayloadSize + 4, last,
|
||||||
|
first_in_frame,
|
||||||
|
/* frag_start */ true);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include "rtp_receiver_video.h"
|
#include "rtp_receiver_video.h"
|
||||||
|
|
||||||
#include "trace.h"
|
|
||||||
#include "critical_section_wrapper.h"
|
#include "critical_section_wrapper.h"
|
||||||
#include "tick_util.h"
|
#include "tick_util.h"
|
||||||
|
|
||||||
@ -466,7 +465,8 @@ RTPReceiverVideo::ReceiveH263Codec(WebRtcRTPHeader* rtpHeader,
|
|||||||
{
|
{
|
||||||
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH263Video,
|
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH263Video,
|
||||||
payloadData,
|
payloadData,
|
||||||
payloadDataLength);
|
payloadDataLength,
|
||||||
|
_id);
|
||||||
ModuleRTPUtility::RTPPayload parsedPacket;
|
ModuleRTPUtility::RTPPayload parsedPacket;
|
||||||
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
||||||
|
|
||||||
@ -491,7 +491,8 @@ RTPReceiverVideo::ReceiveH2631998Codec(WebRtcRTPHeader* rtpHeader,
|
|||||||
{
|
{
|
||||||
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH2631998Video,
|
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH2631998Video,
|
||||||
payloadData,
|
payloadData,
|
||||||
payloadDataLength);
|
payloadDataLength,
|
||||||
|
_id);
|
||||||
|
|
||||||
ModuleRTPUtility::RTPPayload parsedPacket;
|
ModuleRTPUtility::RTPPayload parsedPacket;
|
||||||
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
||||||
@ -581,7 +582,8 @@ RTPReceiverVideo::ReceiveMPEG4Codec(WebRtcRTPHeader* rtpHeader,
|
|||||||
{
|
{
|
||||||
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpMpeg4Video,
|
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpMpeg4Video,
|
||||||
payloadData,
|
payloadData,
|
||||||
payloadDataLength);
|
payloadDataLength,
|
||||||
|
_id);
|
||||||
|
|
||||||
ModuleRTPUtility::RTPPayload parsedPacket;
|
ModuleRTPUtility::RTPPayload parsedPacket;
|
||||||
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
||||||
@ -612,7 +614,8 @@ RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtpHeader,
|
|||||||
{
|
{
|
||||||
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpVp8Video,
|
ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpVp8Video,
|
||||||
payloadData,
|
payloadData,
|
||||||
payloadDataLength);
|
payloadDataLength,
|
||||||
|
_id);
|
||||||
|
|
||||||
ModuleRTPUtility::RTPPayload parsedPacket;
|
ModuleRTPUtility::RTPPayload parsedPacket;
|
||||||
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
const bool success = rtpPayloadParser.Parse(parsedPacket);
|
||||||
@ -631,10 +634,17 @@ RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtpHeader,
|
|||||||
}
|
}
|
||||||
rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta;
|
rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta;
|
||||||
|
|
||||||
rtpHeader->type.Video.codecHeader.VP8.startBit = parsedPacket.info.VP8.startFragment; // Start of partition
|
RTPVideoHeaderVP8 *toHeader = &rtpHeader->type.Video.codecHeader.VP8;
|
||||||
rtpHeader->type.Video.codecHeader.VP8.stopBit= parsedPacket.info.VP8.stopFragment; // Stop of partition
|
ModuleRTPUtility::RTPPayloadVP8 *fromHeader = &parsedPacket.info.VP8;
|
||||||
|
|
||||||
rtpHeader->type.Video.isFirstPacket = parsedPacket.info.VP8.beginningOfFrame;
|
rtpHeader->type.Video.isFirstPacket = fromHeader->beginningOfPartition
|
||||||
|
&& (fromHeader->partitionID == 0);
|
||||||
|
toHeader->pictureId = fromHeader->hasPictureID ? fromHeader->pictureID :
|
||||||
|
kNoPictureId;
|
||||||
|
toHeader->tl0PicIdx = fromHeader->hasTl0PicIdx ? fromHeader->tl0PicIdx :
|
||||||
|
kNoTl0PicIdx;
|
||||||
|
toHeader->temporalIdx = fromHeader->hasTID ? fromHeader->tID :
|
||||||
|
kNoTemporalIdx;
|
||||||
|
|
||||||
if(CallbackOfReceivedPayloadData(parsedPacket.info.VP8.data,
|
if(CallbackOfReceivedPayloadData(parsedPacket.info.VP8.data,
|
||||||
parsedPacket.info.VP8.dataLength,
|
parsedPacket.info.VP8.dataLength,
|
||||||
|
@ -23,10 +23,25 @@
|
|||||||
'.',
|
'.',
|
||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
'rtp_format_vp8_unittest.h',
|
|
||||||
'rtp_format_vp8_unittest.cc',
|
'rtp_format_vp8_unittest.cc',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'target_name': 'rtp_utility_test',
|
||||||
|
'type': 'executable',
|
||||||
|
'dependencies': [
|
||||||
|
'rtp_rtcp.gyp:rtp_rtcp',
|
||||||
|
'../../../../testing/gtest.gyp:gtest',
|
||||||
|
'../../../../testing/gtest.gyp:gtest_main',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'.',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'rtp_utility_test.cc',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include <cmath> // ceil
|
#include <cmath> // ceil
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <Windows.h> // FILETIME
|
#include <Windows.h> // FILETIME
|
||||||
#include <WinSock.h> // timeval
|
#include <WinSock.h> // timeval
|
||||||
@ -374,12 +376,15 @@ ModuleRTPUtility::RTPPayload::SetType(RtpVideoCodecTypes videoType)
|
|||||||
}
|
}
|
||||||
case kRtpVp8Video:
|
case kRtpVp8Video:
|
||||||
{
|
{
|
||||||
info.VP8.beginningOfFrame = false;
|
|
||||||
info.VP8.nonReferenceFrame = false;
|
info.VP8.nonReferenceFrame = false;
|
||||||
|
info.VP8.beginningOfPartition = false;
|
||||||
|
info.VP8.partitionID = 0;
|
||||||
info.VP8.hasPictureID = false;
|
info.VP8.hasPictureID = false;
|
||||||
info.VP8.fragments = false;
|
info.VP8.hasTl0PicIdx = false;
|
||||||
info.VP8.startFragment = false;
|
info.VP8.hasTID = false;
|
||||||
info.VP8.stopFragment = false;
|
info.VP8.pictureID = -1;
|
||||||
|
info.VP8.tl0PicIdx = -1;
|
||||||
|
info.VP8.tID = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -581,10 +586,13 @@ ModuleRTPUtility::RTPHeaderParser::Parse(WebRtcRTPHeader& parsedPacket) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RTP payload parser
|
// RTP payload parser
|
||||||
ModuleRTPUtility::RTPPayloadParser::RTPPayloadParser(const RtpVideoCodecTypes videoType,
|
ModuleRTPUtility::RTPPayloadParser::RTPPayloadParser(
|
||||||
const WebRtc_UWord8* payloadData,
|
const RtpVideoCodecTypes videoType,
|
||||||
const WebRtc_UWord16 payloadDataLength)
|
const WebRtc_UWord8* payloadData,
|
||||||
|
const WebRtc_UWord16 payloadDataLength,
|
||||||
|
const WebRtc_Word32 id)
|
||||||
:
|
:
|
||||||
|
_id(id),
|
||||||
_dataPtr(payloadData),
|
_dataPtr(payloadData),
|
||||||
_dataLength(payloadDataLength),
|
_dataLength(payloadDataLength),
|
||||||
_videoType(videoType)
|
_videoType(videoType)
|
||||||
@ -793,63 +801,176 @@ ModuleRTPUtility::RTPPayloadParser::ParseMPEG4(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// VP8 format:
|
||||||
|
//
|
||||||
|
// Payload descriptor
|
||||||
|
// 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// |X|R|N|S|PartID | (REQUIRED)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// X: |I|L|T| RSV-A | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// I: | PictureID | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// L: | TL0PICIDX | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// T: | TID | RSV-B | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
//
|
||||||
|
// Payload header (considered part of the actual payload, sent to decoder)
|
||||||
|
// 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// |Size0|H| VER |P|
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// | ... |
|
||||||
|
// + +
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ModuleRTPUtility::RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const
|
ModuleRTPUtility::RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const
|
||||||
{
|
{
|
||||||
parsedPacket.info.VP8.hasPictureID = (_dataPtr[0] & 0x10)?true:false;
|
RTPPayloadVP8 *vp8 = &parsedPacket.info.VP8;
|
||||||
parsedPacket.info.VP8.nonReferenceFrame = (_dataPtr[0] & 0x08)?true:false;
|
const WebRtc_UWord8 *dataPtr = _dataPtr;
|
||||||
parsedPacket.info.VP8.fragments = (_dataPtr[0] & 0x06)?true:false;
|
int dataLength = _dataLength;
|
||||||
parsedPacket.info.VP8.beginningOfFrame = (_dataPtr[0] & 0x01)?true:false;
|
|
||||||
|
|
||||||
if(parsedPacket.info.VP8.fragments)
|
// Parse mandatory first byte of payload descriptor
|
||||||
|
bool extension = (*dataPtr & 0x80); // X bit
|
||||||
|
vp8->nonReferenceFrame = (*dataPtr & 0x20); // N bit
|
||||||
|
vp8->beginningOfPartition = (*dataPtr & 0x10); // S bit
|
||||||
|
vp8->partitionID = (*dataPtr & 0x0F); // PartID field
|
||||||
|
|
||||||
|
// Advance dataPtr and decrease remaining payload size
|
||||||
|
dataPtr++;
|
||||||
|
dataLength--;
|
||||||
|
|
||||||
|
if (extension)
|
||||||
{
|
{
|
||||||
WebRtc_UWord8 fragments = (_dataPtr[0] >> 1) & 0x03;
|
const int parsedBytes = ParseVP8Extension(vp8, dataPtr, dataLength);
|
||||||
if( fragments == 1)
|
if (parsedBytes < 0) return false;
|
||||||
{
|
dataPtr += parsedBytes;
|
||||||
parsedPacket.info.VP8.startFragment = true;
|
dataLength -= parsedBytes;
|
||||||
parsedPacket.info.VP8.stopFragment = false;
|
|
||||||
} else if(fragments == 3)
|
|
||||||
{
|
|
||||||
parsedPacket.info.VP8.startFragment = false;
|
|
||||||
parsedPacket.info.VP8.stopFragment = true;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
parsedPacket.info.VP8.startFragment = false;
|
|
||||||
parsedPacket.info.VP8.stopFragment = false;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
parsedPacket.info.VP8.startFragment = true;
|
|
||||||
parsedPacket.info.VP8.stopFragment = true;
|
|
||||||
}
|
}
|
||||||
if(parsedPacket.info.VP8.hasPictureID)
|
|
||||||
{
|
|
||||||
WebRtc_UWord8 numBytesPictureId = 1;
|
|
||||||
while(_dataPtr[numBytesPictureId] & 0x80)
|
|
||||||
{
|
|
||||||
numBytesPictureId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedPacket.frameType = (_dataPtr[1+numBytesPictureId] & 0x01) ? kPFrame : kIFrame; // first bit after picture id
|
if (dataLength <= 0)
|
||||||
|
|
||||||
if(!parsedPacket.info.VP8.startFragment)
|
|
||||||
{
|
|
||||||
// if not start fragment parse away all picture IDs
|
|
||||||
parsedPacket.info.VP8.hasPictureID = false;
|
|
||||||
parsedPacket.info.VP8.data = _dataPtr+numBytesPictureId;
|
|
||||||
parsedPacket.info.VP8.dataLength = _dataLength-numBytesPictureId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
{
|
||||||
parsedPacket.frameType = (_dataPtr[1] & 0x01) ? kPFrame : kIFrame; // first bit after picture id
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
||||||
|
"Error parsing VP8 payload descriptor; payload too short");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
parsedPacket.info.VP8.data = _dataPtr+1;
|
|
||||||
parsedPacket.info.VP8.dataLength = _dataLength-1;
|
// Read P bit from payload header (only at beginning of first partition)
|
||||||
|
if (dataLength > 0 && vp8->beginningOfPartition && vp8->partitionID == 0)
|
||||||
|
{
|
||||||
|
parsedPacket.frameType = (*dataPtr & 0x01) ? kPFrame : kIFrame;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parsedPacket.frameType = kPFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedPacket.info.VP8.data = dataPtr;
|
||||||
|
parsedPacket.info.VP8.dataLength = dataLength;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ModuleRTPUtility::RTPPayloadParser::ParseVP8Extension(
|
||||||
|
RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 *dataPtr,
|
||||||
|
int dataLength) const
|
||||||
|
{
|
||||||
|
int parsedBytes = 0;
|
||||||
|
if (dataLength <= 0) return -1;
|
||||||
|
// Optional X field is present
|
||||||
|
vp8->hasPictureID = (*dataPtr & 0x80); // I bit
|
||||||
|
vp8->hasTl0PicIdx = (*dataPtr & 0x40); // L bit
|
||||||
|
vp8->hasTID = (*dataPtr & 0x20); // T bit
|
||||||
|
|
||||||
|
// Advance dataPtr and decrease remaining payload size
|
||||||
|
dataPtr++;
|
||||||
|
parsedBytes++;
|
||||||
|
dataLength--;
|
||||||
|
|
||||||
|
if (vp8->hasPictureID)
|
||||||
|
{
|
||||||
|
if (ParseVP8PictureID(vp8, &dataPtr, &dataLength, &parsedBytes) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vp8->hasTl0PicIdx)
|
||||||
|
{
|
||||||
|
if (ParseVP8Tl0PicIdx(vp8, &dataPtr, &dataLength, &parsedBytes) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vp8->hasTID)
|
||||||
|
{
|
||||||
|
if (ParseVP8TID(vp8, &dataPtr, &dataLength, &parsedBytes) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parsedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ModuleRTPUtility::RTPPayloadParser::ParseVP8PictureID(
|
||||||
|
RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 **dataPtr,
|
||||||
|
int *dataLength,
|
||||||
|
int *parsedBytes) const
|
||||||
|
{
|
||||||
|
if (*dataLength <= 0) return -1;
|
||||||
|
vp8->pictureID = (**dataPtr & 0x7F);
|
||||||
|
if (**dataPtr & 0x80)
|
||||||
|
{
|
||||||
|
(*dataPtr)++;
|
||||||
|
(*parsedBytes)++;
|
||||||
|
if (--(*dataLength) <= 0) return -1;
|
||||||
|
// PictureID is 15 bits
|
||||||
|
vp8->pictureID = (vp8->pictureID << 8) + **dataPtr;
|
||||||
|
}
|
||||||
|
(*dataPtr)++;
|
||||||
|
(*parsedBytes)++;
|
||||||
|
(*dataLength)--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ModuleRTPUtility::RTPPayloadParser::ParseVP8Tl0PicIdx(
|
||||||
|
RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 **dataPtr,
|
||||||
|
int *dataLength,
|
||||||
|
int *parsedBytes) const
|
||||||
|
{
|
||||||
|
if (*dataLength <= 0) return -1;
|
||||||
|
vp8->tl0PicIdx = **dataPtr;
|
||||||
|
(*dataPtr)++;
|
||||||
|
(*parsedBytes)++;
|
||||||
|
(*dataLength)--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModuleRTPUtility::RTPPayloadParser::ParseVP8TID(
|
||||||
|
RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 **dataPtr,
|
||||||
|
int *dataLength,
|
||||||
|
int *parsedBytes) const
|
||||||
|
{
|
||||||
|
if (*dataLength <= 0) return -1;
|
||||||
|
vp8->tID = ((**dataPtr >> 5) & 0x07);
|
||||||
|
(*dataPtr)++;
|
||||||
|
(*parsedBytes)++;
|
||||||
|
(*dataLength)--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ModuleRTPUtility::RTPPayloadParser::H263PictureStartCode(const WebRtc_UWord8* data, const bool skipFirst2bytes) const
|
ModuleRTPUtility::RTPPayloadParser::H263PictureStartCode(const WebRtc_UWord8* data, const bool skipFirst2bytes) const
|
||||||
{
|
{
|
||||||
|
@ -139,12 +139,15 @@ namespace ModuleRTPUtility
|
|||||||
};
|
};
|
||||||
struct RTPPayloadVP8
|
struct RTPPayloadVP8
|
||||||
{
|
{
|
||||||
bool beginningOfFrame;
|
|
||||||
bool nonReferenceFrame;
|
bool nonReferenceFrame;
|
||||||
|
bool beginningOfPartition;
|
||||||
|
int partitionID;
|
||||||
bool hasPictureID;
|
bool hasPictureID;
|
||||||
bool fragments;
|
bool hasTl0PicIdx;
|
||||||
bool startFragment;
|
bool hasTID;
|
||||||
bool stopFragment;
|
int pictureID;
|
||||||
|
int tl0PicIdx;
|
||||||
|
int tID;
|
||||||
|
|
||||||
const WebRtc_UWord8* data;
|
const WebRtc_UWord8* data;
|
||||||
WebRtc_UWord16 dataLength;
|
WebRtc_UWord16 dataLength;
|
||||||
@ -172,7 +175,8 @@ namespace ModuleRTPUtility
|
|||||||
public:
|
public:
|
||||||
RTPPayloadParser(const RtpVideoCodecTypes payloadType,
|
RTPPayloadParser(const RtpVideoCodecTypes payloadType,
|
||||||
const WebRtc_UWord8* payloadData,
|
const WebRtc_UWord8* payloadData,
|
||||||
const WebRtc_UWord16 payloadDataLength); // Length w/o padding.
|
const WebRtc_UWord16 payloadDataLength, // Length w/o padding.
|
||||||
|
const WebRtc_Word32 id);
|
||||||
|
|
||||||
~RTPPayloadParser();
|
~RTPPayloadParser();
|
||||||
|
|
||||||
@ -188,6 +192,25 @@ namespace ModuleRTPUtility
|
|||||||
|
|
||||||
bool ParseVP8(RTPPayload& parsedPacket) const;
|
bool ParseVP8(RTPPayload& parsedPacket) const;
|
||||||
|
|
||||||
|
int ParseVP8Extension(RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 *dataPtr,
|
||||||
|
int dataLength) const;
|
||||||
|
|
||||||
|
int ParseVP8PictureID(RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 **dataPtr,
|
||||||
|
int *dataLength,
|
||||||
|
int *parsedBytes) const;
|
||||||
|
|
||||||
|
int ParseVP8Tl0PicIdx(RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 **dataPtr,
|
||||||
|
int *dataLength,
|
||||||
|
int *parsedBytes) const;
|
||||||
|
|
||||||
|
int ParseVP8TID(RTPPayloadVP8 *vp8,
|
||||||
|
const WebRtc_UWord8 **dataPtr,
|
||||||
|
int *dataLength,
|
||||||
|
int *parsedBytes) const;
|
||||||
|
|
||||||
// H.263
|
// H.263
|
||||||
bool H263PictureStartCode(const WebRtc_UWord8* data,
|
bool H263PictureStartCode(const WebRtc_UWord8* data,
|
||||||
const bool skipFirst2bytes = false) const;
|
const bool skipFirst2bytes = false) const;
|
||||||
@ -199,6 +222,7 @@ namespace ModuleRTPUtility
|
|||||||
FrameTypes GetH263FrameType(const WebRtc_UWord8* inputVideoBuffer) const;
|
FrameTypes GetH263FrameType(const WebRtc_UWord8* inputVideoBuffer) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
WebRtc_Word32 _id;
|
||||||
const WebRtc_UWord8* _dataPtr;
|
const WebRtc_UWord8* _dataPtr;
|
||||||
const WebRtc_UWord16 _dataLength;
|
const WebRtc_UWord16 _dataLength;
|
||||||
const RtpVideoCodecTypes _videoType;
|
const RtpVideoCodecTypes _videoType;
|
||||||
|
269
src/modules/rtp_rtcp/source/rtp_utility_test.cc
Normal file
269
src/modules/rtp_rtcp/source/rtp_utility_test.cc
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file includes unit tests for the ModuleRTPUtility.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
#include "rtp_utility.h"
|
||||||
|
#include "rtp_format_vp8.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using webrtc::ModuleRTPUtility::RTPPayloadParser;
|
||||||
|
using webrtc::ModuleRTPUtility::RTPPayload;
|
||||||
|
using webrtc::ModuleRTPUtility::RTPPayloadVP8;
|
||||||
|
using webrtc::RtpVideoCodecTypes;
|
||||||
|
|
||||||
|
// Payload descriptor
|
||||||
|
// 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// |X|R|N|S|PartID | (REQUIRED)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// X: |I|L|T| RSV-A | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// I: | PictureID | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// L: | TL0PICIDX | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// T: | TID | RSV-B | (OPTIONAL)
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
//
|
||||||
|
// Payload header
|
||||||
|
// 0 1 2 3 4 5 6 7
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// |Size0|H| VER |P|
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// | Size1 |
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// | Size2 |
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// | Bytes 4..N of |
|
||||||
|
// | VP8 payload |
|
||||||
|
// : :
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
// | OPTIONAL RTP |
|
||||||
|
// | padding |
|
||||||
|
// : :
|
||||||
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
void VerifyBasicHeader(const RTPPayloadVP8 &header, bool N, bool S, int PartID)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(N, header.nonReferenceFrame);
|
||||||
|
EXPECT_EQ(S, header.beginningOfPartition);
|
||||||
|
EXPECT_EQ(PartID, header.partitionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyExtensions(const RTPPayloadVP8 &header, bool I, bool L, bool T)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(I, header.hasPictureID);
|
||||||
|
EXPECT_EQ(L, header.hasTl0PicIdx);
|
||||||
|
EXPECT_EQ(T, header.hasTID);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, BasicHeader) {
|
||||||
|
WebRtc_UWord8 payload[4] = {0};
|
||||||
|
payload[0] = 0x14; // binary 0001 0100; S = 1, PartID = 4
|
||||||
|
payload[1] = 0x01; // P frame
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 4, 0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
|
||||||
|
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
|
||||||
|
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 4 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 0 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ(payload + 1, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(4 - 1, parsedPacket.info.VP8.dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, PictureID) {
|
||||||
|
WebRtc_UWord8 payload[10] = {0};
|
||||||
|
payload[0] = 0xA0;
|
||||||
|
payload[1] = 0x80;
|
||||||
|
payload[2] = 17;
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
|
||||||
|
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
|
||||||
|
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ(17, parsedPacket.info.VP8.pictureID);
|
||||||
|
|
||||||
|
EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength);
|
||||||
|
|
||||||
|
|
||||||
|
// Re-use payload, but change to long PictureID
|
||||||
|
payload[2] = 0x80 | 17;
|
||||||
|
payload[3] = 17;
|
||||||
|
RTPPayloadParser rtpPayloadParser2(webrtc::kRtpVp8Video, payload, 10, 0);
|
||||||
|
|
||||||
|
ASSERT_TRUE(rtpPayloadParser2.Parse(parsedPacket));
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8, 1 /*N*/, 0 /*S*/, 0 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 0 /*L*/, 0 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID);
|
||||||
|
|
||||||
|
EXPECT_EQ(payload + 4, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(10 - 4, parsedPacket.info.VP8.dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, Tl0PicIdx) {
|
||||||
|
WebRtc_UWord8 payload[10] = {0};
|
||||||
|
payload[0] = 0x90;
|
||||||
|
payload[1] = 0x40;
|
||||||
|
payload[2] = 17;
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
|
||||||
|
EXPECT_EQ(webrtc::ModuleRTPUtility::kIFrame, parsedPacket.frameType);
|
||||||
|
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 1 /*S*/, 0 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 1 /*L*/, 0 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ(17, parsedPacket.info.VP8.tl0PicIdx);
|
||||||
|
|
||||||
|
EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, TID) {
|
||||||
|
WebRtc_UWord8 payload[10] = {0};
|
||||||
|
payload[0] = 0x88;
|
||||||
|
payload[1] = 0x20;
|
||||||
|
payload[2] = 0x40;
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
|
||||||
|
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
|
||||||
|
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8, 0 /*I*/, 0 /*L*/, 1 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ(2, parsedPacket.info.VP8.tID);
|
||||||
|
|
||||||
|
EXPECT_EQ(payload + 3, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(10 - 3, parsedPacket.info.VP8.dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, MultipleExtensions) {
|
||||||
|
WebRtc_UWord8 payload[10] = {0};
|
||||||
|
payload[0] = 0x88;
|
||||||
|
payload[1] = 0x80 | 0x40 | 0x20;
|
||||||
|
payload[2] = 0x80 | 17; // PictureID, high 7 bits
|
||||||
|
payload[3] = 17; // PictureID, low 8 bits
|
||||||
|
payload[4] = 42; // Tl0PicIdx
|
||||||
|
payload[5] = 0x40; // TID
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 10, 0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
|
||||||
|
EXPECT_EQ(webrtc::ModuleRTPUtility::kPFrame, parsedPacket.frameType);
|
||||||
|
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8, 0 /*N*/, 0 /*S*/, 8 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8, 1 /*I*/, 1 /*L*/, 1 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ((17<<8) + 17, parsedPacket.info.VP8.pictureID);
|
||||||
|
EXPECT_EQ(42, parsedPacket.info.VP8.tl0PicIdx);
|
||||||
|
EXPECT_EQ(2, parsedPacket.info.VP8.tID);
|
||||||
|
|
||||||
|
EXPECT_EQ(payload + 6, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(10 - 6, parsedPacket.info.VP8.dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, TooShortHeader) {
|
||||||
|
WebRtc_UWord8 payload[4] = {0};
|
||||||
|
payload[0] = 0x88;
|
||||||
|
payload[1] = 0x80 | 0x40 | 0x20; // All extensions are enabled
|
||||||
|
payload[2] = 0x80 | 17; //... but only 2 bytes PictureID is provided
|
||||||
|
payload[3] = 17; // PictureID, low 8 bits
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, payload, 4, 0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
EXPECT_FALSE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
}
|
||||||
|
|
||||||
|
using webrtc::RtpFormatVp8;
|
||||||
|
using webrtc::RTPVideoHeaderVP8;
|
||||||
|
|
||||||
|
TEST(ParseVP8Test, TestWithPacketizer) {
|
||||||
|
WebRtc_UWord8 payload[10] = {0};
|
||||||
|
WebRtc_UWord8 packet[20] = {0};
|
||||||
|
RTPVideoHeaderVP8 inputHeader;
|
||||||
|
inputHeader.nonReference = true;
|
||||||
|
inputHeader.pictureId = 300;
|
||||||
|
inputHeader.temporalIdx = 1;
|
||||||
|
inputHeader.tl0PicIdx = -1; // disable
|
||||||
|
RtpFormatVp8 packetizer = RtpFormatVp8(payload, 10, inputHeader);
|
||||||
|
bool last;
|
||||||
|
int send_bytes;
|
||||||
|
ASSERT_EQ(0, packetizer.NextPacket(20, packet, &send_bytes, &last));
|
||||||
|
ASSERT_TRUE(last);
|
||||||
|
|
||||||
|
RTPPayloadParser rtpPayloadParser(webrtc::kRtpVp8Video, packet, send_bytes,
|
||||||
|
0);
|
||||||
|
|
||||||
|
RTPPayload parsedPacket;
|
||||||
|
ASSERT_TRUE(rtpPayloadParser.Parse(parsedPacket));
|
||||||
|
|
||||||
|
EXPECT_EQ(webrtc::ModuleRTPUtility::kIFrame, parsedPacket.frameType);
|
||||||
|
EXPECT_EQ(webrtc::kRtpVp8Video, parsedPacket.type);
|
||||||
|
|
||||||
|
VerifyBasicHeader(parsedPacket.info.VP8,
|
||||||
|
inputHeader.nonReference /*N*/,
|
||||||
|
1 /*S*/,
|
||||||
|
0 /*PartID*/);
|
||||||
|
VerifyExtensions(parsedPacket.info.VP8,
|
||||||
|
1 /*I*/,
|
||||||
|
0 /*L*/,
|
||||||
|
1 /*T*/);
|
||||||
|
|
||||||
|
EXPECT_EQ(inputHeader.pictureId, parsedPacket.info.VP8.pictureID);
|
||||||
|
EXPECT_EQ(inputHeader.temporalIdx, parsedPacket.info.VP8.tID);
|
||||||
|
|
||||||
|
EXPECT_EQ(packet + 5, parsedPacket.info.VP8.data);
|
||||||
|
EXPECT_EQ(send_bytes - 5, parsedPacket.info.VP8.dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
@ -201,7 +201,7 @@ public:
|
|||||||
// WEBRTC_VIDEO_CODEC_ERR_PARAMETER
|
// WEBRTC_VIDEO_CODEC_ERR_PARAMETER
|
||||||
virtual WebRtc_Word32 Decode(const EncodedImage& inputImage,
|
virtual WebRtc_Word32 Decode(const EncodedImage& inputImage,
|
||||||
bool missingFrames,
|
bool missingFrames,
|
||||||
const CodecSpecificInfo* /*codecSpecificInfo*/,
|
const CodecSpecificInfo* codecSpecificInfo,
|
||||||
WebRtc_Word64 /*renderTimeMs*/);
|
WebRtc_Word64 /*renderTimeMs*/);
|
||||||
|
|
||||||
// Register a decode complete callback object.
|
// Register a decode complete callback object.
|
||||||
|
@ -694,7 +694,7 @@ VP8Decoder::InitDecode(const VideoCodec* inst,
|
|||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VP8Decoder::Decode(const EncodedImage& inputImage,
|
VP8Decoder::Decode(const EncodedImage& inputImage,
|
||||||
bool missingFrames,
|
bool missingFrames,
|
||||||
const CodecSpecificInfo* /*codecSpecificInfo*/,
|
const CodecSpecificInfo* codecSpecificInfo,
|
||||||
WebRtc_Word64 /*renderTimeMs*/)
|
WebRtc_Word64 /*renderTimeMs*/)
|
||||||
{
|
{
|
||||||
if (!_inited)
|
if (!_inited)
|
||||||
@ -727,16 +727,6 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
vpx_dec_iter_t _iter = NULL;
|
vpx_dec_iter_t _iter = NULL;
|
||||||
vpx_image_t* img;
|
vpx_image_t* img;
|
||||||
|
|
||||||
// scan for number of bytes used for picture ID
|
|
||||||
WebRtc_UWord64 pictureID = inputImage._buffer[0] & 0x7F;
|
|
||||||
WebRtc_UWord8 numberOfBytes = 1;
|
|
||||||
if (inputImage._buffer[0] & 0x80)
|
|
||||||
{
|
|
||||||
pictureID <<= 8;
|
|
||||||
pictureID += inputImage._buffer[1];
|
|
||||||
++numberOfBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for missing frames
|
// check for missing frames
|
||||||
if (missingFrames)
|
if (missingFrames)
|
||||||
{
|
{
|
||||||
@ -749,8 +739,8 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
|
|
||||||
// we remove the picture ID here
|
// we remove the picture ID here
|
||||||
if (vpx_codec_decode(_decoder,
|
if (vpx_codec_decode(_decoder,
|
||||||
inputImage._buffer + numberOfBytes,
|
inputImage._buffer,
|
||||||
inputImage._length - numberOfBytes,
|
inputImage._length,
|
||||||
0,
|
0,
|
||||||
VPX_DL_REALTIME))
|
VPX_DL_REALTIME))
|
||||||
{
|
{
|
||||||
@ -761,7 +751,7 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
if (inputImage._frameType == kKeyFrame)
|
if (inputImage._frameType == kKeyFrame)
|
||||||
{
|
{
|
||||||
// Reduce size due to PictureID that we won't copy.
|
// Reduce size due to PictureID that we won't copy.
|
||||||
const WebRtc_UWord32 bytesToCopy = inputImage._length - numberOfBytes;
|
const WebRtc_UWord32 bytesToCopy = inputImage._length;
|
||||||
if (_lastKeyFrame._size < bytesToCopy)
|
if (_lastKeyFrame._size < bytesToCopy)
|
||||||
{
|
{
|
||||||
delete [] _lastKeyFrame._buffer;
|
delete [] _lastKeyFrame._buffer;
|
||||||
@ -781,8 +771,7 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
_lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size];
|
_lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size];
|
||||||
}
|
}
|
||||||
// Copy encoded frame.
|
// Copy encoded frame.
|
||||||
memcpy(_lastKeyFrame._buffer, inputImage._buffer + numberOfBytes,
|
memcpy(_lastKeyFrame._buffer, inputImage._buffer, bytesToCopy);
|
||||||
bytesToCopy);
|
|
||||||
_lastKeyFrame._length = bytesToCopy;
|
_lastKeyFrame._length = bytesToCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,15 +843,22 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
|
|
||||||
// TODO(pw): how do we know it's a golden or alt reference frame? libvpx will
|
// TODO(pw): how do we know it's a golden or alt reference frame? libvpx will
|
||||||
// provide an API for now I added it temporarily
|
// provide an API for now I added it temporarily
|
||||||
if((lastRefUpdates & VP8_GOLD_FRAME) || (lastRefUpdates & VP8_ALTR_FRAME))
|
WebRtc_Word16 pictureId = codecSpecificInfo->codecSpecific.VP8.pictureId;
|
||||||
|
if (codecSpecificInfo && pictureId > -1)
|
||||||
{
|
{
|
||||||
if (!missingFrames && (inputImage._completeFrame == true))
|
if ((lastRefUpdates & VP8_GOLD_FRAME)
|
||||||
//if (!corrupted) // TODO(pw): Can we engage this line intead of the above?
|
|| (lastRefUpdates & VP8_ALTR_FRAME))
|
||||||
{
|
{
|
||||||
_decodeCompleteCallback->ReceivedDecodedReferenceFrame(pictureID);
|
if (!missingFrames && (inputImage._completeFrame == true))
|
||||||
|
//if (!corrupted) // TODO(pw): Can we engage this line instead of
|
||||||
|
// the above?
|
||||||
|
{
|
||||||
|
_decodeCompleteCallback->ReceivedDecodedReferenceFrame(
|
||||||
|
pictureId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_decodeCompleteCallback->ReceivedDecodedFrame(pictureId);
|
||||||
}
|
}
|
||||||
_decodeCompleteCallback->ReceivedDecodedFrame(pictureID);
|
|
||||||
|
|
||||||
#ifdef DEV_PIC_LOSS
|
#ifdef DEV_PIC_LOSS
|
||||||
if (corrupted)
|
if (corrupted)
|
||||||
|
@ -248,14 +248,8 @@ void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info,
|
|||||||
switch (info.codecType)
|
switch (info.codecType)
|
||||||
{
|
{
|
||||||
case kVideoCodecVP8: {
|
case kVideoCodecVP8: {
|
||||||
if (info.codecSpecific.VP8.pictureId < 0)
|
(*rtp)->VP8.InitRTPVideoHeaderVP8();
|
||||||
{
|
(*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId;
|
||||||
(*rtp)->VP8.pictureId = kNoPictureId;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId;
|
|
||||||
}
|
|
||||||
(*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference;
|
(*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user