blob: 8a0810f4451a735baeada089b301cda73d5c124b [file] [log] [blame]
/*
* Copyright (c) 2018 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_generic_frame_descriptor_extension.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
constexpr uint8_t kFlagBeginOfSubframe = 0x80;
constexpr uint8_t kFlagEndOfSubframe = 0x40;
// In version 00, the flags F and L in the first byte correspond to
// kFlagFirstSubframeV00 and kFlagLastSubframeV00. In practice, they were
// always set to `true`.
constexpr uint8_t kFlagFirstSubframeV00 = 0x20;
constexpr uint8_t kFlagLastSubframeV00 = 0x10;
constexpr uint8_t kFlagDependencies = 0x08;
constexpr uint8_t kMaskTemporalLayer = 0x07;
constexpr uint8_t kFlagMoreDependencies = 0x01;
constexpr uint8_t kFlageXtendedOffset = 0x02;
} // namespace
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |B|E|F|L|D| T |
// +-+-+-+-+-+-+-+-+
// B: | S |
// +-+-+-+-+-+-+-+-+
// | |
// B: + FID +
// | |
// +-+-+-+-+-+-+-+-+
// | |
// + Width +
// B=1 | |
// and +-+-+-+-+-+-+-+-+
// D=0 | |
// + Height +
// | |
// +-+-+-+-+-+-+-+-+
// D: | FDIFF |X|M|
// +---------------+
// X: | ... |
// +-+-+-+-+-+-+-+-+
// M: | FDIFF |X|M|
// +---------------+
// | ... |
// +-+-+-+-+-+-+-+-+
constexpr RTPExtensionType RtpGenericFrameDescriptorExtension00::kId;
bool RtpGenericFrameDescriptorExtension00::Parse(
rtc::ArrayView<const uint8_t> data,
RtpGenericFrameDescriptor* descriptor) {
if (data.empty()) {
return false;
}
bool begins_subframe = (data[0] & kFlagBeginOfSubframe) != 0;
descriptor->SetFirstPacketInSubFrame(begins_subframe);
descriptor->SetLastPacketInSubFrame((data[0] & kFlagEndOfSubframe) != 0);
// Parse Subframe details provided in 1st packet of subframe.
if (!begins_subframe) {
return data.size() == 1;
}
if (data.size() < 4) {
return false;
}
descriptor->SetTemporalLayer(data[0] & kMaskTemporalLayer);
descriptor->SetSpatialLayersBitmask(data[1]);
descriptor->SetFrameId(data[2] | (data[3] << 8));
// Parse dependencies.
descriptor->ClearFrameDependencies();
size_t offset = 4;
bool has_more_dependencies = (data[0] & kFlagDependencies) != 0;
if (!has_more_dependencies && data.size() >= offset + 4) {
uint16_t width = (data[offset] << 8) | data[offset + 1];
uint16_t height = (data[offset + 2] << 8) | data[offset + 3];
descriptor->SetResolution(width, height);
offset += 4;
}
while (has_more_dependencies) {
if (data.size() == offset)
return false;
has_more_dependencies = (data[offset] & kFlagMoreDependencies) != 0;
bool extended = (data[offset] & kFlageXtendedOffset) != 0;
uint16_t fdiff = data[offset] >> 2;
offset++;
if (extended) {
if (data.size() == offset)
return false;
fdiff |= (data[offset] << 6);
offset++;
}
if (!descriptor->AddFrameDependencyDiff(fdiff))
return false;
}
return true;
}
size_t RtpGenericFrameDescriptorExtension00::ValueSize(
const RtpGenericFrameDescriptor& descriptor) {
if (!descriptor.FirstPacketInSubFrame())
return 1;
size_t size = 4;
for (uint16_t fdiff : descriptor.FrameDependenciesDiffs()) {
size += (fdiff >= (1 << 6)) ? 2 : 1;
}
if (descriptor.FirstPacketInSubFrame() &&
descriptor.FrameDependenciesDiffs().empty() && descriptor.Width() > 0 &&
descriptor.Height() > 0) {
size += 4;
}
return size;
}
bool RtpGenericFrameDescriptorExtension00::Write(
rtc::ArrayView<uint8_t> data,
const RtpGenericFrameDescriptor& descriptor) {
RTC_CHECK_EQ(data.size(), ValueSize(descriptor));
uint8_t base_header =
(descriptor.FirstPacketInSubFrame() ? kFlagBeginOfSubframe : 0) |
(descriptor.LastPacketInSubFrame() ? kFlagEndOfSubframe : 0);
base_header |= kFlagFirstSubframeV00;
base_header |= kFlagLastSubframeV00;
if (!descriptor.FirstPacketInSubFrame()) {
data[0] = base_header;
return true;
}
data[0] =
base_header |
(descriptor.FrameDependenciesDiffs().empty() ? 0 : kFlagDependencies) |
descriptor.TemporalLayer();
data[1] = descriptor.SpatialLayersBitmask();
uint16_t frame_id = descriptor.FrameId();
data[2] = frame_id & 0xff;
data[3] = frame_id >> 8;
rtc::ArrayView<const uint16_t> fdiffs = descriptor.FrameDependenciesDiffs();
size_t offset = 4;
if (descriptor.FirstPacketInSubFrame() && fdiffs.empty() &&
descriptor.Width() > 0 && descriptor.Height() > 0) {
data[offset++] = (descriptor.Width() >> 8);
data[offset++] = (descriptor.Width() & 0xFF);
data[offset++] = (descriptor.Height() >> 8);
data[offset++] = (descriptor.Height() & 0xFF);
}
for (size_t i = 0; i < fdiffs.size(); i++) {
bool extended = fdiffs[i] >= (1 << 6);
bool more = i < fdiffs.size() - 1;
data[offset++] = ((fdiffs[i] & 0x3f) << 2) |
(extended ? kFlageXtendedOffset : 0) |
(more ? kFlagMoreDependencies : 0);
if (extended) {
data[offset++] = fdiffs[i] >> 6;
}
}
return true;
}
} // namespace webrtc