Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,3 @@
|
|||
include_rules = [
|
||||
"+media/base",
|
||||
]
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/video_codecs/scalability_mode.h"
|
||||
#include "api/video_codecs/sdp_video_format.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "modules/video_coding/include/video_codec_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Returns a vector with all supported internal VP9 profiles that we can
|
||||
// negotiate in SDP, in order of preference.
|
||||
std::vector<SdpVideoFormat> SupportedVP9Codecs(
|
||||
bool add_scalability_modes = false);
|
||||
|
||||
// Returns a vector with all supported internal VP9 decode profiles in order of
|
||||
// preference. These will be availble for receive-only connections.
|
||||
std::vector<SdpVideoFormat> SupportedVP9DecoderCodecs();
|
||||
|
||||
class VP9Encoder : public VideoEncoder {
|
||||
public:
|
||||
// Deprecated. Returns default implementation using VP9 Profile 0.
|
||||
// TODO(emircan): Remove once this is no longer used.
|
||||
static std::unique_ptr<VP9Encoder> Create();
|
||||
// Parses VP9 Profile from `codec` and returns the appropriate implementation.
|
||||
static std::unique_ptr<VP9Encoder> Create(const cricket::VideoCodec& codec);
|
||||
static bool SupportsScalabilityMode(ScalabilityMode scalability_mode);
|
||||
|
||||
~VP9Encoder() override {}
|
||||
};
|
||||
|
||||
class VP9Decoder : public VideoDecoder {
|
||||
public:
|
||||
static std::unique_ptr<VP9Decoder> Create();
|
||||
|
||||
~VP9Decoder() override {}
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
|
||||
// This file contains codec dependent definitions that are needed in
|
||||
// order to compile the WebRTC codebase, even if this codec is not used.
|
||||
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "modules/video_coding/codecs/interface/common_constants.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const int16_t kMaxOneBytePictureId = 0x7F; // 7 bits
|
||||
const int16_t kMaxTwoBytePictureId = 0x7FFF; // 15 bits
|
||||
const uint8_t kNoSpatialIdx = 0xFF;
|
||||
const uint8_t kNoGofIdx = 0xFF;
|
||||
const uint8_t kNumVp9Buffers = 8;
|
||||
const size_t kMaxVp9RefPics = 3;
|
||||
const size_t kMaxVp9FramesInGof = 0xFF; // 8 bits
|
||||
const size_t kMaxVp9NumberOfSpatialLayers = 8;
|
||||
|
||||
const size_t kMinVp9SpatialLayerLongSideLength = 240;
|
||||
const size_t kMinVp9SpatialLayerShortSideLength = 135;
|
||||
|
||||
enum TemporalStructureMode {
|
||||
kTemporalStructureMode1, // 1 temporal layer structure - i.e., IPPP...
|
||||
kTemporalStructureMode2, // 2 temporal layers 01...
|
||||
kTemporalStructureMode3, // 3 temporal layers 0212...
|
||||
};
|
||||
|
||||
struct GofInfoVP9 {
|
||||
void SetGofInfoVP9(TemporalStructureMode tm) {
|
||||
switch (tm) {
|
||||
case kTemporalStructureMode1:
|
||||
num_frames_in_gof = 1;
|
||||
temporal_idx[0] = 0;
|
||||
temporal_up_switch[0] = true;
|
||||
num_ref_pics[0] = 1;
|
||||
pid_diff[0][0] = 1;
|
||||
break;
|
||||
case kTemporalStructureMode2:
|
||||
num_frames_in_gof = 2;
|
||||
temporal_idx[0] = 0;
|
||||
temporal_up_switch[0] = true;
|
||||
num_ref_pics[0] = 1;
|
||||
pid_diff[0][0] = 2;
|
||||
|
||||
temporal_idx[1] = 1;
|
||||
temporal_up_switch[1] = true;
|
||||
num_ref_pics[1] = 1;
|
||||
pid_diff[1][0] = 1;
|
||||
break;
|
||||
case kTemporalStructureMode3:
|
||||
num_frames_in_gof = 4;
|
||||
temporal_idx[0] = 0;
|
||||
temporal_up_switch[0] = true;
|
||||
num_ref_pics[0] = 1;
|
||||
pid_diff[0][0] = 4;
|
||||
|
||||
temporal_idx[1] = 2;
|
||||
temporal_up_switch[1] = true;
|
||||
num_ref_pics[1] = 1;
|
||||
pid_diff[1][0] = 1;
|
||||
|
||||
temporal_idx[2] = 1;
|
||||
temporal_up_switch[2] = true;
|
||||
num_ref_pics[2] = 1;
|
||||
pid_diff[2][0] = 2;
|
||||
|
||||
temporal_idx[3] = 2;
|
||||
temporal_up_switch[3] = true;
|
||||
num_ref_pics[3] = 1;
|
||||
pid_diff[3][0] = 1;
|
||||
break;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void CopyGofInfoVP9(const GofInfoVP9& src) {
|
||||
num_frames_in_gof = src.num_frames_in_gof;
|
||||
for (size_t i = 0; i < num_frames_in_gof; ++i) {
|
||||
temporal_idx[i] = src.temporal_idx[i];
|
||||
temporal_up_switch[i] = src.temporal_up_switch[i];
|
||||
num_ref_pics[i] = src.num_ref_pics[i];
|
||||
for (uint8_t r = 0; r < num_ref_pics[i]; ++r) {
|
||||
pid_diff[i][r] = src.pid_diff[i][r];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
friend bool operator==(const GofInfoVP9& lhs, const GofInfoVP9& rhs) {
|
||||
if (lhs.num_frames_in_gof != rhs.num_frames_in_gof ||
|
||||
lhs.pid_start != rhs.pid_start)
|
||||
return false;
|
||||
for (size_t i = 0; i < lhs.num_frames_in_gof; ++i) {
|
||||
if (lhs.temporal_idx[i] != rhs.temporal_idx[i] ||
|
||||
lhs.temporal_up_switch[i] != rhs.temporal_up_switch[i] ||
|
||||
lhs.num_ref_pics[i] != rhs.num_ref_pics[i]) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t r = 0; r < lhs.num_ref_pics[i]; ++r) {
|
||||
if (lhs.pid_diff[i][r] != rhs.pid_diff[i][r])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const GofInfoVP9& lhs, const GofInfoVP9& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
size_t num_frames_in_gof;
|
||||
uint8_t temporal_idx[kMaxVp9FramesInGof];
|
||||
bool temporal_up_switch[kMaxVp9FramesInGof];
|
||||
uint8_t num_ref_pics[kMaxVp9FramesInGof];
|
||||
uint8_t pid_diff[kMaxVp9FramesInGof][kMaxVp9RefPics];
|
||||
uint16_t pid_start;
|
||||
};
|
||||
|
||||
struct RTPVideoHeaderVP9 {
|
||||
void InitRTPVideoHeaderVP9() {
|
||||
inter_pic_predicted = false;
|
||||
flexible_mode = false;
|
||||
beginning_of_frame = false;
|
||||
end_of_frame = false;
|
||||
ss_data_available = false;
|
||||
non_ref_for_inter_layer_pred = false;
|
||||
picture_id = kNoPictureId;
|
||||
max_picture_id = kMaxTwoBytePictureId;
|
||||
tl0_pic_idx = kNoTl0PicIdx;
|
||||
temporal_idx = kNoTemporalIdx;
|
||||
spatial_idx = kNoSpatialIdx;
|
||||
temporal_up_switch = false;
|
||||
inter_layer_predicted = false;
|
||||
gof_idx = kNoGofIdx;
|
||||
num_ref_pics = 0;
|
||||
num_spatial_layers = 1;
|
||||
first_active_layer = 0;
|
||||
end_of_picture = true;
|
||||
}
|
||||
|
||||
friend bool operator==(const RTPVideoHeaderVP9& lhs,
|
||||
const RTPVideoHeaderVP9& rhs) {
|
||||
if (lhs.inter_pic_predicted != rhs.inter_pic_predicted ||
|
||||
lhs.flexible_mode != rhs.flexible_mode ||
|
||||
lhs.beginning_of_frame != rhs.beginning_of_frame ||
|
||||
lhs.end_of_frame != rhs.end_of_frame ||
|
||||
lhs.ss_data_available != rhs.ss_data_available ||
|
||||
lhs.non_ref_for_inter_layer_pred != rhs.non_ref_for_inter_layer_pred ||
|
||||
lhs.picture_id != rhs.picture_id ||
|
||||
lhs.max_picture_id != rhs.max_picture_id ||
|
||||
lhs.tl0_pic_idx != rhs.tl0_pic_idx ||
|
||||
lhs.temporal_idx != rhs.temporal_idx ||
|
||||
lhs.spatial_idx != rhs.spatial_idx || lhs.gof_idx != rhs.gof_idx ||
|
||||
lhs.temporal_up_switch != rhs.temporal_up_switch ||
|
||||
lhs.inter_layer_predicted != rhs.inter_layer_predicted ||
|
||||
lhs.num_ref_pics != rhs.num_ref_pics ||
|
||||
lhs.end_of_picture != rhs.end_of_picture) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t i = 0; i < lhs.num_ref_pics; ++i) {
|
||||
if (lhs.pid_diff[i] != rhs.pid_diff[i] ||
|
||||
lhs.ref_picture_id[i] != rhs.ref_picture_id[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lhs.ss_data_available) {
|
||||
if (lhs.spatial_layer_resolution_present !=
|
||||
rhs.spatial_layer_resolution_present ||
|
||||
lhs.num_spatial_layers != rhs.num_spatial_layers ||
|
||||
lhs.first_active_layer != rhs.first_active_layer ||
|
||||
lhs.gof != rhs.gof) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.spatial_layer_resolution_present) {
|
||||
for (size_t i = 0; i < lhs.num_spatial_layers; i++) {
|
||||
if (lhs.width[i] != rhs.width[i] || lhs.height[i] != rhs.height[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const RTPVideoHeaderVP9& lhs,
|
||||
const RTPVideoHeaderVP9& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool inter_pic_predicted; // This layer frame is dependent on previously
|
||||
// coded frame(s).
|
||||
bool flexible_mode; // This frame is in flexible mode.
|
||||
bool beginning_of_frame; // True if this packet is the first in a VP9 layer
|
||||
// frame.
|
||||
bool end_of_frame; // True if this packet is the last in a VP9 layer frame.
|
||||
bool ss_data_available; // True if SS data is available in this payload
|
||||
// descriptor.
|
||||
bool non_ref_for_inter_layer_pred; // True for frame which is not used as
|
||||
// reference for inter-layer prediction.
|
||||
int16_t picture_id; // PictureID index, 15 bits;
|
||||
// kNoPictureId if PictureID does not exist.
|
||||
int16_t max_picture_id; // Maximum picture ID index; either 0x7F or 0x7FFF;
|
||||
int16_t tl0_pic_idx; // TL0PIC_IDX, 8 bits;
|
||||
// kNoTl0PicIdx means no value provided.
|
||||
uint8_t temporal_idx; // Temporal layer index, or kNoTemporalIdx.
|
||||
uint8_t spatial_idx; // Spatial layer index, or kNoSpatialIdx.
|
||||
bool temporal_up_switch; // True if upswitch to higher frame rate is possible
|
||||
// meaning subsequent higher temporal layer pictures
|
||||
// will not depend on any picture before the current
|
||||
// picture (in coding order) with temporal layer ID
|
||||
// greater than `temporal_idx` of this frame.
|
||||
bool inter_layer_predicted; // Frame is dependent on directly lower spatial
|
||||
// layer frame.
|
||||
|
||||
uint8_t gof_idx; // Index to predefined temporal frame info in SS data.
|
||||
|
||||
uint8_t num_ref_pics; // Number of reference pictures used by this layer
|
||||
// frame.
|
||||
uint8_t pid_diff[kMaxVp9RefPics]; // P_DIFF signaled to derive the PictureID
|
||||
// of the reference pictures.
|
||||
int16_t ref_picture_id[kMaxVp9RefPics]; // PictureID of reference pictures.
|
||||
|
||||
// SS data.
|
||||
size_t num_spatial_layers; // Always populated.
|
||||
size_t first_active_layer; // Not sent on wire, used to adjust ss data.
|
||||
bool spatial_layer_resolution_present;
|
||||
uint16_t width[kMaxVp9NumberOfSpatialLayers];
|
||||
uint16_t height[kMaxVp9NumberOfSpatialLayers];
|
||||
GofInfoVP9 gof;
|
||||
|
||||
bool end_of_picture; // This frame is the last frame in picture.
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
|
||||
#include "modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
#include "api/video/color_space.h"
|
||||
#include "api/video/i010_buffer.h"
|
||||
#include "common_video/include/video_frame_buffer.h"
|
||||
#include "modules/video_coding/utility/vp9_uncompressed_header_parser.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "third_party/libyuv/include/libyuv/convert.h"
|
||||
#include <vpx/vp8dx.h>
|
||||
#include <vpx/vpx_decoder.h>
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
// Helper class for extracting VP9 colorspace.
|
||||
ColorSpace ExtractVP9ColorSpace(vpx_color_space_t space_t,
|
||||
vpx_color_range_t range_t,
|
||||
unsigned int bit_depth) {
|
||||
ColorSpace::PrimaryID primaries = ColorSpace::PrimaryID::kUnspecified;
|
||||
ColorSpace::TransferID transfer = ColorSpace::TransferID::kUnspecified;
|
||||
ColorSpace::MatrixID matrix = ColorSpace::MatrixID::kUnspecified;
|
||||
switch (space_t) {
|
||||
case VPX_CS_BT_601:
|
||||
case VPX_CS_SMPTE_170:
|
||||
primaries = ColorSpace::PrimaryID::kSMPTE170M;
|
||||
transfer = ColorSpace::TransferID::kSMPTE170M;
|
||||
matrix = ColorSpace::MatrixID::kSMPTE170M;
|
||||
break;
|
||||
case VPX_CS_SMPTE_240:
|
||||
primaries = ColorSpace::PrimaryID::kSMPTE240M;
|
||||
transfer = ColorSpace::TransferID::kSMPTE240M;
|
||||
matrix = ColorSpace::MatrixID::kSMPTE240M;
|
||||
break;
|
||||
case VPX_CS_BT_709:
|
||||
primaries = ColorSpace::PrimaryID::kBT709;
|
||||
transfer = ColorSpace::TransferID::kBT709;
|
||||
matrix = ColorSpace::MatrixID::kBT709;
|
||||
break;
|
||||
case VPX_CS_BT_2020:
|
||||
primaries = ColorSpace::PrimaryID::kBT2020;
|
||||
switch (bit_depth) {
|
||||
case 8:
|
||||
transfer = ColorSpace::TransferID::kBT709;
|
||||
break;
|
||||
case 10:
|
||||
transfer = ColorSpace::TransferID::kBT2020_10;
|
||||
break;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
matrix = ColorSpace::MatrixID::kBT2020_NCL;
|
||||
break;
|
||||
case VPX_CS_SRGB:
|
||||
primaries = ColorSpace::PrimaryID::kBT709;
|
||||
transfer = ColorSpace::TransferID::kIEC61966_2_1;
|
||||
matrix = ColorSpace::MatrixID::kBT709;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ColorSpace::RangeID range = ColorSpace::RangeID::kInvalid;
|
||||
switch (range_t) {
|
||||
case VPX_CR_STUDIO_RANGE:
|
||||
range = ColorSpace::RangeID::kLimited;
|
||||
break;
|
||||
case VPX_CR_FULL_RANGE:
|
||||
range = ColorSpace::RangeID::kFull;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ColorSpace(primaries, transfer, matrix, range);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LibvpxVp9Decoder::LibvpxVp9Decoder()
|
||||
: decode_complete_callback_(nullptr),
|
||||
inited_(false),
|
||||
decoder_(nullptr),
|
||||
key_frame_required_(true) {}
|
||||
|
||||
LibvpxVp9Decoder::~LibvpxVp9Decoder() {
|
||||
inited_ = true; // in order to do the actual release
|
||||
Release();
|
||||
int num_buffers_in_use = libvpx_buffer_pool_.GetNumBuffersInUse();
|
||||
if (num_buffers_in_use > 0) {
|
||||
// The frame buffers are reference counted and frames are exposed after
|
||||
// decoding. There may be valid usage cases where previous frames are still
|
||||
// referenced after ~LibvpxVp9Decoder that is not a leak.
|
||||
RTC_LOG(LS_INFO) << num_buffers_in_use
|
||||
<< " Vp9FrameBuffers are still "
|
||||
"referenced during ~LibvpxVp9Decoder.";
|
||||
}
|
||||
}
|
||||
|
||||
bool LibvpxVp9Decoder::Configure(const Settings& settings) {
|
||||
if (Release() < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decoder_ == nullptr) {
|
||||
decoder_ = new vpx_codec_ctx_t;
|
||||
memset(decoder_, 0, sizeof(*decoder_));
|
||||
}
|
||||
vpx_codec_dec_cfg_t cfg;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
// We focus on webrtc fuzzing here, not libvpx itself. Use single thread for
|
||||
// fuzzing, because:
|
||||
// - libvpx's VP9 single thread decoder is more fuzzer friendly. It detects
|
||||
// errors earlier than the multi-threads version.
|
||||
// - Make peak CPU usage under control (not depending on input)
|
||||
cfg.threads = 1;
|
||||
#else
|
||||
const RenderResolution& resolution = settings.max_render_resolution();
|
||||
if (!resolution.Valid()) {
|
||||
// Postpone configuring number of threads until resolution is known.
|
||||
cfg.threads = 1;
|
||||
} else {
|
||||
// We want to use multithreading when decoding high resolution videos. But
|
||||
// not too many in order to avoid overhead when many stream are decoded
|
||||
// concurrently.
|
||||
// Set 2 thread as target for 1280x720 pixel count, and then scale up
|
||||
// linearly from there - but cap at physical core count.
|
||||
// For common resolutions this results in:
|
||||
// 1 for 360p
|
||||
// 2 for 720p
|
||||
// 4 for 1080p
|
||||
// 8 for 1440p
|
||||
// 18 for 4K
|
||||
int num_threads = std::max(
|
||||
1, 2 * resolution.Width() * resolution.Height() / (1280 * 720));
|
||||
cfg.threads = std::min(settings.number_of_cores(), num_threads);
|
||||
}
|
||||
#endif
|
||||
|
||||
current_settings_ = settings;
|
||||
|
||||
vpx_codec_flags_t flags = 0;
|
||||
if (vpx_codec_dec_init(decoder_, vpx_codec_vp9_dx(), &cfg, flags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!libvpx_buffer_pool_.InitializeVpxUsePool(decoder_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inited_ = true;
|
||||
// Always start with a complete key frame.
|
||||
key_frame_required_ = true;
|
||||
if (absl::optional<int> buffer_pool_size = settings.buffer_pool_size()) {
|
||||
if (!libvpx_buffer_pool_.Resize(*buffer_pool_size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vpx_codec_err_t status =
|
||||
vpx_codec_control(decoder_, VP9D_SET_LOOP_FILTER_OPT, 1);
|
||||
if (status != VPX_CODEC_OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to enable VP9D_SET_LOOP_FILTER_OPT. "
|
||||
<< vpx_codec_error(decoder_);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int LibvpxVp9Decoder::Decode(const EncodedImage& input_image,
|
||||
int64_t /*render_time_ms*/) {
|
||||
if (!inited_) {
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
if (decode_complete_callback_ == nullptr) {
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (input_image._frameType == VideoFrameType::kVideoFrameKey) {
|
||||
absl::optional<Vp9UncompressedHeader> frame_info =
|
||||
ParseUncompressedVp9Header(
|
||||
rtc::MakeArrayView(input_image.data(), input_image.size()));
|
||||
if (frame_info) {
|
||||
RenderResolution frame_resolution(frame_info->frame_width,
|
||||
frame_info->frame_height);
|
||||
if (frame_resolution != current_settings_.max_render_resolution()) {
|
||||
// Resolution has changed, tear down and re-init a new decoder in
|
||||
// order to get correct sizing.
|
||||
Release();
|
||||
current_settings_.set_max_render_resolution(frame_resolution);
|
||||
if (!Configure(current_settings_)) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to re-init decoder.";
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse VP9 header from key-frame.";
|
||||
}
|
||||
}
|
||||
|
||||
// Always start with a complete key frame.
|
||||
if (key_frame_required_) {
|
||||
if (input_image._frameType != VideoFrameType::kVideoFrameKey)
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
key_frame_required_ = false;
|
||||
}
|
||||
vpx_codec_iter_t iter = nullptr;
|
||||
vpx_image_t* img;
|
||||
const uint8_t* buffer = input_image.data();
|
||||
if (input_image.size() == 0) {
|
||||
buffer = nullptr; // Triggers full frame concealment.
|
||||
}
|
||||
// During decode libvpx may get and release buffers from
|
||||
// `libvpx_buffer_pool_`. In practice libvpx keeps a few (~3-4) buffers alive
|
||||
// at a time.
|
||||
if (vpx_codec_decode(decoder_, buffer,
|
||||
static_cast<unsigned int>(input_image.size()), 0,
|
||||
VPX_DL_REALTIME)) {
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
// `img->fb_priv` contains the image data, a reference counted Vp9FrameBuffer.
|
||||
// It may be released by libvpx during future vpx_codec_decode or
|
||||
// vpx_codec_destroy calls.
|
||||
img = vpx_codec_get_frame(decoder_, &iter);
|
||||
int qp;
|
||||
vpx_codec_err_t vpx_ret =
|
||||
vpx_codec_control(decoder_, VPXD_GET_LAST_QUANTIZER, &qp);
|
||||
RTC_DCHECK_EQ(vpx_ret, VPX_CODEC_OK);
|
||||
int ret = ReturnFrame(img, input_image.RtpTimestamp(), qp,
|
||||
input_image.ColorSpace());
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int LibvpxVp9Decoder::ReturnFrame(
|
||||
const vpx_image_t* img,
|
||||
uint32_t timestamp,
|
||||
int qp,
|
||||
const webrtc::ColorSpace* explicit_color_space) {
|
||||
if (img == nullptr) {
|
||||
// Decoder OK and nullptr image => No show frame.
|
||||
return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
|
||||
}
|
||||
|
||||
// This buffer contains all of `img`'s image data, a reference counted
|
||||
// Vp9FrameBuffer. (libvpx is done with the buffers after a few
|
||||
// vpx_codec_decode calls or vpx_codec_destroy).
|
||||
rtc::scoped_refptr<Vp9FrameBufferPool::Vp9FrameBuffer> img_buffer(
|
||||
static_cast<Vp9FrameBufferPool::Vp9FrameBuffer*>(img->fb_priv));
|
||||
|
||||
// The buffer can be used directly by the VideoFrame (without copy) by
|
||||
// using a Wrapped*Buffer.
|
||||
rtc::scoped_refptr<VideoFrameBuffer> img_wrapped_buffer;
|
||||
switch (img->fmt) {
|
||||
case VPX_IMG_FMT_I420:
|
||||
img_wrapped_buffer = WrapI420Buffer(
|
||||
img->d_w, img->d_h, img->planes[VPX_PLANE_Y],
|
||||
img->stride[VPX_PLANE_Y], img->planes[VPX_PLANE_U],
|
||||
img->stride[VPX_PLANE_U], img->planes[VPX_PLANE_V],
|
||||
img->stride[VPX_PLANE_V],
|
||||
// WrappedI420Buffer's mechanism for allowing the release of its
|
||||
// frame buffer is through a callback function. This is where we
|
||||
// should release `img_buffer`.
|
||||
[img_buffer] {});
|
||||
break;
|
||||
case VPX_IMG_FMT_I422:
|
||||
img_wrapped_buffer = WrapI422Buffer(
|
||||
img->d_w, img->d_h, img->planes[VPX_PLANE_Y],
|
||||
img->stride[VPX_PLANE_Y], img->planes[VPX_PLANE_U],
|
||||
img->stride[VPX_PLANE_U], img->planes[VPX_PLANE_V],
|
||||
img->stride[VPX_PLANE_V],
|
||||
// WrappedI444Buffer's mechanism for allowing the release of its
|
||||
// frame buffer is through a callback function. This is where we
|
||||
// should release `img_buffer`.
|
||||
[img_buffer] {});
|
||||
break;
|
||||
case VPX_IMG_FMT_I444:
|
||||
img_wrapped_buffer = WrapI444Buffer(
|
||||
img->d_w, img->d_h, img->planes[VPX_PLANE_Y],
|
||||
img->stride[VPX_PLANE_Y], img->planes[VPX_PLANE_U],
|
||||
img->stride[VPX_PLANE_U], img->planes[VPX_PLANE_V],
|
||||
img->stride[VPX_PLANE_V],
|
||||
// WrappedI444Buffer's mechanism for allowing the release of its
|
||||
// frame buffer is through a callback function. This is where we
|
||||
// should release `img_buffer`.
|
||||
[img_buffer] {});
|
||||
break;
|
||||
case VPX_IMG_FMT_I42016:
|
||||
img_wrapped_buffer = WrapI010Buffer(
|
||||
img->d_w, img->d_h,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_Y]),
|
||||
img->stride[VPX_PLANE_Y] / 2,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_U]),
|
||||
img->stride[VPX_PLANE_U] / 2,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_V]),
|
||||
img->stride[VPX_PLANE_V] / 2, [img_buffer] {});
|
||||
break;
|
||||
case VPX_IMG_FMT_I42216:
|
||||
img_wrapped_buffer = WrapI210Buffer(
|
||||
img->d_w, img->d_h,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_Y]),
|
||||
img->stride[VPX_PLANE_Y] / 2,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_U]),
|
||||
img->stride[VPX_PLANE_U] / 2,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_V]),
|
||||
img->stride[VPX_PLANE_V] / 2, [img_buffer] {});
|
||||
break;
|
||||
case VPX_IMG_FMT_I44416:
|
||||
img_wrapped_buffer = WrapI410Buffer(
|
||||
img->d_w, img->d_h,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_Y]),
|
||||
img->stride[VPX_PLANE_Y] / 2,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_U]),
|
||||
img->stride[VPX_PLANE_U] / 2,
|
||||
reinterpret_cast<const uint16_t*>(img->planes[VPX_PLANE_V]),
|
||||
img->stride[VPX_PLANE_V] / 2, [img_buffer] {});
|
||||
break;
|
||||
default:
|
||||
RTC_LOG(LS_ERROR) << "Unsupported pixel format produced by the decoder: "
|
||||
<< static_cast<int>(img->fmt);
|
||||
return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
|
||||
}
|
||||
|
||||
auto builder = VideoFrame::Builder()
|
||||
.set_video_frame_buffer(img_wrapped_buffer)
|
||||
.set_timestamp_rtp(timestamp);
|
||||
if (explicit_color_space) {
|
||||
builder.set_color_space(*explicit_color_space);
|
||||
} else {
|
||||
builder.set_color_space(
|
||||
ExtractVP9ColorSpace(img->cs, img->range, img->bit_depth));
|
||||
}
|
||||
VideoFrame decoded_image = builder.build();
|
||||
|
||||
decode_complete_callback_->Decoded(decoded_image, absl::nullopt, qp);
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int LibvpxVp9Decoder::RegisterDecodeCompleteCallback(
|
||||
DecodedImageCallback* callback) {
|
||||
decode_complete_callback_ = callback;
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int LibvpxVp9Decoder::Release() {
|
||||
int ret_val = WEBRTC_VIDEO_CODEC_OK;
|
||||
|
||||
if (decoder_ != nullptr) {
|
||||
if (inited_) {
|
||||
// When a codec is destroyed libvpx will release any buffers of
|
||||
// `libvpx_buffer_pool_` it is currently using.
|
||||
if (vpx_codec_destroy(decoder_)) {
|
||||
ret_val = WEBRTC_VIDEO_CODEC_MEMORY;
|
||||
}
|
||||
}
|
||||
delete decoder_;
|
||||
decoder_ = nullptr;
|
||||
}
|
||||
// Releases buffers from the pool. Any buffers not in use are deleted. Buffers
|
||||
// still referenced externally are deleted once fully released, not returning
|
||||
// to the pool.
|
||||
libvpx_buffer_pool_.ClearPool();
|
||||
inited_ = false;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
VideoDecoder::DecoderInfo LibvpxVp9Decoder::GetDecoderInfo() const {
|
||||
DecoderInfo info;
|
||||
info.implementation_name = "libvpx";
|
||||
info.is_hardware_accelerated = false;
|
||||
return info;
|
||||
}
|
||||
|
||||
const char* LibvpxVp9Decoder::ImplementationName() const {
|
||||
return "libvpx";
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_ENABLE_VP9
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_LIBVPX_VP9_DECODER_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP9_LIBVPX_VP9_DECODER_H_
|
||||
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
|
||||
#include "api/video_codecs/video_decoder.h"
|
||||
#include "modules/video_coding/codecs/vp9/include/vp9.h"
|
||||
#include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
|
||||
#include <vpx/vp8cx.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class LibvpxVp9Decoder : public VP9Decoder {
|
||||
public:
|
||||
LibvpxVp9Decoder();
|
||||
virtual ~LibvpxVp9Decoder();
|
||||
|
||||
bool Configure(const Settings& settings) override;
|
||||
|
||||
int Decode(const EncodedImage& input_image,
|
||||
int64_t /*render_time_ms*/) override;
|
||||
|
||||
int RegisterDecodeCompleteCallback(DecodedImageCallback* callback) override;
|
||||
|
||||
int Release() override;
|
||||
|
||||
DecoderInfo GetDecoderInfo() const override;
|
||||
const char* ImplementationName() const override;
|
||||
|
||||
private:
|
||||
int ReturnFrame(const vpx_image_t* img,
|
||||
uint32_t timestamp,
|
||||
int qp,
|
||||
const webrtc::ColorSpace* explicit_color_space);
|
||||
|
||||
// Memory pool used to share buffers between libvpx and webrtc.
|
||||
Vp9FrameBufferPool libvpx_buffer_pool_;
|
||||
DecodedImageCallback* decode_complete_callback_;
|
||||
bool inited_;
|
||||
vpx_codec_ctx_t* decoder_;
|
||||
bool key_frame_required_;
|
||||
Settings current_settings_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_ENABLE_VP9
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP9_LIBVPX_VP9_DECODER_H_
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_LIBVPX_VP9_ENCODER_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP9_LIBVPX_VP9_ENCODER_H_
|
||||
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/fec_controller_override.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/video_codecs/scalability_mode.h"
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "api/video_codecs/vp9_profile.h"
|
||||
#include "common_video/include/video_frame_buffer_pool.h"
|
||||
#include "modules/video_coding/codecs/interface/libvpx_interface.h"
|
||||
#include "modules/video_coding/codecs/vp9/include/vp9.h"
|
||||
#include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
|
||||
#include "modules/video_coding/svc/scalable_video_controller.h"
|
||||
#include "modules/video_coding/utility/framerate_controller_deprecated.h"
|
||||
#include "rtc_base/containers/flat_map.h"
|
||||
#include "rtc_base/experiments/encoder_info_settings.h"
|
||||
#include <vpx/vp8cx.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class LibvpxVp9Encoder : public VP9Encoder {
|
||||
public:
|
||||
LibvpxVp9Encoder(const cricket::VideoCodec& codec,
|
||||
std::unique_ptr<LibvpxInterface> interface,
|
||||
const FieldTrialsView& trials);
|
||||
|
||||
~LibvpxVp9Encoder() override;
|
||||
|
||||
void SetFecControllerOverride(
|
||||
FecControllerOverride* fec_controller_override) override;
|
||||
|
||||
int Release() override;
|
||||
|
||||
int InitEncode(const VideoCodec* codec_settings,
|
||||
const Settings& settings) override;
|
||||
|
||||
int Encode(const VideoFrame& input_image,
|
||||
const std::vector<VideoFrameType>* frame_types) override;
|
||||
|
||||
int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
|
||||
|
||||
void SetRates(const RateControlParameters& parameters) override;
|
||||
|
||||
EncoderInfo GetEncoderInfo() const override;
|
||||
|
||||
private:
|
||||
// Determine number of encoder threads to use.
|
||||
int NumberOfThreads(int width, int height, int number_of_cores);
|
||||
|
||||
// Call encoder initialize function and set control settings.
|
||||
int InitAndSetControlSettings(const VideoCodec* inst);
|
||||
|
||||
bool PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
|
||||
absl::optional<int>* spatial_idx,
|
||||
absl::optional<int>* temporal_idx,
|
||||
const vpx_codec_cx_pkt& pkt);
|
||||
void FillReferenceIndices(const vpx_codec_cx_pkt& pkt,
|
||||
size_t pic_num,
|
||||
bool inter_layer_predicted,
|
||||
CodecSpecificInfoVP9* vp9_info);
|
||||
void UpdateReferenceBuffers(const vpx_codec_cx_pkt& pkt, size_t pic_num);
|
||||
vpx_svc_ref_frame_config_t SetReferences(bool is_key_pic,
|
||||
int first_active_spatial_layer_id);
|
||||
|
||||
bool ExplicitlyConfiguredSpatialLayers() const;
|
||||
bool SetSvcRates(const VideoBitrateAllocation& bitrate_allocation);
|
||||
|
||||
// Configures which spatial layers libvpx should encode according to
|
||||
// configuration provided by svc_controller_.
|
||||
void EnableSpatialLayer(int sid);
|
||||
void DisableSpatialLayer(int sid);
|
||||
void SetActiveSpatialLayers();
|
||||
|
||||
void GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt);
|
||||
|
||||
// Callback function for outputting packets per spatial layer.
|
||||
static void EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
|
||||
void* user_data);
|
||||
|
||||
void DeliverBufferedFrame(bool end_of_picture);
|
||||
|
||||
bool DropFrame(uint8_t spatial_idx, uint32_t rtp_timestamp);
|
||||
|
||||
// Determine maximum target for Intra frames
|
||||
//
|
||||
// Input:
|
||||
// - optimal_buffer_size : Optimal buffer size
|
||||
// Return Value : Max target size for Intra frames represented as
|
||||
// percentage of the per frame bandwidth
|
||||
uint32_t MaxIntraTarget(uint32_t optimal_buffer_size);
|
||||
|
||||
size_t SteadyStateSize(int sid, int tid);
|
||||
|
||||
void MaybeRewrapRawWithFormat(vpx_img_fmt fmt);
|
||||
// Prepares `raw_` to reference image data of `buffer`, or of mapped or scaled
|
||||
// versions of `buffer`. Returns the buffer that got referenced as a result,
|
||||
// allowing the caller to keep a reference to it until after encoding has
|
||||
// finished. On failure to convert the buffer, null is returned.
|
||||
rtc::scoped_refptr<VideoFrameBuffer> PrepareBufferForProfile0(
|
||||
rtc::scoped_refptr<VideoFrameBuffer> buffer);
|
||||
|
||||
const std::unique_ptr<LibvpxInterface> libvpx_;
|
||||
EncodedImage encoded_image_;
|
||||
CodecSpecificInfo codec_specific_;
|
||||
EncodedImageCallback* encoded_complete_callback_;
|
||||
VideoCodec codec_;
|
||||
const VP9Profile profile_;
|
||||
bool inited_;
|
||||
int64_t timestamp_;
|
||||
uint32_t rc_max_intra_target_;
|
||||
vpx_codec_ctx_t* encoder_;
|
||||
vpx_codec_enc_cfg_t* config_;
|
||||
vpx_image_t* raw_;
|
||||
vpx_svc_extra_cfg_t svc_params_;
|
||||
const VideoFrame* input_image_;
|
||||
GofInfoVP9 gof_; // Contains each frame's temporal information for
|
||||
// non-flexible mode.
|
||||
bool force_key_frame_;
|
||||
size_t pics_since_key_;
|
||||
uint8_t num_temporal_layers_;
|
||||
uint8_t num_spatial_layers_; // Number of configured SLs
|
||||
uint8_t num_active_spatial_layers_; // Number of actively encoded SLs
|
||||
uint8_t first_active_layer_;
|
||||
bool layer_deactivation_requires_key_frame_;
|
||||
bool is_svc_;
|
||||
InterLayerPredMode inter_layer_pred_;
|
||||
bool external_ref_control_;
|
||||
const bool trusted_rate_controller_;
|
||||
vpx_svc_frame_drop_t svc_drop_frame_;
|
||||
bool first_frame_in_picture_;
|
||||
VideoBitrateAllocation current_bitrate_allocation_;
|
||||
bool ss_info_needed_;
|
||||
bool force_all_active_layers_;
|
||||
|
||||
std::unique_ptr<ScalableVideoController> svc_controller_;
|
||||
absl::optional<ScalabilityMode> scalability_mode_;
|
||||
std::vector<FramerateControllerDeprecated> framerate_controller_;
|
||||
|
||||
// Used for flexible mode.
|
||||
bool is_flexible_mode_;
|
||||
struct RefFrameBuffer {
|
||||
bool operator==(const RefFrameBuffer& o) {
|
||||
return pic_num == o.pic_num && spatial_layer_id == o.spatial_layer_id &&
|
||||
temporal_layer_id == o.temporal_layer_id;
|
||||
}
|
||||
|
||||
size_t pic_num = 0;
|
||||
int spatial_layer_id = 0;
|
||||
int temporal_layer_id = 0;
|
||||
};
|
||||
std::array<RefFrameBuffer, kNumVp9Buffers> ref_buf_;
|
||||
std::vector<ScalableVideoController::LayerFrameConfig> layer_frames_;
|
||||
|
||||
// Variable frame-rate related fields and methods.
|
||||
const struct VariableFramerateExperiment {
|
||||
bool enabled;
|
||||
// Framerate is limited to this value in steady state.
|
||||
float framerate_limit;
|
||||
// This qp or below is considered a steady state.
|
||||
int steady_state_qp;
|
||||
// Frames of at least this percentage below ideal for configured bitrate are
|
||||
// considered in a steady state.
|
||||
int steady_state_undershoot_percentage;
|
||||
// Number of consecutive frames with good QP and size required to detect
|
||||
// the steady state.
|
||||
int frames_before_steady_state;
|
||||
} variable_framerate_experiment_;
|
||||
static VariableFramerateExperiment ParseVariableFramerateConfig(
|
||||
const FieldTrialsView& trials);
|
||||
FramerateControllerDeprecated variable_framerate_controller_;
|
||||
|
||||
const struct QualityScalerExperiment {
|
||||
int low_qp;
|
||||
int high_qp;
|
||||
bool enabled;
|
||||
} quality_scaler_experiment_;
|
||||
static QualityScalerExperiment ParseQualityScalerConfig(
|
||||
const FieldTrialsView& trials);
|
||||
const bool external_ref_ctrl_;
|
||||
|
||||
// Flags that can affect speed vs quality tradeoff, and are configureable per
|
||||
// resolution ranges.
|
||||
struct PerformanceFlags {
|
||||
// If false, a lookup will be made in `settings_by_resolution` base on the
|
||||
// highest currently active resolution, and the overall speed then set to
|
||||
// to the `base_layer_speed` matching that entry.
|
||||
// If true, each active resolution will have it's speed and deblock_mode set
|
||||
// based on it resolution, and the high layer speed configured for non
|
||||
// base temporal layer frames.
|
||||
bool use_per_layer_speed = false;
|
||||
|
||||
struct ParameterSet {
|
||||
int base_layer_speed = -1; // Speed setting for TL0.
|
||||
int high_layer_speed = -1; // Speed setting for TL1-TL3.
|
||||
// 0 = deblock all temporal layers (TL)
|
||||
// 1 = disable deblock for top-most TL
|
||||
// 2 = disable deblock for all TLs
|
||||
int deblock_mode = 0;
|
||||
bool allow_denoising = true;
|
||||
};
|
||||
// Map from min pixel count to settings for that resolution and above.
|
||||
// E.g. if you want some settings A if below wvga (640x360) and some other
|
||||
// setting B at wvga and above, you'd use map {{0, A}, {230400, B}}.
|
||||
flat_map<int, ParameterSet> settings_by_resolution;
|
||||
};
|
||||
// Performance flags, ordered by `min_pixel_count`.
|
||||
const PerformanceFlags performance_flags_;
|
||||
// Caching of of `speed_configs_`, where index i maps to the resolution as
|
||||
// specified in `codec_.spatialLayer[i]`.
|
||||
std::vector<PerformanceFlags::ParameterSet>
|
||||
performance_flags_by_spatial_index_;
|
||||
void UpdatePerformanceFlags();
|
||||
static PerformanceFlags ParsePerformanceFlagsFromTrials(
|
||||
const FieldTrialsView& trials);
|
||||
static PerformanceFlags GetDefaultPerformanceFlags();
|
||||
|
||||
int num_steady_state_frames_;
|
||||
// Only set config when this flag is set.
|
||||
bool config_changed_;
|
||||
|
||||
const LibvpxVp9EncoderInfoSettings encoder_info_override_;
|
||||
|
||||
const struct SvcFrameDropConfig {
|
||||
bool enabled;
|
||||
int layer_drop_mode; // SVC_LAYER_DROP_MODE
|
||||
int max_consec_drop;
|
||||
} svc_frame_drop_config_;
|
||||
static SvcFrameDropConfig ParseSvcFrameDropConfig(
|
||||
const FieldTrialsView& trials);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_ENABLE_VP9
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP9_LIBVPX_VP9_ENCODER_H_
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* 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/video_coding/codecs/vp9/svc_config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "media/base/video_common.h"
|
||||
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
|
||||
#include "modules/video_coding/svc/create_scalability_structure.h"
|
||||
#include "modules/video_coding/svc/scalability_mode_util.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
const size_t kMinVp9SvcBitrateKbps = 30;
|
||||
|
||||
const size_t kMaxNumLayersForScreenSharing = 3;
|
||||
const float kMaxScreenSharingLayerFramerateFps[] = {5.0, 10.0, 30.0};
|
||||
const size_t kMinScreenSharingLayerBitrateKbps[] = {30, 200, 500};
|
||||
const size_t kTargetScreenSharingLayerBitrateKbps[] = {150, 350, 950};
|
||||
const size_t kMaxScreenSharingLayerBitrateKbps[] = {250, 500, 950};
|
||||
|
||||
// Gets limited number of layers for given resolution.
|
||||
size_t GetLimitedNumSpatialLayers(size_t width, size_t height) {
|
||||
const bool is_landscape = width >= height;
|
||||
const size_t min_width = is_landscape ? kMinVp9SpatialLayerLongSideLength
|
||||
: kMinVp9SpatialLayerShortSideLength;
|
||||
const size_t min_height = is_landscape ? kMinVp9SpatialLayerShortSideLength
|
||||
: kMinVp9SpatialLayerLongSideLength;
|
||||
const size_t num_layers_fit_horz = static_cast<size_t>(
|
||||
std::floor(1 + std::max(0.0f, std::log2(1.0f * width / min_width))));
|
||||
const size_t num_layers_fit_vert = static_cast<size_t>(
|
||||
std::floor(1 + std::max(0.0f, std::log2(1.0f * height / min_height))));
|
||||
return std::min(num_layers_fit_horz, num_layers_fit_vert);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<SpatialLayer> ConfigureSvcScreenSharing(size_t input_width,
|
||||
size_t input_height,
|
||||
float max_framerate_fps,
|
||||
size_t num_spatial_layers) {
|
||||
num_spatial_layers =
|
||||
std::min(num_spatial_layers, kMaxNumLayersForScreenSharing);
|
||||
std::vector<SpatialLayer> spatial_layers;
|
||||
|
||||
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
|
||||
SpatialLayer spatial_layer = {0};
|
||||
spatial_layer.width = input_width;
|
||||
spatial_layer.height = input_height;
|
||||
spatial_layer.maxFramerate =
|
||||
std::min(kMaxScreenSharingLayerFramerateFps[sl_idx], max_framerate_fps);
|
||||
spatial_layer.numberOfTemporalLayers = 1;
|
||||
spatial_layer.minBitrate =
|
||||
static_cast<int>(kMinScreenSharingLayerBitrateKbps[sl_idx]);
|
||||
spatial_layer.maxBitrate =
|
||||
static_cast<int>(kMaxScreenSharingLayerBitrateKbps[sl_idx]);
|
||||
spatial_layer.targetBitrate =
|
||||
static_cast<int>(kTargetScreenSharingLayerBitrateKbps[sl_idx]);
|
||||
spatial_layer.active = true;
|
||||
spatial_layers.push_back(spatial_layer);
|
||||
}
|
||||
|
||||
return spatial_layers;
|
||||
}
|
||||
|
||||
std::vector<SpatialLayer> ConfigureSvcNormalVideo(
|
||||
size_t input_width,
|
||||
size_t input_height,
|
||||
float max_framerate_fps,
|
||||
size_t first_active_layer,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers,
|
||||
absl::optional<ScalableVideoController::StreamLayersConfig> config) {
|
||||
RTC_DCHECK_LT(first_active_layer, num_spatial_layers);
|
||||
|
||||
// Limit number of layers for given resolution.
|
||||
size_t limited_num_spatial_layers =
|
||||
GetLimitedNumSpatialLayers(input_width, input_height);
|
||||
if (limited_num_spatial_layers < num_spatial_layers) {
|
||||
RTC_LOG(LS_WARNING) << "Reducing number of spatial layers from "
|
||||
<< num_spatial_layers << " to "
|
||||
<< limited_num_spatial_layers
|
||||
<< " due to low input resolution.";
|
||||
num_spatial_layers = limited_num_spatial_layers;
|
||||
}
|
||||
|
||||
// First active layer must be configured.
|
||||
num_spatial_layers = std::max(num_spatial_layers, first_active_layer + 1);
|
||||
|
||||
// Ensure top layer is even enough.
|
||||
int required_divisiblity = 1 << (num_spatial_layers - first_active_layer - 1);
|
||||
if (config) {
|
||||
required_divisiblity = 1;
|
||||
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
|
||||
required_divisiblity = cricket::LeastCommonMultiple(
|
||||
required_divisiblity, config->scaling_factor_den[sl_idx]);
|
||||
}
|
||||
}
|
||||
input_width = input_width - input_width % required_divisiblity;
|
||||
input_height = input_height - input_height % required_divisiblity;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers;
|
||||
for (size_t sl_idx = first_active_layer; sl_idx < num_spatial_layers;
|
||||
++sl_idx) {
|
||||
SpatialLayer spatial_layer = {0};
|
||||
spatial_layer.width = input_width >> (num_spatial_layers - sl_idx - 1);
|
||||
spatial_layer.height = input_height >> (num_spatial_layers - sl_idx - 1);
|
||||
spatial_layer.maxFramerate = max_framerate_fps;
|
||||
spatial_layer.numberOfTemporalLayers = num_temporal_layers;
|
||||
spatial_layer.active = true;
|
||||
|
||||
if (config) {
|
||||
spatial_layer.width = input_width * config->scaling_factor_num[sl_idx] /
|
||||
config->scaling_factor_den[sl_idx];
|
||||
spatial_layer.height = input_height * config->scaling_factor_num[sl_idx] /
|
||||
config->scaling_factor_den[sl_idx];
|
||||
}
|
||||
|
||||
// minBitrate and maxBitrate formulas were derived from
|
||||
// subjective-quality data to determing bit rates below which video
|
||||
// quality is unacceptable and above which additional bits do not provide
|
||||
// benefit. The formulas express rate in units of kbps.
|
||||
|
||||
// TODO(ssilkin): Add to the comment PSNR/SSIM we get at encoding certain
|
||||
// video to min/max bitrate specified by those formulas.
|
||||
const size_t num_pixels = spatial_layer.width * spatial_layer.height;
|
||||
int min_bitrate =
|
||||
static_cast<int>((600. * std::sqrt(num_pixels) - 95000.) / 1000.);
|
||||
min_bitrate = std::max(min_bitrate, 0);
|
||||
spatial_layer.minBitrate =
|
||||
std::max(static_cast<size_t>(min_bitrate), kMinVp9SvcBitrateKbps);
|
||||
spatial_layer.maxBitrate =
|
||||
static_cast<int>((1.6 * num_pixels + 50 * 1000) / 1000);
|
||||
spatial_layer.targetBitrate =
|
||||
(spatial_layer.minBitrate + spatial_layer.maxBitrate) / 2;
|
||||
spatial_layers.push_back(spatial_layer);
|
||||
}
|
||||
|
||||
// A workaround for situation when single HD layer is left with minBitrate
|
||||
// about 500kbps. This would mean that there will always be at least 500kbps
|
||||
// allocated to video regardless of how low is the actual BWE.
|
||||
// Also, boost maxBitrate for the first layer to account for lost ability to
|
||||
// predict from previous layers.
|
||||
if (first_active_layer > 0) {
|
||||
spatial_layers[0].minBitrate = kMinVp9SvcBitrateKbps;
|
||||
// TODO(ilnik): tune this value or come up with a different formula to
|
||||
// ensure that all singlecast configurations look good and not too much
|
||||
// bitrate is added.
|
||||
spatial_layers[0].maxBitrate *= 1.1;
|
||||
}
|
||||
|
||||
return spatial_layers;
|
||||
}
|
||||
|
||||
// Uses scalability mode to configure spatial layers.
|
||||
std::vector<SpatialLayer> GetVp9SvcConfig(VideoCodec& codec) {
|
||||
RTC_DCHECK_EQ(codec.codecType, kVideoCodecVP9);
|
||||
|
||||
absl::optional<ScalabilityMode> scalability_mode = codec.GetScalabilityMode();
|
||||
RTC_DCHECK(scalability_mode.has_value());
|
||||
|
||||
bool requested_single_spatial_layer =
|
||||
ScalabilityModeToNumSpatialLayers(*scalability_mode) == 1;
|
||||
|
||||
// Limit number of spatial layers for given resolution.
|
||||
int limited_num_spatial_layers =
|
||||
GetLimitedNumSpatialLayers(codec.width, codec.height);
|
||||
if (limited_num_spatial_layers <
|
||||
ScalabilityModeToNumSpatialLayers(*scalability_mode)) {
|
||||
ScalabilityMode limited_scalability_mode =
|
||||
LimitNumSpatialLayers(*scalability_mode, limited_num_spatial_layers);
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Reducing number of spatial layers due to low input resolution: "
|
||||
<< ScalabilityModeToString(*scalability_mode) << " to "
|
||||
<< ScalabilityModeToString(limited_scalability_mode);
|
||||
scalability_mode = limited_scalability_mode;
|
||||
codec.SetScalabilityMode(limited_scalability_mode);
|
||||
}
|
||||
|
||||
codec.VP9()->interLayerPred =
|
||||
ScalabilityModeToInterLayerPredMode(*scalability_mode);
|
||||
|
||||
absl::optional<ScalableVideoController::StreamLayersConfig> info =
|
||||
ScalabilityStructureConfig(*scalability_mode);
|
||||
if (!info.has_value()) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to create structure "
|
||||
<< ScalabilityModeToString(*scalability_mode);
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org/11607): Add support for screensharing.
|
||||
std::vector<SpatialLayer> spatial_layers =
|
||||
GetSvcConfig(codec.width, codec.height, codec.maxFramerate,
|
||||
/*first_active_layer=*/0, info->num_spatial_layers,
|
||||
info->num_temporal_layers, /*is_screen_sharing=*/false,
|
||||
codec.GetScalabilityMode() ? info : absl::nullopt);
|
||||
RTC_DCHECK(!spatial_layers.empty());
|
||||
|
||||
spatial_layers[0].minBitrate = kMinVp9SvcBitrateKbps;
|
||||
|
||||
// Use codec bitrate limits if spatial layering is not requested.
|
||||
if (requested_single_spatial_layer) {
|
||||
SpatialLayer& spatial_layer = spatial_layers[0];
|
||||
spatial_layer.minBitrate = codec.minBitrate;
|
||||
spatial_layer.maxBitrate = codec.maxBitrate;
|
||||
spatial_layer.targetBitrate = codec.maxBitrate;
|
||||
}
|
||||
|
||||
return spatial_layers;
|
||||
}
|
||||
|
||||
std::vector<SpatialLayer> GetSvcConfig(
|
||||
size_t input_width,
|
||||
size_t input_height,
|
||||
float max_framerate_fps,
|
||||
size_t first_active_layer,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers,
|
||||
bool is_screen_sharing,
|
||||
absl::optional<ScalableVideoController::StreamLayersConfig> config) {
|
||||
RTC_DCHECK_GT(input_width, 0);
|
||||
RTC_DCHECK_GT(input_height, 0);
|
||||
RTC_DCHECK_GT(num_spatial_layers, 0);
|
||||
RTC_DCHECK_GT(num_temporal_layers, 0);
|
||||
|
||||
if (is_screen_sharing) {
|
||||
return ConfigureSvcScreenSharing(input_width, input_height,
|
||||
max_framerate_fps, num_spatial_layers);
|
||||
} else {
|
||||
return ConfigureSvcNormalVideo(input_width, input_height, max_framerate_fps,
|
||||
first_active_layer, num_spatial_layers,
|
||||
num_temporal_layers, config);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_SVC_CONFIG_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP9_SVC_CONFIG_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/video_codecs/spatial_layer.h"
|
||||
#include "api/video_codecs/video_codec.h"
|
||||
#include "modules/video_coding/svc/scalable_video_controller.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Uses scalability mode to configure spatial layers.
|
||||
std::vector<SpatialLayer> GetVp9SvcConfig(VideoCodec& video_codec);
|
||||
|
||||
std::vector<SpatialLayer> GetSvcConfig(
|
||||
size_t input_width,
|
||||
size_t input_height,
|
||||
float max_framerate_fps,
|
||||
size_t first_active_layer,
|
||||
size_t num_spatial_layers,
|
||||
size_t num_temporal_layers,
|
||||
bool is_screen_sharing,
|
||||
absl::optional<ScalableVideoController::StreamLayersConfig> config =
|
||||
absl::nullopt);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP9_SVC_CONFIG_H_
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* 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/video_coding/codecs/vp9/svc_config.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Field;
|
||||
|
||||
namespace webrtc {
|
||||
TEST(SvcConfig, NumSpatialLayers) {
|
||||
const size_t max_num_spatial_layers = 6;
|
||||
const size_t first_active_layer = 0;
|
||||
const size_t num_spatial_layers = 2;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
|
||||
kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1),
|
||||
kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30,
|
||||
first_active_layer, max_num_spatial_layers, 1, false);
|
||||
|
||||
EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, NumSpatialLayersPortrait) {
|
||||
const size_t max_num_spatial_layers = 6;
|
||||
const size_t first_active_layer = 0;
|
||||
const size_t num_spatial_layers = 2;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
|
||||
kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1),
|
||||
kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1), 30,
|
||||
first_active_layer, max_num_spatial_layers, 1, false);
|
||||
|
||||
EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, NumSpatialLayersWithScalabilityMode) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 960;
|
||||
codec.height = 540;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T3_KEY);
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, ElementsAre(Field(&SpatialLayer::height, 135),
|
||||
Field(&SpatialLayer::height, 270),
|
||||
Field(&SpatialLayer::height, 540)));
|
||||
EXPECT_THAT(spatial_layers,
|
||||
ElementsAre(Field(&SpatialLayer::numberOfTemporalLayers, 3),
|
||||
Field(&SpatialLayer::numberOfTemporalLayers, 3),
|
||||
Field(&SpatialLayer::numberOfTemporalLayers, 3)));
|
||||
EXPECT_EQ(codec.GetScalabilityMode(), ScalabilityMode::kL3T3_KEY);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, UpdatesInterLayerPredModeBasedOnScalabilityMode) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 1280;
|
||||
codec.height = 720;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T3_KEY);
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_EQ(codec.VP9()->interLayerPred, InterLayerPredMode::kOnKeyPic);
|
||||
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T3);
|
||||
spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_EQ(codec.VP9()->interLayerPred, InterLayerPredMode::kOn);
|
||||
|
||||
codec.SetScalabilityMode(ScalabilityMode::kS3T3);
|
||||
spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_EQ(codec.VP9()->interLayerPred, InterLayerPredMode::kOff);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, NumSpatialLayersLimitedWithScalabilityMode) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 480;
|
||||
codec.height = 270;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T3_KEY);
|
||||
|
||||
// Scalability mode updated.
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, ElementsAre(Field(&SpatialLayer::height, 135),
|
||||
Field(&SpatialLayer::height, 270)));
|
||||
EXPECT_THAT(spatial_layers,
|
||||
ElementsAre(Field(&SpatialLayer::numberOfTemporalLayers, 3),
|
||||
Field(&SpatialLayer::numberOfTemporalLayers, 3)));
|
||||
EXPECT_EQ(codec.GetScalabilityMode(), ScalabilityMode::kL2T3_KEY);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, NumSpatialLayersLimitedWithScalabilityModePortrait) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 270;
|
||||
codec.height = 480;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T1);
|
||||
|
||||
// Scalability mode updated.
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, ElementsAre(Field(&SpatialLayer::width, 135),
|
||||
Field(&SpatialLayer::width, 270)));
|
||||
EXPECT_THAT(spatial_layers,
|
||||
ElementsAre(Field(&SpatialLayer::numberOfTemporalLayers, 1),
|
||||
Field(&SpatialLayer::numberOfTemporalLayers, 1)));
|
||||
EXPECT_EQ(codec.GetScalabilityMode(), ScalabilityMode::kL2T1);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, NumSpatialLayersWithScalabilityModeResolutionRatio1_5) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 270;
|
||||
codec.height = 480;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL2T1h); // 1.5:1
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, ElementsAre(Field(&SpatialLayer::width, 180),
|
||||
Field(&SpatialLayer::width, 270)));
|
||||
EXPECT_THAT(spatial_layers,
|
||||
ElementsAre(Field(&SpatialLayer::numberOfTemporalLayers, 1),
|
||||
Field(&SpatialLayer::numberOfTemporalLayers, 1)));
|
||||
EXPECT_EQ(codec.GetScalabilityMode(), ScalabilityMode::kL2T1h);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, NumSpatialLayersLimitedWithScalabilityModeResolutionRatio1_5) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 320;
|
||||
codec.height = 180;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T1h); // 1.5:1
|
||||
|
||||
// Scalability mode updated.
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, ElementsAre(Field(&SpatialLayer::width, 320)));
|
||||
EXPECT_THAT(spatial_layers,
|
||||
ElementsAre(Field(&SpatialLayer::numberOfTemporalLayers, 1)));
|
||||
EXPECT_EQ(codec.GetScalabilityMode(), ScalabilityMode::kL1T1);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, AlwaysSendsAtLeastOneLayer) {
|
||||
const size_t max_num_spatial_layers = 6;
|
||||
const size_t first_active_layer = 5;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
|
||||
kMinVp9SpatialLayerLongSideLength, kMinVp9SpatialLayerShortSideLength, 30,
|
||||
first_active_layer, max_num_spatial_layers, 1, false);
|
||||
EXPECT_EQ(spatial_layers.size(), 1u);
|
||||
EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerLongSideLength);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, AlwaysSendsAtLeastOneLayerPortrait) {
|
||||
const size_t max_num_spatial_layers = 6;
|
||||
const size_t first_active_layer = 5;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
|
||||
kMinVp9SpatialLayerShortSideLength, kMinVp9SpatialLayerLongSideLength, 30,
|
||||
first_active_layer, max_num_spatial_layers, 1, false);
|
||||
EXPECT_EQ(spatial_layers.size(), 1u);
|
||||
EXPECT_EQ(spatial_layers.back().width, kMinVp9SpatialLayerShortSideLength);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, EnforcesMinimalRequiredParity) {
|
||||
const size_t max_num_spatial_layers = 3;
|
||||
const size_t kOddSize = 1023;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers =
|
||||
GetSvcConfig(kOddSize, kOddSize, 30,
|
||||
/*first_active_layer=*/1, max_num_spatial_layers, 1, false);
|
||||
// Since there are 2 layers total (1, 2), divisiblity by 2 is required.
|
||||
EXPECT_EQ(spatial_layers.back().width, kOddSize - 1);
|
||||
EXPECT_EQ(spatial_layers.back().width, kOddSize - 1);
|
||||
|
||||
spatial_layers =
|
||||
GetSvcConfig(kOddSize, kOddSize, 30,
|
||||
/*first_active_layer=*/0, max_num_spatial_layers, 1, false);
|
||||
// Since there are 3 layers total (0, 1, 2), divisiblity by 4 is required.
|
||||
EXPECT_EQ(spatial_layers.back().width, kOddSize - 3);
|
||||
EXPECT_EQ(spatial_layers.back().width, kOddSize - 3);
|
||||
|
||||
spatial_layers =
|
||||
GetSvcConfig(kOddSize, kOddSize, 30,
|
||||
/*first_active_layer=*/2, max_num_spatial_layers, 1, false);
|
||||
// Since there is only 1 layer active (2), divisiblity by 1 is required.
|
||||
EXPECT_EQ(spatial_layers.back().width, kOddSize);
|
||||
EXPECT_EQ(spatial_layers.back().width, kOddSize);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, EnforcesMinimalRequiredParityWithScalabilityMode) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 1023;
|
||||
codec.height = 1023;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL3T1);
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, // Divisiblity by 4 required.
|
||||
ElementsAre(Field(&SpatialLayer::width, 255),
|
||||
Field(&SpatialLayer::width, 510),
|
||||
Field(&SpatialLayer::width, 1020)));
|
||||
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL2T1);
|
||||
spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, // Divisiblity by 2 required.
|
||||
ElementsAre(Field(&SpatialLayer::width, 511),
|
||||
Field(&SpatialLayer::width, 1022)));
|
||||
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL1T1);
|
||||
spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, // Divisiblity by 1 required.
|
||||
ElementsAre(Field(&SpatialLayer::width, 1023)));
|
||||
}
|
||||
|
||||
TEST(SvcConfig, EnforcesMinimalRequiredParityWithScalabilityModeResRatio1_5) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 1280;
|
||||
codec.height = 1280;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL2T1h); // 1.5:1
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, // Divisiblity by 3 required.
|
||||
ElementsAre(Field(&SpatialLayer::width, 852),
|
||||
Field(&SpatialLayer::width, 1278)));
|
||||
}
|
||||
|
||||
TEST(SvcConfig, SkipsInactiveLayers) {
|
||||
const size_t num_spatial_layers = 4;
|
||||
const size_t first_active_layer = 2;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
|
||||
kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1),
|
||||
kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30,
|
||||
first_active_layer, num_spatial_layers, 1, false);
|
||||
EXPECT_EQ(spatial_layers.size(), 2u);
|
||||
EXPECT_EQ(spatial_layers.back().width,
|
||||
kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1));
|
||||
}
|
||||
|
||||
TEST(SvcConfig, BitrateThresholds) {
|
||||
const size_t first_active_layer = 0;
|
||||
const size_t num_spatial_layers = 3;
|
||||
std::vector<SpatialLayer> spatial_layers = GetSvcConfig(
|
||||
kMinVp9SpatialLayerLongSideLength << (num_spatial_layers - 1),
|
||||
kMinVp9SpatialLayerShortSideLength << (num_spatial_layers - 1), 30,
|
||||
first_active_layer, num_spatial_layers, 1, false);
|
||||
|
||||
EXPECT_EQ(spatial_layers.size(), num_spatial_layers);
|
||||
|
||||
for (const SpatialLayer& layer : spatial_layers) {
|
||||
EXPECT_LE(layer.minBitrate, layer.maxBitrate);
|
||||
EXPECT_LE(layer.minBitrate, layer.targetBitrate);
|
||||
EXPECT_LE(layer.targetBitrate, layer.maxBitrate);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SvcConfig, BitrateThresholdsWithScalabilityMode) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.width = 960;
|
||||
codec.height = 540;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kS3T3);
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_THAT(spatial_layers, ElementsAre(Field(&SpatialLayer::height, 135),
|
||||
Field(&SpatialLayer::height, 270),
|
||||
Field(&SpatialLayer::height, 540)));
|
||||
|
||||
for (const SpatialLayer& layer : spatial_layers) {
|
||||
EXPECT_LE(layer.minBitrate, layer.maxBitrate);
|
||||
EXPECT_LE(layer.minBitrate, layer.targetBitrate);
|
||||
EXPECT_LE(layer.targetBitrate, layer.maxBitrate);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SvcConfig, CopiesMinMaxBitrateForSingleSpatialLayer) {
|
||||
VideoCodec codec;
|
||||
codec.codecType = kVideoCodecVP9;
|
||||
codec.SetScalabilityMode(ScalabilityMode::kL1T3);
|
||||
codec.width = 1280;
|
||||
codec.height = 720;
|
||||
codec.minBitrate = 100;
|
||||
codec.maxBitrate = 500;
|
||||
|
||||
std::vector<SpatialLayer> spatial_layers = GetVp9SvcConfig(codec);
|
||||
EXPECT_EQ(spatial_layers[0].minBitrate, 100u);
|
||||
EXPECT_EQ(spatial_layers[0].maxBitrate, 500u);
|
||||
EXPECT_LE(spatial_layers[0].targetBitrate, 500u);
|
||||
}
|
||||
|
||||
TEST(SvcConfig, ScreenSharing) {
|
||||
std::vector<SpatialLayer> spatial_layers =
|
||||
GetSvcConfig(1920, 1080, 30, 1, 3, 3, true);
|
||||
|
||||
EXPECT_EQ(spatial_layers.size(), 3UL);
|
||||
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
const SpatialLayer& layer = spatial_layers[i];
|
||||
EXPECT_EQ(layer.width, 1920);
|
||||
EXPECT_EQ(layer.height, 1080);
|
||||
EXPECT_EQ(layer.maxFramerate, (i < 1) ? 5 : (i < 2 ? 10 : 30));
|
||||
EXPECT_EQ(layer.numberOfTemporalLayers, 1);
|
||||
EXPECT_LE(layer.minBitrate, layer.maxBitrate);
|
||||
EXPECT_LE(layer.minBitrate, layer.targetBitrate);
|
||||
EXPECT_LE(layer.targetBitrate, layer.maxBitrate);
|
||||
}
|
||||
}
|
||||
} // namespace webrtc
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* 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/video_coding/codecs/vp9/include/vp9.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
#include "api/video_codecs/scalability_mode.h"
|
||||
#include "api/video_codecs/sdp_video_format.h"
|
||||
#include "api/video_codecs/vp9_profile.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h"
|
||||
#include "modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h"
|
||||
#include "modules/video_coding/svc/create_scalability_structure.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include <vpx/vp8cx.h>
|
||||
#include <vpx/vp8dx.h>
|
||||
#include <vpx/vpx_codec.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::vector<SdpVideoFormat> SupportedVP9Codecs(bool add_scalability_modes) {
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
// Profile 2 might not be available on some platforms until
|
||||
// https://bugs.chromium.org/p/webm/issues/detail?id=1544 is solved.
|
||||
static bool vpx_supports_high_bit_depth =
|
||||
(vpx_codec_get_caps(vpx_codec_vp9_cx()) & VPX_CODEC_CAP_HIGHBITDEPTH) !=
|
||||
0 &&
|
||||
(vpx_codec_get_caps(vpx_codec_vp9_dx()) & VPX_CODEC_CAP_HIGHBITDEPTH) !=
|
||||
0;
|
||||
|
||||
absl::InlinedVector<ScalabilityMode, kScalabilityModeCount> scalability_modes;
|
||||
if (add_scalability_modes) {
|
||||
for (const auto scalability_mode : kAllScalabilityModes) {
|
||||
if (ScalabilityStructureConfig(scalability_mode).has_value()) {
|
||||
scalability_modes.push_back(scalability_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<SdpVideoFormat> supported_formats{SdpVideoFormat(
|
||||
cricket::kVp9CodecName,
|
||||
{{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}},
|
||||
scalability_modes)};
|
||||
if (vpx_supports_high_bit_depth) {
|
||||
supported_formats.push_back(SdpVideoFormat(
|
||||
cricket::kVp9CodecName,
|
||||
{{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile2)}},
|
||||
scalability_modes));
|
||||
}
|
||||
|
||||
return supported_formats;
|
||||
#else
|
||||
return std::vector<SdpVideoFormat>();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<SdpVideoFormat> SupportedVP9DecoderCodecs() {
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
std::vector<SdpVideoFormat> supported_formats = SupportedVP9Codecs();
|
||||
// The WebRTC internal decoder supports VP9 profile 1 and 3. However, there's
|
||||
// currently no way of sending VP9 profile 1 or 3 using the internal encoder.
|
||||
// It would require extended support for I444, I422, and I440 buffers.
|
||||
supported_formats.push_back(SdpVideoFormat(
|
||||
cricket::kVp9CodecName,
|
||||
{{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile1)}}));
|
||||
supported_formats.push_back(SdpVideoFormat(
|
||||
cricket::kVp9CodecName,
|
||||
{{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile3)}}));
|
||||
return supported_formats;
|
||||
#else
|
||||
return std::vector<SdpVideoFormat>();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<VP9Encoder> VP9Encoder::Create() {
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
return std::make_unique<LibvpxVp9Encoder>(
|
||||
cricket::CreateVideoCodec(cricket::kVp9CodecName),
|
||||
LibvpxInterface::Create(), FieldTrialBasedConfig());
|
||||
#else
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<VP9Encoder> VP9Encoder::Create(
|
||||
const cricket::VideoCodec& codec) {
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
return std::make_unique<LibvpxVp9Encoder>(codec, LibvpxInterface::Create(),
|
||||
FieldTrialBasedConfig());
|
||||
#else
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VP9Encoder::SupportsScalabilityMode(ScalabilityMode scalability_mode) {
|
||||
return ScalabilityStructureConfig(scalability_mode).has_value();
|
||||
}
|
||||
|
||||
std::unique_ptr<VP9Decoder> VP9Decoder::Create() {
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
return std::make_unique<LibvpxVp9Decoder>();
|
||||
#else
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
|
||||
#include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include <vpx/vpx_codec.h>
|
||||
#include <vpx/vpx_decoder.h>
|
||||
#include <vpx/vpx_frame_buffer.h>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
uint8_t* Vp9FrameBufferPool::Vp9FrameBuffer::GetData() {
|
||||
return data_.data<uint8_t>();
|
||||
}
|
||||
|
||||
size_t Vp9FrameBufferPool::Vp9FrameBuffer::GetDataSize() const {
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
void Vp9FrameBufferPool::Vp9FrameBuffer::SetSize(size_t size) {
|
||||
data_.SetSize(size);
|
||||
}
|
||||
|
||||
bool Vp9FrameBufferPool::InitializeVpxUsePool(
|
||||
vpx_codec_ctx* vpx_codec_context) {
|
||||
RTC_DCHECK(vpx_codec_context);
|
||||
// Tell libvpx to use this pool.
|
||||
if (vpx_codec_set_frame_buffer_functions(
|
||||
// In which context to use these callback functions.
|
||||
vpx_codec_context,
|
||||
// Called by libvpx when it needs another frame buffer.
|
||||
&Vp9FrameBufferPool::VpxGetFrameBuffer,
|
||||
// Called by libvpx when it no longer uses a frame buffer.
|
||||
&Vp9FrameBufferPool::VpxReleaseFrameBuffer,
|
||||
// `this` will be passed as `user_priv` to VpxGetFrameBuffer.
|
||||
this)) {
|
||||
// Failed to configure libvpx to use Vp9FrameBufferPool.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<Vp9FrameBufferPool::Vp9FrameBuffer>
|
||||
Vp9FrameBufferPool::GetFrameBuffer(size_t min_size) {
|
||||
RTC_DCHECK_GT(min_size, 0);
|
||||
rtc::scoped_refptr<Vp9FrameBuffer> available_buffer = nullptr;
|
||||
{
|
||||
MutexLock lock(&buffers_lock_);
|
||||
// Do we have a buffer we can recycle?
|
||||
for (const auto& buffer : allocated_buffers_) {
|
||||
if (buffer->HasOneRef()) {
|
||||
available_buffer = buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Otherwise create one.
|
||||
if (available_buffer == nullptr) {
|
||||
available_buffer = new Vp9FrameBuffer();
|
||||
allocated_buffers_.push_back(available_buffer);
|
||||
if (allocated_buffers_.size() > max_num_buffers_) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< allocated_buffers_.size()
|
||||
<< " Vp9FrameBuffers have been "
|
||||
"allocated by a Vp9FrameBufferPool (exceeding what is "
|
||||
"considered reasonable, "
|
||||
<< max_num_buffers_ << ").";
|
||||
|
||||
// TODO(phoglund): this limit is being hit in tests since Oct 5 2016.
|
||||
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=6484.
|
||||
// RTC_DCHECK_NOTREACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
available_buffer->SetSize(min_size);
|
||||
return available_buffer;
|
||||
}
|
||||
|
||||
int Vp9FrameBufferPool::GetNumBuffersInUse() const {
|
||||
int num_buffers_in_use = 0;
|
||||
MutexLock lock(&buffers_lock_);
|
||||
for (const auto& buffer : allocated_buffers_) {
|
||||
if (!buffer->HasOneRef())
|
||||
++num_buffers_in_use;
|
||||
}
|
||||
return num_buffers_in_use;
|
||||
}
|
||||
|
||||
bool Vp9FrameBufferPool::Resize(size_t max_number_of_buffers) {
|
||||
MutexLock lock(&buffers_lock_);
|
||||
size_t used_buffers_count = 0;
|
||||
for (const auto& buffer : allocated_buffers_) {
|
||||
// If the buffer is in use, the ref count will be >= 2, one from the list we
|
||||
// are looping over and one from the application. If the ref count is 1,
|
||||
// then the list we are looping over holds the only reference and it's safe
|
||||
// to reuse.
|
||||
if (!buffer->HasOneRef()) {
|
||||
used_buffers_count++;
|
||||
}
|
||||
}
|
||||
if (used_buffers_count > max_number_of_buffers) {
|
||||
return false;
|
||||
}
|
||||
max_num_buffers_ = max_number_of_buffers;
|
||||
|
||||
size_t buffers_to_purge = allocated_buffers_.size() - max_num_buffers_;
|
||||
auto iter = allocated_buffers_.begin();
|
||||
while (iter != allocated_buffers_.end() && buffers_to_purge > 0) {
|
||||
if ((*iter)->HasOneRef()) {
|
||||
iter = allocated_buffers_.erase(iter);
|
||||
buffers_to_purge--;
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Vp9FrameBufferPool::ClearPool() {
|
||||
MutexLock lock(&buffers_lock_);
|
||||
allocated_buffers_.clear();
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t Vp9FrameBufferPool::VpxGetFrameBuffer(void* user_priv,
|
||||
size_t min_size,
|
||||
vpx_codec_frame_buffer* fb) {
|
||||
RTC_DCHECK(user_priv);
|
||||
RTC_DCHECK(fb);
|
||||
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
// Limit size of 8k YUV highdef frame
|
||||
size_t size_limit = 7680 * 4320 * 3 / 2 * 2;
|
||||
if (min_size > size_limit)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
Vp9FrameBufferPool* pool = static_cast<Vp9FrameBufferPool*>(user_priv);
|
||||
|
||||
rtc::scoped_refptr<Vp9FrameBuffer> buffer = pool->GetFrameBuffer(min_size);
|
||||
fb->data = buffer->GetData();
|
||||
fb->size = buffer->GetDataSize();
|
||||
// Store Vp9FrameBuffer* in `priv` for use in VpxReleaseFrameBuffer.
|
||||
// This also makes vpx_codec_get_frame return images with their `fb_priv` set
|
||||
// to `buffer` which is important for external reference counting.
|
||||
// Release from refptr so that the buffer's `ref_count_` remains 1 when
|
||||
// `buffer` goes out of scope.
|
||||
fb->priv = static_cast<void*>(buffer.release());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t Vp9FrameBufferPool::VpxReleaseFrameBuffer(void* user_priv,
|
||||
vpx_codec_frame_buffer* fb) {
|
||||
RTC_DCHECK(user_priv);
|
||||
RTC_DCHECK(fb);
|
||||
Vp9FrameBuffer* buffer = static_cast<Vp9FrameBuffer*>(fb->priv);
|
||||
if (buffer != nullptr) {
|
||||
buffer->Release();
|
||||
// When libvpx fails to decode and you continue to try to decode (and fail)
|
||||
// libvpx can for some reason try to release the same buffer multiple times.
|
||||
// Setting `priv` to null protects against trying to Release multiple times.
|
||||
fb->priv = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_ENABLE_VP9
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MODULES_VIDEO_CODING_CODECS_VP9_VP9_FRAME_BUFFER_POOL_H_
|
||||
#define MODULES_VIDEO_CODING_CODECS_VP9_VP9_FRAME_BUFFER_POOL_H_
|
||||
|
||||
#ifdef RTC_ENABLE_VP9
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
|
||||
struct vpx_codec_ctx;
|
||||
struct vpx_codec_frame_buffer;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// If more buffers than this are allocated we print warnings and crash if in
|
||||
// debug mode. VP9 is defined to have 8 reference buffers, of which 3 can be
|
||||
// referenced by any frame, see
|
||||
// https://tools.ietf.org/html/draft-grange-vp9-bitstream-00#section-2.2.2.
|
||||
// Assuming VP9 holds on to at most 8 buffers, any more buffers than that
|
||||
// would have to be by application code. Decoded frames should not be
|
||||
// referenced for longer than necessary. If we allow ~60 additional buffers
|
||||
// then the application has ~1 second to e.g. render each frame of a 60 fps
|
||||
// video.
|
||||
constexpr size_t kDefaultMaxNumBuffers = 68;
|
||||
|
||||
// This memory pool is used to serve buffers to libvpx for decoding purposes in
|
||||
// VP9, which is set up in InitializeVPXUsePool. After the initialization any
|
||||
// time libvpx wants to decode a frame it will use buffers provided and released
|
||||
// through VpxGetFrameBuffer and VpxReleaseFrameBuffer.
|
||||
// The benefit of owning the pool that libvpx relies on for decoding is that the
|
||||
// decoded frames returned by libvpx (from vpx_codec_get_frame) use parts of our
|
||||
// buffers for the decoded image data. By retaining ownership of this buffer
|
||||
// using scoped_refptr, the image buffer can be reused by VideoFrames and no
|
||||
// frame copy has to occur during decoding and frame delivery.
|
||||
//
|
||||
// Pseudo example usage case:
|
||||
// Vp9FrameBufferPool pool;
|
||||
// pool.InitializeVpxUsePool(decoder_ctx);
|
||||
// ...
|
||||
//
|
||||
// // During decoding, libvpx will get and release buffers from the pool.
|
||||
// vpx_codec_decode(decoder_ctx, ...);
|
||||
//
|
||||
// vpx_image_t* img = vpx_codec_get_frame(decoder_ctx, &iter);
|
||||
// // Important to use scoped_refptr to protect it against being recycled by
|
||||
// // the pool.
|
||||
// scoped_refptr<Vp9FrameBuffer> img_buffer = (Vp9FrameBuffer*)img->fb_priv;
|
||||
// ...
|
||||
//
|
||||
// // Destroying the codec will make libvpx release any buffers it was using.
|
||||
// vpx_codec_destroy(decoder_ctx);
|
||||
class Vp9FrameBufferPool {
|
||||
public:
|
||||
class Vp9FrameBuffer final
|
||||
: public rtc::RefCountedNonVirtual<Vp9FrameBuffer> {
|
||||
public:
|
||||
uint8_t* GetData();
|
||||
size_t GetDataSize() const;
|
||||
void SetSize(size_t size);
|
||||
|
||||
using rtc::RefCountedNonVirtual<Vp9FrameBuffer>::HasOneRef;
|
||||
|
||||
private:
|
||||
// Data as an easily resizable buffer.
|
||||
rtc::Buffer data_;
|
||||
};
|
||||
|
||||
// Configures libvpx to, in the specified context, use this memory pool for
|
||||
// buffers used to decompress frames. This is only supported for VP9.
|
||||
bool InitializeVpxUsePool(vpx_codec_ctx* vpx_codec_context);
|
||||
|
||||
// Gets a frame buffer of at least `min_size`, recycling an available one or
|
||||
// creating a new one. When no longer referenced from the outside the buffer
|
||||
// becomes recyclable.
|
||||
rtc::scoped_refptr<Vp9FrameBuffer> GetFrameBuffer(size_t min_size);
|
||||
// Gets the number of buffers currently in use (not ready to be recycled).
|
||||
int GetNumBuffersInUse() const;
|
||||
// Changes the max amount of buffers in the pool to the new value.
|
||||
// Returns true if change was successful and false if the amount of already
|
||||
// allocated buffers is bigger than new value.
|
||||
bool Resize(size_t max_number_of_buffers);
|
||||
// Releases allocated buffers, deleting available buffers. Buffers in use are
|
||||
// not deleted until they are no longer referenced.
|
||||
void ClearPool();
|
||||
|
||||
// InitializeVpxUsePool configures libvpx to call this function when it needs
|
||||
// a new frame buffer. Parameters:
|
||||
// `user_priv` Private data passed to libvpx, InitializeVpxUsePool sets it up
|
||||
// to be a pointer to the pool.
|
||||
// `min_size` Minimum size needed by libvpx (to decompress a frame).
|
||||
// `fb` Pointer to the libvpx frame buffer object, this is updated to
|
||||
// use the pool's buffer.
|
||||
// Returns 0 on success. Returns < 0 on failure.
|
||||
static int32_t VpxGetFrameBuffer(void* user_priv,
|
||||
size_t min_size,
|
||||
vpx_codec_frame_buffer* fb);
|
||||
|
||||
// InitializeVpxUsePool configures libvpx to call this function when it has
|
||||
// finished using one of the pool's frame buffer. Parameters:
|
||||
// `user_priv` Private data passed to libvpx, InitializeVpxUsePool sets it up
|
||||
// to be a pointer to the pool.
|
||||
// `fb` Pointer to the libvpx frame buffer object, its `priv` will be
|
||||
// a pointer to one of the pool's Vp9FrameBuffer.
|
||||
static int32_t VpxReleaseFrameBuffer(void* user_priv,
|
||||
vpx_codec_frame_buffer* fb);
|
||||
|
||||
private:
|
||||
// Protects `allocated_buffers_`.
|
||||
mutable Mutex buffers_lock_;
|
||||
// All buffers, in use or ready to be recycled.
|
||||
std::vector<rtc::scoped_refptr<Vp9FrameBuffer>> allocated_buffers_
|
||||
RTC_GUARDED_BY(buffers_lock_);
|
||||
size_t max_num_buffers_ = kDefaultMaxNumBuffers;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_ENABLE_VP9
|
||||
|
||||
#endif // MODULES_VIDEO_CODING_CODECS_VP9_VP9_FRAME_BUFFER_POOL_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue