Add H.264 packetization.
This also includes:
- Creating new packetizer and depacketizer interfaces.
- Moved VP8 packetization was H264 packetization and depacketization to these interfaces. This is a work in progress and should be continued to get this 100% generic. This also required changing the return type for RtpFormatVp8::NextPacket(), which now returns bool instead of the index of the first partition.
- Created a Create() factory method for packetizers and depacketizers.
R=niklas.enbom@webrtc.org, pbos@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/21009004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@6804 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/interface/module_common_types.h b/modules/interface/module_common_types.h
index 398808f..60bb7ab 100644
--- a/modules/interface/module_common_types.h
+++ b/modules/interface/module_common_types.h
@@ -61,8 +61,8 @@
};
struct RTPVideoHeaderH264 {
- uint8_t nalu_header;
- bool single_nalu;
+ bool stap_a;
+ bool single_nalu;
};
union RTPVideoTypeHeader {
diff --git a/modules/modules.gyp b/modules/modules.gyp
index 2a3ba74..10a1860 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -206,6 +206,7 @@
'rtp_rtcp/source/rtcp_receiver_unittest.cc',
'rtp_rtcp/source/rtcp_sender_unittest.cc',
'rtp_rtcp/source/rtp_fec_unittest.cc',
+ 'rtp_rtcp/source/rtp_format_h264_unittest.cc',
'rtp_rtcp/source/rtp_format_vp8_unittest.cc',
'rtp_rtcp/source/rtp_format_vp8_test_helper.cc',
'rtp_rtcp/source/rtp_format_vp8_test_helper.h',
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index e9a2e8d..a7499bc 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -79,6 +79,10 @@
"source/rtp_sender_video.cc",
"source/rtp_sender_video.h",
"source/video_codec_information.h",
+ 'source/rtp_format.cc',
+ 'source/rtp_format.h',
+ 'source/rtp_format_h264.cc',
+ 'source/rtp_format_h264.h',
"source/rtp_format_vp8.cc",
"source/rtp_format_vp8.h",
"source/rtp_format_video_generic.h",
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index c954aa2..2caa631 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -19,6 +19,17 @@
namespace webrtc {
+class MockRtpData : public RtpData {
+ public:
+ MOCK_METHOD3(OnReceivedPayloadData,
+ int32_t(const uint8_t* payloadData,
+ const uint16_t payloadSize,
+ const WebRtcRTPHeader* rtpHeader));
+
+ MOCK_METHOD2(OnRecoveredPacket,
+ bool(const uint8_t* packet, int packet_length));
+};
+
class MockRtpRtcp : public RtpRtcp {
public:
MOCK_METHOD1(ChangeUniqueId,
diff --git a/modules/rtp_rtcp/source/fec_receiver_unittest.cc b/modules/rtp_rtcp/source/fec_receiver_unittest.cc
index 0b12449..2a3b155 100644
--- a/modules/rtp_rtcp/source/fec_receiver_unittest.cc
+++ b/modules/rtp_rtcp/source/fec_receiver_unittest.cc
@@ -15,6 +15,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
+#include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
#include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h"
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
@@ -25,17 +26,6 @@
namespace webrtc {
-class MockRtpData : public RtpData {
- public:
- MOCK_METHOD3(OnReceivedPayloadData,
- int32_t(const uint8_t* payloadData,
- const uint16_t payloadSize,
- const WebRtcRTPHeader* rtpHeader));
-
- MOCK_METHOD2(OnRecoveredPacket,
- bool(const uint8_t* packet, int packet_length));
-};
-
class ReceiverFecTest : public ::testing::Test {
protected:
virtual void SetUp() {
diff --git a/modules/rtp_rtcp/source/rtp_format.cc b/modules/rtp_rtcp/source/rtp_format.cc
new file mode 100644
index 0000000..da1f2df
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtp_format.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
+
+namespace webrtc {
+RtpPacketizer* RtpPacketizer::Create(RtpVideoCodecTypes type,
+ size_t max_payload_len) {
+ switch (type) {
+ case kRtpVideoH264:
+ return new RtpPacketizerH264(max_payload_len);
+ case kRtpVideoNone:
+ case kRtpVideoGeneric:
+ case kRtpVideoVp8:
+ assert(false);
+ }
+ return NULL;
+}
+
+RtpDepacketizer* RtpDepacketizer::Create(RtpVideoCodecTypes type,
+ RtpData* const callback) {
+ switch (type) {
+ case kRtpVideoH264:
+ return new RtpDepacketizerH264(callback);
+ case kRtpVideoNone:
+ case kRtpVideoGeneric:
+ case kRtpVideoVp8:
+ assert(false);
+ }
+ return NULL;
+}
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_format.h b/modules/rtp_rtcp/source/rtp_format.h
new file mode 100644
index 0000000..d7dba48
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtp_format.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H_
+#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H_
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
+
+namespace webrtc {
+
+class RtpPacketizer {
+ public:
+ static RtpPacketizer* Create(RtpVideoCodecTypes type, size_t max_payload_len);
+
+ virtual ~RtpPacketizer() {}
+
+ virtual void SetPayloadData(const uint8_t* payload_data,
+ size_t payload_size,
+ const RTPFragmentationHeader* fragmentation) = 0;
+
+ // Get the next payload with 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. The 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 true on success or false if there was no payload to packetize.
+ virtual bool NextPacket(uint8_t* buffer,
+ size_t* bytes_to_send,
+ bool* last_packet) = 0;
+};
+
+class RtpDepacketizer {
+ public:
+ static RtpDepacketizer* Create(RtpVideoCodecTypes type,
+ RtpData* const callback);
+
+ virtual ~RtpDepacketizer() {}
+
+ virtual bool Parse(WebRtcRTPHeader* rtp_header,
+ const uint8_t* payload_data,
+ size_t payload_data_length) = 0;
+};
+} // namespace webrtc
+#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H_
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.cc b/modules/rtp_rtcp/source/rtp_format_h264.cc
new file mode 100644
index 0000000..d986eae
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtp_format_h264.cc
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#include <string.h>
+
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
+
+namespace webrtc {
+namespace {
+
+enum Nalu {
+ kSlice = 1,
+ kIdr = 5,
+ kSei = 6,
+ kSps = 7,
+ kPps = 8,
+ kStapA = 24,
+ kFuA = 28
+};
+
+static const size_t kNalHeaderSize = 1;
+static const size_t kFuAHeaderSize = 2;
+
+// Bit masks for FU (A and B) indicators.
+enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F };
+
+// Bit masks for FU (A and B) headers.
+enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 };
+
+void ParseSingleNalu(WebRtcRTPHeader* rtp_header,
+ const uint8_t* payload_data,
+ size_t payload_data_length) {
+ rtp_header->type.Video.codec = kRtpVideoH264;
+ rtp_header->type.Video.isFirstPacket = true;
+ RTPVideoHeaderH264* h264_header = &rtp_header->type.Video.codecHeader.H264;
+ h264_header->single_nalu = true;
+ h264_header->stap_a = false;
+
+ uint8_t nal_type = payload_data[0] & NalDefs::kTypeMask;
+ if (nal_type == Nalu::kStapA) {
+ nal_type = payload_data[3] & NalDefs::kTypeMask;
+ h264_header->stap_a = true;
+ }
+
+ switch (nal_type) {
+ case Nalu::kSps:
+ case Nalu::kPps:
+ case Nalu::kIdr:
+ rtp_header->frameType = kVideoFrameKey;
+ break;
+ default:
+ rtp_header->frameType = kVideoFrameDelta;
+ break;
+ }
+}
+
+void ParseFuaNalu(WebRtcRTPHeader* rtp_header,
+ const uint8_t* payload_data,
+ size_t payload_data_length,
+ size_t* offset) {
+ uint8_t fnri = payload_data[0] & (NalDefs::kFBit | NalDefs::kNriMask);
+ uint8_t original_nal_type = payload_data[1] & NalDefs::kTypeMask;
+ bool first_fragment = (payload_data[1] & FuDefs::kSBit) > 0;
+
+ uint8_t original_nal_header = fnri | original_nal_type;
+ if (first_fragment) {
+ *offset = kNalHeaderSize;
+ uint8_t* payload = const_cast<uint8_t*>(payload_data + *offset);
+ payload[0] = original_nal_header;
+ } else {
+ *offset = kFuAHeaderSize;
+ }
+
+ if (original_nal_type == Nalu::kIdr) {
+ rtp_header->frameType = kVideoFrameKey;
+ } else {
+ rtp_header->frameType = kVideoFrameDelta;
+ }
+ rtp_header->type.Video.codec = kRtpVideoH264;
+ rtp_header->type.Video.isFirstPacket = first_fragment;
+ RTPVideoHeaderH264* h264_header = &rtp_header->type.Video.codecHeader.H264;
+ h264_header->single_nalu = false;
+ h264_header->stap_a = false;
+}
+} // namespace
+
+RtpPacketizerH264::RtpPacketizerH264(size_t max_payload_len)
+ : payload_data_(NULL), payload_size_(0), max_payload_len_(max_payload_len) {
+}
+
+RtpPacketizerH264::~RtpPacketizerH264() {
+}
+
+void RtpPacketizerH264::SetPayloadData(
+ const uint8_t* payload_data,
+ size_t payload_size,
+ const RTPFragmentationHeader* fragmentation) {
+ assert(packets_.empty());
+ assert(fragmentation);
+ payload_data_ = payload_data;
+ payload_size_ = payload_size;
+ fragmentation_.CopyFrom(*fragmentation);
+ GeneratePackets();
+}
+
+void RtpPacketizerH264::GeneratePackets() {
+ for (size_t i = 0; i < fragmentation_.fragmentationVectorSize;) {
+ size_t fragment_offset = fragmentation_.fragmentationOffset[i];
+ size_t fragment_length = fragmentation_.fragmentationLength[i];
+ if (fragment_length > max_payload_len_) {
+ PacketizeFuA(fragment_offset, fragment_length);
+ ++i;
+ } else {
+ i = PacketizeStapA(i, fragment_offset, fragment_length);
+ }
+ }
+}
+
+void RtpPacketizerH264::PacketizeFuA(size_t fragment_offset,
+ size_t fragment_length) {
+ // Fragment payload into packets (FU-A).
+ // Strip out the original header and leave room for the FU-A header.
+ fragment_length -= kNalHeaderSize;
+ size_t offset = fragment_offset + kNalHeaderSize;
+ size_t bytes_available = max_payload_len_ - kFuAHeaderSize;
+ size_t fragments =
+ (fragment_length + (bytes_available - 1)) / bytes_available;
+ size_t avg_size = (fragment_length + fragments - 1) / fragments;
+ while (fragment_length > 0) {
+ size_t packet_length = avg_size;
+ if (fragment_length < avg_size)
+ packet_length = fragment_length;
+ uint8_t header = payload_data_[fragment_offset];
+ packets_.push(Packet(offset,
+ packet_length,
+ offset - kNalHeaderSize == fragment_offset,
+ fragment_length == packet_length,
+ false,
+ header));
+ offset += packet_length;
+ fragment_length -= packet_length;
+ }
+}
+
+int RtpPacketizerH264::PacketizeStapA(size_t fragment_index,
+ size_t fragment_offset,
+ size_t fragment_length) {
+ // Aggregate fragments into one packet (STAP-A).
+ size_t payload_size_left = max_payload_len_;
+ int aggregated_fragments = 0;
+ assert(payload_size_left >= fragment_length);
+ while (payload_size_left >= fragment_length) {
+ if (fragment_length > 0) {
+ assert(fragment_length > 0);
+ uint8_t header = payload_data_[fragment_offset];
+ packets_.push(Packet(fragment_offset,
+ fragment_length,
+ aggregated_fragments == 0,
+ false,
+ true,
+ header));
+ // If we are going to try to aggregate more fragments into this packet
+ // we need to add the STAP-A NALU header.
+ if (aggregated_fragments == 0)
+ payload_size_left -= kNalHeaderSize;
+ payload_size_left -= fragment_length;
+ ++aggregated_fragments;
+ }
+ // Next fragment.
+ ++fragment_index;
+ if (fragment_index == fragmentation_.fragmentationVectorSize)
+ break;
+ fragment_offset = fragmentation_.fragmentationOffset[fragment_index];
+ fragment_length = fragmentation_.fragmentationLength[fragment_index];
+ }
+ packets_.back().last_fragment = true;
+ return fragment_index;
+}
+
+bool RtpPacketizerH264::NextPacket(uint8_t* buffer,
+ size_t* bytes_to_send,
+ bool* last_packet) {
+ *bytes_to_send = 0;
+ if (packets_.empty()) {
+ *bytes_to_send = 0;
+ *last_packet = true;
+ return false;
+ }
+
+ Packet packet = packets_.front();
+
+ if (packet.first_fragment && packet.last_fragment) {
+ // Single NAL unit packet.
+ *bytes_to_send = packet.size;
+ memcpy(buffer, &payload_data_[packet.offset], packet.size);
+ packets_.pop();
+ } else if (packet.aggregated) {
+ NextAggregatePacket(buffer, bytes_to_send);
+ } else {
+ NextFragmentPacket(buffer, bytes_to_send);
+ }
+ *last_packet = packets_.empty();
+ assert(*bytes_to_send <= max_payload_len_);
+ return true;
+}
+
+void RtpPacketizerH264::NextAggregatePacket(uint8_t* buffer,
+ size_t* bytes_to_send) {
+ Packet packet = packets_.front();
+ assert(packet.first_fragment);
+ // STAP-A NALU header.
+ buffer[0] = (packet.header & (kFBit | kNriMask)) | kStapA;
+ int index = kNalHeaderSize;
+ *bytes_to_send += kNalHeaderSize;
+ while (packet.aggregated) {
+ // Add NAL unit length field.
+ RtpUtility::AssignUWord16ToBuffer(&buffer[index], packet.size);
+ index += 2;
+ *bytes_to_send += 2;
+ // Add NAL unit.
+ memcpy(&buffer[index], &payload_data_[packet.offset], packet.size);
+ index += packet.size;
+ *bytes_to_send += packet.size;
+ packets_.pop();
+ if (packet.last_fragment)
+ break;
+ packet = packets_.front();
+ }
+ assert(packet.last_fragment);
+}
+
+void RtpPacketizerH264::NextFragmentPacket(uint8_t* buffer,
+ size_t* bytes_to_send) {
+ Packet packet = packets_.front();
+ // NAL unit fragmented over multiple packets (FU-A).
+ // We do not send original NALU header, so it will be replaced by the
+ // FU indicator header of the first packet.
+ uint8_t fu_indicator = (packet.header & (kFBit | kNriMask)) | kFuA;
+ uint8_t fu_header = 0;
+
+ // S | E | R | 5 bit type.
+ fu_header |= (packet.first_fragment ? kSBit : 0);
+ fu_header |= (packet.last_fragment ? kEBit : 0);
+ uint8_t type = packet.header & kTypeMask;
+ fu_header |= type;
+ buffer[0] = fu_indicator;
+ buffer[1] = fu_header;
+
+ if (packet.last_fragment) {
+ *bytes_to_send = packet.size + kFuAHeaderSize;
+ memcpy(buffer + kFuAHeaderSize, &payload_data_[packet.offset], packet.size);
+ } else {
+ *bytes_to_send = packet.size + kFuAHeaderSize;
+ memcpy(buffer + kFuAHeaderSize, &payload_data_[packet.offset], packet.size);
+ }
+ packets_.pop();
+}
+
+RtpDepacketizerH264::RtpDepacketizerH264(RtpData* const callback)
+ : callback_(callback) {
+}
+
+bool RtpDepacketizerH264::Parse(WebRtcRTPHeader* rtp_header,
+ const uint8_t* payload_data,
+ size_t payload_data_length) {
+ uint8_t nal_type = payload_data[0] & NalDefs::kTypeMask;
+ size_t offset = 0;
+ if (nal_type == Nalu::kFuA) {
+ // Fragmented NAL units (FU-A).
+ ParseFuaNalu(rtp_header, payload_data, payload_data_length, &offset);
+ } else {
+ // We handle STAP-A and single NALU's the same way here. The jitter buffer
+ // will depacketize the STAP-A into NAL units later.
+ ParseSingleNalu(rtp_header, payload_data, payload_data_length);
+ }
+ if (callback_->OnReceivedPayloadData(payload_data + offset,
+ payload_data_length - offset,
+ rtp_header) != 0) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.h b/modules/rtp_rtcp/source/rtp_format_h264.h
new file mode 100644
index 0000000..773fa18
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtp_format_h264.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_
+#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_
+
+#include <queue>
+
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+
+namespace webrtc {
+
+class RtpPacketizerH264 : public RtpPacketizer {
+ public:
+ // Initialize with payload from encoder.
+ // The payload_data must be exactly one encoded H264 frame.
+ explicit RtpPacketizerH264(size_t max_payload_len);
+
+ virtual ~RtpPacketizerH264();
+
+ virtual void SetPayloadData(
+ const uint8_t* payload_data,
+ size_t payload_size,
+ const RTPFragmentationHeader* fragmentation) OVERRIDE;
+
+ // Get the next payload with H264 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. The 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 true on success or false if there was no payload to packetize.
+ virtual bool NextPacket(uint8_t* buffer,
+ size_t* bytes_to_send,
+ bool* last_packet) OVERRIDE;
+
+ private:
+ struct Packet {
+ Packet(size_t offset,
+ size_t size,
+ bool first_fragment,
+ bool last_fragment,
+ bool aggregated,
+ uint8_t header)
+ : offset(offset),
+ size(size),
+ first_fragment(first_fragment),
+ last_fragment(last_fragment),
+ aggregated(aggregated),
+ header(header) {}
+
+ size_t offset;
+ size_t size;
+ bool first_fragment;
+ bool last_fragment;
+ bool aggregated;
+ uint8_t header;
+ };
+ typedef std::queue<Packet> PacketQueue;
+
+ void GeneratePackets();
+ void PacketizeFuA(size_t fragment_offset, size_t fragment_length);
+ int PacketizeStapA(size_t fragment_index,
+ size_t fragment_offset,
+ size_t fragment_length);
+ void NextAggregatePacket(uint8_t* buffer, size_t* bytes_to_send);
+ void NextFragmentPacket(uint8_t* buffer, size_t* bytes_to_send);
+
+ const uint8_t* payload_data_;
+ size_t payload_size_;
+ const size_t max_payload_len_;
+ RTPFragmentationHeader fragmentation_;
+ PacketQueue packets_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtpPacketizerH264);
+};
+
+// Depacketizer for H264.
+class RtpDepacketizerH264 : public RtpDepacketizer {
+ public:
+ explicit RtpDepacketizerH264(RtpData* const callback);
+
+ virtual ~RtpDepacketizerH264() {}
+
+ virtual bool Parse(WebRtcRTPHeader* rtp_header,
+ const uint8_t* payload_data,
+ size_t payload_data_length) OVERRIDE;
+
+ private:
+ RtpData* const callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtpDepacketizerH264);
+};
+} // namespace webrtc
+#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_H264_H_
diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
new file mode 100644
index 0000000..61c7f4c
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+using ::testing::_;
+using ::testing::Args;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::SaveArgPointee;
+
+namespace webrtc {
+namespace {
+const size_t kMaxPayloadSize = 1200;
+const size_t kLengthFieldLength = 2;
+
+enum Nalu {
+ kSlice = 1,
+ kIdr = 5,
+ kSei = 6,
+ kSps = 7,
+ kPps = 8,
+ kStapA = 24,
+ kFuA = 28
+};
+
+static const size_t kNalHeaderSize = 1;
+static const size_t kFuAHeaderSize = 2;
+
+// Bit masks for FU (A and B) indicators.
+enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F };
+
+// Bit masks for FU (A and B) headers.
+enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 };
+
+void VerifyFua(size_t fua_index,
+ const uint8_t* expected_payload,
+ int offset,
+ const uint8_t* packet,
+ size_t length,
+ const std::vector<size_t>& expected_sizes) {
+ ASSERT_EQ(expected_sizes[fua_index] + kFuAHeaderSize, length)
+ << "FUA index: " << fua_index;
+ const uint8_t kFuIndicator = 0x1C; // F=0, NRI=0, Type=28.
+ EXPECT_EQ(kFuIndicator, packet[0]) << "FUA index: " << fua_index;
+ bool should_be_last_fua = (fua_index == expected_sizes.size() - 1);
+ uint8_t fu_header = 0;
+ if (fua_index == 0)
+ fu_header = 0x85; // S=1, E=0, R=0, Type=5.
+ else if (should_be_last_fua)
+ fu_header = 0x45; // S=0, E=1, R=0, Type=5.
+ else
+ fu_header = 0x05; // S=0, E=0, R=0, Type=5.
+ EXPECT_EQ(fu_header, packet[1]) << "FUA index: " << fua_index;
+ std::vector<uint8_t> expected_packet_payload(
+ &expected_payload[offset],
+ &expected_payload[offset + expected_sizes[fua_index]]);
+ EXPECT_THAT(
+ expected_packet_payload,
+ ::testing::ElementsAreArray(&packet[2], expected_sizes[fua_index]))
+ << "FUA index: " << fua_index;
+}
+
+void TestFua(size_t frame_size,
+ size_t max_payload_size,
+ const std::vector<size_t>& expected_sizes) {
+ scoped_ptr<uint8_t[]> frame;
+ frame.reset(new uint8_t[frame_size]);
+ frame[0] = 0x05; // F=0, NRI=0, Type=5.
+ for (size_t i = 0; i < frame_size - kNalHeaderSize; ++i) {
+ frame[i + kNalHeaderSize] = i;
+ }
+ RTPFragmentationHeader fragmentation;
+ fragmentation.VerifyAndAllocateFragmentationHeader(1);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = frame_size;
+ scoped_ptr<RtpPacketizer> packetizer(
+ RtpPacketizer::Create(kRtpVideoH264, max_payload_size));
+ packetizer->SetPayloadData(frame.get(), frame_size, &fragmentation);
+
+ scoped_ptr<uint8_t[]> packet(new uint8_t[max_payload_size]);
+ size_t length = 0;
+ bool last = false;
+ size_t offset = kNalHeaderSize;
+ for (size_t i = 0; i < expected_sizes.size(); ++i) {
+ ASSERT_TRUE(packetizer->NextPacket(packet.get(), &length, &last));
+ VerifyFua(i, frame.get(), offset, packet.get(), length, expected_sizes);
+ EXPECT_EQ(i == expected_sizes.size() - 1, last) << "FUA index: " << i;
+ offset += expected_sizes[i];
+ }
+
+ EXPECT_FALSE(packetizer->NextPacket(packet.get(), &length, &last));
+}
+
+size_t GetExpectedNaluOffset(const RTPFragmentationHeader& fragmentation,
+ size_t start_index,
+ size_t nalu_index) {
+ assert(nalu_index < fragmentation.fragmentationVectorSize);
+ size_t expected_nalu_offset = kNalHeaderSize; // STAP-A header.
+ for (size_t i = start_index; i < nalu_index; ++i) {
+ expected_nalu_offset +=
+ kLengthFieldLength + fragmentation.fragmentationLength[i];
+ }
+ return expected_nalu_offset;
+}
+
+void VerifyStapAPayload(const RTPFragmentationHeader& fragmentation,
+ size_t first_stapa_index,
+ size_t nalu_index,
+ const uint8_t* frame,
+ size_t frame_length,
+ const uint8_t* packet,
+ size_t packet_length) {
+ size_t expected_payload_offset =
+ GetExpectedNaluOffset(fragmentation, first_stapa_index, nalu_index) +
+ kLengthFieldLength;
+ size_t offset = fragmentation.fragmentationOffset[nalu_index];
+ const uint8_t* expected_payload = &frame[offset];
+ size_t expected_payload_length =
+ fragmentation.fragmentationLength[nalu_index];
+ ASSERT_LE(offset + expected_payload_length, frame_length);
+ ASSERT_LE(expected_payload_offset + expected_payload_length, packet_length);
+ std::vector<uint8_t> expected_payload_vector(
+ expected_payload, &expected_payload[expected_payload_length]);
+ EXPECT_THAT(expected_payload_vector,
+ ::testing::ElementsAreArray(&packet[expected_payload_offset],
+ expected_payload_length));
+}
+} // namespace
+
+TEST(RtpPacketizerH264Test, TestSingleNalu) {
+ const uint8_t frame[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5.
+ RTPFragmentationHeader fragmentation;
+ fragmentation.VerifyAndAllocateFragmentationHeader(1);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = sizeof(frame);
+ scoped_ptr<RtpPacketizer> packetizer(
+ RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize));
+ packetizer->SetPayloadData(frame, sizeof(frame), &fragmentation);
+ uint8_t packet[kMaxPayloadSize] = {0};
+ size_t length = 0;
+ bool last = false;
+ ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last));
+ EXPECT_EQ(2u, length);
+ EXPECT_TRUE(last);
+ EXPECT_EQ(frame[0], packet[0]);
+ EXPECT_EQ(frame[1], packet[1]);
+ EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last));
+}
+
+TEST(RtpPacketizerH264Test, TestStapA) {
+ const size_t kFrameSize = kMaxPayloadSize - 100;
+ uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7.
+ 0x08, 0xFF, // F=0, NRI=0, Type=8.
+ 0x05}; // F=0, NRI=0, Type=5.
+ const size_t kPayloadOffset = 5;
+ for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i)
+ frame[i + kPayloadOffset] = i;
+ RTPFragmentationHeader fragmentation;
+ fragmentation.VerifyAndAllocateFragmentationHeader(3);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = 2;
+ fragmentation.fragmentationOffset[1] = 2;
+ fragmentation.fragmentationLength[1] = 2;
+ fragmentation.fragmentationOffset[2] = 4;
+ fragmentation.fragmentationLength[2] =
+ kNalHeaderSize + kFrameSize - kPayloadOffset;
+ scoped_ptr<RtpPacketizer> packetizer(
+ RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize));
+ packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
+
+ uint8_t packet[kMaxPayloadSize] = {0};
+ size_t length = 0;
+ bool last = false;
+ ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last));
+ size_t expected_packet_size =
+ kNalHeaderSize + 3 * kLengthFieldLength + kFrameSize;
+ ASSERT_EQ(expected_packet_size, length);
+ EXPECT_TRUE(last);
+ for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i)
+ VerifyStapAPayload(fragmentation, 0, i, frame, kFrameSize, packet, length);
+
+ EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last));
+}
+
+TEST(RtpPacketizerH264Test, TestMixedStapA_FUA) {
+ const size_t kFuaNaluSize = 2 * (kMaxPayloadSize - 100);
+ const size_t kStapANaluSize = 100;
+ RTPFragmentationHeader fragmentation;
+ fragmentation.VerifyAndAllocateFragmentationHeader(3);
+ fragmentation.fragmentationOffset[0] = 0;
+ fragmentation.fragmentationLength[0] = kFuaNaluSize;
+ fragmentation.fragmentationOffset[1] = kFuaNaluSize;
+ fragmentation.fragmentationLength[1] = kStapANaluSize;
+ fragmentation.fragmentationOffset[2] = kFuaNaluSize + kStapANaluSize;
+ fragmentation.fragmentationLength[2] = kStapANaluSize;
+ const size_t kFrameSize = kFuaNaluSize + 2 * kStapANaluSize;
+ uint8_t frame[kFrameSize];
+ size_t nalu_offset = 0;
+ for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) {
+ nalu_offset = fragmentation.fragmentationOffset[i];
+ frame[nalu_offset] = 0x05; // F=0, NRI=0, Type=5.
+ for (size_t j = 1; j < fragmentation.fragmentationLength[i]; ++j) {
+ frame[nalu_offset + j] = i + j;
+ }
+ }
+ scoped_ptr<RtpPacketizer> packetizer(
+ RtpPacketizer::Create(kRtpVideoH264, kMaxPayloadSize));
+ packetizer->SetPayloadData(frame, kFrameSize, &fragmentation);
+
+ // First expecting two FU-A packets.
+ std::vector<size_t> fua_sizes;
+ fua_sizes.push_back(1100);
+ fua_sizes.push_back(1099);
+ uint8_t packet[kMaxPayloadSize] = {0};
+ size_t length = 0;
+ bool last = false;
+ int fua_offset = kNalHeaderSize;
+ for (size_t i = 0; i < 2; ++i) {
+ ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last));
+ VerifyFua(i, frame, fua_offset, packet, length, fua_sizes);
+ EXPECT_FALSE(last);
+ fua_offset += fua_sizes[i];
+ }
+ // Then expecting one STAP-A packet with two nal units.
+ ASSERT_TRUE(packetizer->NextPacket(packet, &length, &last));
+ size_t expected_packet_size =
+ kNalHeaderSize + 2 * kLengthFieldLength + 2 * kStapANaluSize;
+ ASSERT_EQ(expected_packet_size, length);
+ EXPECT_TRUE(last);
+ for (size_t i = 1; i < fragmentation.fragmentationVectorSize; ++i)
+ VerifyStapAPayload(fragmentation, 1, i, frame, kFrameSize, packet, length);
+
+ EXPECT_FALSE(packetizer->NextPacket(packet, &length, &last));
+}
+
+TEST(RtpPacketizerH264Test, TestFUAOddSize) {
+ const size_t kExpectedPayloadSizes[2] = {600, 600};
+ TestFua(
+ kMaxPayloadSize + 1,
+ kMaxPayloadSize,
+ std::vector<size_t>(kExpectedPayloadSizes,
+ kExpectedPayloadSizes +
+ sizeof(kExpectedPayloadSizes) / sizeof(size_t)));
+}
+
+TEST(RtpPacketizerH264Test, TestFUAEvenSize) {
+ const size_t kExpectedPayloadSizes[2] = {601, 600};
+ TestFua(
+ kMaxPayloadSize + 2,
+ kMaxPayloadSize,
+ std::vector<size_t>(kExpectedPayloadSizes,
+ kExpectedPayloadSizes +
+ sizeof(kExpectedPayloadSizes) / sizeof(size_t)));
+}
+
+TEST(RtpPacketizerH264Test, TestFUARounding) {
+ const size_t kExpectedPayloadSizes[8] = {1266, 1266, 1266, 1266,
+ 1266, 1266, 1266, 1261};
+ TestFua(
+ 10124,
+ 1448,
+ std::vector<size_t>(kExpectedPayloadSizes,
+ kExpectedPayloadSizes +
+ sizeof(kExpectedPayloadSizes) / sizeof(size_t)));
+}
+
+TEST(RtpPacketizerH264Test, TestFUABig) {
+ const size_t kExpectedPayloadSizes[10] = {1198, 1198, 1198, 1198, 1198,
+ 1198, 1198, 1198, 1198, 1198};
+ // Generate 10 full sized packets, leave room for FU-A headers minus the NALU
+ // header.
+ TestFua(
+ 10 * (kMaxPayloadSize - kFuAHeaderSize) + kNalHeaderSize,
+ kMaxPayloadSize,
+ std::vector<size_t>(kExpectedPayloadSizes,
+ kExpectedPayloadSizes +
+ sizeof(kExpectedPayloadSizes) / sizeof(size_t)));
+}
+
+class RtpDepacketizerH264Test : public ::testing::Test {
+ protected:
+ RtpDepacketizerH264Test()
+ : callback_(),
+ depacketizer_(RtpDepacketizer::Create(kRtpVideoH264, &callback_)) {
+ memset(&last_header_, 0, sizeof(last_header_));
+ }
+
+ void ExpectPacket(const uint8_t* data, size_t length) {
+ EXPECT_CALL(callback_, OnReceivedPayloadData(_, length, _))
+ .With(Args<0, 1>(ElementsAreArray(data, length)))
+ .Times(1)
+ .WillRepeatedly(DoAll(SaveArgPointee<2>(&last_header_), Return(0)));
+ }
+
+ MockRtpData callback_;
+ scoped_ptr<RtpDepacketizer> depacketizer_;
+ WebRtcRTPHeader last_header_;
+};
+
+TEST_F(RtpDepacketizerH264Test, TestSingleNalu) {
+ uint8_t packet[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5.
+
+ WebRtcRTPHeader expected_header;
+ memset(&expected_header, 0, sizeof(expected_header));
+ ExpectPacket(packet, sizeof(packet));
+ EXPECT_TRUE(depacketizer_->Parse(&expected_header, packet, sizeof(packet)));
+ EXPECT_EQ(kVideoFrameKey, last_header_.frameType);
+ EXPECT_TRUE(last_header_.type.Video.isFirstPacket);
+ EXPECT_TRUE(last_header_.type.Video.codecHeader.H264.single_nalu);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.stap_a);
+}
+
+TEST_F(RtpDepacketizerH264Test, TestStapAKey) {
+ uint8_t packet[16] = {Nalu::kStapA, // F=0, NRI=0, Type=24.
+ // Length, nal header, payload.
+ 0, 0x02, Nalu::kIdr, 0xFF, 0,
+ 0x03, Nalu::kIdr, 0xFF, 0x00, 0,
+ 0x04, Nalu::kIdr, 0xFF, 0x00, 0x11};
+
+ WebRtcRTPHeader expected_header;
+ memset(&expected_header, 0, sizeof(expected_header));
+ ExpectPacket(packet, sizeof(packet));
+ EXPECT_TRUE(depacketizer_->Parse(&expected_header, packet, sizeof(packet)));
+ EXPECT_EQ(kVideoFrameKey, last_header_.frameType);
+ EXPECT_TRUE(last_header_.type.Video.isFirstPacket);
+ EXPECT_TRUE(last_header_.type.Video.codecHeader.H264.single_nalu);
+ EXPECT_TRUE(last_header_.type.Video.codecHeader.H264.stap_a);
+}
+
+TEST_F(RtpDepacketizerH264Test, TestStapADelta) {
+ uint8_t packet[16] = {Nalu::kStapA, // F=0, NRI=0, Type=24.
+ // Length, nal header, payload.
+ 0, 0x02, Nalu::kSlice, 0xFF, 0,
+ 0x03, Nalu::kSlice, 0xFF, 0x00, 0,
+ 0x04, Nalu::kSlice, 0xFF, 0x00, 0x11};
+
+ WebRtcRTPHeader expected_header;
+ memset(&expected_header, 0, sizeof(expected_header));
+ ExpectPacket(packet, sizeof(packet));
+ EXPECT_TRUE(depacketizer_->Parse(&expected_header, packet, sizeof(packet)));
+ EXPECT_EQ(kVideoFrameDelta, last_header_.frameType);
+ EXPECT_TRUE(last_header_.type.Video.isFirstPacket);
+ EXPECT_TRUE(last_header_.type.Video.codecHeader.H264.single_nalu);
+ EXPECT_TRUE(last_header_.type.Video.codecHeader.H264.stap_a);
+}
+
+TEST_F(RtpDepacketizerH264Test, TestFuA) {
+ uint8_t packet1[3] = {
+ Nalu::kFuA, // F=0, NRI=0, Type=28.
+ FuDefs::kSBit | Nalu::kIdr, // FU header.
+ 0x01 // Payload.
+ };
+ const uint8_t kExpected1[2] = {Nalu::kIdr, 0x01};
+
+ uint8_t packet2[3] = {
+ Nalu::kFuA, // F=0, NRI=0, Type=28.
+ Nalu::kIdr, // FU header.
+ 0x02 // Payload.
+ };
+ const uint8_t kExpected2[1] = {0x02};
+
+ uint8_t packet3[3] = {
+ Nalu::kFuA, // F=0, NRI=0, Type=28.
+ FuDefs::kEBit | Nalu::kIdr, // FU header.
+ 0x03 // Payload.
+ };
+ const uint8_t kExpected3[1] = {0x03};
+
+ WebRtcRTPHeader expected_header;
+ memset(&expected_header, 0, sizeof(expected_header));
+
+ // We expect that the first packet is one byte shorter since the FU-A header
+ // has been replaced by the original nal header.
+ ExpectPacket(kExpected1, sizeof(kExpected1));
+ EXPECT_TRUE(depacketizer_->Parse(&expected_header, packet1, sizeof(packet1)));
+ EXPECT_EQ(kVideoFrameKey, last_header_.frameType);
+ EXPECT_TRUE(last_header_.type.Video.isFirstPacket);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.single_nalu);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.stap_a);
+
+ // Following packets will be 2 bytes shorter since they will only be appended
+ // onto the first packet.
+ ExpectPacket(kExpected2, sizeof(kExpected2));
+ EXPECT_TRUE(depacketizer_->Parse(&expected_header, packet2, sizeof(packet2)));
+ EXPECT_EQ(kVideoFrameKey, last_header_.frameType);
+ EXPECT_FALSE(last_header_.type.Video.isFirstPacket);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.single_nalu);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.stap_a);
+
+ ExpectPacket(kExpected3, sizeof(kExpected3));
+ EXPECT_TRUE(depacketizer_->Parse(&expected_header, packet3, sizeof(packet3)));
+ EXPECT_EQ(kVideoFrameKey, last_header_.frameType);
+ EXPECT_FALSE(last_header_.type.Video.isFirstPacket);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.single_nalu);
+ EXPECT_FALSE(last_header_.type.Video.codecHeader.H264.stap_a);
+}
+} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.cc b/modules/rtp_rtcp/source/rtp_format_vp8.cc
index 9c04c25..049bd05 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8.cc
+++ b/modules/rtp_rtcp/source/rtp_format_vp8.cc
@@ -21,57 +21,65 @@
// Define how the VP8PacketizerModes are implemented.
// Modes are: kStrict, kAggregate, kEqualSize.
-const RtpFormatVp8::AggregationMode RtpFormatVp8::aggr_modes_[kNumModes] =
- { kAggrNone, kAggrPartitions, kAggrFragments };
-const bool RtpFormatVp8::balance_modes_[kNumModes] =
- { true, true, true };
-const bool RtpFormatVp8::separate_first_modes_[kNumModes] =
- { true, false, false };
+const RtpPacketizerVp8::AggregationMode RtpPacketizerVp8::aggr_modes_
+ [kNumModes] = {kAggrNone, kAggrPartitions, kAggrFragments};
+const bool RtpPacketizerVp8::balance_modes_[kNumModes] = {true, true, true};
+const bool RtpPacketizerVp8::separate_first_modes_[kNumModes] = {true, false,
+ false};
-RtpFormatVp8::RtpFormatVp8(const uint8_t* payload_data,
- uint32_t payload_size,
- const RTPVideoHeaderVP8& hdr_info,
- int max_payload_len,
- const RTPFragmentationHeader& fragmentation,
- VP8PacketizerMode mode)
- : payload_data_(payload_data),
- payload_size_(static_cast<int>(payload_size)),
+RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info,
+ int max_payload_len,
+ VP8PacketizerMode mode)
+ : payload_data_(NULL),
+ payload_size_(0),
vp8_fixed_payload_descriptor_bytes_(1),
aggr_mode_(aggr_modes_[mode]),
balance_(balance_modes_[mode]),
separate_first_(separate_first_modes_[mode]),
hdr_info_(hdr_info),
- num_partitions_(fragmentation.fragmentationVectorSize),
+ num_partitions_(0),
max_payload_len_(max_payload_len),
packets_calculated_(false) {
- part_info_.CopyFrom(fragmentation);
}
-RtpFormatVp8::RtpFormatVp8(const uint8_t* payload_data,
- uint32_t payload_size,
- const RTPVideoHeaderVP8& hdr_info,
- int max_payload_len)
- : payload_data_(payload_data),
- payload_size_(static_cast<int>(payload_size)),
+RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info,
+ int max_payload_len)
+ : payload_data_(NULL),
+ payload_size_(0),
part_info_(),
vp8_fixed_payload_descriptor_bytes_(1),
aggr_mode_(aggr_modes_[kEqualSize]),
balance_(balance_modes_[kEqualSize]),
separate_first_(separate_first_modes_[kEqualSize]),
hdr_info_(hdr_info),
- num_partitions_(1),
+ num_partitions_(0),
max_payload_len_(max_payload_len),
packets_calculated_(false) {
+}
+
+RtpPacketizerVp8::~RtpPacketizerVp8() {
+}
+
+void RtpPacketizerVp8::SetPayloadData(
+ const uint8_t* payload_data,
+ size_t payload_size,
+ const RTPFragmentationHeader* fragmentation) {
+ payload_data_ = payload_data;
+ payload_size_ = payload_size;
+ if (fragmentation) {
+ part_info_.CopyFrom(*fragmentation);
+ num_partitions_ = fragmentation->fragmentationVectorSize;
+ } else {
part_info_.VerifyAndAllocateFragmentationHeader(1);
part_info_.fragmentationLength[0] = payload_size;
part_info_.fragmentationOffset[0] = 0;
+ num_partitions_ = part_info_.fragmentationVectorSize;
+ }
}
-RtpFormatVp8::~RtpFormatVp8() {}
-
-int RtpFormatVp8::NextPacket(uint8_t* buffer,
- int* bytes_to_send,
- bool* last_packet) {
+bool RtpPacketizerVp8::NextPacket(uint8_t* buffer,
+ size_t* bytes_to_send,
+ bool* last_packet) {
if (!packets_calculated_) {
int ret = 0;
if (aggr_mode_ == kAggrPartitions && balance_) {
@@ -80,26 +88,28 @@
ret = GeneratePackets();
}
if (ret < 0) {
- return ret;
+ return false;
}
}
if (packets_.empty()) {
- return -1;
+ return false;
}
InfoStruct packet_info = packets_.front();
packets_.pop();
- *bytes_to_send = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_);
- if (*bytes_to_send < 0) {
- return -1;
+ int bytes = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_);
+ if (bytes < 0) {
+ return false;
}
+ *bytes_to_send = bytes;
*last_packet = packets_.empty();
- return packet_info.first_partition_ix;
+ return true;
}
-int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes,
- bool split_payload) const {
+int RtpPacketizerVp8::CalcNextSize(int max_payload_len,
+ int remaining_bytes,
+ bool split_payload) const {
if (max_payload_len == 0 || remaining_bytes == 0) {
return 0;
}
@@ -121,7 +131,7 @@
}
}
-int RtpFormatVp8::GeneratePackets() {
+int RtpPacketizerVp8::GeneratePackets() {
if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_
+ PayloadDescriptorExtraLength() + 1) {
// The provided payload length is not long enough for the payload
@@ -182,7 +192,7 @@
return 0;
}
-int RtpFormatVp8::GeneratePacketsBalancedAggregates() {
+int RtpPacketizerVp8::GeneratePacketsBalancedAggregates() {
if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_
+ PayloadDescriptorExtraLength() + 1) {
// The provided payload length is not long enough for the payload
@@ -241,9 +251,9 @@
return 0;
}
-void RtpFormatVp8::AggregateSmallPartitions(std::vector<int>* partition_vec,
- int* min_size,
- int* max_size) {
+void RtpPacketizerVp8::AggregateSmallPartitions(std::vector<int>* partition_vec,
+ int* min_size,
+ int* max_size) {
assert(min_size && max_size);
*min_size = -1;
*max_size = -1;
@@ -285,10 +295,10 @@
}
}
-void RtpFormatVp8::QueuePacket(int start_pos,
- int packet_size,
- int first_partition_in_packet,
- bool start_on_new_fragment) {
+void RtpPacketizerVp8::QueuePacket(int start_pos,
+ int packet_size,
+ int first_partition_in_packet,
+ bool start_on_new_fragment) {
// Write info to packet info struct and store in packet info queue.
InfoStruct packet_info;
packet_info.payload_start_pos = start_pos;
@@ -298,9 +308,9 @@
packets_.push(packet_info);
}
-int RtpFormatVp8::WriteHeaderAndPayload(const InfoStruct& packet_info,
- uint8_t* buffer,
- int buffer_length) const {
+int RtpPacketizerVp8::WriteHeaderAndPayload(const InfoStruct& packet_info,
+ uint8_t* buffer,
+ int buffer_length) const {
// Write the VP8 payload descriptor.
// 0
// 0 1 2 3 4 5 6 7 8
@@ -333,8 +343,8 @@
+ extension_length;
}
-int RtpFormatVp8::WriteExtensionFields(uint8_t* buffer,
- int buffer_length) const {
+int RtpPacketizerVp8::WriteExtensionFields(uint8_t* buffer,
+ int buffer_length) const {
int extension_length = 0;
if (XFieldPresent()) {
uint8_t* x_field = buffer + vp8_fixed_payload_descriptor_bytes_;
@@ -363,10 +373,10 @@
return extension_length;
}
-int RtpFormatVp8::WritePictureIDFields(uint8_t* x_field,
- uint8_t* buffer,
- int buffer_length,
- int* extension_length) const {
+int RtpPacketizerVp8::WritePictureIDFields(uint8_t* x_field,
+ uint8_t* 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,
@@ -377,8 +387,7 @@
return 0;
}
-int RtpFormatVp8::WritePictureID(uint8_t* buffer,
- int buffer_length) const {
+int RtpPacketizerVp8::WritePictureID(uint8_t* buffer, int buffer_length) const {
const uint16_t pic_id =
static_cast<uint16_t> (hdr_info_.pictureId);
int picture_id_len = PictureIdLength();
@@ -392,10 +401,10 @@
return picture_id_len;
}
-int RtpFormatVp8::WriteTl0PicIdxFields(uint8_t* x_field,
- uint8_t* buffer,
- int buffer_length,
- int* extension_length) const {
+int RtpPacketizerVp8::WriteTl0PicIdxFields(uint8_t* x_field,
+ uint8_t* buffer,
+ int buffer_length,
+ int* extension_length) const {
if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length
+ 1) {
return -1;
@@ -407,10 +416,10 @@
return 0;
}
-int RtpFormatVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field,
- uint8_t* buffer,
- int buffer_length,
- int* extension_length) const {
+int RtpPacketizerVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field,
+ uint8_t* buffer,
+ int buffer_length,
+ int* extension_length) const {
if (buffer_length < vp8_fixed_payload_descriptor_bytes_ + *extension_length
+ 1) {
return -1;
@@ -432,7 +441,7 @@
return 0;
}
-int RtpFormatVp8::PayloadDescriptorExtraLength() const {
+int RtpPacketizerVp8::PayloadDescriptorExtraLength() const {
int length_bytes = PictureIdLength();
if (TL0PicIdxFieldPresent()) ++length_bytes;
if (TIDFieldPresent() || KeyIdxFieldPresent()) ++length_bytes;
@@ -440,7 +449,7 @@
return length_bytes;
}
-int RtpFormatVp8::PictureIdLength() const {
+int RtpPacketizerVp8::PictureIdLength() const {
if (hdr_info_.pictureId == kNoPictureId) {
return 0;
}
@@ -450,22 +459,22 @@
return 2;
}
-bool RtpFormatVp8::XFieldPresent() const {
+bool RtpPacketizerVp8::XFieldPresent() const {
return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent()
|| KeyIdxFieldPresent());
}
-bool RtpFormatVp8::TIDFieldPresent() const {
+bool RtpPacketizerVp8::TIDFieldPresent() const {
assert((hdr_info_.layerSync == false) ||
(hdr_info_.temporalIdx != kNoTemporalIdx));
return (hdr_info_.temporalIdx != kNoTemporalIdx);
}
-bool RtpFormatVp8::KeyIdxFieldPresent() const {
+bool RtpPacketizerVp8::KeyIdxFieldPresent() const {
return (hdr_info_.keyIdx != kNoKeyIdx);
}
-bool RtpFormatVp8::TL0PicIdxFieldPresent() const {
+bool RtpPacketizerVp8::TL0PicIdxFieldPresent() const {
return (hdr_info_.tl0PicIdx != kNoTl0PicIdx);
}
} // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.h b/modules/rtp_rtcp/source/rtp_format_vp8.h
index e4d3dc0..755d19e 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8.h
+++ b/modules/rtp_rtcp/source/rtp_format_vp8.h
@@ -30,6 +30,7 @@
#include "webrtc/base/constructormagic.h"
#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
#include "webrtc/typedefs.h"
namespace webrtc {
@@ -44,25 +45,24 @@
};
// Packetizer for VP8.
-class RtpFormatVp8 {
+class RtpPacketizerVp8 : public RtpPacketizer {
public:
// Initialize with payload from encoder and fragmentation info.
// The payload_data must be exactly one encoded VP8 frame.
- RtpFormatVp8(const uint8_t* payload_data,
- uint32_t payload_size,
- const RTPVideoHeaderVP8& hdr_info,
- int max_payload_len,
- const RTPFragmentationHeader& fragmentation,
- VP8PacketizerMode mode);
+ RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info,
+ int max_payload_len,
+ VP8PacketizerMode mode);
// Initialize without fragmentation info. Mode kEqualSize will be used.
// The payload_data must be exactly one encoded VP8 frame.
- RtpFormatVp8(const uint8_t* payload_data,
- uint32_t payload_size,
- const RTPVideoHeaderVP8& hdr_info,
- int max_payload_len);
+ RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, int max_payload_len);
- ~RtpFormatVp8();
+ virtual ~RtpPacketizerVp8();
+
+ virtual void SetPayloadData(
+ const uint8_t* payload_data,
+ size_t payload_size,
+ const RTPFragmentationHeader* fragmentation) OVERRIDE;
// Get the next payload with VP8 payload header.
// max_payload_len limits the sum length of payload and VP8 payload header.
@@ -75,9 +75,9 @@
// the first payload byte in the packet is taken, with the first partition
// having index 0; returns negative on error.
// For the kEqualSize mode: returns 0 on success, return negative on error.
- int NextPacket(uint8_t* buffer,
- int* bytes_to_send,
- bool* last_packet);
+ virtual bool NextPacket(uint8_t* buffer,
+ size_t* bytes_to_send,
+ bool* last_packet) OVERRIDE;
private:
typedef struct {
@@ -187,7 +187,7 @@
bool PictureIdPresent() const { return (PictureIdLength() > 0); }
const uint8_t* payload_data_;
- const int payload_size_;
+ int payload_size_;
RTPFragmentationHeader part_info_;
const int vp8_fixed_payload_descriptor_bytes_; // Length of VP8 payload
// descriptors's fixed part.
@@ -195,14 +195,12 @@
const bool balance_;
const bool separate_first_;
const RTPVideoHeaderVP8 hdr_info_;
- const int num_partitions_;
+ int num_partitions_;
const int max_payload_len_;
InfoQueue packets_;
bool packets_calculated_;
- DISALLOW_COPY_AND_ASSIGN(RtpFormatVp8);
+ DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp8);
};
-
} // namespace
-
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_H_
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc
index a121a69..549512b 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc
+++ b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc
@@ -64,20 +64,19 @@
}
void RtpFormatVp8TestHelper::GetAllPacketsAndCheck(
- RtpFormatVp8* packetizer,
+ RtpPacketizerVp8* packetizer,
const int* expected_sizes,
const int* expected_part,
const bool* expected_frag_start,
int expected_num_packets) {
ASSERT_TRUE(inited_);
- int send_bytes = 0;
+ size_t send_bytes = 0;
bool last = false;
for (int i = 0; i < expected_num_packets; ++i) {
std::ostringstream ss;
ss << "Checking packet " << i;
SCOPED_TRACE(ss.str());
- EXPECT_EQ(expected_part[i],
- packetizer->NextPacket(buffer_, &send_bytes, &last));
+ EXPECT_TRUE(packetizer->NextPacket(buffer_, &send_bytes, &last));
CheckPacket(send_bytes, expected_sizes[i], last,
expected_frag_start[i]);
}
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h
index e146492..22e6803 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h
+++ b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h
@@ -32,7 +32,7 @@
explicit RtpFormatVp8TestHelper(const RTPVideoHeaderVP8* hdr);
~RtpFormatVp8TestHelper();
bool Init(const int* partition_sizes, int num_partitions);
- void GetAllPacketsAndCheck(RtpFormatVp8* packetizer,
+ void GetAllPacketsAndCheck(RtpPacketizerVp8* packetizer,
const int* expected_sizes,
const int* expected_part,
const bool* expected_frag_start,
diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc b/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc
index 1d8b2be..8dfc27a 100644
--- a/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc
@@ -24,9 +24,9 @@
namespace webrtc {
-class RtpFormatVp8Test : public ::testing::Test {
+class RtpPacketizerVp8Test : public ::testing::Test {
protected:
- RtpFormatVp8Test() : helper_(NULL) {}
+ RtpPacketizerVp8Test() : helper_(NULL) {}
virtual void TearDown() { delete helper_; }
bool Init(const int* partition_sizes, int num_partitions) {
hdr_info_.pictureId = kNoPictureId;
@@ -44,19 +44,17 @@
test::RtpFormatVp8TestHelper* helper_;
};
-TEST_F(RtpFormatVp8Test, TestStrictMode) {
+TEST_F(RtpPacketizerVp8Test, TestStrictMode) {
const int kSizeVector[] = {10, 8, 27};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID.
const int kMaxSize = 13;
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kStrict);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kStrict);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// The expected sizes are obtained by running a verified good implementation.
const int kExpectedSizes[] = {9, 9, 12, 11, 11, 11, 10};
@@ -71,19 +69,17 @@
kExpectedFragStart, kExpectedNum);
}
-TEST_F(RtpFormatVp8Test, TestAggregateMode) {
+TEST_F(RtpPacketizerVp8Test, TestAggregateMode) {
const int kSizeVector[] = {60, 10, 10};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID.
const int kMaxSize = 25;
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// The expected sizes are obtained by running a verified good implementation.
const int kExpectedSizes[] = {23, 23, 23, 23};
@@ -97,19 +93,17 @@
kExpectedFragStart, kExpectedNum);
}
-TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions1) {
+TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions1) {
const int kSizeVector[] = {1600, 200, 200, 200, 200, 200, 200, 200, 200};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID.
const int kMaxSize = 1500;
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// The expected sizes are obtained by running a verified good implementation.
const int kExpectedSizes[] = {803, 803, 803, 803};
@@ -123,19 +117,17 @@
kExpectedFragStart, kExpectedNum);
}
-TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions2) {
+TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions2) {
const int kSizeVector[] = {1599, 200, 200, 200, 1600, 200, 200, 200, 200};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID.
const int kMaxSize = 1500;
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// The expected sizes are obtained by running a verified good implementation.
const int kExpectedSizes[] = {803, 802, 603, 803, 803, 803};
@@ -149,19 +141,17 @@
kExpectedFragStart, kExpectedNum);
}
-TEST_F(RtpFormatVp8Test, TestAggregateModeTwoLargePartitions) {
+TEST_F(RtpPacketizerVp8Test, TestAggregateModeTwoLargePartitions) {
const int kSizeVector[] = {1654, 2268};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID.
const int kMaxSize = 1460;
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// The expected sizes are obtained by running a verified good implementation.
const int kExpectedSizes[] = {830, 830, 1137, 1137};
@@ -176,17 +166,16 @@
}
// Verify that EqualSize mode is forced if fragmentation info is missing.
-TEST_F(RtpFormatVp8Test, TestEqualSizeModeFallback) {
+TEST_F(RtpPacketizerVp8Test, TestEqualSizeModeFallback) {
const int kSizeVector[] = {10, 10, 10};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID
const int kMaxSize = 12; // Small enough to produce 4 packets.
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize);
+ packetizer.SetPayloadData(
+ helper_->payload_data(), helper_->payload_size(), NULL);
// Expecting three full packets, and one with the remainder.
const int kExpectedSizes[] = {12, 11, 12, 11};
@@ -203,17 +192,16 @@
}
// Verify that non-reference bit is set. EqualSize mode fallback is expected.
-TEST_F(RtpFormatVp8Test, TestNonReferenceBit) {
+TEST_F(RtpPacketizerVp8Test, TestNonReferenceBit) {
const int kSizeVector[] = {10, 10, 10};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
hdr_info_.nonReference = true;
const int kMaxSize = 25; // Small enough to produce two packets.
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize);
+ packetizer.SetPayloadData(
+ helper_->payload_data(), helper_->payload_size(), NULL);
// EqualSize mode => First packet full; other not.
const int kExpectedSizes[] = {16, 16};
@@ -230,7 +218,7 @@
}
// Verify Tl0PicIdx and TID fields, and layerSync bit.
-TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) {
+TEST_F(RtpPacketizerVp8Test, TestTl0PicIdxAndTID) {
const int kSizeVector[] = {10, 10, 10};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
@@ -240,12 +228,10 @@
hdr_info_.layerSync = true;
// kMaxSize is only limited by allocated buffer size.
const int kMaxSize = helper_->buffer_size();
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// Expect one single packet of payload_size() + 4 bytes header.
const int kExpectedSizes[1] = {helper_->payload_size() + 4};
@@ -260,7 +246,7 @@
}
// Verify KeyIdx field.
-TEST_F(RtpFormatVp8Test, TestKeyIdx) {
+TEST_F(RtpPacketizerVp8Test, TestKeyIdx) {
const int kSizeVector[] = {10, 10, 10};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
@@ -268,12 +254,10 @@
hdr_info_.keyIdx = 17;
// kMaxSize is only limited by allocated buffer size.
const int kMaxSize = helper_->buffer_size();
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// Expect one single packet of payload_size() + 3 bytes header.
const int kExpectedSizes[1] = {helper_->payload_size() + 3};
@@ -288,7 +272,7 @@
}
// Verify TID field and KeyIdx field in combination.
-TEST_F(RtpFormatVp8Test, TestTIDAndKeyIdx) {
+TEST_F(RtpPacketizerVp8Test, TestTIDAndKeyIdx) {
const int kSizeVector[] = {10, 10, 10};
const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]);
ASSERT_TRUE(Init(kSizeVector, kNumPartitions));
@@ -297,12 +281,10 @@
hdr_info_.keyIdx = 5;
// kMaxSize is only limited by allocated buffer size.
const int kMaxSize = helper_->buffer_size();
- RtpFormatVp8 packetizer(helper_->payload_data(),
- helper_->payload_size(),
- hdr_info_,
- kMaxSize,
- *(helper_->fragmentation()),
- kAggregate);
+ RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate);
+ packetizer.SetPayloadData(helper_->payload_data(),
+ helper_->payload_size(),
+ helper_->fragmentation());
// Expect one single packet of payload_size() + 3 bytes header.
const int kExpectedSizes[1] = {helper_->payload_size() + 3};
diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.cc b/modules/rtp_rtcp/source/rtp_receiver_video.cc
index c058ed5..5eb5710 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_video.cc
+++ b/modules/rtp_rtcp/source/rtp_receiver_video.cc
@@ -14,6 +14,7 @@
#include <string.h>
#include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
@@ -113,8 +114,13 @@
return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length);
case kRtpVideoVp8:
return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length);
- case kRtpVideoH264:
- assert(false); // Not yet supported.
+ case kRtpVideoH264: {
+ scoped_ptr<RtpDepacketizer> depacketizer(RtpDepacketizer::Create(
+ rtp_header->type.Video.codec, data_callback_));
+ return depacketizer->Parse(rtp_header, payload_data, payload_data_length)
+ ? 0
+ : -1;
+ }
case kRtpVideoNone:
break;
}
diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.h b/modules/rtp_rtcp/source/rtp_receiver_video.h
index 4d81cb3..365e4d7 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_video.h
+++ b/modules/rtp_rtcp/source/rtp_receiver_video.h
@@ -60,7 +60,7 @@
void SetPacketOverHead(uint16_t packet_over_head);
- protected:
+ private:
int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header,
const uint8_t* payload_data,
uint16_t payload_data_length);
@@ -69,10 +69,13 @@
const uint8_t* payload_data,
uint16_t payload_data_length);
+ int32_t ReceiveH264Codec(WebRtcRTPHeader* rtp_header,
+ const uint8_t* payload_data,
+ uint16_t payload_data_length);
+
int32_t BuildRTPheader(const WebRtcRTPHeader* rtp_header,
uint8_t* data_buffer) const;
- private:
int32_t ParseVideoCodecSpecific(
WebRtcRTPHeader* rtp_header,
const uint8_t* payload_data,
diff --git a/modules/rtp_rtcp/source/rtp_rtcp.gypi b/modules/rtp_rtcp/source/rtp_rtcp.gypi
index dcd6598..05741a0 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp.gypi
+++ b/modules/rtp_rtcp/source/rtp_rtcp.gypi
@@ -86,6 +86,10 @@
'rtp_sender_video.cc',
'rtp_sender_video.h',
'video_codec_information.h',
+ 'rtp_format.cc',
+ 'rtp_format.h',
+ 'rtp_format_h264.cc',
+ 'rtp_format_h264.h',
'rtp_format_vp8.cc',
'rtp_format_vp8.h',
'rtp_format_video_generic.h',
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
index c53dd21..95e8f97 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -275,61 +275,63 @@
return 0;
}
-int32_t
-RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
- const FrameType frameType,
- const int8_t payloadType,
- const uint32_t captureTimeStamp,
- int64_t capture_time_ms,
- const uint8_t* payloadData,
- const uint32_t payloadSize,
- const RTPFragmentationHeader* fragmentation,
- VideoCodecInformation* codecInfo,
- const RTPVideoTypeHeader* rtpTypeHdr)
-{
- if( payloadSize == 0)
- {
- return -1;
- }
+int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
+ const FrameType frameType,
+ const int8_t payloadType,
+ const uint32_t captureTimeStamp,
+ int64_t capture_time_ms,
+ const uint8_t* payloadData,
+ const uint32_t payloadSize,
+ const RTPFragmentationHeader* fragmentation,
+ VideoCodecInformation* codecInfo,
+ const RTPVideoTypeHeader* rtpTypeHdr) {
+ if (payloadSize == 0) {
+ return -1;
+ }
- if (frameType == kVideoFrameKey) {
- producer_fec_.SetFecParameters(&key_fec_params_,
- _numberFirstPartition);
- } else {
- producer_fec_.SetFecParameters(&delta_fec_params_,
- _numberFirstPartition);
- }
+ if (frameType == kVideoFrameKey) {
+ producer_fec_.SetFecParameters(&key_fec_params_, _numberFirstPartition);
+ } else {
+ producer_fec_.SetFecParameters(&delta_fec_params_, _numberFirstPartition);
+ }
- // Default setting for number of first partition packets:
- // Will be extracted in SendVP8 for VP8 codec; other codecs use 0
- _numberFirstPartition = 0;
+ // Default setting for number of first partition packets:
+ // Will be extracted in SendVP8 for VP8 codec; other codecs use 0
+ _numberFirstPartition = 0;
- int32_t retVal = -1;
- switch(videoType)
- {
+ switch (videoType) {
case kRtpVideoGeneric:
- retVal = SendGeneric(frameType, payloadType, captureTimeStamp,
- capture_time_ms, payloadData, payloadSize);
- break;
- case kRtpVideoVp8:
- retVal = SendVP8(frameType,
+ return SendGeneric(frameType,
payloadType,
captureTimeStamp,
capture_time_ms,
payloadData,
- payloadSize,
- fragmentation,
- rtpTypeHdr);
- break;
+ payloadSize);
+ case kRtpVideoVp8:
+ return SendVP8(frameType,
+ payloadType,
+ captureTimeStamp,
+ capture_time_ms,
+ payloadData,
+ payloadSize,
+ fragmentation,
+ rtpTypeHdr);
+ case kRtpVideoH264:
+ return SendH264(frameType,
+ payloadType,
+ captureTimeStamp,
+ capture_time_ms,
+ payloadData,
+ payloadSize,
+ fragmentation,
+ rtpTypeHdr)
+ ? 0
+ : -1;
default:
- assert(false);
- break;
- }
- if(retVal <= 0)
- {
- return retVal;
- }
- return 0;
+ assert(false);
+ break;
+ }
+ return 0;
}
int32_t RTPSenderVideo::SendGeneric(const FrameType frame_type,
@@ -427,8 +429,8 @@
assert(rtpTypeHdr);
// Initialize disregarding partition boundaries: this will use kEqualSize
// packetization mode, which produces ~equal size packets for each frame.
- RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8,
- maxPayloadLengthVP8);
+ RtpPacketizerVp8 packetizer(rtpTypeHdr->VP8, maxPayloadLengthVP8);
+ packetizer.SetPayloadData(data, payloadBytesToSend, NULL);
StorageType storage = kAllowRetransmission;
if (rtpTypeHdr->VP8.temporalIdx == 0 &&
@@ -450,22 +452,10 @@
{
// Write VP8 Payload Descriptor and VP8 payload.
uint8_t dataBuffer[IP_PACKET_SIZE] = {0};
- int payloadBytesInPacket = 0;
- int packetStartPartition =
- packetizer.NextPacket(&dataBuffer[rtpHeaderLength],
- &payloadBytesInPacket, &last);
- // TODO(holmer): Temporarily disable first partition packet counting
- // to avoid a bug in ProducerFec which doesn't properly handle
- // important packets.
- // if (packetStartPartition == 0)
- // {
- // ++_numberFirstPartition;
- // }
- // else
- if (packetStartPartition < 0)
- {
- return -1;
- }
+ size_t payloadBytesInPacket = 0;
+ if (!packetizer.NextPacket(
+ &dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last))
+ return -1;
// Write RTP header.
// Set marker bit true if this is the last packet in frame.
@@ -485,6 +475,55 @@
return 0;
}
+bool RTPSenderVideo::SendH264(const FrameType frameType,
+ const int8_t payloadType,
+ const uint32_t captureTimeStamp,
+ int64_t capture_time_ms,
+ const uint8_t* payloadData,
+ const uint32_t payloadSize,
+ const RTPFragmentationHeader* fragmentation,
+ const RTPVideoTypeHeader* rtpTypeHdr) {
+ size_t rtp_header_length = _rtpSender.RTPHeaderLength();
+ int32_t payload_bytes_to_send = payloadSize;
+ const uint8_t* data = payloadData;
+ size_t max_payload_length = _rtpSender.MaxDataPayloadLength();
+
+ scoped_ptr<RtpPacketizer> packetizer(
+ RtpPacketizer::Create(kRtpVideoH264, max_payload_length));
+ packetizer->SetPayloadData(data, payload_bytes_to_send, fragmentation);
+
+ StorageType storage = kAllowRetransmission;
+ bool protect = (frameType == kVideoFrameKey);
+ bool last = false;
+
+ while (!last) {
+ // Write H264 payload.
+ uint8_t dataBuffer[IP_PACKET_SIZE] = {0};
+ size_t payload_bytes_in_packet = 0;
+ if (!packetizer->NextPacket(
+ &dataBuffer[rtp_header_length], &payload_bytes_in_packet, &last)) {
+ return false;
+ }
+
+ // Write RTP header.
+ // Set marker bit true if this is the last packet in frame.
+ _rtpSender.BuildRTPheader(
+ dataBuffer, payloadType, last, captureTimeStamp, capture_time_ms);
+ if (SendVideoPacket(dataBuffer,
+ payload_bytes_in_packet,
+ rtp_header_length,
+ captureTimeStamp,
+ capture_time_ms,
+ storage,
+ protect)) {
+ LOG(LS_WARNING)
+ << "RTPSenderVideo::SendH264 failed to send packet number "
+ << _rtpSender.SequenceNumber();
+ }
+ }
+ return true;
+}
+
void RTPSenderVideo::ProcessBitrate() {
_videoBitrate.Process();
_fecOverheadRate.Process();
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h
index 82bd1de..77b91c9 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.h
+++ b/modules/rtp_rtcp/source/rtp_sender_video.h
@@ -110,6 +110,15 @@
const RTPFragmentationHeader* fragmentation,
const RTPVideoTypeHeader* rtpTypeHdr);
+ bool SendH264(const FrameType frameType,
+ const int8_t payloadType,
+ const uint32_t captureTimeStamp,
+ int64_t capture_time_ms,
+ const uint8_t* payloadData,
+ const uint32_t payloadSize,
+ const RTPFragmentationHeader* fragmentation,
+ const RTPVideoTypeHeader* rtpTypeHdr);
+
private:
RTPSenderInterface& _rtpSender;
diff --git a/modules/rtp_rtcp/source/rtp_utility_unittest.cc b/modules/rtp_rtcp/source/rtp_utility_unittest.cc
index 27170d5..82ec953 100644
--- a/modules/rtp_rtcp/source/rtp_utility_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_utility_unittest.cc
@@ -251,10 +251,11 @@
inputHeader.layerSync = false;
inputHeader.tl0PicIdx = kNoTl0PicIdx; // Disable.
inputHeader.keyIdx = 31;
- RtpFormatVp8 packetizer(payload, 10, inputHeader, 20);
+ RtpPacketizerVp8 packetizer(inputHeader, 20);
+ packetizer.SetPayloadData(payload, 10, NULL);
bool last;
- int send_bytes;
- ASSERT_EQ(0, packetizer.NextPacket(packet, &send_bytes, &last));
+ size_t send_bytes;
+ ASSERT_TRUE(packetizer.NextPacket(packet, &send_bytes, &last));
ASSERT_TRUE(last);
RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, packet, send_bytes);
diff --git a/modules/video_coding/codecs/interface/video_codec_interface.h b/modules/video_coding/codecs/interface/video_codec_interface.h
index 9776479..e6b1d09 100644
--- a/modules/video_coding/codecs/interface/video_codec_interface.h
+++ b/modules/video_coding/codecs/interface/video_codec_interface.h
@@ -28,29 +28,30 @@
// Note: if any pointers are added to this struct, it must be fitted
// with a copy-constructor. See below.
-struct CodecSpecificInfoVP8
-{
- bool hasReceivedSLI;
- uint8_t pictureIdSLI;
- bool hasReceivedRPSI;
- uint64_t pictureIdRPSI;
- int16_t pictureId; // negative value to skip pictureId
- bool nonReference;
- uint8_t simulcastIdx;
- uint8_t temporalIdx;
- bool layerSync;
- int tl0PicIdx; // Negative value to skip tl0PicIdx
- int8_t keyIdx; // negative value to skip keyIdx
+struct CodecSpecificInfoVP8 {
+ bool hasReceivedSLI;
+ uint8_t pictureIdSLI;
+ bool hasReceivedRPSI;
+ uint64_t pictureIdRPSI;
+ int16_t pictureId; // Negative value to skip pictureId.
+ bool nonReference;
+ uint8_t simulcastIdx;
+ uint8_t temporalIdx;
+ bool layerSync;
+ int tl0PicIdx; // Negative value to skip tl0PicIdx.
+ int8_t keyIdx; // Negative value to skip keyIdx.
};
struct CodecSpecificInfoGeneric {
uint8_t simulcast_idx;
};
-union CodecSpecificInfoUnion
-{
- CodecSpecificInfoGeneric generic;
- CodecSpecificInfoVP8 VP8;
+struct CodecSpecificInfoH264 {};
+
+union CodecSpecificInfoUnion {
+ CodecSpecificInfoGeneric generic;
+ CodecSpecificInfoVP8 VP8;
+ CodecSpecificInfoH264 H264;
};
// Note: if any pointers are added to this struct or its sub-structs, it
diff --git a/modules/video_coding/main/source/encoded_frame.cc b/modules/video_coding/main/source/encoded_frame.cc
index 3ccf0b0..0d07955 100644
--- a/modules/video_coding/main/source/encoded_frame.cc
+++ b/modules/video_coding/main/source/encoded_frame.cc
@@ -100,49 +100,45 @@
void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header)
{
- if (header)
- {
- switch (header->codec)
- {
- case kRtpVideoVp8:
- {
- if (_codecSpecificInfo.codecType != kVideoCodecVP8)
- {
- // This is the first packet for this frame.
- _codecSpecificInfo.codecSpecific.VP8.pictureId = -1;
- _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0;
- _codecSpecificInfo.codecSpecific.VP8.layerSync = false;
- _codecSpecificInfo.codecSpecific.VP8.keyIdx = -1;
- _codecSpecificInfo.codecType = kVideoCodecVP8;
- }
- _codecSpecificInfo.codecSpecific.VP8.nonReference =
- header->codecHeader.VP8.nonReference;
- if (header->codecHeader.VP8.pictureId != kNoPictureId)
- {
- _codecSpecificInfo.codecSpecific.VP8.pictureId =
- header->codecHeader.VP8.pictureId;
- }
- if (header->codecHeader.VP8.temporalIdx != kNoTemporalIdx)
- {
- _codecSpecificInfo.codecSpecific.VP8.temporalIdx =
- header->codecHeader.VP8.temporalIdx;
- _codecSpecificInfo.codecSpecific.VP8.layerSync =
- header->codecHeader.VP8.layerSync;
- }
- if (header->codecHeader.VP8.keyIdx != kNoKeyIdx)
- {
- _codecSpecificInfo.codecSpecific.VP8.keyIdx =
- header->codecHeader.VP8.keyIdx;
- }
- break;
- }
- default:
- {
- _codecSpecificInfo.codecType = kVideoCodecUnknown;
- break;
- }
+ if (header) {
+ switch (header->codec) {
+ case kRtpVideoVp8: {
+ if (_codecSpecificInfo.codecType != kVideoCodecVP8) {
+ // This is the first packet for this frame.
+ _codecSpecificInfo.codecSpecific.VP8.pictureId = -1;
+ _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0;
+ _codecSpecificInfo.codecSpecific.VP8.layerSync = false;
+ _codecSpecificInfo.codecSpecific.VP8.keyIdx = -1;
+ _codecSpecificInfo.codecType = kVideoCodecVP8;
}
+ _codecSpecificInfo.codecSpecific.VP8.nonReference =
+ header->codecHeader.VP8.nonReference;
+ if (header->codecHeader.VP8.pictureId != kNoPictureId) {
+ _codecSpecificInfo.codecSpecific.VP8.pictureId =
+ header->codecHeader.VP8.pictureId;
+ }
+ if (header->codecHeader.VP8.temporalIdx != kNoTemporalIdx) {
+ _codecSpecificInfo.codecSpecific.VP8.temporalIdx =
+ header->codecHeader.VP8.temporalIdx;
+ _codecSpecificInfo.codecSpecific.VP8.layerSync =
+ header->codecHeader.VP8.layerSync;
+ }
+ if (header->codecHeader.VP8.keyIdx != kNoKeyIdx) {
+ _codecSpecificInfo.codecSpecific.VP8.keyIdx =
+ header->codecHeader.VP8.keyIdx;
+ }
+ break;
+ }
+ case kRtpVideoH264: {
+ _codecSpecificInfo.codecType = kVideoCodecH264;
+ break;
+ }
+ default: {
+ _codecSpecificInfo.codecType = kVideoCodecUnknown;
+ break;
+ }
}
+ }
}
const RTPFragmentationHeader* VCMEncodedFrame::FragmentationHeader() const {
diff --git a/modules/video_coding/main/source/generic_encoder.cc b/modules/video_coding/main/source/generic_encoder.cc
index 6fb2c9f..655f7ac 100644
--- a/modules/video_coding/main/source/generic_encoder.cc
+++ b/modules/video_coding/main/source/generic_encoder.cc
@@ -38,6 +38,9 @@
(*rtp)->simulcastIdx = info->codecSpecific.VP8.simulcastIdx;
return;
}
+ case kVideoCodecH264:
+ (*rtp)->codec = kRtpVideoH264;
+ return;
case kVideoCodecGeneric:
(*rtp)->codec = kRtpVideoGeneric;
(*rtp)->simulcastIdx = info->codecSpecific.generic.simulcast_idx;
diff --git a/modules/video_coding/main/source/jitter_buffer.cc b/modules/video_coding/main/source/jitter_buffer.cc
index d8792f2..9aa3409 100644
--- a/modules/video_coding/main/source/jitter_buffer.cc
+++ b/modules/video_coding/main/source/jitter_buffer.cc
@@ -482,7 +482,6 @@
VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode(uint32_t timestamp) {
CriticalSectionScoped cs(crit_sect_);
-
if (!running_) {
return NULL;
}
@@ -611,7 +610,6 @@
if (error != kNoError && frame == NULL) {
return error;
}
-
int64_t now_ms = clock_->TimeInMilliseconds();
// We are keeping track of the first and latest seq numbers, and
// the number of wraps to be able to calculate how many packets we expect.
diff --git a/modules/video_coding/main/source/packet.cc b/modules/video_coding/main/source/packet.cc
index c1f1a04..63dcd63 100644
--- a/modules/video_coding/main/source/packet.cc
+++ b/modules/video_coding/main/source/packet.cc
@@ -94,33 +94,44 @@
memset(&codecSpecificHeader, 0, sizeof(RTPVideoHeader));
}
-void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader)
-{
- switch(videoHeader.codec)
- {
- case kRtpVideoVp8:
- {
- // Handle all packets within a frame as depending on the previous packet
- // TODO(holmer): This should be changed to make fragments independent
- // when the VP8 RTP receiver supports fragments.
- if (isFirstPacket && markerBit)
- completeNALU = kNaluComplete;
- else if (isFirstPacket)
- completeNALU = kNaluStart;
- else if (markerBit)
- completeNALU = kNaluEnd;
- else
- completeNALU = kNaluIncomplete;
+void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) {
+ switch (videoHeader.codec) {
+ case kRtpVideoVp8:
+ // Handle all packets within a frame as depending on the previous packet
+ // TODO(holmer): This should be changed to make fragments independent
+ // when the VP8 RTP receiver supports fragments.
+ if (isFirstPacket && markerBit)
+ completeNALU = kNaluComplete;
+ else if (isFirstPacket)
+ completeNALU = kNaluStart;
+ else if (markerBit)
+ completeNALU = kNaluEnd;
+ else
+ completeNALU = kNaluIncomplete;
- codec = kVideoCodecVP8;
- break;
- }
- default:
- {
- codec = kVideoCodecUnknown;
- break;
- }
- }
+ codec = kVideoCodecVP8;
+ return;
+ case kRtpVideoH264:
+ isFirstPacket = videoHeader.isFirstPacket;
+ if (isFirstPacket)
+ insertStartCode = true;
+
+ if (videoHeader.codecHeader.H264.single_nalu) {
+ completeNALU = kNaluComplete;
+ } else if (isFirstPacket) {
+ completeNALU = kNaluStart;
+ } else if (markerBit) {
+ completeNALU = kNaluEnd;
+ } else {
+ completeNALU = kNaluIncomplete;
+ }
+ codec = kVideoCodecH264;
+ return;
+ case kRtpVideoGeneric:
+ case kRtpVideoNone:
+ codec = kVideoCodecUnknown;
+ return;
+ }
}
-}
+} // namespace webrtc
diff --git a/modules/video_coding/main/source/session_info.cc b/modules/video_coding/main/source/session_info.cc
index dab3da1..b50a01a 100644
--- a/modules/video_coding/main/source/session_info.cc
+++ b/modules/video_coding/main/source/session_info.cc
@@ -14,7 +14,7 @@
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
-
+namespace {
// Used in determining whether a frame is decodable.
enum {kRttThreshold = 100}; // Not decodable if Rtt is lower than this.
@@ -23,6 +23,11 @@
static const float kLowPacketPercentageThreshold = 0.2f;
static const float kHighPacketPercentageThreshold = 0.8f;
+uint16_t BufferToUWord16(const uint8_t* dataBuffer) {
+ return (dataBuffer[0] << 8) | dataBuffer[1];
+}
+} // namespace
+
VCMSessionInfo::VCMSessionInfo()
: session_nack_(false),
complete_(false),
@@ -121,9 +126,6 @@
VCMPacket& packet = *packet_it;
PacketIterator it;
- int packet_size = packet.sizeBytes;
- packet_size += (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
-
// Calculate the offset into the frame buffer for this packet.
int offset = 0;
for (it = packets_.begin(); it != packet_it; ++it)
@@ -131,23 +133,63 @@
// Set the data pointer to pointing to the start of this packet in the
// frame buffer.
- const uint8_t* data = packet.dataPtr;
+ const uint8_t* packet_buffer = packet.dataPtr;
packet.dataPtr = frame_buffer + offset;
- packet.sizeBytes = packet_size;
- ShiftSubsequentPackets(packet_it, packet_size);
-
- const unsigned char startCode[] = {0, 0, 0, 1};
- if (packet.insertStartCode) {
- memcpy(const_cast<uint8_t*>(packet.dataPtr), startCode,
- kH264StartCodeLengthBytes);
+ // We handle H.264 STAP-A packets in a special way as we need to remove the
+ // two length bytes between each NAL unit, and potentially add start codes.
+ const size_t kH264NALHeaderLengthInBytes = 1;
+ const size_t kLengthFieldLength = 2;
+ if (packet.codecSpecificHeader.codecHeader.H264.stap_a) {
+ size_t required_length = 0;
+ const uint8_t* nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes;
+ while (nalu_ptr < packet_buffer + packet.sizeBytes) {
+ uint32_t length = BufferToUWord16(nalu_ptr);
+ required_length +=
+ length + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
+ nalu_ptr += kLengthFieldLength + length;
+ }
+ ShiftSubsequentPackets(packet_it, required_length);
+ nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes;
+ uint8_t* frame_buffer_ptr = frame_buffer + offset;
+ while (nalu_ptr < packet_buffer + packet.sizeBytes) {
+ uint32_t length = BufferToUWord16(nalu_ptr);
+ nalu_ptr += kLengthFieldLength;
+ frame_buffer_ptr += Insert(nalu_ptr,
+ length,
+ packet.insertStartCode,
+ const_cast<uint8_t*>(frame_buffer_ptr));
+ nalu_ptr += length;
+ }
+ packet.sizeBytes = required_length;
+ return packet.sizeBytes;
}
- memcpy(const_cast<uint8_t*>(packet.dataPtr
- + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0)),
- data,
- packet.sizeBytes);
+ ShiftSubsequentPackets(
+ packet_it,
+ packet.sizeBytes +
+ (packet.insertStartCode ? kH264StartCodeLengthBytes : 0));
- return packet_size;
+ packet.sizeBytes = Insert(packet_buffer,
+ packet.sizeBytes,
+ packet.insertStartCode,
+ const_cast<uint8_t*>(packet.dataPtr));
+ return packet.sizeBytes;
+}
+
+size_t VCMSessionInfo::Insert(const uint8_t* buffer,
+ size_t length,
+ bool insert_start_code,
+ uint8_t* frame_buffer) {
+ if (insert_start_code) {
+ const unsigned char startCode[] = {0, 0, 0, 1};
+ memcpy(frame_buffer, startCode, kH264StartCodeLengthBytes);
+ }
+ memcpy(frame_buffer + (insert_start_code ? kH264StartCodeLengthBytes : 0),
+ buffer,
+ length);
+ length += (insert_start_code ? kH264StartCodeLengthBytes : 0);
+
+ return length;
}
void VCMSessionInfo::ShiftSubsequentPackets(PacketIterator it,
@@ -420,34 +462,49 @@
(*rit).seqNum == packet.seqNum && (*rit).sizeBytes > 0)
return -2;
- // Only insert media packets between first and last packets (when available).
- // Placing check here, as to properly account for duplicate packets.
- // Check if this is first packet (only valid for some codecs)
- // Should only be set for one packet per session.
- if (packet.isFirstPacket && first_packet_seq_num_ == -1) {
- // The first packet in a frame signals the frame type.
+ if (packet.codec == kVideoCodecH264) {
frame_type_ = packet.frameType;
- // Store the sequence number for the first packet.
- first_packet_seq_num_ = static_cast<int>(packet.seqNum);
- } else if (first_packet_seq_num_ != -1 &&
- !IsNewerSequenceNumber(packet.seqNum, first_packet_seq_num_)) {
- LOG(LS_WARNING) << "Received packet with a sequence number which is out of"
- "frame boundaries";
- return -3;
- } else if (frame_type_ == kFrameEmpty && packet.frameType != kFrameEmpty) {
- // Update the frame type with the type of the first media packet.
- // TODO(mikhal): Can this trigger?
- frame_type_ = packet.frameType;
- }
+ if (packet.isFirstPacket &&
+ (first_packet_seq_num_ == -1 ||
+ IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum))) {
+ first_packet_seq_num_ = packet.seqNum;
+ }
+ if (packet.markerBit &&
+ (last_packet_seq_num_ == -1 ||
+ IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_))) {
+ last_packet_seq_num_ = packet.seqNum;
+ }
+ } else {
+ // Only insert media packets between first and last packets (when
+ // available).
+ // Placing check here, as to properly account for duplicate packets.
+ // Check if this is first packet (only valid for some codecs)
+ // Should only be set for one packet per session.
+ if (packet.isFirstPacket && first_packet_seq_num_ == -1) {
+ // The first packet in a frame signals the frame type.
+ frame_type_ = packet.frameType;
+ // Store the sequence number for the first packet.
+ first_packet_seq_num_ = static_cast<int>(packet.seqNum);
+ } else if (first_packet_seq_num_ != -1 &&
+ !IsNewerSequenceNumber(packet.seqNum, first_packet_seq_num_)) {
+ LOG(LS_WARNING) << "Received packet with a sequence number which is out "
+ "of frame boundaries";
+ return -3;
+ } else if (frame_type_ == kFrameEmpty && packet.frameType != kFrameEmpty) {
+ // Update the frame type with the type of the first media packet.
+ // TODO(mikhal): Can this trigger?
+ frame_type_ = packet.frameType;
+ }
- // Track the marker bit, should only be set for one packet per session.
- if (packet.markerBit && last_packet_seq_num_ == -1) {
- last_packet_seq_num_ = static_cast<int>(packet.seqNum);
- } else if (last_packet_seq_num_ != -1 &&
- IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) {
- LOG(LS_WARNING) << "Received packet with a sequence number which is out of"
- "frame boundaries";
- return -3;
+ // Track the marker bit, should only be set for one packet per session.
+ if (packet.markerBit && last_packet_seq_num_ == -1) {
+ last_packet_seq_num_ = static_cast<int>(packet.seqNum);
+ } else if (last_packet_seq_num_ != -1 &&
+ IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) {
+ LOG(LS_WARNING) << "Received packet with a sequence number which is out "
+ "of frame boundaries";
+ return -3;
+ }
}
// The insert operation invalidates the iterator |rit|.
diff --git a/modules/video_coding/main/source/session_info.h b/modules/video_coding/main/source/session_info.h
index cae3ee1..25216c7 100644
--- a/modules/video_coding/main/source/session_info.h
+++ b/modules/video_coding/main/source/session_info.h
@@ -116,6 +116,10 @@
const PacketIterator& prev_it);
int InsertBuffer(uint8_t* frame_buffer,
PacketIterator packetIterator);
+ size_t Insert(const uint8_t* buffer,
+ size_t length,
+ bool insert_start_code,
+ uint8_t* frame_buffer);
void ShiftSubsequentPackets(PacketIterator it, int steps_to_shift);
PacketIterator FindNaluEnd(PacketIterator packet_iter) const;
// Deletes the data of all packets between |start| and |end|, inclusively.