blob: ffb5f77494307ad5c7dbbd4808d2dd33f473ce32 [file] [log] [blame]
/*
* 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>
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "webrtc/rtc_base/logging.h"
namespace webrtc {
static const size_t kGenericHeaderLength = 1;
RtpPacketizerGeneric::RtpPacketizerGeneric(FrameType frame_type,
size_t max_payload_len,
size_t last_packet_reduction_len)
: payload_data_(NULL),
payload_size_(0),
max_payload_len_(max_payload_len - kGenericHeaderLength),
last_packet_reduction_len_(last_packet_reduction_len),
frame_type_(frame_type),
num_packets_left_(0),
num_larger_packets_(0) {}
RtpPacketizerGeneric::~RtpPacketizerGeneric() {
}
size_t RtpPacketizerGeneric::SetPayloadData(
const uint8_t* payload_data,
size_t payload_size,
const RTPFragmentationHeader* fragmentation) {
payload_data_ = payload_data;
payload_size_ = payload_size;
// Fragment packets such that they are almost the same size, even accounting
// for larger header in the last packet.
// Since we are given how much extra space is occupied by the longer header
// in the last packet, we can pretend that RTP headers are the same, but
// there's last_packet_reduction_len_ virtual payload, to be put at the end of
// the last packet.
//
size_t total_bytes = payload_size_ + last_packet_reduction_len_;
// Minimum needed number of packets to fit payload and virtual payload in the
// last packet.
num_packets_left_ = (total_bytes + max_payload_len_ - 1) / max_payload_len_;
// Given number of packets, calculate average size rounded down.
payload_len_per_packet_ = total_bytes / num_packets_left_;
// If we can't divide everything perfectly evenly, we put 1 extra byte in some
// last packets: 14 bytes in 4 packets would be split as 3+3+4+4.
num_larger_packets_ = total_bytes % num_packets_left_;
RTC_DCHECK_LE(payload_len_per_packet_, max_payload_len_);
generic_header_ = RtpFormatVideoGeneric::kFirstPacketBit;
if (frame_type_ == kVideoFrameKey) {
generic_header_ |= RtpFormatVideoGeneric::kKeyFrameBit;
}
return num_packets_left_;
}
bool RtpPacketizerGeneric::NextPacket(RtpPacketToSend* packet) {
RTC_DCHECK(packet);
if (num_packets_left_ == 0)
return false;
// Last larger_packets_ packets are 1 byte larger than previous packets.
// Increase per packet payload once needed.
if (num_packets_left_ == num_larger_packets_)
++payload_len_per_packet_;
size_t next_packet_payload_len = payload_len_per_packet_;
if (payload_size_ <= next_packet_payload_len) {
// Whole payload fits into this packet.
next_packet_payload_len = payload_size_;
if (num_packets_left_ == 2) {
// This is the penultimate packet. Leave at least 1 payload byte for the
// last packet.
--next_packet_payload_len;
RTC_DCHECK_GT(next_packet_payload_len, 0);
}
}
RTC_DCHECK_LE(next_packet_payload_len, max_payload_len_);
uint8_t* out_ptr =
packet->AllocatePayload(kGenericHeaderLength + next_packet_payload_len);
// Put generic header in packet.
out_ptr[0] = generic_header_;
// Remove first-packet bit, following packets are intermediate.
generic_header_ &= ~RtpFormatVideoGeneric::kFirstPacketBit;
// Put payload in packet.
memcpy(out_ptr + kGenericHeaderLength, payload_data_,
next_packet_payload_len);
payload_data_ += next_packet_payload_len;
payload_size_ -= next_packet_payload_len;
--num_packets_left_;
// Packets left to produce and data left to split should end at the same time.
RTC_DCHECK_EQ(num_packets_left_ == 0, payload_size_ == 0);
packet->SetMarker(payload_size_ == 0);
return true;
}
std::string RtpPacketizerGeneric::ToString() {
return "RtpPacketizerGeneric";
}
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) {
LOG(LS_ERROR) << "Empty payload.";
return false;
}
uint8_t generic_header = *payload_data++;
--payload_data_length;
parsed_payload->frame_type =
((generic_header & RtpFormatVideoGeneric::kKeyFrameBit) != 0)
? kVideoFrameKey
: kVideoFrameDelta;
parsed_payload->type.Video.is_first_packet_in_frame =
(generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0;
parsed_payload->type.Video.codec = kRtpVideoGeneric;
parsed_payload->type.Video.width = 0;
parsed_payload->type.Video.height = 0;
parsed_payload->payload = payload_data;
parsed_payload->payload_length = payload_data_length;
return true;
}
} // namespace webrtc