blob: a8b04db78d5e6be180cc083e239718211a049bcd [file] [log] [blame]
/*
* Copyright (c) 2019 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/video_rtp_depacketizer_vp9.h"
#include <string.h>
#include "api/video/video_codec_constants.h"
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "modules/video_coding/codecs/interface/common_constants.h"
#include "rtc_base/bitstream_reader.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
// Picture ID:
//
// +-+-+-+-+-+-+-+-+
// I: |M| PICTURE ID | M:0 => picture id is 7 bits.
// +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits.
// M: | EXTENDED PID |
// +-+-+-+-+-+-+-+-+
//
void ParsePictureId(BitstreamReader& parser, RTPVideoHeaderVP9* vp9) {
if (parser.ReadBit()) { // m_bit
vp9->picture_id = parser.ReadBits(15);
vp9->max_picture_id = kMaxTwoBytePictureId;
} else {
vp9->picture_id = parser.ReadBits(7);
vp9->max_picture_id = kMaxOneBytePictureId;
}
}
// Layer indices :
//
// +-+-+-+-+-+-+-+-+
// L: | T |U| S |D|
// +-+-+-+-+-+-+-+-+
// | TL0PICIDX | (non-flexible mode only)
// +-+-+-+-+-+-+-+-+
//
void ParseLayerInfo(BitstreamReader& parser, RTPVideoHeaderVP9* vp9) {
vp9->temporal_idx = parser.ReadBits(3);
vp9->temporal_up_switch = parser.Read<bool>();
vp9->spatial_idx = parser.ReadBits(3);
vp9->inter_layer_predicted = parser.Read<bool>();
if (vp9->spatial_idx >= kMaxSpatialLayers) {
parser.Invalidate();
return;
}
if (!vp9->flexible_mode) {
vp9->tl0_pic_idx = parser.Read<uint8_t>();
}
}
// Reference indices:
//
// +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index
// P,F: | P_DIFF |N| up to 3 times has to be specified.
// +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows
// current P_DIFF.
//
void ParseRefIndices(BitstreamReader& parser, RTPVideoHeaderVP9* vp9) {
if (vp9->picture_id == kNoPictureId) {
parser.Invalidate();
return;
}
vp9->num_ref_pics = 0;
bool n_bit;
do {
if (vp9->num_ref_pics == kMaxVp9RefPics) {
parser.Invalidate();
return;
}
uint8_t p_diff = parser.ReadBits(7);
n_bit = parser.Read<bool>();
vp9->pid_diff[vp9->num_ref_pics] = p_diff;
uint32_t scaled_pid = vp9->picture_id;
if (p_diff > scaled_pid) {
// TODO(asapersson): Max should correspond to the picture id of last wrap.
scaled_pid += vp9->max_picture_id + 1;
}
vp9->ref_picture_id[vp9->num_ref_pics++] = scaled_pid - p_diff;
} while (n_bit);
}
// Scalability structure (SS).
//
// +-+-+-+-+-+-+-+-+
// V: | N_S |Y|G|-|-|-|
// +-+-+-+-+-+-+-+-+ -|
// Y: | WIDTH | (OPTIONAL) .
// + + .
// | | (OPTIONAL) .
// +-+-+-+-+-+-+-+-+ . N_S + 1 times
// | HEIGHT | (OPTIONAL) .
// + + .
// | | (OPTIONAL) .
// +-+-+-+-+-+-+-+-+ -|
// G: | N_G | (OPTIONAL)
// +-+-+-+-+-+-+-+-+ -|
// N_G: | T |U| R |-|-| (OPTIONAL) .
// +-+-+-+-+-+-+-+-+ -| . N_G times
// | P_DIFF | (OPTIONAL) . R times .
// +-+-+-+-+-+-+-+-+ -| -|
//
void ParseSsData(BitstreamReader& parser, RTPVideoHeaderVP9* vp9) {
vp9->num_spatial_layers = parser.ReadBits(3) + 1;
vp9->spatial_layer_resolution_present = parser.Read<bool>();
bool g_bit = parser.Read<bool>();
parser.ConsumeBits(3);
vp9->gof.num_frames_in_gof = 0;
if (vp9->spatial_layer_resolution_present) {
for (size_t i = 0; i < vp9->num_spatial_layers; ++i) {
vp9->width[i] = parser.Read<uint16_t>();
vp9->height[i] = parser.Read<uint16_t>();
}
}
if (g_bit) {
vp9->gof.num_frames_in_gof = parser.Read<uint8_t>();
}
for (size_t i = 0; i < vp9->gof.num_frames_in_gof; ++i) {
vp9->gof.temporal_idx[i] = parser.ReadBits(3);
vp9->gof.temporal_up_switch[i] = parser.Read<bool>();
vp9->gof.num_ref_pics[i] = parser.ReadBits(2);
parser.ConsumeBits(2);
for (uint8_t p = 0; p < vp9->gof.num_ref_pics[i]; ++p) {
vp9->gof.pid_diff[i][p] = parser.Read<uint8_t>();
}
}
}
} // namespace
absl::optional<VideoRtpDepacketizer::ParsedRtpPayload>
VideoRtpDepacketizerVp9::Parse(rtc::CopyOnWriteBuffer rtp_payload) {
absl::optional<ParsedRtpPayload> result(absl::in_place);
int offset = ParseRtpPayload(rtp_payload, &result->video_header);
if (offset == 0)
return absl::nullopt;
RTC_DCHECK_LT(offset, rtp_payload.size());
result->video_payload =
rtp_payload.Slice(offset, rtp_payload.size() - offset);
return result;
}
int VideoRtpDepacketizerVp9::ParseRtpPayload(
rtc::ArrayView<const uint8_t> rtp_payload,
RTPVideoHeader* video_header) {
RTC_DCHECK(video_header);
// Parse mandatory first byte of payload descriptor.
BitstreamReader parser(rtp_payload);
uint8_t first_byte = parser.Read<uint8_t>();
bool i_bit = first_byte & 0b1000'0000; // PictureId present .
bool p_bit = first_byte & 0b0100'0000; // Inter-picture predicted.
bool l_bit = first_byte & 0b0010'0000; // Layer indices present.
bool f_bit = first_byte & 0b0001'0000; // Flexible mode.
bool b_bit = first_byte & 0b0000'1000; // Begins frame flag.
bool e_bit = first_byte & 0b0000'0100; // Ends frame flag.
bool v_bit = first_byte & 0b0000'0010; // Scalability structure present.
bool z_bit = first_byte & 0b0000'0001; // Not used for inter-layer prediction
// Parsed payload.
video_header->width = 0;
video_header->height = 0;
video_header->simulcastIdx = 0;
video_header->codec = kVideoCodecVP9;
auto& vp9_header =
video_header->video_type_header.emplace<RTPVideoHeaderVP9>();
vp9_header.InitRTPVideoHeaderVP9();
vp9_header.inter_pic_predicted = p_bit;
vp9_header.flexible_mode = f_bit;
vp9_header.beginning_of_frame = b_bit;
vp9_header.end_of_frame = e_bit;
vp9_header.ss_data_available = v_bit;
vp9_header.non_ref_for_inter_layer_pred = z_bit;
// Parse fields that are present.
if (i_bit) {
ParsePictureId(parser, &vp9_header);
}
if (l_bit) {
ParseLayerInfo(parser, &vp9_header);
}
if (p_bit && f_bit) {
ParseRefIndices(parser, &vp9_header);
}
if (v_bit) {
ParseSsData(parser, &vp9_header);
if (vp9_header.spatial_layer_resolution_present) {
// TODO(asapersson): Add support for spatial layers.
video_header->width = vp9_header.width[0];
video_header->height = vp9_header.height[0];
}
}
video_header->frame_type = p_bit || vp9_header.inter_layer_predicted
? VideoFrameType::kVideoFrameDelta
: VideoFrameType::kVideoFrameKey;
video_header->is_first_packet_in_frame = b_bit;
video_header->is_last_packet_in_frame = e_bit;
int num_remaining_bits = parser.RemainingBitCount();
if (num_remaining_bits <= 0) {
// Failed to parse or empty vp9 payload data.
return 0;
}
// vp9 descriptor is byte aligned.
RTC_DCHECK_EQ(num_remaining_bits % 8, 0);
return rtp_payload.size() - num_remaining_bits / 8;
}
} // namespace webrtc