| /* |
| * 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 <assert.h> |
| #include <string.h> |
| |
| #include "absl/types/optional.h" |
| #include "modules/rtp_rtcp/source/rtp_format_video_generic.h" |
| #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/logging.h" |
| |
| namespace webrtc { |
| |
| static const size_t kGenericHeaderLength = 1; |
| static const size_t kExtendedHeaderLength = 2; |
| |
| RtpPacketizerGeneric::RtpPacketizerGeneric( |
| rtc::ArrayView<const uint8_t> payload, |
| PayloadSizeLimits limits, |
| const RTPVideoHeader& rtp_video_header, |
| VideoFrameType frame_type) |
| : remaining_payload_(payload) { |
| BuildHeader(rtp_video_header, frame_type); |
| |
| limits.max_payload_len -= header_size_; |
| payload_sizes_ = SplitAboutEqually(payload.size(), limits); |
| current_packet_ = payload_sizes_.begin(); |
| } |
| |
| RtpPacketizerGeneric::RtpPacketizerGeneric( |
| rtc::ArrayView<const uint8_t> payload, |
| PayloadSizeLimits limits) |
| : header_size_(0), remaining_payload_(payload) { |
| payload_sizes_ = SplitAboutEqually(payload.size(), limits); |
| current_packet_ = payload_sizes_.begin(); |
| } |
| |
| RtpPacketizerGeneric::~RtpPacketizerGeneric() = default; |
| |
| size_t RtpPacketizerGeneric::NumPackets() const { |
| return payload_sizes_.end() - current_packet_; |
| } |
| |
| bool RtpPacketizerGeneric::NextPacket(RtpPacketToSend* packet) { |
| RTC_DCHECK(packet); |
| if (current_packet_ == payload_sizes_.end()) |
| return false; |
| |
| size_t next_packet_payload_len = *current_packet_; |
| |
| uint8_t* out_ptr = |
| packet->AllocatePayload(header_size_ + next_packet_payload_len); |
| RTC_CHECK(out_ptr); |
| |
| if (header_size_ > 0) { |
| memcpy(out_ptr, header_, header_size_); |
| // Remove first-packet bit, following packets are intermediate. |
| header_[0] &= ~RtpFormatVideoGeneric::kFirstPacketBit; |
| } |
| |
| memcpy(out_ptr + header_size_, remaining_payload_.data(), |
| next_packet_payload_len); |
| |
| remaining_payload_ = remaining_payload_.subview(next_packet_payload_len); |
| |
| ++current_packet_; |
| |
| // Packets left to produce and data left to split should end at the same time. |
| RTC_DCHECK_EQ(current_packet_ == payload_sizes_.end(), |
| remaining_payload_.empty()); |
| |
| packet->SetMarker(remaining_payload_.empty()); |
| return true; |
| } |
| |
| void RtpPacketizerGeneric::BuildHeader(const RTPVideoHeader& rtp_video_header, |
| VideoFrameType frame_type) { |
| header_size_ = kGenericHeaderLength; |
| header_[0] = RtpFormatVideoGeneric::kFirstPacketBit; |
| if (frame_type == VideoFrameType::kVideoFrameKey) { |
| header_[0] |= RtpFormatVideoGeneric::kKeyFrameBit; |
| } |
| if (rtp_video_header.generic.has_value()) { |
| // Store bottom 15 bits of the picture id. Only 15 bits are used for |
| // compatibility with other packetizer implemenetations. |
| uint16_t picture_id = rtp_video_header.generic->frame_id & 0x7FFF; |
| header_[0] |= RtpFormatVideoGeneric::kExtendedHeaderBit; |
| header_[1] = (picture_id >> 8) & 0x7F; |
| header_[2] = picture_id & 0xFF; |
| header_size_ += kExtendedHeaderLength; |
| } |
| } |
| |
| RtpDepacketizerGeneric::RtpDepacketizerGeneric(bool generic_header_enabled) |
| : generic_header_enabled_(generic_header_enabled) {} |
| |
| RtpDepacketizerGeneric::~RtpDepacketizerGeneric() = default; |
| |
| bool RtpDepacketizerGeneric::Parse(ParsedPayload* parsed_payload, |
| const uint8_t* payload_data, |
| size_t payload_data_length) { |
| assert(parsed_payload != NULL); |
| if (payload_data_length == 0) { |
| RTC_LOG(LS_WARNING) << "Empty payload."; |
| return false; |
| } |
| |
| if (generic_header_enabled_) { |
| uint8_t generic_header = *payload_data++; |
| --payload_data_length; |
| |
| parsed_payload->video_header().frame_type = |
| ((generic_header & RtpFormatVideoGeneric::kKeyFrameBit) != 0) |
| ? VideoFrameType::kVideoFrameKey |
| : VideoFrameType::kVideoFrameDelta; |
| parsed_payload->video_header().is_first_packet_in_frame = |
| (generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0; |
| parsed_payload->video_header().codec = kVideoCodecGeneric; |
| parsed_payload->video_header().width = 0; |
| parsed_payload->video_header().height = 0; |
| |
| if (generic_header & RtpFormatVideoGeneric::kExtendedHeaderBit) { |
| if (payload_data_length < kExtendedHeaderLength) { |
| RTC_LOG(LS_WARNING) << "Too short payload for generic header."; |
| return false; |
| } |
| parsed_payload->video_header().generic.emplace(); |
| parsed_payload->video_header().generic->frame_id = |
| ((payload_data[0] & 0x7F) << 8) | payload_data[1]; |
| payload_data += kExtendedHeaderLength; |
| payload_data_length -= kExtendedHeaderLength; |
| } |
| } |
| |
| parsed_payload->payload = payload_data; |
| parsed_payload->payload_length = payload_data_length; |
| return true; |
| } |
| } // namespace webrtc |