/* * 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 "modules/rtp_rtcp/source/rtp_format_video_generic.h" #include #include #include "absl/types/optional.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 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 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