mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 14:20:45 +01:00

Previously, number of decoder threads for VP9 were always set to 8 but with a cap at number of cores. This was done since we "can't know" the resolution that will be used. With this change, we now intialize the number of threads based on resolution given in InitDecode(). If a resolution change happens in flight, it requires a keyframe. We therefore parse the header from any key frame and if it has a new resolution, we re-initialize the decoder. The number of threads used is based on pixel count. We set one thread as target for 1280x720, and scale up lineraly from there. The 8-thread cap is gone, but still limit it core count. This means for instance: 1 <= 720p, 2 for 1080p, 4 for 1440p, 9 for 4K. Bug: webrtc:11551 Change-Id: I14c169a6c651c50bd1b870c4b22bc4495c8448fd Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174460 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31507}
329 lines
9.7 KiB
C++
329 lines
9.7 KiB
C++
/*
|
|
* Copyright (c) 2017 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/video_coding/utility/vp9_uncompressed_header_parser.h"
|
|
|
|
#include "rtc_base/bit_buffer.h"
|
|
#include "rtc_base/logging.h"
|
|
|
|
namespace webrtc {
|
|
|
|
#define RETURN_FALSE_IF_ERROR(x) \
|
|
if (!(x)) { \
|
|
return false; \
|
|
}
|
|
|
|
namespace vp9 {
|
|
namespace {
|
|
const size_t kVp9NumRefsPerFrame = 3;
|
|
const size_t kVp9MaxRefLFDeltas = 4;
|
|
const size_t kVp9MaxModeLFDeltas = 2;
|
|
|
|
bool Vp9ReadProfile(rtc::BitBuffer* br, uint8_t* profile) {
|
|
uint32_t high_bit;
|
|
uint32_t low_bit;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&low_bit, 1));
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&high_bit, 1));
|
|
*profile = (high_bit << 1) + low_bit;
|
|
if (*profile > 2) {
|
|
uint32_t reserved_bit;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1));
|
|
if (reserved_bit) {
|
|
RTC_LOG(LS_WARNING) << "Failed to get QP. Unsupported bitstream profile.";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Vp9ReadSyncCode(rtc::BitBuffer* br) {
|
|
uint32_t sync_code;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&sync_code, 24));
|
|
if (sync_code != 0x498342) {
|
|
RTC_LOG(LS_WARNING) << "Failed to get QP. Invalid sync code.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Vp9ReadColorConfig(rtc::BitBuffer* br,
|
|
uint8_t profile,
|
|
FrameInfo* frame_info) {
|
|
if (profile == 0 || profile == 1) {
|
|
frame_info->bit_detph = BitDept::k8Bit;
|
|
} else if (profile == 2 || profile == 3) {
|
|
uint32_t ten_or_twelve_bits;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&ten_or_twelve_bits, 1));
|
|
frame_info->bit_detph =
|
|
ten_or_twelve_bits ? BitDept::k12Bit : BitDept::k10Bit;
|
|
}
|
|
uint32_t color_space;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&color_space, 3));
|
|
frame_info->color_space = static_cast<ColorSpace>(color_space);
|
|
|
|
// SRGB is 7.
|
|
if (color_space != 7) {
|
|
uint32_t color_range;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&color_range, 1));
|
|
frame_info->color_range =
|
|
color_range ? ColorRange::kFull : ColorRange::kStudio;
|
|
|
|
if (profile == 1 || profile == 3) {
|
|
uint32_t subsampling_x;
|
|
uint32_t subsampling_y;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&subsampling_x, 1));
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&subsampling_y, 1));
|
|
if (subsampling_x) {
|
|
frame_info->sub_sampling =
|
|
subsampling_y ? YuvSubsampling::k420 : YuvSubsampling::k422;
|
|
} else {
|
|
frame_info->sub_sampling =
|
|
subsampling_y ? YuvSubsampling::k440 : YuvSubsampling::k444;
|
|
}
|
|
|
|
uint32_t reserved_bit;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1));
|
|
if (reserved_bit) {
|
|
RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set.";
|
|
return false;
|
|
}
|
|
} else {
|
|
// Profile 0 or 2.
|
|
frame_info->sub_sampling = YuvSubsampling::k420;
|
|
}
|
|
} else {
|
|
// SRGB
|
|
frame_info->color_range = ColorRange::kFull;
|
|
if (profile == 1 || profile == 3) {
|
|
frame_info->sub_sampling = YuvSubsampling::k444;
|
|
uint32_t reserved_bit;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1));
|
|
if (reserved_bit) {
|
|
RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set.";
|
|
return false;
|
|
}
|
|
} else {
|
|
RTC_LOG(LS_WARNING) << "Failed to parse header. 4:4:4 color not supported"
|
|
" in profile 0 or 2.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Vp9ReadFrameSize(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
|
// 16 bits: frame width - 1.
|
|
uint16_t frame_width_minus_one;
|
|
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&frame_width_minus_one));
|
|
// 16 bits: frame height - 1.
|
|
uint16_t frame_height_minus_one;
|
|
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&frame_height_minus_one));
|
|
frame_info->frame_width = frame_width_minus_one + 1;
|
|
frame_info->frame_height = frame_height_minus_one + 1;
|
|
return true;
|
|
}
|
|
|
|
bool Vp9ReadRenderSize(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
|
uint32_t render_and_frame_size_different;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&render_and_frame_size_different, 1));
|
|
if (render_and_frame_size_different) {
|
|
// 16 bits: render width - 1.
|
|
uint16_t render_width_minus_one;
|
|
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&render_width_minus_one));
|
|
// 16 bits: render height - 1.
|
|
uint16_t render_height_minus_one;
|
|
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&render_height_minus_one));
|
|
frame_info->render_width = render_width_minus_one + 1;
|
|
frame_info->render_height = render_height_minus_one + 1;
|
|
} else {
|
|
frame_info->render_width = frame_info->frame_width;
|
|
frame_info->render_height = frame_info->frame_height;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Vp9ReadFrameSizeFromRefs(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
|
uint32_t found_ref = 0;
|
|
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
|
|
// Size in refs.
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&found_ref, 1));
|
|
if (found_ref)
|
|
break;
|
|
}
|
|
|
|
if (!found_ref) {
|
|
if (!Vp9ReadFrameSize(br, frame_info)) {
|
|
return false;
|
|
}
|
|
}
|
|
return Vp9ReadRenderSize(br, frame_info);
|
|
}
|
|
|
|
bool Vp9ReadInterpolationFilter(rtc::BitBuffer* br) {
|
|
uint32_t bit;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1));
|
|
if (bit)
|
|
return true;
|
|
|
|
return br->ConsumeBits(2);
|
|
}
|
|
|
|
bool Vp9ReadLoopfilter(rtc::BitBuffer* br) {
|
|
// 6 bits: filter level.
|
|
// 3 bits: sharpness level.
|
|
RETURN_FALSE_IF_ERROR(br->ConsumeBits(9));
|
|
|
|
uint32_t mode_ref_delta_enabled;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&mode_ref_delta_enabled, 1));
|
|
if (mode_ref_delta_enabled) {
|
|
uint32_t mode_ref_delta_update;
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&mode_ref_delta_update, 1));
|
|
if (mode_ref_delta_update) {
|
|
uint32_t bit;
|
|
for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) {
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1));
|
|
if (bit) {
|
|
RETURN_FALSE_IF_ERROR(br->ConsumeBits(7));
|
|
}
|
|
}
|
|
for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) {
|
|
RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1));
|
|
if (bit) {
|
|
RETURN_FALSE_IF_ERROR(br->ConsumeBits(7));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
bool Parse(const uint8_t* buf, size_t length, int* qp, FrameInfo* frame_info) {
|
|
rtc::BitBuffer br(buf, length);
|
|
|
|
// Frame marker.
|
|
uint32_t frame_marker;
|
|
RETURN_FALSE_IF_ERROR(br.ReadBits(&frame_marker, 2));
|
|
if (frame_marker != 0x2) {
|
|
RTC_LOG(LS_WARNING) << "Failed to parse header. Frame marker should be 2.";
|
|
return false;
|
|
}
|
|
|
|
// Profile.
|
|
uint8_t profile;
|
|
if (!Vp9ReadProfile(&br, &profile))
|
|
return false;
|
|
frame_info->profile = profile;
|
|
|
|
// Show existing frame.
|
|
uint32_t show_existing_frame;
|
|
RETURN_FALSE_IF_ERROR(br.ReadBits(&show_existing_frame, 1));
|
|
if (show_existing_frame)
|
|
return false;
|
|
|
|
// Frame type: KEY_FRAME(0), INTER_FRAME(1).
|
|
uint32_t frame_type;
|
|
uint32_t show_frame;
|
|
uint32_t error_resilient;
|
|
RETURN_FALSE_IF_ERROR(br.ReadBits(&frame_type, 1));
|
|
RETURN_FALSE_IF_ERROR(br.ReadBits(&show_frame, 1));
|
|
RETURN_FALSE_IF_ERROR(br.ReadBits(&error_resilient, 1));
|
|
frame_info->show_frame = show_frame;
|
|
frame_info->error_resilient = error_resilient;
|
|
|
|
if (frame_type == 0) {
|
|
// Key-frame.
|
|
if (!Vp9ReadSyncCode(&br))
|
|
return false;
|
|
if (!Vp9ReadColorConfig(&br, profile, frame_info))
|
|
return false;
|
|
if (!Vp9ReadFrameSize(&br, frame_info))
|
|
return false;
|
|
if (!Vp9ReadRenderSize(&br, frame_info))
|
|
return false;
|
|
} else {
|
|
// Non-keyframe.
|
|
uint32_t intra_only = 0;
|
|
if (!show_frame)
|
|
RETURN_FALSE_IF_ERROR(br.ReadBits(&intra_only, 1));
|
|
if (!error_resilient)
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(2)); // Reset frame context.
|
|
|
|
if (intra_only) {
|
|
if (!Vp9ReadSyncCode(&br))
|
|
return false;
|
|
|
|
if (profile > 0) {
|
|
if (!Vp9ReadColorConfig(&br, profile, frame_info))
|
|
return false;
|
|
}
|
|
// Refresh frame flags.
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(8));
|
|
if (!Vp9ReadFrameSize(&br, frame_info))
|
|
return false;
|
|
if (!Vp9ReadRenderSize(&br, frame_info))
|
|
return false;
|
|
} else {
|
|
// Refresh frame flags.
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(8));
|
|
|
|
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
|
|
// 3 bits: Ref frame index.
|
|
// 1 bit: Ref frame sign biases.
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(4));
|
|
}
|
|
|
|
if (!Vp9ReadFrameSizeFromRefs(&br, frame_info))
|
|
return false;
|
|
|
|
// Allow high precision mv.
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(1));
|
|
// Interpolation filter.
|
|
if (!Vp9ReadInterpolationFilter(&br))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!error_resilient) {
|
|
// 1 bit: Refresh frame context.
|
|
// 1 bit: Frame parallel decoding mode.
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(2));
|
|
}
|
|
|
|
// Frame context index.
|
|
RETURN_FALSE_IF_ERROR(br.ConsumeBits(2));
|
|
|
|
if (!Vp9ReadLoopfilter(&br))
|
|
return false;
|
|
|
|
// Base QP.
|
|
uint8_t base_q0;
|
|
RETURN_FALSE_IF_ERROR(br.ReadUInt8(&base_q0));
|
|
*qp = base_q0;
|
|
return true;
|
|
}
|
|
|
|
bool GetQp(const uint8_t* buf, size_t length, int* qp) {
|
|
FrameInfo frame_info;
|
|
return Parse(buf, length, qp, &frame_info);
|
|
}
|
|
|
|
absl::optional<FrameInfo> ParseIntraFrameInfo(const uint8_t* buf,
|
|
size_t length) {
|
|
int qp = 0;
|
|
FrameInfo frame_info;
|
|
if (Parse(buf, length, &qp, &frame_info) && frame_info.frame_width > 0) {
|
|
return frame_info;
|
|
}
|
|
return absl::nullopt;
|
|
}
|
|
|
|
} // namespace vp9
|
|
} // namespace webrtc
|