Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,3 @@
include_rules = [
"+media/base",
]

View file

@ -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_

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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_