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@6804 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
		| @@ -61,7 +61,7 @@ struct RTPVideoHeaderVP8 { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| struct RTPVideoHeaderH264 { | struct RTPVideoHeaderH264 { | ||||||
|   uint8_t nalu_header; |   bool stap_a; | ||||||
|   bool single_nalu; |   bool single_nalu; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -206,6 +206,7 @@ | |||||||
|             'rtp_rtcp/source/rtcp_receiver_unittest.cc', |             'rtp_rtcp/source/rtcp_receiver_unittest.cc', | ||||||
|             'rtp_rtcp/source/rtcp_sender_unittest.cc', |             'rtp_rtcp/source/rtcp_sender_unittest.cc', | ||||||
|             'rtp_rtcp/source/rtp_fec_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_unittest.cc', | ||||||
|             'rtp_rtcp/source/rtp_format_vp8_test_helper.cc', |             'rtp_rtcp/source/rtp_format_vp8_test_helper.cc', | ||||||
|             'rtp_rtcp/source/rtp_format_vp8_test_helper.h', |             'rtp_rtcp/source/rtp_format_vp8_test_helper.h', | ||||||
|   | |||||||
| @@ -79,6 +79,10 @@ source_set("rtp_rtcp") { | |||||||
|     "source/rtp_sender_video.cc", |     "source/rtp_sender_video.cc", | ||||||
|     "source/rtp_sender_video.h", |     "source/rtp_sender_video.h", | ||||||
|     "source/video_codec_information.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.cc", | ||||||
|     "source/rtp_format_vp8.h", |     "source/rtp_format_vp8.h", | ||||||
|     "source/rtp_format_video_generic.h", |     "source/rtp_format_video_generic.h", | ||||||
|   | |||||||
| @@ -19,6 +19,17 @@ | |||||||
|  |  | ||||||
| namespace webrtc { | 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 { | class MockRtpRtcp : public RtpRtcp { | ||||||
|  public: |  public: | ||||||
|   MOCK_METHOD1(ChangeUniqueId, |   MOCK_METHOD1(ChangeUniqueId, | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
| #include "testing/gmock/include/gmock/gmock.h" | #include "testing/gmock/include/gmock/gmock.h" | ||||||
| #include "testing/gtest/include/gtest/gtest.h" | #include "testing/gtest/include/gtest/gtest.h" | ||||||
| #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.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/fec_test_helper.h" | ||||||
| #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" | #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h" | ||||||
|  |  | ||||||
| @@ -25,17 +26,6 @@ using ::testing::Return; | |||||||
|  |  | ||||||
| namespace webrtc { | 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 { | class ReceiverFecTest : public ::testing::Test { | ||||||
|  protected: |  protected: | ||||||
|   virtual void SetUp() { |   virtual void SetUp() { | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										54
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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_ | ||||||
							
								
								
									
										293
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format_h264.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
							
								
								
									
										102
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format_h264.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format_h264.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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_ | ||||||
							
								
								
									
										412
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										412
									
								
								webrtc/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -21,56 +21,64 @@ namespace webrtc { | |||||||
|  |  | ||||||
| // Define how the VP8PacketizerModes are implemented. | // Define how the VP8PacketizerModes are implemented. | ||||||
| // Modes are: kStrict, kAggregate, kEqualSize. | // Modes are: kStrict, kAggregate, kEqualSize. | ||||||
| const RtpFormatVp8::AggregationMode RtpFormatVp8::aggr_modes_[kNumModes] = | const RtpPacketizerVp8::AggregationMode RtpPacketizerVp8::aggr_modes_ | ||||||
|     { kAggrNone, kAggrPartitions, kAggrFragments }; |     [kNumModes] = {kAggrNone, kAggrPartitions, kAggrFragments}; | ||||||
| const bool RtpFormatVp8::balance_modes_[kNumModes] = | const bool RtpPacketizerVp8::balance_modes_[kNumModes] = {true, true, true}; | ||||||
|     { true, true, true }; | const bool RtpPacketizerVp8::separate_first_modes_[kNumModes] = {true, false, | ||||||
| const bool RtpFormatVp8::separate_first_modes_[kNumModes] = |                                                                  false}; | ||||||
|     { true, false, false }; |  | ||||||
|  |  | ||||||
| RtpFormatVp8::RtpFormatVp8(const uint8_t* payload_data, | RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, | ||||||
|                            uint32_t payload_size, |  | ||||||
|                            const RTPVideoHeaderVP8& hdr_info, |  | ||||||
|                                    int max_payload_len, |                                    int max_payload_len, | ||||||
|                            const RTPFragmentationHeader& fragmentation, |  | ||||||
|                                    VP8PacketizerMode mode) |                                    VP8PacketizerMode mode) | ||||||
|     : payload_data_(payload_data), |     : payload_data_(NULL), | ||||||
|       payload_size_(static_cast<int>(payload_size)), |       payload_size_(0), | ||||||
|       vp8_fixed_payload_descriptor_bytes_(1), |       vp8_fixed_payload_descriptor_bytes_(1), | ||||||
|       aggr_mode_(aggr_modes_[mode]), |       aggr_mode_(aggr_modes_[mode]), | ||||||
|       balance_(balance_modes_[mode]), |       balance_(balance_modes_[mode]), | ||||||
|       separate_first_(separate_first_modes_[mode]), |       separate_first_(separate_first_modes_[mode]), | ||||||
|       hdr_info_(hdr_info), |       hdr_info_(hdr_info), | ||||||
|       num_partitions_(fragmentation.fragmentationVectorSize), |       num_partitions_(0), | ||||||
|       max_payload_len_(max_payload_len), |       max_payload_len_(max_payload_len), | ||||||
|       packets_calculated_(false) { |       packets_calculated_(false) { | ||||||
|   part_info_.CopyFrom(fragmentation); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| RtpFormatVp8::RtpFormatVp8(const uint8_t* payload_data, | RtpPacketizerVp8::RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, | ||||||
|                            uint32_t payload_size, |  | ||||||
|                            const RTPVideoHeaderVP8& hdr_info, |  | ||||||
|                                    int max_payload_len) |                                    int max_payload_len) | ||||||
|     : payload_data_(payload_data), |     : payload_data_(NULL), | ||||||
|       payload_size_(static_cast<int>(payload_size)), |       payload_size_(0), | ||||||
|       part_info_(), |       part_info_(), | ||||||
|       vp8_fixed_payload_descriptor_bytes_(1), |       vp8_fixed_payload_descriptor_bytes_(1), | ||||||
|       aggr_mode_(aggr_modes_[kEqualSize]), |       aggr_mode_(aggr_modes_[kEqualSize]), | ||||||
|       balance_(balance_modes_[kEqualSize]), |       balance_(balance_modes_[kEqualSize]), | ||||||
|       separate_first_(separate_first_modes_[kEqualSize]), |       separate_first_(separate_first_modes_[kEqualSize]), | ||||||
|       hdr_info_(hdr_info), |       hdr_info_(hdr_info), | ||||||
|       num_partitions_(1), |       num_partitions_(0), | ||||||
|       max_payload_len_(max_payload_len), |       max_payload_len_(max_payload_len), | ||||||
|       packets_calculated_(false) { |       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_.VerifyAndAllocateFragmentationHeader(1); | ||||||
|     part_info_.fragmentationLength[0] = payload_size; |     part_info_.fragmentationLength[0] = payload_size; | ||||||
|     part_info_.fragmentationOffset[0] = 0; |     part_info_.fragmentationOffset[0] = 0; | ||||||
|  |     num_partitions_ = part_info_.fragmentationVectorSize; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| RtpFormatVp8::~RtpFormatVp8() {} | bool RtpPacketizerVp8::NextPacket(uint8_t* buffer, | ||||||
|  |                                   size_t* bytes_to_send, | ||||||
| int RtpFormatVp8::NextPacket(uint8_t* buffer, |  | ||||||
|                              int* bytes_to_send, |  | ||||||
|                                   bool* last_packet) { |                                   bool* last_packet) { | ||||||
|   if (!packets_calculated_) { |   if (!packets_calculated_) { | ||||||
|     int ret = 0; |     int ret = 0; | ||||||
| @@ -80,25 +88,27 @@ int RtpFormatVp8::NextPacket(uint8_t* buffer, | |||||||
|       ret = GeneratePackets(); |       ret = GeneratePackets(); | ||||||
|     } |     } | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|       return ret; |       return false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   if (packets_.empty()) { |   if (packets_.empty()) { | ||||||
|     return -1; |     return false; | ||||||
|   } |   } | ||||||
|   InfoStruct packet_info = packets_.front(); |   InfoStruct packet_info = packets_.front(); | ||||||
|   packets_.pop(); |   packets_.pop(); | ||||||
|  |  | ||||||
|   *bytes_to_send = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_); |   int bytes = WriteHeaderAndPayload(packet_info, buffer, max_payload_len_); | ||||||
|   if (*bytes_to_send < 0) { |   if (bytes < 0) { | ||||||
|     return -1; |     return false; | ||||||
|   } |   } | ||||||
|  |   *bytes_to_send = bytes; | ||||||
|  |  | ||||||
|   *last_packet = packets_.empty(); |   *last_packet = packets_.empty(); | ||||||
|   return packet_info.first_partition_ix; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes, | int RtpPacketizerVp8::CalcNextSize(int max_payload_len, | ||||||
|  |                                    int remaining_bytes, | ||||||
|                                    bool split_payload) const { |                                    bool split_payload) const { | ||||||
|   if (max_payload_len == 0 || remaining_bytes == 0) { |   if (max_payload_len == 0 || remaining_bytes == 0) { | ||||||
|     return 0; |     return 0; | ||||||
| @@ -121,7 +131,7 @@ int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes, | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::GeneratePackets() { | int RtpPacketizerVp8::GeneratePackets() { | ||||||
|   if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ |   if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ | ||||||
|       + PayloadDescriptorExtraLength() + 1) { |       + PayloadDescriptorExtraLength() + 1) { | ||||||
|     // The provided payload length is not long enough for the payload |     // The provided payload length is not long enough for the payload | ||||||
| @@ -182,7 +192,7 @@ int RtpFormatVp8::GeneratePackets() { | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::GeneratePacketsBalancedAggregates() { | int RtpPacketizerVp8::GeneratePacketsBalancedAggregates() { | ||||||
|   if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ |   if (max_payload_len_ < vp8_fixed_payload_descriptor_bytes_ | ||||||
|       + PayloadDescriptorExtraLength() + 1) { |       + PayloadDescriptorExtraLength() + 1) { | ||||||
|     // The provided payload length is not long enough for the payload |     // The provided payload length is not long enough for the payload | ||||||
| @@ -241,7 +251,7 @@ int RtpFormatVp8::GeneratePacketsBalancedAggregates() { | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void RtpFormatVp8::AggregateSmallPartitions(std::vector<int>* partition_vec, | void RtpPacketizerVp8::AggregateSmallPartitions(std::vector<int>* partition_vec, | ||||||
|                                                 int* min_size, |                                                 int* min_size, | ||||||
|                                                 int* max_size) { |                                                 int* max_size) { | ||||||
|   assert(min_size && max_size); |   assert(min_size && max_size); | ||||||
| @@ -285,7 +295,7 @@ void RtpFormatVp8::AggregateSmallPartitions(std::vector<int>* partition_vec, | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void RtpFormatVp8::QueuePacket(int start_pos, | void RtpPacketizerVp8::QueuePacket(int start_pos, | ||||||
|                                    int packet_size, |                                    int packet_size, | ||||||
|                                    int first_partition_in_packet, |                                    int first_partition_in_packet, | ||||||
|                                    bool start_on_new_fragment) { |                                    bool start_on_new_fragment) { | ||||||
| @@ -298,7 +308,7 @@ void RtpFormatVp8::QueuePacket(int start_pos, | |||||||
|   packets_.push(packet_info); |   packets_.push(packet_info); | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::WriteHeaderAndPayload(const InfoStruct& packet_info, | int RtpPacketizerVp8::WriteHeaderAndPayload(const InfoStruct& packet_info, | ||||||
|                                             uint8_t* buffer, |                                             uint8_t* buffer, | ||||||
|                                             int buffer_length) const { |                                             int buffer_length) const { | ||||||
|   // Write the VP8 payload descriptor. |   // Write the VP8 payload descriptor. | ||||||
| @@ -333,7 +343,7 @@ int RtpFormatVp8::WriteHeaderAndPayload(const InfoStruct& packet_info, | |||||||
|       + extension_length; |       + extension_length; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::WriteExtensionFields(uint8_t* buffer, | int RtpPacketizerVp8::WriteExtensionFields(uint8_t* buffer, | ||||||
|                                            int buffer_length) const { |                                            int buffer_length) const { | ||||||
|   int extension_length = 0; |   int extension_length = 0; | ||||||
|   if (XFieldPresent()) { |   if (XFieldPresent()) { | ||||||
| @@ -363,7 +373,7 @@ int RtpFormatVp8::WriteExtensionFields(uint8_t* buffer, | |||||||
|   return extension_length; |   return extension_length; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::WritePictureIDFields(uint8_t* x_field, | int RtpPacketizerVp8::WritePictureIDFields(uint8_t* x_field, | ||||||
|                                            uint8_t* buffer, |                                            uint8_t* buffer, | ||||||
|                                            int buffer_length, |                                            int buffer_length, | ||||||
|                                            int* extension_length) const { |                                            int* extension_length) const { | ||||||
| @@ -377,8 +387,7 @@ int RtpFormatVp8::WritePictureIDFields(uint8_t* x_field, | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::WritePictureID(uint8_t* buffer, | int RtpPacketizerVp8::WritePictureID(uint8_t* buffer, int buffer_length) const { | ||||||
|                                  int buffer_length) const { |  | ||||||
|   const uint16_t pic_id = |   const uint16_t pic_id = | ||||||
|       static_cast<uint16_t> (hdr_info_.pictureId); |       static_cast<uint16_t> (hdr_info_.pictureId); | ||||||
|   int picture_id_len = PictureIdLength(); |   int picture_id_len = PictureIdLength(); | ||||||
| @@ -392,7 +401,7 @@ int RtpFormatVp8::WritePictureID(uint8_t* buffer, | |||||||
|   return picture_id_len; |   return picture_id_len; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::WriteTl0PicIdxFields(uint8_t* x_field, | int RtpPacketizerVp8::WriteTl0PicIdxFields(uint8_t* x_field, | ||||||
|                                            uint8_t* buffer, |                                            uint8_t* buffer, | ||||||
|                                            int buffer_length, |                                            int buffer_length, | ||||||
|                                            int* extension_length) const { |                                            int* extension_length) const { | ||||||
| @@ -407,7 +416,7 @@ int RtpFormatVp8::WriteTl0PicIdxFields(uint8_t* x_field, | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field, | int RtpPacketizerVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field, | ||||||
|                                               uint8_t* buffer, |                                               uint8_t* buffer, | ||||||
|                                               int buffer_length, |                                               int buffer_length, | ||||||
|                                               int* extension_length) const { |                                               int* extension_length) const { | ||||||
| @@ -432,7 +441,7 @@ int RtpFormatVp8::WriteTIDAndKeyIdxFields(uint8_t* x_field, | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::PayloadDescriptorExtraLength() const { | int RtpPacketizerVp8::PayloadDescriptorExtraLength() const { | ||||||
|   int length_bytes = PictureIdLength(); |   int length_bytes = PictureIdLength(); | ||||||
|   if (TL0PicIdxFieldPresent()) ++length_bytes; |   if (TL0PicIdxFieldPresent()) ++length_bytes; | ||||||
|   if (TIDFieldPresent() || KeyIdxFieldPresent()) ++length_bytes; |   if (TIDFieldPresent() || KeyIdxFieldPresent()) ++length_bytes; | ||||||
| @@ -440,7 +449,7 @@ int RtpFormatVp8::PayloadDescriptorExtraLength() const { | |||||||
|   return length_bytes; |   return length_bytes; | ||||||
| } | } | ||||||
|  |  | ||||||
| int RtpFormatVp8::PictureIdLength() const { | int RtpPacketizerVp8::PictureIdLength() const { | ||||||
|   if (hdr_info_.pictureId == kNoPictureId) { |   if (hdr_info_.pictureId == kNoPictureId) { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
| @@ -450,22 +459,22 @@ int RtpFormatVp8::PictureIdLength() const { | |||||||
|   return 2; |   return 2; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool RtpFormatVp8::XFieldPresent() const { | bool RtpPacketizerVp8::XFieldPresent() const { | ||||||
|   return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent() |   return (TIDFieldPresent() || TL0PicIdxFieldPresent() || PictureIdPresent() | ||||||
|       || KeyIdxFieldPresent()); |       || KeyIdxFieldPresent()); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool RtpFormatVp8::TIDFieldPresent() const { | bool RtpPacketizerVp8::TIDFieldPresent() const { | ||||||
|   assert((hdr_info_.layerSync == false) || |   assert((hdr_info_.layerSync == false) || | ||||||
|          (hdr_info_.temporalIdx != kNoTemporalIdx)); |          (hdr_info_.temporalIdx != kNoTemporalIdx)); | ||||||
|   return (hdr_info_.temporalIdx != kNoTemporalIdx); |   return (hdr_info_.temporalIdx != kNoTemporalIdx); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool RtpFormatVp8::KeyIdxFieldPresent() const { | bool RtpPacketizerVp8::KeyIdxFieldPresent() const { | ||||||
|   return (hdr_info_.keyIdx != kNoKeyIdx); |   return (hdr_info_.keyIdx != kNoKeyIdx); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool RtpFormatVp8::TL0PicIdxFieldPresent() const { | bool RtpPacketizerVp8::TL0PicIdxFieldPresent() const { | ||||||
|   return (hdr_info_.tl0PicIdx != kNoTl0PicIdx); |   return (hdr_info_.tl0PicIdx != kNoTl0PicIdx); | ||||||
| } | } | ||||||
| }  // namespace webrtc | }  // namespace webrtc | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ | |||||||
|  |  | ||||||
| #include "webrtc/base/constructormagic.h" | #include "webrtc/base/constructormagic.h" | ||||||
| #include "webrtc/modules/interface/module_common_types.h" | #include "webrtc/modules/interface/module_common_types.h" | ||||||
|  | #include "webrtc/modules/rtp_rtcp/source/rtp_format.h" | ||||||
| #include "webrtc/typedefs.h" | #include "webrtc/typedefs.h" | ||||||
|  |  | ||||||
| namespace webrtc { | namespace webrtc { | ||||||
| @@ -44,25 +45,24 @@ enum VP8PacketizerMode { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| // Packetizer for VP8. | // Packetizer for VP8. | ||||||
| class RtpFormatVp8 { | class RtpPacketizerVp8 : public RtpPacketizer { | ||||||
|  public: |  public: | ||||||
|   // Initialize with payload from encoder and fragmentation info. |   // Initialize with payload from encoder and fragmentation info. | ||||||
|   // The payload_data must be exactly one encoded VP8 frame. |   // The payload_data must be exactly one encoded VP8 frame. | ||||||
|   RtpFormatVp8(const uint8_t* payload_data, |   RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, | ||||||
|                uint32_t payload_size, |  | ||||||
|                const RTPVideoHeaderVP8& hdr_info, |  | ||||||
|                    int max_payload_len, |                    int max_payload_len, | ||||||
|                const RTPFragmentationHeader& fragmentation, |  | ||||||
|                    VP8PacketizerMode mode); |                    VP8PacketizerMode mode); | ||||||
|  |  | ||||||
|   // Initialize without fragmentation info. Mode kEqualSize will be used. |   // Initialize without fragmentation info. Mode kEqualSize will be used. | ||||||
|   // The payload_data must be exactly one encoded VP8 frame. |   // The payload_data must be exactly one encoded VP8 frame. | ||||||
|   RtpFormatVp8(const uint8_t* payload_data, |   RtpPacketizerVp8(const RTPVideoHeaderVP8& hdr_info, int max_payload_len); | ||||||
|                uint32_t payload_size, |  | ||||||
|                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. |   // Get the next payload with VP8 payload header. | ||||||
|   // max_payload_len limits the sum length of payload and VP8 payload header. |   // max_payload_len limits the sum length of payload and VP8 payload header. | ||||||
| @@ -75,9 +75,9 @@ class RtpFormatVp8 { | |||||||
|   // the first payload byte in the packet is taken, with the first partition |   // the first payload byte in the packet is taken, with the first partition | ||||||
|   // having index 0; returns negative on error. |   // having index 0; returns negative on error. | ||||||
|   // For the kEqualSize mode: returns 0 on success, return negative on error. |   // For the kEqualSize mode: returns 0 on success, return negative on error. | ||||||
|   int NextPacket(uint8_t* buffer, |   virtual bool NextPacket(uint8_t* buffer, | ||||||
|                  int* bytes_to_send, |                           size_t* bytes_to_send, | ||||||
|                  bool* last_packet); |                           bool* last_packet) OVERRIDE; | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   typedef struct { |   typedef struct { | ||||||
| @@ -187,7 +187,7 @@ class RtpFormatVp8 { | |||||||
|   bool PictureIdPresent() const { return (PictureIdLength() > 0); } |   bool PictureIdPresent() const { return (PictureIdLength() > 0); } | ||||||
|  |  | ||||||
|   const uint8_t* payload_data_; |   const uint8_t* payload_data_; | ||||||
|   const int payload_size_; |   int payload_size_; | ||||||
|   RTPFragmentationHeader part_info_; |   RTPFragmentationHeader part_info_; | ||||||
|   const int vp8_fixed_payload_descriptor_bytes_;  // Length of VP8 payload |   const int vp8_fixed_payload_descriptor_bytes_;  // Length of VP8 payload | ||||||
|                                                   // descriptors's fixed part. |                                                   // descriptors's fixed part. | ||||||
| @@ -195,14 +195,12 @@ class RtpFormatVp8 { | |||||||
|   const bool balance_; |   const bool balance_; | ||||||
|   const bool separate_first_; |   const bool separate_first_; | ||||||
|   const RTPVideoHeaderVP8 hdr_info_; |   const RTPVideoHeaderVP8 hdr_info_; | ||||||
|   const int num_partitions_; |   int num_partitions_; | ||||||
|   const int max_payload_len_; |   const int max_payload_len_; | ||||||
|   InfoQueue packets_; |   InfoQueue packets_; | ||||||
|   bool packets_calculated_; |   bool packets_calculated_; | ||||||
|  |  | ||||||
|   DISALLOW_COPY_AND_ASSIGN(RtpFormatVp8); |   DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp8); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace | }  // namespace | ||||||
|  |  | ||||||
| #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_H_ | #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP8_H_ | ||||||
|   | |||||||
| @@ -64,20 +64,19 @@ bool RtpFormatVp8TestHelper::Init(const int* partition_sizes, | |||||||
| } | } | ||||||
|  |  | ||||||
| void RtpFormatVp8TestHelper::GetAllPacketsAndCheck( | void RtpFormatVp8TestHelper::GetAllPacketsAndCheck( | ||||||
|     RtpFormatVp8* packetizer, |     RtpPacketizerVp8* packetizer, | ||||||
|     const int* expected_sizes, |     const int* expected_sizes, | ||||||
|     const int* expected_part, |     const int* expected_part, | ||||||
|     const bool* expected_frag_start, |     const bool* expected_frag_start, | ||||||
|     int expected_num_packets) { |     int expected_num_packets) { | ||||||
|   ASSERT_TRUE(inited_); |   ASSERT_TRUE(inited_); | ||||||
|   int send_bytes = 0; |   size_t send_bytes = 0; | ||||||
|   bool last = false; |   bool last = false; | ||||||
|   for (int i = 0; i < expected_num_packets; ++i) { |   for (int i = 0; i < expected_num_packets; ++i) { | ||||||
|     std::ostringstream ss; |     std::ostringstream ss; | ||||||
|     ss << "Checking packet " << i; |     ss << "Checking packet " << i; | ||||||
|     SCOPED_TRACE(ss.str()); |     SCOPED_TRACE(ss.str()); | ||||||
|     EXPECT_EQ(expected_part[i], |     EXPECT_TRUE(packetizer->NextPacket(buffer_, &send_bytes, &last)); | ||||||
|               packetizer->NextPacket(buffer_, &send_bytes, &last)); |  | ||||||
|     CheckPacket(send_bytes, expected_sizes[i], last, |     CheckPacket(send_bytes, expected_sizes[i], last, | ||||||
|                 expected_frag_start[i]); |                 expected_frag_start[i]); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ class RtpFormatVp8TestHelper { | |||||||
|   explicit RtpFormatVp8TestHelper(const RTPVideoHeaderVP8* hdr); |   explicit RtpFormatVp8TestHelper(const RTPVideoHeaderVP8* hdr); | ||||||
|   ~RtpFormatVp8TestHelper(); |   ~RtpFormatVp8TestHelper(); | ||||||
|   bool Init(const int* partition_sizes, int num_partitions); |   bool Init(const int* partition_sizes, int num_partitions); | ||||||
|   void GetAllPacketsAndCheck(RtpFormatVp8* packetizer, |   void GetAllPacketsAndCheck(RtpPacketizerVp8* packetizer, | ||||||
|                              const int* expected_sizes, |                              const int* expected_sizes, | ||||||
|                              const int* expected_part, |                              const int* expected_part, | ||||||
|                              const bool* expected_frag_start, |                              const bool* expected_frag_start, | ||||||
|   | |||||||
| @@ -24,9 +24,9 @@ | |||||||
|  |  | ||||||
| namespace webrtc { | namespace webrtc { | ||||||
|  |  | ||||||
| class RtpFormatVp8Test : public ::testing::Test { | class RtpPacketizerVp8Test : public ::testing::Test { | ||||||
|  protected: |  protected: | ||||||
|   RtpFormatVp8Test() : helper_(NULL) {} |   RtpPacketizerVp8Test() : helper_(NULL) {} | ||||||
|   virtual void TearDown() { delete helper_; } |   virtual void TearDown() { delete helper_; } | ||||||
|   bool Init(const int* partition_sizes, int num_partitions) { |   bool Init(const int* partition_sizes, int num_partitions) { | ||||||
|     hdr_info_.pictureId = kNoPictureId; |     hdr_info_.pictureId = kNoPictureId; | ||||||
| @@ -44,19 +44,17 @@ class RtpFormatVp8Test : public ::testing::Test { | |||||||
|   test::RtpFormatVp8TestHelper* helper_; |   test::RtpFormatVp8TestHelper* helper_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| TEST_F(RtpFormatVp8Test, TestStrictMode) { | TEST_F(RtpPacketizerVp8Test, TestStrictMode) { | ||||||
|   const int kSizeVector[] = {10, 8, 27}; |   const int kSizeVector[] = {10, 8, 27}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.pictureId = 200;  // > 0x7F should produce 2-byte PictureID. |   hdr_info_.pictureId = 200;  // > 0x7F should produce 2-byte PictureID. | ||||||
|   const int kMaxSize = 13; |   const int kMaxSize = 13; | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kStrict); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kStrict); |  | ||||||
|  |  | ||||||
|   // The expected sizes are obtained by running a verified good implementation. |   // The expected sizes are obtained by running a verified good implementation. | ||||||
|   const int kExpectedSizes[] = {9, 9, 12, 11, 11, 11, 10}; |   const int kExpectedSizes[] = {9, 9, 12, 11, 11, 11, 10}; | ||||||
| @@ -71,19 +69,17 @@ TEST_F(RtpFormatVp8Test, TestStrictMode) { | |||||||
|                                  kExpectedFragStart, kExpectedNum); |                                  kExpectedFragStart, kExpectedNum); | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(RtpFormatVp8Test, TestAggregateMode) { | TEST_F(RtpPacketizerVp8Test, TestAggregateMode) { | ||||||
|   const int kSizeVector[] = {60, 10, 10}; |   const int kSizeVector[] = {60, 10, 10}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. |   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. | ||||||
|   const int kMaxSize = 25; |   const int kMaxSize = 25; | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // The expected sizes are obtained by running a verified good implementation. |   // The expected sizes are obtained by running a verified good implementation. | ||||||
|   const int kExpectedSizes[] = {23, 23, 23, 23}; |   const int kExpectedSizes[] = {23, 23, 23, 23}; | ||||||
| @@ -97,19 +93,17 @@ TEST_F(RtpFormatVp8Test, TestAggregateMode) { | |||||||
|                                  kExpectedFragStart, kExpectedNum); |                                  kExpectedFragStart, kExpectedNum); | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions1) { | TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions1) { | ||||||
|   const int kSizeVector[] = {1600, 200, 200, 200, 200, 200, 200, 200, 200}; |   const int kSizeVector[] = {1600, 200, 200, 200, 200, 200, 200, 200, 200}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. |   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. | ||||||
|   const int kMaxSize = 1500; |   const int kMaxSize = 1500; | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // The expected sizes are obtained by running a verified good implementation. |   // The expected sizes are obtained by running a verified good implementation. | ||||||
|   const int kExpectedSizes[] = {803, 803, 803, 803}; |   const int kExpectedSizes[] = {803, 803, 803, 803}; | ||||||
| @@ -123,19 +117,17 @@ TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions1) { | |||||||
|                                  kExpectedFragStart, kExpectedNum); |                                  kExpectedFragStart, kExpectedNum); | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions2) { | TEST_F(RtpPacketizerVp8Test, TestAggregateModeManyPartitions2) { | ||||||
|   const int kSizeVector[] = {1599, 200, 200, 200, 1600, 200, 200, 200, 200}; |   const int kSizeVector[] = {1599, 200, 200, 200, 1600, 200, 200, 200, 200}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. |   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. | ||||||
|   const int kMaxSize = 1500; |   const int kMaxSize = 1500; | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // The expected sizes are obtained by running a verified good implementation. |   // The expected sizes are obtained by running a verified good implementation. | ||||||
|   const int kExpectedSizes[] = {803, 802, 603, 803, 803, 803}; |   const int kExpectedSizes[] = {803, 802, 603, 803, 803, 803}; | ||||||
| @@ -149,19 +141,17 @@ TEST_F(RtpFormatVp8Test, TestAggregateModeManyPartitions2) { | |||||||
|                                  kExpectedFragStart, kExpectedNum); |                                  kExpectedFragStart, kExpectedNum); | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(RtpFormatVp8Test, TestAggregateModeTwoLargePartitions) { | TEST_F(RtpPacketizerVp8Test, TestAggregateModeTwoLargePartitions) { | ||||||
|   const int kSizeVector[] = {1654, 2268}; |   const int kSizeVector[] = {1654, 2268}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. |   hdr_info_.pictureId = 20;  // <= 0x7F should produce 1-byte PictureID. | ||||||
|   const int kMaxSize = 1460; |   const int kMaxSize = 1460; | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // The expected sizes are obtained by running a verified good implementation. |   // The expected sizes are obtained by running a verified good implementation. | ||||||
|   const int kExpectedSizes[] = {830, 830, 1137, 1137}; |   const int kExpectedSizes[] = {830, 830, 1137, 1137}; | ||||||
| @@ -176,17 +166,16 @@ TEST_F(RtpFormatVp8Test, TestAggregateModeTwoLargePartitions) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Verify that EqualSize mode is forced if fragmentation info is missing. | // 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 kSizeVector[] = {10, 10, 10}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.pictureId = 200;  // > 0x7F should produce 2-byte PictureID |   hdr_info_.pictureId = 200;  // > 0x7F should produce 2-byte PictureID | ||||||
|   const int kMaxSize = 12;  // Small enough to produce 4 packets. |   const int kMaxSize = 12;  // Small enough to produce 4 packets. | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize); | ||||||
|                           helper_->payload_size(), |   packetizer.SetPayloadData( | ||||||
|                           hdr_info_, |       helper_->payload_data(), helper_->payload_size(), NULL); | ||||||
|                           kMaxSize); |  | ||||||
|  |  | ||||||
|   // Expecting three full packets, and one with the remainder. |   // Expecting three full packets, and one with the remainder. | ||||||
|   const int kExpectedSizes[] = {12, 11, 12, 11}; |   const int kExpectedSizes[] = {12, 11, 12, 11}; | ||||||
| @@ -203,17 +192,16 @@ TEST_F(RtpFormatVp8Test, TestEqualSizeModeFallback) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Verify that non-reference bit is set. EqualSize mode fallback is expected. | // 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 kSizeVector[] = {10, 10, 10}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
|  |  | ||||||
|   hdr_info_.nonReference = true; |   hdr_info_.nonReference = true; | ||||||
|   const int kMaxSize = 25;  // Small enough to produce two packets. |   const int kMaxSize = 25;  // Small enough to produce two packets. | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize); | ||||||
|                           helper_->payload_size(), |   packetizer.SetPayloadData( | ||||||
|                           hdr_info_, |       helper_->payload_data(), helper_->payload_size(), NULL); | ||||||
|                           kMaxSize); |  | ||||||
|  |  | ||||||
|   // EqualSize mode => First packet full; other not. |   // EqualSize mode => First packet full; other not. | ||||||
|   const int kExpectedSizes[] = {16, 16}; |   const int kExpectedSizes[] = {16, 16}; | ||||||
| @@ -230,7 +218,7 @@ TEST_F(RtpFormatVp8Test, TestNonReferenceBit) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Verify Tl0PicIdx and TID fields, and layerSync bit. | // Verify Tl0PicIdx and TID fields, and layerSync bit. | ||||||
| TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) { | TEST_F(RtpPacketizerVp8Test, TestTl0PicIdxAndTID) { | ||||||
|   const int kSizeVector[] = {10, 10, 10}; |   const int kSizeVector[] = {10, 10, 10}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
| @@ -240,12 +228,10 @@ TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) { | |||||||
|   hdr_info_.layerSync = true; |   hdr_info_.layerSync = true; | ||||||
|   // kMaxSize is only limited by allocated buffer size. |   // kMaxSize is only limited by allocated buffer size. | ||||||
|   const int kMaxSize = helper_->buffer_size(); |   const int kMaxSize = helper_->buffer_size(); | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // Expect one single packet of payload_size() + 4 bytes header. |   // Expect one single packet of payload_size() + 4 bytes header. | ||||||
|   const int kExpectedSizes[1] = {helper_->payload_size() + 4}; |   const int kExpectedSizes[1] = {helper_->payload_size() + 4}; | ||||||
| @@ -260,7 +246,7 @@ TEST_F(RtpFormatVp8Test, TestTl0PicIdxAndTID) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Verify KeyIdx field. | // Verify KeyIdx field. | ||||||
| TEST_F(RtpFormatVp8Test, TestKeyIdx) { | TEST_F(RtpPacketizerVp8Test, TestKeyIdx) { | ||||||
|   const int kSizeVector[] = {10, 10, 10}; |   const int kSizeVector[] = {10, 10, 10}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
| @@ -268,12 +254,10 @@ TEST_F(RtpFormatVp8Test, TestKeyIdx) { | |||||||
|   hdr_info_.keyIdx = 17; |   hdr_info_.keyIdx = 17; | ||||||
|   // kMaxSize is only limited by allocated buffer size. |   // kMaxSize is only limited by allocated buffer size. | ||||||
|   const int kMaxSize = helper_->buffer_size(); |   const int kMaxSize = helper_->buffer_size(); | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // Expect one single packet of payload_size() + 3 bytes header. |   // Expect one single packet of payload_size() + 3 bytes header. | ||||||
|   const int kExpectedSizes[1] = {helper_->payload_size() + 3}; |   const int kExpectedSizes[1] = {helper_->payload_size() + 3}; | ||||||
| @@ -288,7 +272,7 @@ TEST_F(RtpFormatVp8Test, TestKeyIdx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Verify TID field and KeyIdx field in combination. | // Verify TID field and KeyIdx field in combination. | ||||||
| TEST_F(RtpFormatVp8Test, TestTIDAndKeyIdx) { | TEST_F(RtpPacketizerVp8Test, TestTIDAndKeyIdx) { | ||||||
|   const int kSizeVector[] = {10, 10, 10}; |   const int kSizeVector[] = {10, 10, 10}; | ||||||
|   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); |   const int kNumPartitions = sizeof(kSizeVector) / sizeof(kSizeVector[0]); | ||||||
|   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); |   ASSERT_TRUE(Init(kSizeVector, kNumPartitions)); | ||||||
| @@ -297,12 +281,10 @@ TEST_F(RtpFormatVp8Test, TestTIDAndKeyIdx) { | |||||||
|   hdr_info_.keyIdx = 5; |   hdr_info_.keyIdx = 5; | ||||||
|   // kMaxSize is only limited by allocated buffer size. |   // kMaxSize is only limited by allocated buffer size. | ||||||
|   const int kMaxSize = helper_->buffer_size(); |   const int kMaxSize = helper_->buffer_size(); | ||||||
|   RtpFormatVp8 packetizer(helper_->payload_data(), |   RtpPacketizerVp8 packetizer(hdr_info_, kMaxSize, kAggregate); | ||||||
|  |   packetizer.SetPayloadData(helper_->payload_data(), | ||||||
|                             helper_->payload_size(), |                             helper_->payload_size(), | ||||||
|                           hdr_info_, |                             helper_->fragmentation()); | ||||||
|                           kMaxSize, |  | ||||||
|                           *(helper_->fragmentation()), |  | ||||||
|                           kAggregate); |  | ||||||
|  |  | ||||||
|   // Expect one single packet of payload_size() + 3 bytes header. |   // Expect one single packet of payload_size() + 3 bytes header. | ||||||
|   const int kExpectedSizes[1] = {helper_->payload_size() + 3}; |   const int kExpectedSizes[1] = {helper_->payload_size() + 3}; | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.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_format_video_generic.h" | ||||||
| #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" | #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" | ||||||
| #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | ||||||
| @@ -113,8 +114,13 @@ int32_t RTPReceiverVideo::ParseVideoCodecSpecific( | |||||||
|       return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length); |       return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length); | ||||||
|     case kRtpVideoVp8: |     case kRtpVideoVp8: | ||||||
|       return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length); |       return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length); | ||||||
|     case kRtpVideoH264: |     case kRtpVideoH264: { | ||||||
|       assert(false);  // Not yet supported. |       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: |     case kRtpVideoNone: | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ class RTPReceiverVideo : public RTPReceiverStrategy { | |||||||
|  |  | ||||||
|   void SetPacketOverHead(uint16_t packet_over_head); |   void SetPacketOverHead(uint16_t packet_over_head); | ||||||
|  |  | ||||||
|  protected: |  private: | ||||||
|   int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header, |   int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header, | ||||||
|                               const uint8_t* payload_data, |                               const uint8_t* payload_data, | ||||||
|                               uint16_t payload_data_length); |                               uint16_t payload_data_length); | ||||||
| @@ -69,10 +69,13 @@ class RTPReceiverVideo : public RTPReceiverStrategy { | |||||||
|                           const uint8_t* payload_data, |                           const uint8_t* payload_data, | ||||||
|                           uint16_t payload_data_length); |                           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, |   int32_t BuildRTPheader(const WebRtcRTPHeader* rtp_header, | ||||||
|                          uint8_t* data_buffer) const; |                          uint8_t* data_buffer) const; | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   int32_t ParseVideoCodecSpecific( |   int32_t ParseVideoCodecSpecific( | ||||||
|       WebRtcRTPHeader* rtp_header, |       WebRtcRTPHeader* rtp_header, | ||||||
|       const uint8_t* payload_data, |       const uint8_t* payload_data, | ||||||
|   | |||||||
| @@ -86,6 +86,10 @@ | |||||||
|         'rtp_sender_video.cc', |         'rtp_sender_video.cc', | ||||||
|         'rtp_sender_video.h', |         'rtp_sender_video.h', | ||||||
|         'video_codec_information.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.cc', | ||||||
|         'rtp_format_vp8.h', |         'rtp_format_vp8.h', | ||||||
|         'rtp_format_video_generic.h', |         'rtp_format_video_generic.h', | ||||||
|   | |||||||
| @@ -275,8 +275,7 @@ int32_t RTPSenderVideo::SetFecParameters( | |||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int32_t | int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, | ||||||
| RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, |  | ||||||
|                                   const FrameType frameType, |                                   const FrameType frameType, | ||||||
|                                   const int8_t payloadType, |                                   const int8_t payloadType, | ||||||
|                                   const uint32_t captureTimeStamp, |                                   const uint32_t captureTimeStamp, | ||||||
| @@ -285,34 +284,31 @@ RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, | |||||||
|                                   const uint32_t payloadSize, |                                   const uint32_t payloadSize, | ||||||
|                                   const RTPFragmentationHeader* fragmentation, |                                   const RTPFragmentationHeader* fragmentation, | ||||||
|                                   VideoCodecInformation* codecInfo, |                                   VideoCodecInformation* codecInfo, | ||||||
|                           const RTPVideoTypeHeader* rtpTypeHdr) |                                   const RTPVideoTypeHeader* rtpTypeHdr) { | ||||||
| { |   if (payloadSize == 0) { | ||||||
|     if( payloadSize == 0) |  | ||||||
|     { |  | ||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (frameType == kVideoFrameKey) { |   if (frameType == kVideoFrameKey) { | ||||||
|       producer_fec_.SetFecParameters(&key_fec_params_, |     producer_fec_.SetFecParameters(&key_fec_params_, _numberFirstPartition); | ||||||
|                                      _numberFirstPartition); |  | ||||||
|   } else { |   } else { | ||||||
|       producer_fec_.SetFecParameters(&delta_fec_params_, |     producer_fec_.SetFecParameters(&delta_fec_params_, _numberFirstPartition); | ||||||
|                                      _numberFirstPartition); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Default setting for number of first partition packets: |   // Default setting for number of first partition packets: | ||||||
|   // Will be extracted in SendVP8 for VP8 codec; other codecs use 0 |   // Will be extracted in SendVP8 for VP8 codec; other codecs use 0 | ||||||
|   _numberFirstPartition = 0; |   _numberFirstPartition = 0; | ||||||
|  |  | ||||||
|     int32_t retVal = -1; |   switch (videoType) { | ||||||
|     switch(videoType) |  | ||||||
|     { |  | ||||||
|     case kRtpVideoGeneric: |     case kRtpVideoGeneric: | ||||||
|         retVal = SendGeneric(frameType, payloadType, captureTimeStamp, |       return SendGeneric(frameType, | ||||||
|                              capture_time_ms, payloadData, payloadSize); |                          payloadType, | ||||||
|         break; |                          captureTimeStamp, | ||||||
|  |                          capture_time_ms, | ||||||
|  |                          payloadData, | ||||||
|  |                          payloadSize); | ||||||
|     case kRtpVideoVp8: |     case kRtpVideoVp8: | ||||||
|         retVal = SendVP8(frameType, |       return SendVP8(frameType, | ||||||
|                      payloadType, |                      payloadType, | ||||||
|                      captureTimeStamp, |                      captureTimeStamp, | ||||||
|                      capture_time_ms, |                      capture_time_ms, | ||||||
| @@ -320,15 +316,21 @@ RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, | |||||||
|                      payloadSize, |                      payloadSize, | ||||||
|                      fragmentation, |                      fragmentation, | ||||||
|                      rtpTypeHdr); |                      rtpTypeHdr); | ||||||
|         break; |     case kRtpVideoH264: | ||||||
|  |       return SendH264(frameType, | ||||||
|  |                       payloadType, | ||||||
|  |                       captureTimeStamp, | ||||||
|  |                       capture_time_ms, | ||||||
|  |                       payloadData, | ||||||
|  |                       payloadSize, | ||||||
|  |                       fragmentation, | ||||||
|  |                       rtpTypeHdr) | ||||||
|  |                  ? 0 | ||||||
|  |                  : -1; | ||||||
|     default: |     default: | ||||||
|       assert(false); |       assert(false); | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|     if(retVal <= 0) |  | ||||||
|     { |  | ||||||
|         return retVal; |  | ||||||
|     } |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -427,8 +429,8 @@ RTPSenderVideo::SendVP8(const FrameType frameType, | |||||||
|     assert(rtpTypeHdr); |     assert(rtpTypeHdr); | ||||||
|     // Initialize disregarding partition boundaries: this will use kEqualSize |     // Initialize disregarding partition boundaries: this will use kEqualSize | ||||||
|     // packetization mode, which produces ~equal size packets for each frame. |     // packetization mode, which produces ~equal size packets for each frame. | ||||||
|     RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8, |     RtpPacketizerVp8 packetizer(rtpTypeHdr->VP8, maxPayloadLengthVP8); | ||||||
|                             maxPayloadLengthVP8); |     packetizer.SetPayloadData(data, payloadBytesToSend, NULL); | ||||||
|  |  | ||||||
|     StorageType storage = kAllowRetransmission; |     StorageType storage = kAllowRetransmission; | ||||||
|     if (rtpTypeHdr->VP8.temporalIdx == 0 && |     if (rtpTypeHdr->VP8.temporalIdx == 0 && | ||||||
| @@ -450,22 +452,10 @@ RTPSenderVideo::SendVP8(const FrameType frameType, | |||||||
|     { |     { | ||||||
|         // Write VP8 Payload Descriptor and VP8 payload. |         // Write VP8 Payload Descriptor and VP8 payload. | ||||||
|         uint8_t dataBuffer[IP_PACKET_SIZE] = {0}; |         uint8_t dataBuffer[IP_PACKET_SIZE] = {0}; | ||||||
|         int payloadBytesInPacket = 0; |         size_t payloadBytesInPacket = 0; | ||||||
|         int packetStartPartition = |         if (!packetizer.NextPacket( | ||||||
|             packetizer.NextPacket(&dataBuffer[rtpHeaderLength], |                 &dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last)) | ||||||
|                                   &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; |           return -1; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Write RTP header. |         // Write RTP header. | ||||||
|         // Set marker bit true if this is the last packet in frame. |         // Set marker bit true if this is the last packet in frame. | ||||||
| @@ -485,6 +475,55 @@ RTPSenderVideo::SendVP8(const FrameType frameType, | |||||||
|     return 0; |     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() { | void RTPSenderVideo::ProcessBitrate() { | ||||||
|   _videoBitrate.Process(); |   _videoBitrate.Process(); | ||||||
|   _fecOverheadRate.Process(); |   _fecOverheadRate.Process(); | ||||||
|   | |||||||
| @@ -110,6 +110,15 @@ private: | |||||||
|                     const RTPFragmentationHeader* fragmentation, |                     const RTPFragmentationHeader* fragmentation, | ||||||
|                     const RTPVideoTypeHeader* rtpTypeHdr); |                     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: | private: | ||||||
|     RTPSenderInterface&        _rtpSender; |     RTPSenderInterface&        _rtpSender; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -251,10 +251,11 @@ TEST(ParseVP8Test, TestWithPacketizer) { | |||||||
|   inputHeader.layerSync = false; |   inputHeader.layerSync = false; | ||||||
|   inputHeader.tl0PicIdx = kNoTl0PicIdx;  // Disable. |   inputHeader.tl0PicIdx = kNoTl0PicIdx;  // Disable. | ||||||
|   inputHeader.keyIdx = 31; |   inputHeader.keyIdx = 31; | ||||||
|   RtpFormatVp8 packetizer(payload, 10, inputHeader, 20); |   RtpPacketizerVp8 packetizer(inputHeader, 20); | ||||||
|  |   packetizer.SetPayloadData(payload, 10, NULL); | ||||||
|   bool last; |   bool last; | ||||||
|   int send_bytes; |   size_t send_bytes; | ||||||
|   ASSERT_EQ(0, packetizer.NextPacket(packet, &send_bytes, &last)); |   ASSERT_TRUE(packetizer.NextPacket(packet, &send_bytes, &last)); | ||||||
|   ASSERT_TRUE(last); |   ASSERT_TRUE(last); | ||||||
|  |  | ||||||
|   RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, packet, send_bytes); |   RTPPayloadParser rtpPayloadParser(kRtpVideoVp8, packet, send_bytes); | ||||||
|   | |||||||
| @@ -28,29 +28,30 @@ class RTPFragmentationHeader; // forward declaration | |||||||
|  |  | ||||||
| // Note: if any pointers are added to this struct, it must be fitted | // Note: if any pointers are added to this struct, it must be fitted | ||||||
| // with a copy-constructor. See below. | // with a copy-constructor. See below. | ||||||
| struct CodecSpecificInfoVP8 | struct CodecSpecificInfoVP8 { | ||||||
| { |  | ||||||
|   bool hasReceivedSLI; |   bool hasReceivedSLI; | ||||||
|   uint8_t pictureIdSLI; |   uint8_t pictureIdSLI; | ||||||
|   bool hasReceivedRPSI; |   bool hasReceivedRPSI; | ||||||
|   uint64_t pictureIdRPSI; |   uint64_t pictureIdRPSI; | ||||||
|     int16_t    pictureId;         // negative value to skip pictureId |   int16_t pictureId;  // Negative value to skip pictureId. | ||||||
|   bool nonReference; |   bool nonReference; | ||||||
|   uint8_t simulcastIdx; |   uint8_t simulcastIdx; | ||||||
|   uint8_t temporalIdx; |   uint8_t temporalIdx; | ||||||
|   bool layerSync; |   bool layerSync; | ||||||
|     int              tl0PicIdx;         // Negative value to skip tl0PicIdx |   int tl0PicIdx;  // Negative value to skip tl0PicIdx. | ||||||
|     int8_t     keyIdx;            // negative value to skip keyIdx |   int8_t keyIdx;  // Negative value to skip keyIdx. | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CodecSpecificInfoGeneric { | struct CodecSpecificInfoGeneric { | ||||||
|   uint8_t simulcast_idx; |   uint8_t simulcast_idx; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| union CodecSpecificInfoUnion | struct CodecSpecificInfoH264 {}; | ||||||
| { |  | ||||||
|  | union CodecSpecificInfoUnion { | ||||||
|   CodecSpecificInfoGeneric generic; |   CodecSpecificInfoGeneric generic; | ||||||
|   CodecSpecificInfoVP8 VP8; |   CodecSpecificInfoVP8 VP8; | ||||||
|  |   CodecSpecificInfoH264 H264; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Note: if any pointers are added to this struct or its sub-structs, it | // Note: if any pointers are added to this struct or its sub-structs, it | ||||||
|   | |||||||
| @@ -100,14 +100,10 @@ void VCMEncodedFrame::Reset() | |||||||
|  |  | ||||||
| void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header) | void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header) | ||||||
| { | { | ||||||
|     if (header) |   if (header) { | ||||||
|     { |     switch (header->codec) { | ||||||
|         switch (header->codec) |       case kRtpVideoVp8: { | ||||||
|         { |         if (_codecSpecificInfo.codecType != kVideoCodecVP8) { | ||||||
|             case kRtpVideoVp8: |  | ||||||
|             { |  | ||||||
|                 if (_codecSpecificInfo.codecType != kVideoCodecVP8) |  | ||||||
|                 { |  | ||||||
|           // This is the first packet for this frame. |           // This is the first packet for this frame. | ||||||
|           _codecSpecificInfo.codecSpecific.VP8.pictureId = -1; |           _codecSpecificInfo.codecSpecific.VP8.pictureId = -1; | ||||||
|           _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0; |           _codecSpecificInfo.codecSpecific.VP8.temporalIdx = 0; | ||||||
| @@ -117,27 +113,27 @@ void VCMEncodedFrame::CopyCodecSpecific(const RTPVideoHeader* header) | |||||||
|         } |         } | ||||||
|         _codecSpecificInfo.codecSpecific.VP8.nonReference = |         _codecSpecificInfo.codecSpecific.VP8.nonReference = | ||||||
|             header->codecHeader.VP8.nonReference; |             header->codecHeader.VP8.nonReference; | ||||||
|                 if (header->codecHeader.VP8.pictureId != kNoPictureId) |         if (header->codecHeader.VP8.pictureId != kNoPictureId) { | ||||||
|                 { |  | ||||||
|           _codecSpecificInfo.codecSpecific.VP8.pictureId = |           _codecSpecificInfo.codecSpecific.VP8.pictureId = | ||||||
|               header->codecHeader.VP8.pictureId; |               header->codecHeader.VP8.pictureId; | ||||||
|         } |         } | ||||||
|                 if (header->codecHeader.VP8.temporalIdx != kNoTemporalIdx) |         if (header->codecHeader.VP8.temporalIdx != kNoTemporalIdx) { | ||||||
|                 { |  | ||||||
|           _codecSpecificInfo.codecSpecific.VP8.temporalIdx = |           _codecSpecificInfo.codecSpecific.VP8.temporalIdx = | ||||||
|               header->codecHeader.VP8.temporalIdx; |               header->codecHeader.VP8.temporalIdx; | ||||||
|           _codecSpecificInfo.codecSpecific.VP8.layerSync = |           _codecSpecificInfo.codecSpecific.VP8.layerSync = | ||||||
|               header->codecHeader.VP8.layerSync; |               header->codecHeader.VP8.layerSync; | ||||||
|         } |         } | ||||||
|                 if (header->codecHeader.VP8.keyIdx != kNoKeyIdx) |         if (header->codecHeader.VP8.keyIdx != kNoKeyIdx) { | ||||||
|                 { |  | ||||||
|           _codecSpecificInfo.codecSpecific.VP8.keyIdx = |           _codecSpecificInfo.codecSpecific.VP8.keyIdx = | ||||||
|               header->codecHeader.VP8.keyIdx; |               header->codecHeader.VP8.keyIdx; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|             default: |       case kRtpVideoH264: { | ||||||
|             { |         _codecSpecificInfo.codecType = kVideoCodecH264; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       default: { | ||||||
|         _codecSpecificInfo.codecType = kVideoCodecUnknown; |         _codecSpecificInfo.codecType = kVideoCodecUnknown; | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -38,6 +38,9 @@ void CopyCodecSpecific(const CodecSpecificInfo* info, RTPVideoHeader** rtp) { | |||||||
|       (*rtp)->simulcastIdx = info->codecSpecific.VP8.simulcastIdx; |       (*rtp)->simulcastIdx = info->codecSpecific.VP8.simulcastIdx; | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |     case kVideoCodecH264: | ||||||
|  |       (*rtp)->codec = kRtpVideoH264; | ||||||
|  |       return; | ||||||
|     case kVideoCodecGeneric: |     case kVideoCodecGeneric: | ||||||
|       (*rtp)->codec = kRtpVideoGeneric; |       (*rtp)->codec = kRtpVideoGeneric; | ||||||
|       (*rtp)->simulcastIdx = info->codecSpecific.generic.simulcast_idx; |       (*rtp)->simulcastIdx = info->codecSpecific.generic.simulcast_idx; | ||||||
|   | |||||||
| @@ -482,7 +482,6 @@ bool VCMJitterBuffer::NextMaybeIncompleteTimestamp(uint32_t* timestamp) { | |||||||
|  |  | ||||||
| VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode(uint32_t timestamp) { | VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode(uint32_t timestamp) { | ||||||
|   CriticalSectionScoped cs(crit_sect_); |   CriticalSectionScoped cs(crit_sect_); | ||||||
|  |  | ||||||
|   if (!running_) { |   if (!running_) { | ||||||
|     return NULL; |     return NULL; | ||||||
|   } |   } | ||||||
| @@ -611,7 +610,6 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, | |||||||
|   if (error != kNoError && frame == NULL) { |   if (error != kNoError && frame == NULL) { | ||||||
|     return error; |     return error; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int64_t now_ms = clock_->TimeInMilliseconds(); |   int64_t now_ms = clock_->TimeInMilliseconds(); | ||||||
|   // We are keeping track of the first and latest seq numbers, and |   // 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. |   // the number of wraps to be able to calculate how many packets we expect. | ||||||
|   | |||||||
| @@ -94,12 +94,9 @@ void VCMPacket::Reset() { | |||||||
|   memset(&codecSpecificHeader, 0, sizeof(RTPVideoHeader)); |   memset(&codecSpecificHeader, 0, sizeof(RTPVideoHeader)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) | void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) { | ||||||
| { |   switch (videoHeader.codec) { | ||||||
|     switch(videoHeader.codec) |  | ||||||
|     { |  | ||||||
|     case kRtpVideoVp8: |     case kRtpVideoVp8: | ||||||
|             { |  | ||||||
|       // Handle all packets within a frame as depending on the previous packet |       // Handle all packets within a frame as depending on the previous packet | ||||||
|       // TODO(holmer): This should be changed to make fragments independent |       // TODO(holmer): This should be changed to make fragments independent | ||||||
|       // when the VP8 RTP receiver supports fragments. |       // when the VP8 RTP receiver supports fragments. | ||||||
| @@ -113,14 +110,28 @@ void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) | |||||||
|         completeNALU = kNaluIncomplete; |         completeNALU = kNaluIncomplete; | ||||||
|  |  | ||||||
|       codec = kVideoCodecVP8; |       codec = kVideoCodecVP8; | ||||||
|                 break; |       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; | ||||||
|       } |       } | ||||||
|         default: |       codec = kVideoCodecH264; | ||||||
|             { |       return; | ||||||
|  |     case kRtpVideoGeneric: | ||||||
|  |     case kRtpVideoNone: | ||||||
|       codec = kVideoCodecUnknown; |       codec = kVideoCodecUnknown; | ||||||
|                 break; |       return; | ||||||
|             } |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| } | }  // namespace webrtc | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ | |||||||
| #include "webrtc/system_wrappers/interface/logging.h" | #include "webrtc/system_wrappers/interface/logging.h" | ||||||
|  |  | ||||||
| namespace webrtc { | namespace webrtc { | ||||||
|  | namespace { | ||||||
| // Used in determining whether a frame is decodable. | // Used in determining whether a frame is decodable. | ||||||
| enum {kRttThreshold = 100};  // Not decodable if Rtt is lower than this. | enum {kRttThreshold = 100};  // Not decodable if Rtt is lower than this. | ||||||
|  |  | ||||||
| @@ -23,6 +23,11 @@ enum {kRttThreshold = 100};  // Not decodable if Rtt is lower than this. | |||||||
| static const float kLowPacketPercentageThreshold = 0.2f; | static const float kLowPacketPercentageThreshold = 0.2f; | ||||||
| static const float kHighPacketPercentageThreshold = 0.8f; | static const float kHighPacketPercentageThreshold = 0.8f; | ||||||
|  |  | ||||||
|  | uint16_t BufferToUWord16(const uint8_t* dataBuffer) { | ||||||
|  |   return (dataBuffer[0] << 8) | dataBuffer[1]; | ||||||
|  | } | ||||||
|  | }  // namespace | ||||||
|  |  | ||||||
| VCMSessionInfo::VCMSessionInfo() | VCMSessionInfo::VCMSessionInfo() | ||||||
|     : session_nack_(false), |     : session_nack_(false), | ||||||
|       complete_(false), |       complete_(false), | ||||||
| @@ -121,9 +126,6 @@ int VCMSessionInfo::InsertBuffer(uint8_t* frame_buffer, | |||||||
|   VCMPacket& packet = *packet_it; |   VCMPacket& packet = *packet_it; | ||||||
|   PacketIterator it; |   PacketIterator it; | ||||||
|  |  | ||||||
|   int packet_size = packet.sizeBytes; |  | ||||||
|   packet_size += (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); |  | ||||||
|  |  | ||||||
|   // Calculate the offset into the frame buffer for this packet. |   // Calculate the offset into the frame buffer for this packet. | ||||||
|   int offset = 0; |   int offset = 0; | ||||||
|   for (it = packets_.begin(); it != packet_it; ++it) |   for (it = packets_.begin(); it != packet_it; ++it) | ||||||
| @@ -131,23 +133,63 @@ int VCMSessionInfo::InsertBuffer(uint8_t* frame_buffer, | |||||||
|  |  | ||||||
|   // Set the data pointer to pointing to the start of this packet in the |   // Set the data pointer to pointing to the start of this packet in the | ||||||
|   // frame buffer. |   // frame buffer. | ||||||
|   const uint8_t* data = packet.dataPtr; |   const uint8_t* packet_buffer = packet.dataPtr; | ||||||
|   packet.dataPtr = frame_buffer + offset; |   packet.dataPtr = frame_buffer + offset; | ||||||
|   packet.sizeBytes = packet_size; |  | ||||||
|  |  | ||||||
|   ShiftSubsequentPackets(packet_it, packet_size); |   // 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 unsigned char startCode[] = {0, 0, 0, 1}; |   const size_t kH264NALHeaderLengthInBytes = 1; | ||||||
|   if (packet.insertStartCode) { |   const size_t kLengthFieldLength = 2; | ||||||
|     memcpy(const_cast<uint8_t*>(packet.dataPtr), startCode, |   if (packet.codecSpecificHeader.codecHeader.H264.stap_a) { | ||||||
|            kH264StartCodeLengthBytes); |     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; | ||||||
|     } |     } | ||||||
|   memcpy(const_cast<uint8_t*>(packet.dataPtr |     ShiftSubsequentPackets(packet_it, required_length); | ||||||
|       + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0)), |     nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes; | ||||||
|       data, |     uint8_t* frame_buffer_ptr = frame_buffer + offset; | ||||||
|       packet.sizeBytes); |     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; | ||||||
|  |   } | ||||||
|  |   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, | void VCMSessionInfo::ShiftSubsequentPackets(PacketIterator it, | ||||||
| @@ -420,7 +462,21 @@ int VCMSessionInfo::InsertPacket(const VCMPacket& packet, | |||||||
|       (*rit).seqNum == packet.seqNum && (*rit).sizeBytes > 0) |       (*rit).seqNum == packet.seqNum && (*rit).sizeBytes > 0) | ||||||
|     return -2; |     return -2; | ||||||
|  |  | ||||||
|   // Only insert media packets between first and last packets (when available). |   if (packet.codec == kVideoCodecH264) { | ||||||
|  |     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. |     // Placing check here, as to properly account for duplicate packets. | ||||||
|     // Check if this is first packet (only valid for some codecs) |     // Check if this is first packet (only valid for some codecs) | ||||||
|     // Should only be set for one packet per session. |     // Should only be set for one packet per session. | ||||||
| @@ -431,8 +487,8 @@ int VCMSessionInfo::InsertPacket(const VCMPacket& packet, | |||||||
|       first_packet_seq_num_ = static_cast<int>(packet.seqNum); |       first_packet_seq_num_ = static_cast<int>(packet.seqNum); | ||||||
|     } else if (first_packet_seq_num_ != -1 && |     } else if (first_packet_seq_num_ != -1 && | ||||||
|                !IsNewerSequenceNumber(packet.seqNum, first_packet_seq_num_)) { |                !IsNewerSequenceNumber(packet.seqNum, first_packet_seq_num_)) { | ||||||
|     LOG(LS_WARNING) << "Received packet with a sequence number which is out of" |       LOG(LS_WARNING) << "Received packet with a sequence number which is out " | ||||||
|                        "frame boundaries"; |                          "of frame boundaries"; | ||||||
|       return -3; |       return -3; | ||||||
|     } else if (frame_type_ == kFrameEmpty && packet.frameType != kFrameEmpty) { |     } else if (frame_type_ == kFrameEmpty && packet.frameType != kFrameEmpty) { | ||||||
|       // Update the frame type with the type of the first media packet. |       // Update the frame type with the type of the first media packet. | ||||||
| @@ -445,10 +501,11 @@ int VCMSessionInfo::InsertPacket(const VCMPacket& packet, | |||||||
|       last_packet_seq_num_ = static_cast<int>(packet.seqNum); |       last_packet_seq_num_ = static_cast<int>(packet.seqNum); | ||||||
|     } else if (last_packet_seq_num_ != -1 && |     } else if (last_packet_seq_num_ != -1 && | ||||||
|                IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) { |                IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) { | ||||||
|     LOG(LS_WARNING) << "Received packet with a sequence number which is out of" |       LOG(LS_WARNING) << "Received packet with a sequence number which is out " | ||||||
|                        "frame boundaries"; |                          "of frame boundaries"; | ||||||
|       return -3; |       return -3; | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // The insert operation invalidates the iterator |rit|. |   // The insert operation invalidates the iterator |rit|. | ||||||
|   PacketIterator packet_list_it = packets_.insert(rit.base(), packet); |   PacketIterator packet_list_it = packets_.insert(rit.base(), packet); | ||||||
|   | |||||||
| @@ -116,6 +116,10 @@ class VCMSessionInfo { | |||||||
|                          const PacketIterator& prev_it); |                          const PacketIterator& prev_it); | ||||||
|   int InsertBuffer(uint8_t* frame_buffer, |   int InsertBuffer(uint8_t* frame_buffer, | ||||||
|                    PacketIterator packetIterator); |                    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); |   void ShiftSubsequentPackets(PacketIterator it, int steps_to_shift); | ||||||
|   PacketIterator FindNaluEnd(PacketIterator packet_iter) const; |   PacketIterator FindNaluEnd(PacketIterator packet_iter) const; | ||||||
|   // Deletes the data of all packets between |start| and |end|, inclusively. |   // Deletes the data of all packets between |start| and |end|, inclusively. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 stefan@webrtc.org
					stefan@webrtc.org