From f0a476bf760cf04539b5a381f6e7e18d72700775 Mon Sep 17 00:00:00 2001 From: "hlundin@google.com" Date: Thu, 7 Jul 2011 08:04:23 +0000 Subject: [PATCH] Add PictureID and NonReference to codec information The PictureID and NonReference information is now routed from the encoder to the RTP packetizer through CodecSpecificInfo and RTPVideoHeaderVP8. Review URL: http://webrtc-codereview.appspot.com/51003 git-svn-id: http://webrtc.googlecode.com/svn/trunk@155 4adac7df-926f-26a2-2b94-8c16560cd09d --- modules/interface/module_common_types.h | 8 +- modules/rtp_rtcp/source/rtp_format_vp8.cc | 129 ++++-- modules/rtp_rtcp/source/rtp_format_vp8.h | 32 +- .../source/rtp_format_vp8_unittest.cc | 353 +++++++++++++++ .../rtp_rtcp_tests.gyp} | 17 +- modules/rtp_rtcp/source/rtp_sender_video.cc | 15 +- modules/rtp_rtcp/source/rtp_sender_video.h | 3 +- .../test/test_rtp_format_vp8/unit_test.cc | 404 ------------------ .../codecs/interface/video_codec_interface.h | 2 + .../codecs/vp8/main/source/vp8.cc | 53 +-- .../main/source/generic_encoder.cc | 13 +- 11 files changed, 523 insertions(+), 506 deletions(-) create mode 100644 modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc rename modules/rtp_rtcp/{test/test_rtp_format_vp8/test_rtp_format_vp8.gyp => source/rtp_rtcp_tests.gyp} (65%) delete mode 100644 modules/rtp_rtcp/test/test_rtp_format_vp8/unit_test.cc diff --git a/modules/interface/module_common_types.h b/modules/interface/module_common_types.h index c47f8a190..1865b0f60 100644 --- a/modules/interface/module_common_types.h +++ b/modules/interface/module_common_types.h @@ -41,10 +41,14 @@ struct RTPVideoHeaderH263 bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the // first byte of this packet }; +enum {kNoPictureId = -1}; struct RTPVideoHeaderVP8 { - bool startBit; // Start of partition - bool stopBit; // Stop of partition + bool startBit; // Start of partition. + bool stopBit; // Stop of partition. + WebRtc_Word16 pictureId; // Picture ID index, 15 bits; + // kNoPictureId if PictureID does not exist. + bool nonReference; // Frame is discardable. }; union RTPVideoTypeHeader { diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.cc b/modules/rtp_rtcp/source/rtp_format_vp8.cc index 6939a5b50..c0ed4927c 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8.cc +++ b/modules/rtp_rtcp/source/rtp_format_vp8.cc @@ -11,7 +11,6 @@ #include "rtp_format_vp8.h" #include // assert -#include // ceil #include // memcpy namespace webrtc { @@ -20,13 +19,14 @@ namespace webrtc { // Modes are: kStrict, kAggregate, kSloppy. const RtpFormatVp8::AggregationMode RtpFormatVp8::aggr_modes_[kNumModes] = { kAggrNone, kAggrPartitions, kAggrFragments }; -const bool RtpFormatVp8::bal_modes_[kNumModes] = +const bool RtpFormatVp8::balance_modes_[kNumModes] = { true, false, false }; -const bool RtpFormatVp8::sep_first_modes_[kNumModes] = +const bool RtpFormatVp8::separate_first_modes_[kNumModes] = { true, false, false }; RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data, WebRtc_UWord32 payload_size, + const RTPVideoHeaderVP8& hdr_info, const RTPFragmentationHeader& fragmentation, VP8PacketizerMode mode) : payload_data_(payload_data), @@ -37,14 +37,16 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data, first_fragment_(true), vp8_header_bytes_(1), aggr_mode_(aggr_modes_[mode]), - balance_(bal_modes_[mode]), - separate_first_(sep_first_modes_[mode]) + balance_(balance_modes_[mode]), + separate_first_(separate_first_modes_[mode]), + hdr_info_(hdr_info) { part_info_ = fragmentation; } RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data, - WebRtc_UWord32 payload_size) + WebRtc_UWord32 payload_size, + const RTPVideoHeaderVP8& hdr_info) : payload_data_(payload_data), payload_size_(static_cast(payload_size)), part_info_(), @@ -54,8 +56,9 @@ RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data, first_fragment_(true), vp8_header_bytes_(1), aggr_mode_(aggr_modes_[kSloppy]), - balance_(bal_modes_[kSloppy]), - separate_first_(sep_first_modes_[kSloppy]) + balance_(balance_modes_[kSloppy]), + separate_first_(separate_first_modes_[kSloppy]), + hdr_info_(hdr_info) { part_info_.VerifyAndAllocateFragmentationHeader(1); part_info_.fragmentationLength[0] = payload_size; @@ -79,8 +82,7 @@ int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes, // Balance payload sizes to produce (almost) equal size // fragments. // Number of fragments for remaining_bytes: - int num_frags = ceil( - static_cast(remaining_bytes) / max_payload_len); + int num_frags = remaining_bytes / max_payload_len + 1; // Number of bytes in this fragment: return static_cast(static_cast(remaining_bytes) / num_frags + 0.5); @@ -98,8 +100,9 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer, const int num_partitions = part_info_.fragmentationVectorSize; int send_bytes = 0; // How much data to send in this packet. bool split_payload = true; // Splitting of partitions is initially allowed. - int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] - - payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_]; + int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] - + payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] + + FirstHeaderExtraLength(); // Add header extra length to payload length. int rem_payload_len = max_payload_len - vp8_header_bytes_; while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition, @@ -137,9 +140,12 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer, ++part_ix_; // Advance to next partition. } + send_bytes -= FirstHeaderExtraLength(); // Remove the extra length again. + assert(send_bytes > 0); const bool end_of_fragment = (remaining_in_partition == 0); // Write the payload header and the payload to buffer. - *bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer); + *bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer, + max_payload_len); if (*bytes_to_send < 0) { return -1; @@ -150,46 +156,99 @@ int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer, return 0; } -int RtpFormatVp8::WriteHeaderAndPayload(int send_bytes, +int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes, bool end_of_fragment, - WebRtc_UWord8* buffer) + WebRtc_UWord8* buffer, + int buffer_length) { // Write the VP8 payload header. - // 0 1 2 3 4 5 6 7 - // +-+-+-+-+-+-+-+-+ - // | RSV |I|N|FI |B| - // +-+-+-+-+-+-+-+-+ + // 0 1 2 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | RSV |I|N|FI |B| PictureID (1 or 2 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - if (send_bytes < 0) + if (payload_bytes < 0) { return -1; } - if (payload_bytes_sent_ + send_bytes > payload_size_) + if (payload_bytes_sent_ + payload_bytes > payload_size_) { return -1; } - // PictureID always present in first packet - const int picture_id_present = beginning_; - // TODO(hlundin): must pipe this info from VP8 encoder - const int kNonrefFrame = 0; - buffer[0] = 0; - if (picture_id_present) buffer[0] |= (0x01 << 4); // I - if (kNonrefFrame) buffer[0] |= (0x01 << 3); // N - if (!first_fragment_) buffer[0] |= (0x01 << 2); // FI - if (!end_of_fragment) buffer[0] |= (0x01 << 1); // FI - if (beginning_) buffer[0] |= 0x01; // B + if (hdr_info_.nonReference) buffer[0] |= (0x01 << 3); // N + if (!first_fragment_) buffer[0] |= (0x01 << 2); // FI + if (!end_of_fragment) buffer[0] |= (0x01 << 1); // FI + if (beginning_) buffer[0] |= 0x01; // B - memcpy(&buffer[vp8_header_bytes_], &payload_data_[payload_bytes_sent_], - send_bytes); + int pic_id_len = WritePictureID(&buffer[vp8_header_bytes_], + buffer_length - vp8_header_bytes_); + if (pic_id_len < 0) return pic_id_len; // error + if (pic_id_len > 0) buffer[0] |= (0x01 << 4); // I + + if (vp8_header_bytes_ + pic_id_len + payload_bytes > buffer_length) + { + return -1; + } + memcpy(&buffer[vp8_header_bytes_ + pic_id_len], + &payload_data_[payload_bytes_sent_], payload_bytes); beginning_ = false; // next packet cannot be first packet in frame // next packet starts new fragment if this ended one first_fragment_ = end_of_fragment; - payload_bytes_sent_ += send_bytes; + payload_bytes_sent_ += payload_bytes; // Return total length of written data. - return send_bytes + vp8_header_bytes_; + return payload_bytes + vp8_header_bytes_ + pic_id_len; } + +int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const +{ + const WebRtc_UWord16 pic_id = + static_cast (hdr_info_.pictureId); + int picture_id_len = PictureIdLength(); + if (picture_id_len > buffer_length) return -1; // error + if (picture_id_len == 2) + { + buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F); + buffer[1] = pic_id & 0xFF; + } + else if (picture_id_len == 1) + { + buffer[0] = pic_id & 0x7F; + } + return picture_id_len; +} + +int RtpFormatVp8::FirstHeaderExtraLength() const +{ + if (!beginning_) + { + return 0; + } + int length = 0; + + length += PictureIdLength(); + + return length; +} + +int RtpFormatVp8::PictureIdLength() const +{ + if (!beginning_ || hdr_info_.pictureId == kNoPictureId) + { + return 0; + } + if (hdr_info_.pictureId <= 0x7F) + { + return 1; + } + else + { + return 2; + } +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.h b/modules/rtp_rtcp/source/rtp_format_vp8.h index 98c0b1fb0..bad3abe20 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8.h +++ b/modules/rtp_rtcp/source/rtp_format_vp8.h @@ -47,21 +47,23 @@ public: // The payload_data must be exactly one encoded VP8 frame. RtpFormatVp8(const WebRtc_UWord8* payload_data, WebRtc_UWord32 payload_size, + const RTPVideoHeaderVP8& hdr_info, const RTPFragmentationHeader& fragmentation, VP8PacketizerMode mode); // Initialize without fragmentation info. Mode kSloppy will be used. // The payload_data must be exactly one encoded VP8 frame. RtpFormatVp8(const WebRtc_UWord8* payload_data, - WebRtc_UWord32 payload_size); + WebRtc_UWord32 payload_size, + const RTPVideoHeaderVP8& hdr_info); // Get the next payload with VP8 payload header. // max_payload_len limits the sum length of payload and VP8 payload header. // buffer is a pointer to where the output will be written. // bytes_to_send is an output variable that will contain number of bytes - // written to buffer. - // Returns true for the last packet of the frame, false otherwise (i.e., - // call the function again to get the next packet). + // written to buffer. Parameter last_packet is true for the last packet of + // the frame, false otherwise (i.e., call the function again to get the + // next packet). Returns negative on error, zero otherwise. int NextPacket(int max_payload_len, WebRtc_UWord8* buffer, int* bytes_to_send, bool* last_packet); @@ -74,8 +76,8 @@ private: }; static const AggregationMode aggr_modes_[kNumModes]; - static const bool bal_modes_[kNumModes]; - static const bool sep_first_modes_[kNumModes]; + static const bool balance_modes_[kNumModes]; + static const bool separate_first_modes_[kNumModes]; // Calculate size of next chunk to send. Returns 0 if none can be sent. int CalcNextSize(int max_payload_len, int remaining_bytes, @@ -86,7 +88,20 @@ private: // last_fragment indicates that this packet ends with the last byte of a // partition. int WriteHeaderAndPayload(int send_bytes, bool end_of_fragment, - WebRtc_UWord8* buffer); + WebRtc_UWord8* buffer, int buffer_length); + + // Write the PictureID from codec_specific_info_ to buffer. One or two + // bytes are written, depending on magnitude of PictureID. The function + // returns the number of bytes written. + int WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const; + + // Calculate and return length (octets) of the variable header fields in + // the next header (i.e., header length in addition to vp8_header_bytes_). + int FirstHeaderExtraLength() const; + + // Calculate and return length (octets) of PictureID field in the next + // header. Can be 0, 1, or 2. + int PictureIdLength() const; const WebRtc_UWord8* payload_data_; const int payload_size_; @@ -95,10 +110,11 @@ private: int part_ix_; bool beginning_; // first partition in this frame bool first_fragment_; // first fragment of a partition - const int vp8_header_bytes_; // length of VP8 payload header + const int vp8_header_bytes_; // length of VP8 payload header's fixed part AggregationMode aggr_mode_; bool balance_; bool separate_first_; + const RTPVideoHeaderVP8 hdr_info_; }; } diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc b/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc new file mode 100644 index 000000000..0c9ad609c --- /dev/null +++ b/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +/* + * This file includes unit tests for the VP8 packetizer. + */ + +#include + +#include "typedefs.h" +#include "rtp_format_vp8.h" + +namespace { + +using webrtc::RTPFragmentationHeader; +using webrtc::RtpFormatVp8; +using webrtc::RTPVideoHeaderVP8; + +const WebRtc_UWord32 kPayloadSize = 30; + +class RtpFormatVp8Test : public ::testing::Test { + protected: + RtpFormatVp8Test() {}; + virtual void SetUp(); + virtual void TearDown(); + void CheckHeader(bool first_in_frame, bool frag_start, bool frag_end); + void CheckPayload(int payload_end); + void CheckLast(bool last) const; + void CheckPacket(int send_bytes, int expect_bytes, bool last, + bool first_in_frame, bool frag_start, bool frag_end); + WebRtc_UWord8 payload_data_[kPayloadSize]; + WebRtc_UWord8 buffer_[kPayloadSize]; + WebRtc_UWord8 *data_ptr_; + RTPFragmentationHeader* fragmentation_; + RTPVideoHeaderVP8 hdr_info_; + int payload_start_; +}; + +void RtpFormatVp8Test::SetUp() { + for (int i = 0; i < kPayloadSize; i++) + { + payload_data_[i] = i / 10; // integer division + } + data_ptr_ = payload_data_; + + fragmentation_ = new RTPFragmentationHeader; + fragmentation_->VerifyAndAllocateFragmentationHeader(3); + fragmentation_->fragmentationLength[0] = 10; + fragmentation_->fragmentationLength[1] = 10; + fragmentation_->fragmentationLength[2] = 10; + fragmentation_->fragmentationOffset[0] = 0; + fragmentation_->fragmentationOffset[1] = 10; + fragmentation_->fragmentationOffset[2] = 20; + + hdr_info_.pictureId = 0; + hdr_info_.nonReference = false; +} + +void RtpFormatVp8Test::TearDown() { + delete fragmentation_; +} + +#define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a) + +#define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0) + +//#define EXPECT_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0)) +#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 4, a) + +#define EXPECT_BIT_N_EQ(x,a) EXPECT_EQ((((x)&0x08) > 0), (a > 0)) + +#define EXPECT_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a) + +#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0)) + +void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start, + bool frag_end) +{ + payload_start_ = 1; + EXPECT_RSV_ZERO(buffer_[0]); + if (first_in_frame & hdr_info_.pictureId != webrtc::kNoPictureId) + { + EXPECT_BIT_I_EQ(buffer_[0], 1); + if (hdr_info_.pictureId > 0x7F) + { + EXPECT_BIT_EQ(buffer_[1], 7, 1); + EXPECT_EQ(buffer_[1] & 0x7F, + (hdr_info_.pictureId >> 8) & 0x7F); + EXPECT_EQ(buffer_[2], hdr_info_.pictureId & 0xFF); + payload_start_ += 2; + } + else + { + EXPECT_BIT_EQ(buffer_[1], 7, 0); + EXPECT_EQ(buffer_[1] & 0x7F, + (hdr_info_.pictureId) & 0x7F); + payload_start_ += 1; + } + } + EXPECT_BIT_N_EQ(buffer_[0], 0); + WebRtc_UWord8 fi = 0x03; + if (frag_start) fi = fi & 0x01; + if (frag_end) fi = fi & 0x02; + EXPECT_FI_EQ(buffer_[0], fi); + if (first_in_frame) EXPECT_BIT_B_EQ(buffer_[0], 1); +} + +void RtpFormatVp8Test::CheckPayload(int payload_end) +{ + for (int i = payload_start_; i < payload_end; i++, data_ptr_++) + EXPECT_EQ(buffer_[i], *data_ptr_); +} + +void RtpFormatVp8Test::CheckLast(bool last) const +{ + EXPECT_EQ(last, data_ptr_ == payload_data_ + kPayloadSize); +} + +void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last, + bool first_in_frame, bool frag_start, bool frag_end) +{ + EXPECT_EQ(send_bytes, expect_bytes); + CheckHeader(first_in_frame, frag_start, frag_end); + CheckPayload(send_bytes); + CheckLast(last); +} + +TEST_F(RtpFormatVp8Test, TestStrictMode) +{ + int send_bytes = 0; + bool last; + bool first_in_frame = true; + + hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID + RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, + hdr_info_, *fragmentation_, webrtc::kStrict); + + // get first packet, expect balanced size = same as second packet + EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 7, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ false); + first_in_frame = false; + + // get second packet + EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 7, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ true); + + // Second partition + // Get first (and only) packet + EXPECT_EQ(0, packetizer.NextPacket(20, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 11, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ true); + + // Third partition + // Get first packet (of four) + EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 4, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ false); + + // Get second packet (of four) + EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 3, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ false); + + // Get third packet (of four) + EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 4, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ false); + + // Get fourth and last packet + EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 3, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ true); + +} + +TEST_F(RtpFormatVp8Test, TestAggregateMode) +{ + int send_bytes = 0; + bool last; + bool first_in_frame = true; + + hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID + RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, + hdr_info_, *fragmentation_, webrtc::kAggregate); + + // get first packet + // first half of first partition + EXPECT_EQ(0, packetizer.NextPacket(6, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 6, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ false); + first_in_frame = false; + + // get second packet + // second half of first partition + EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 7, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ true); + + // get third packet + // last two partitions aggregated + EXPECT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 21, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ true); + +} + +TEST_F(RtpFormatVp8Test, TestSloppyMode) +{ + int send_bytes = 0; + bool last; + bool first_in_frame = true; + + hdr_info_.pictureId = webrtc::kNoPictureId; // no PictureID + RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, + hdr_info_, *fragmentation_, webrtc::kSloppy); + + // get first packet + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ false); + first_in_frame = false; + + // get second packet + // fragments of first and second partitions + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ false); + + // get third packet + // fragments of second and third partitions + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ false); + + // get fourth packet + // second half of last partition + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 7, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ true); + +} + +// Verify that sloppy mode is forced if fragmentation info is missing. +TEST_F(RtpFormatVp8Test, TestSloppyModeFallback) +{ + int send_bytes = 0; + bool last; + bool first_in_frame = true; + + hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID + RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, + hdr_info_); + + // get first packet + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ true, + /* frag_end */ false); + first_in_frame = false; + + // get second packet + // fragments of first and second partitions + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ false); + + // get third packet + // fragments of second and third partitions + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ false); + + // get fourth packet + // second half of last partition + EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); + CheckPacket(send_bytes, 9, last, + first_in_frame, + /* frag_start */ false, + /* frag_end */ true); + +} + +// Verify that non-reference bit is set. +TEST_F(RtpFormatVp8Test, TestNonReferenceBit) { + int send_bytes = 0; + bool last; + bool first_in_frame = true; + + hdr_info_.nonReference = true; + RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, + hdr_info_); + + // get first packet + ASSERT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); + ASSERT_FALSE(last); + EXPECT_BIT_N_EQ(buffer_[0], 1); + + // get second packet + ASSERT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); + ASSERT_TRUE(last); + EXPECT_BIT_N_EQ(buffer_[0], 1); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} + +} // namespace diff --git a/modules/rtp_rtcp/test/test_rtp_format_vp8/test_rtp_format_vp8.gyp b/modules/rtp_rtcp/source/rtp_rtcp_tests.gyp similarity index 65% rename from modules/rtp_rtcp/test/test_rtp_format_vp8/test_rtp_format_vp8.gyp rename to modules/rtp_rtcp/source/rtp_rtcp_tests.gyp index d28efd79f..cfa0258f5 100644 --- a/modules/rtp_rtcp/test/test_rtp_format_vp8/test_rtp_format_vp8.gyp +++ b/modules/rtp_rtcp/source/rtp_rtcp_tests.gyp @@ -8,24 +8,23 @@ { 'includes': [ - '../../../../common_settings.gypi', # Common settings + '../../../common_settings.gypi', # Common settings ], 'targets': [ { - 'target_name': 'test_rtp_format_vp8', + 'target_name': 'rtp_format_vp8_unittest', 'type': 'executable', 'dependencies': [ - '../../source/rtp_rtcp.gyp:rtp_rtcp', - '../../../../../testing/gtest.gyp:gtest', - '../../../../../testing/gtest.gyp:gtest_main', + 'rtp_rtcp.gyp:rtp_rtcp', + '../../../../testing/gtest.gyp:gtest', + '../../../../testing/gtest.gyp:gtest_main', ], 'include_dirs': [ - '../../source', + '.', ], 'sources': [ - 'unit_test.h', - 'unit_test.cc', - '../../source/rtp_format_vp8.cc', + 'rtp_format_vp8_unittest.h', + 'rtp_format_vp8_unittest.cc', ], }, ], diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index 2ca780cce..8aa4382b9 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -1195,17 +1195,6 @@ RTPSenderVideo::SendH263MBs(const FrameType frameType, return 0; } -/* -0 1 2 3 -0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| RSV |I|N|FI |B| PictureID (integer #bytes) | -+-+-+-+-+-+-+-+-+ | -: : -| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| : (VP8 data or VP8 payload header; byte aligned)| -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ WebRtc_Word32 RTPSenderVideo::SendVP8(const FrameType frameType, const WebRtc_Word8 payloadType, @@ -1227,7 +1216,9 @@ RTPSenderVideo::SendVP8(const FrameType frameType, WebRtc_UWord16 maxPayloadLengthVP8 = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength; - RtpFormatVp8 packetizer(data, payloadBytesToSend, *fragmentation, kStrict); + assert(rtpTypeHdr); + RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8, + *fragmentation, kStrict); bool last = false; while (!last) diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index c7af9c0c4..078ec833e 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -120,8 +120,7 @@ private: const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize, const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* /*rtpTypeHdr*/); - // TODO(hlundin): Remove comments once we start using rtpTypeHdr. + const RTPVideoTypeHeader* rtpTypeHdr); // MPEG 4 WebRtc_Word32 FindMPEG4NALU(const WebRtc_UWord8* inData ,WebRtc_Word32 MaxPayloadLength); diff --git a/modules/rtp_rtcp/test/test_rtp_format_vp8/unit_test.cc b/modules/rtp_rtcp/test/test_rtp_format_vp8/unit_test.cc deleted file mode 100644 index c740ccaee..000000000 --- a/modules/rtp_rtcp/test/test_rtp_format_vp8/unit_test.cc +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -/* - * This file includes unit tests for the VP8 packetizer. - */ - -#include - -#include "typedefs.h" -#include "rtp_format_vp8.h" - -namespace { - -using webrtc::RTPFragmentationHeader; -using webrtc::RtpFormatVp8; - -const WebRtc_UWord32 kPayloadSize = 30; - -class RtpFormatVp8Test : public ::testing::Test { - protected: - RtpFormatVp8Test() {}; - virtual void SetUp(); - virtual void TearDown(); - WebRtc_UWord8* payload_data; - RTPFragmentationHeader* fragmentation; -}; - -void RtpFormatVp8Test::SetUp() { - payload_data = new WebRtc_UWord8[kPayloadSize]; - for (int i = 0; i < kPayloadSize; i++) - { - payload_data[i] = i / 10; // integer division - } - fragmentation = new RTPFragmentationHeader; - fragmentation->VerifyAndAllocateFragmentationHeader(3); - fragmentation->fragmentationLength[0] = 10; - fragmentation->fragmentationLength[1] = 10; - fragmentation->fragmentationLength[2] = 10; - fragmentation->fragmentationOffset[0] = 0; - fragmentation->fragmentationOffset[1] = 10; - fragmentation->fragmentationOffset[2] = 20; -} - -void RtpFormatVp8Test::TearDown() { - delete [] payload_data; - delete fragmentation; -} - -#define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a) - -#define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0) - -//#define EXPECT_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0)) -#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 4, a) - -#define EXPECT_BIT_N_EQ(x,a) EXPECT_EQ((((x)&0x08) > 0), (a > 0)) - -#define EXPECT_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a) - -#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0)) - -TEST_F(RtpFormatVp8Test, TestStrictMode) -{ - WebRtc_UWord8 buffer[20]; - int send_bytes = 0; - bool last; - - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize, - *fragmentation, webrtc::kStrict); - - // get first packet, expect balanced size = same as second packet - EXPECT_EQ(0, packetizer.NextPacket(8, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,6); - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 1); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x01); - EXPECT_BIT_B_EQ(buffer[0], 1); - for (int i = 1; i < 6; i++) - { - EXPECT_EQ(buffer[i], 0); - } - - // get second packet - EXPECT_EQ(0, packetizer.NextPacket(8, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,6); // 5 remaining from partition, 1 header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x02); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 6; i++) - { - EXPECT_EQ(buffer[i], 0); - } - - // Second partition - // Get first (and only) packet - EXPECT_EQ(0, packetizer.NextPacket(20, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,11); - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x00); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 11; i++) - { - EXPECT_EQ(buffer[i], 1); - } - - // Third partition - // Get first packet (of four) - EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,4); - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x01); // first fragment - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 4; i++) - { - EXPECT_EQ(buffer[i], 2); - } - - // Get second packet (of four) - EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,3); - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x03); // middle fragment - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 3; i++) - { - EXPECT_EQ(buffer[i], 2); - } - - // Get third packet (of four) - EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,4); - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x03); // middle fragment - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 4; i++) - { - EXPECT_EQ(buffer[i], 2); - } - - // Get fourth and last packet - EXPECT_EQ(0, packetizer.NextPacket(4, buffer, &send_bytes, &last)); - EXPECT_TRUE(last); // last packet in frame - EXPECT_EQ(send_bytes,3); // 2 bytes payload left, 1 header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x02); // last fragment - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 3; i++) - { - EXPECT_EQ(buffer[i], 2); - } - -} - -TEST_F(RtpFormatVp8Test, TestAggregateMode) -{ - WebRtc_UWord8 buffer[kPayloadSize]; - int send_bytes = 0; - bool last; - - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize, - *fragmentation, webrtc::kAggregate); - - // get first packet - // first half of first partition - EXPECT_EQ(0, packetizer.NextPacket(6, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,6); // First 5 from first partition, 1 header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 1); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x01); - EXPECT_BIT_B_EQ(buffer[0], 1); - for (int i = 1; i < 6; i++) - { - EXPECT_EQ(buffer[i], 0); - } - - // get second packet - // second half of first partition - EXPECT_EQ(0, packetizer.NextPacket(10, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,6); // Last 5 from first partition, 1 header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x02); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 6; i++) - { - EXPECT_EQ(buffer[i], 0); - } - - // get third packet - // last two partitions aggregated - EXPECT_EQ(0, packetizer.NextPacket(25, buffer, &send_bytes, &last)); - EXPECT_TRUE(last); // last packet - EXPECT_EQ(send_bytes,21); // Two 10-byte partitions and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x00); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 11; i++) - { - EXPECT_EQ(buffer[i], 1); - } - for (int i = 11; i < 21; i++) - { - EXPECT_EQ(buffer[i], 2); - } - -} - -TEST_F(RtpFormatVp8Test, TestSloppyMode) -{ - WebRtc_UWord8 buffer[kPayloadSize]; - int send_bytes = 0; - bool last; - - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize, - *fragmentation, webrtc::kSloppy); - - // get first packet - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,9); // 8 bytes payload and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 1); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x01); - EXPECT_BIT_B_EQ(buffer[0], 1); - for (int i = 1; i < 9; i++) - { - EXPECT_EQ(buffer[i], 0); - } - - // get second packet - // fragments of first and second partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,9); // 8 bytes (2+6) payload and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x03); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i <= 2; i++) - { - EXPECT_EQ(buffer[i], 0); - } - for (int i = 3; i < 9; i++) - { - EXPECT_EQ(buffer[i], 1); - } - - // get third packet - // fragments of second and third partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,9); // 8 bytes (4+4) payload and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x03); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i <= 4; i++) - { - EXPECT_EQ(buffer[i], 1); - } - for (int i = 5; i < 9; i++) - { - EXPECT_EQ(buffer[i], 2); - } - - // get fourth packet - // second half of last partition - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_TRUE(last); // last packet - EXPECT_EQ(send_bytes,7); // Last 6 from last partition, 1 header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x02); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 5; i++) - { - EXPECT_EQ(buffer[i], 2); - } - -} - -// Verify that sloppy mode is forced if fragmentation info is missing. -TEST_F(RtpFormatVp8Test, TestSloppyModeFallback) -{ - WebRtc_UWord8 buffer[kPayloadSize]; - int send_bytes = 0; - bool last; - - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data, kPayloadSize); - - // get first packet - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,9); // 8 bytes payload and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 1); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x01); - EXPECT_BIT_B_EQ(buffer[0], 1); - for (int i = 1; i < 9; i++) - { - EXPECT_EQ(buffer[i], 0); - } - - // get second packet - // fragments of first and second partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,9); // 8 bytes (2+6) payload and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x03); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i <= 2; i++) - { - EXPECT_EQ(buffer[i], 0); - } - for (int i = 3; i < 9; i++) - { - EXPECT_EQ(buffer[i], 1); - } - - // get third packet - // fragments of second and third partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_FALSE(last); - EXPECT_EQ(send_bytes,9); // 8 bytes (4+4) payload and 1 byte header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x03); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i <= 4; i++) - { - EXPECT_EQ(buffer[i], 1); - } - for (int i = 5; i < 9; i++) - { - EXPECT_EQ(buffer[i], 2); - } - - // get fourth packet - // second half of last partition - EXPECT_EQ(0, packetizer.NextPacket(9, buffer, &send_bytes, &last)); - EXPECT_TRUE(last); // last packet - EXPECT_EQ(send_bytes,7); // Last 6 from last partition, 1 header - EXPECT_RSV_ZERO(buffer[0]); - EXPECT_BIT_I_EQ(buffer[0], 0); - EXPECT_BIT_N_EQ(buffer[0], 0); - EXPECT_FI_EQ(buffer[0], 0x02); - EXPECT_BIT_B_EQ(buffer[0], 0); - for (int i = 1; i < 5; i++) - { - EXPECT_EQ(buffer[i], 2); - } - -} - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} - -} // namespace diff --git a/modules/video_coding/codecs/interface/video_codec_interface.h b/modules/video_coding/codecs/interface/video_codec_interface.h index 6aea1491d..106039177 100644 --- a/modules/video_coding/codecs/interface/video_codec_interface.h +++ b/modules/video_coding/codecs/interface/video_codec_interface.h @@ -27,6 +27,8 @@ struct CodecSpecificInfoVP8 WebRtc_UWord8 pictureIdSLI; bool hasReceivedRPSI; WebRtc_UWord64 pictureIdRPSI; + WebRtc_Word16 pictureId; // negative value to skip pictureId + bool nonReference; }; union CodecSpecificInfoUnion diff --git a/modules/video_coding/codecs/vp8/main/source/vp8.cc b/modules/video_coding/codecs/vp8/main/source/vp8.cc index 26b47f631..c9403ab9e 100644 --- a/modules/video_coding/codecs/vp8/main/source/vp8.cc +++ b/modules/video_coding/codecs/vp8/main/source/vp8.cc @@ -241,7 +241,7 @@ VP8Encoder::InitEncode(const VideoCodec* inst, _height = inst->height; // random start 16 bits is enough - _pictureID = (WebRtc_UWord16)rand(); + _pictureID = ((WebRtc_UWord16)rand()) % 0x7FFF; // allocate memory for encoded image if (_encodedImage._buffer != NULL) @@ -535,26 +535,16 @@ VP8Encoder::Encode(const RawImage& inputImage, } else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - // attach Picture ID - // we use 14 bits generating 1 or 2 bytes - // TODO(hlundin): update to follow latest RTP spec - WebRtc_UWord8 pictureIdSize = 2; - // TODO(hlundin): we should refactor this so that the pictureID is - // signaled through a codec specific struct and added in the RTP module. - if (_pictureID > 0x7f) - { - // more than 7 bits - _encodedImage._buffer[0] = 0x80 | (WebRtc_UWord8)(_pictureID >> 7); - _encodedImage._buffer[1] = (WebRtc_UWord8)(_pictureID & 0x7f); - } - else - { - _encodedImage._buffer[0] = (WebRtc_UWord8)_pictureID; - pictureIdSize = 1; - } + CodecSpecificInfo codecSpecific; + codecSpecific.codecType = kVideoCodecVP8; + CodecSpecificInfoVP8 *vp8Info = &(codecSpecific.codecSpecific.VP8); - memcpy(_encodedImage._buffer+pictureIdSize, pkt->data.frame.buf, pkt->data.frame.sz); - _encodedImage._length = WebRtc_UWord32(pkt->data.frame.sz) + pictureIdSize; + vp8Info->pictureId = _pictureID; + vp8Info->nonReference + = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE); + + memcpy(_encodedImage._buffer, pkt->data.frame.buf, pkt->data.frame.sz); + _encodedImage._length = WebRtc_UWord32(pkt->data.frame.sz); _encodedImage._encodedHeight = _raw->h; _encodedImage._encodedWidth = _raw->w; @@ -574,12 +564,10 @@ VP8Encoder::Encode(const RawImage& inputImage, // First partition fragInfo.fragmentationOffset[0] = 0; - WebRtc_UWord8 *firstByte = &_encodedImage._buffer[pictureIdSize]; + WebRtc_UWord8 *firstByte = _encodedImage._buffer; WebRtc_UWord32 tmpSize = (firstByte[2] << 16) | (firstByte[1] << 8) | firstByte[0]; fragInfo.fragmentationLength[0] = (tmpSize >> 5) & 0x7FFFF; - // Let the PictureID belong to the first partition. - fragInfo.fragmentationLength[0] += pictureIdSize; fragInfo.fragmentationPlType[0] = 0; // not known here fragInfo.fragmentationTimeDiff[0] = 0; @@ -590,10 +578,11 @@ VP8Encoder::Encode(const RawImage& inputImage, fragInfo.fragmentationPlType[1] = 0; // not known here fragInfo.fragmentationTimeDiff[1] = 0; - _encodedCompleteCallback->Encoded(_encodedImage, NULL, &fragInfo); + _encodedCompleteCallback->Encoded(_encodedImage, &codecSpecific, + &fragInfo); } - _pictureID++; // prepare next + _pictureID = (_pictureID + 1) % 0x7FFF; // prepare next return WEBRTC_VIDEO_CODEC_OK; } return WEBRTC_VIDEO_CODEC_ERROR; @@ -732,18 +721,16 @@ VP8Decoder::Decode(const EncodedImage& inputImage, } vpx_dec_iter_t _iter = NULL; vpx_image_t* img; - WebRtc_UWord64 pictureID = 0; // scan for number of bytes used for picture ID - WebRtc_UWord8 numberOfBytes; - for (numberOfBytes = 0;(inputImage._buffer[numberOfBytes] & 0x80 ) && - numberOfBytes < 8; numberOfBytes++) + WebRtc_UWord64 pictureID = inputImage._buffer[0] & 0x7F; + WebRtc_UWord8 numberOfBytes = 1; + if (inputImage._buffer[0] & 0x80) { - pictureID += inputImage._buffer[numberOfBytes] & 0x7f; - pictureID <<= 7; + pictureID <<= 8; + pictureID += inputImage._buffer[1]; + ++numberOfBytes; } - pictureID += inputImage._buffer[numberOfBytes] & 0x7f; - numberOfBytes++; // check for missing frames if (missingFrames) diff --git a/modules/video_coding/main/source/generic_encoder.cc b/modules/video_coding/main/source/generic_encoder.cc index eeb3f32a5..44d287086 100644 --- a/modules/video_coding/main/source/generic_encoder.cc +++ b/modules/video_coding/main/source/generic_encoder.cc @@ -247,7 +247,18 @@ void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info, RTPVideoTypeHeader** rtp) { switch (info.codecType) { - //TODO(hlundin): Implement case for kVideoCodecVP8. + case kVideoCodecVP8: { + if (info.codecSpecific.VP8.pictureId < 0) + { + (*rtp)->VP8.pictureId = kNoPictureId; + } + else + { + (*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId; + } + (*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference; + return; + } default: { // No codec specific info. Change RTP header pointer to NULL. *rtp = NULL;