mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

Adds a field to EncoderInfo called preferred_pixel_formats which a software encoder populates with the pixel formats it supports. When a kNative frame is received for encoding, the VideoStreamEncoder will first try to get a frame that is accessible by the software encoder in that pixel format from the kNative frame. If this fails it will fallback to converting the frame using ToI420. This minimizes the number of conversions made in the case that the encoder supports the pixel format of the native buffer or where conversion can be accelerated. For example, in Chromium, the capturer can emit an NV12 frame, which can be consumed by libvpx which supports NV12. Testing: Tested in Chrome with media::VideoFrame adapters. Bug: webrtc:11977 Change-Id: I9becc4100136b0c0128f4fa06dedf9ee4dc62f37 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/187121 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Markus Handell <handellm@webrtc.org> Commit-Queue: Evan Shrubsole <eshr@google.com> Cr-Commit-Position: refs/heads/master@{#32353}
331 lines
11 KiB
C++
331 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "api/video_codecs/video_encoder.h"
|
|
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/strings/string_builder.h"
|
|
|
|
namespace webrtc {
|
|
|
|
// TODO(mflodman): Add default complexity for VP9 and VP9.
|
|
VideoCodecVP8 VideoEncoder::GetDefaultVp8Settings() {
|
|
VideoCodecVP8 vp8_settings;
|
|
memset(&vp8_settings, 0, sizeof(vp8_settings));
|
|
|
|
vp8_settings.numberOfTemporalLayers = 1;
|
|
vp8_settings.denoisingOn = true;
|
|
vp8_settings.automaticResizeOn = false;
|
|
vp8_settings.frameDroppingOn = true;
|
|
vp8_settings.keyFrameInterval = 3000;
|
|
|
|
return vp8_settings;
|
|
}
|
|
|
|
VideoCodecVP9 VideoEncoder::GetDefaultVp9Settings() {
|
|
VideoCodecVP9 vp9_settings;
|
|
memset(&vp9_settings, 0, sizeof(vp9_settings));
|
|
|
|
vp9_settings.numberOfTemporalLayers = 1;
|
|
vp9_settings.denoisingOn = true;
|
|
vp9_settings.frameDroppingOn = true;
|
|
vp9_settings.keyFrameInterval = 3000;
|
|
vp9_settings.adaptiveQpMode = true;
|
|
vp9_settings.automaticResizeOn = true;
|
|
vp9_settings.numberOfSpatialLayers = 1;
|
|
vp9_settings.flexibleMode = false;
|
|
vp9_settings.interLayerPred = InterLayerPredMode::kOn;
|
|
|
|
return vp9_settings;
|
|
}
|
|
|
|
VideoCodecH264 VideoEncoder::GetDefaultH264Settings() {
|
|
VideoCodecH264 h264_settings;
|
|
memset(&h264_settings, 0, sizeof(h264_settings));
|
|
|
|
h264_settings.frameDroppingOn = true;
|
|
h264_settings.keyFrameInterval = 3000;
|
|
h264_settings.numberOfTemporalLayers = 1;
|
|
|
|
return h264_settings;
|
|
}
|
|
|
|
VideoEncoder::ScalingSettings::ScalingSettings() = default;
|
|
|
|
VideoEncoder::ScalingSettings::ScalingSettings(KOff) : ScalingSettings() {}
|
|
|
|
VideoEncoder::ScalingSettings::ScalingSettings(int low, int high)
|
|
: thresholds(QpThresholds(low, high)) {}
|
|
|
|
VideoEncoder::ScalingSettings::ScalingSettings(int low,
|
|
int high,
|
|
int min_pixels)
|
|
: thresholds(QpThresholds(low, high)), min_pixels_per_frame(min_pixels) {}
|
|
|
|
VideoEncoder::ScalingSettings::ScalingSettings(const ScalingSettings&) =
|
|
default;
|
|
|
|
VideoEncoder::ScalingSettings::~ScalingSettings() {}
|
|
|
|
// static
|
|
constexpr VideoEncoder::ScalingSettings::KOff
|
|
VideoEncoder::ScalingSettings::kOff;
|
|
// static
|
|
constexpr uint8_t VideoEncoder::EncoderInfo::kMaxFramerateFraction;
|
|
|
|
bool VideoEncoder::ResolutionBitrateLimits::operator==(
|
|
const ResolutionBitrateLimits& rhs) const {
|
|
return frame_size_pixels == rhs.frame_size_pixels &&
|
|
min_start_bitrate_bps == rhs.min_start_bitrate_bps &&
|
|
min_bitrate_bps == rhs.min_bitrate_bps &&
|
|
max_bitrate_bps == rhs.max_bitrate_bps;
|
|
}
|
|
|
|
VideoEncoder::EncoderInfo::EncoderInfo()
|
|
: scaling_settings(VideoEncoder::ScalingSettings::kOff),
|
|
requested_resolution_alignment(1),
|
|
apply_alignment_to_all_simulcast_layers(false),
|
|
supports_native_handle(false),
|
|
implementation_name("unknown"),
|
|
has_trusted_rate_controller(false),
|
|
is_hardware_accelerated(true),
|
|
has_internal_source(false),
|
|
fps_allocation{absl::InlinedVector<uint8_t, kMaxTemporalStreams>(
|
|
1,
|
|
kMaxFramerateFraction)},
|
|
supports_simulcast(false),
|
|
preferred_pixel_formats{VideoFrameBuffer::Type::kI420} {}
|
|
|
|
VideoEncoder::EncoderInfo::EncoderInfo(const EncoderInfo&) = default;
|
|
|
|
VideoEncoder::EncoderInfo::~EncoderInfo() = default;
|
|
|
|
std::string VideoEncoder::EncoderInfo::ToString() const {
|
|
char string_buf[2048];
|
|
rtc::SimpleStringBuilder oss(string_buf);
|
|
|
|
oss << "EncoderInfo { "
|
|
"ScalingSettings { ";
|
|
if (scaling_settings.thresholds) {
|
|
oss << "Thresholds { "
|
|
"low = "
|
|
<< scaling_settings.thresholds->low
|
|
<< ", high = " << scaling_settings.thresholds->high << "}, ";
|
|
}
|
|
oss << "min_pixels_per_frame = " << scaling_settings.min_pixels_per_frame
|
|
<< " }";
|
|
oss << ", requested_resolution_alignment = " << requested_resolution_alignment
|
|
<< ", apply_alignment_to_all_simulcast_layers = "
|
|
<< apply_alignment_to_all_simulcast_layers
|
|
<< ", supports_native_handle = " << supports_native_handle
|
|
<< ", implementation_name = '" << implementation_name
|
|
<< "'"
|
|
", has_trusted_rate_controller = "
|
|
<< has_trusted_rate_controller
|
|
<< ", is_hardware_accelerated = " << is_hardware_accelerated
|
|
<< ", has_internal_source = " << has_internal_source
|
|
<< ", fps_allocation = [";
|
|
bool first = true;
|
|
for (size_t i = 0; i < fps_allocation->size(); ++i) {
|
|
if (!first) {
|
|
oss << ", ";
|
|
}
|
|
const absl::InlinedVector<uint8_t, kMaxTemporalStreams>& fractions =
|
|
fps_allocation[i];
|
|
if (!fractions.empty()) {
|
|
first = false;
|
|
oss << "[ ";
|
|
for (size_t i = 0; i < fractions.size(); ++i) {
|
|
if (i > 0) {
|
|
oss << ", ";
|
|
}
|
|
oss << (static_cast<double>(fractions[i]) / kMaxFramerateFraction);
|
|
}
|
|
oss << "] ";
|
|
}
|
|
}
|
|
oss << "]";
|
|
oss << ", resolution_bitrate_limits = [";
|
|
for (size_t i = 0; i < resolution_bitrate_limits.size(); ++i) {
|
|
if (i > 0) {
|
|
oss << ", ";
|
|
}
|
|
ResolutionBitrateLimits l = resolution_bitrate_limits[i];
|
|
oss << "Limits { "
|
|
"frame_size_pixels = "
|
|
<< l.frame_size_pixels
|
|
<< ", min_start_bitrate_bps = " << l.min_start_bitrate_bps
|
|
<< ", min_bitrate_bps = " << l.min_bitrate_bps
|
|
<< ", max_bitrate_bps = " << l.max_bitrate_bps << "} ";
|
|
}
|
|
oss << "] "
|
|
", supports_simulcast = "
|
|
<< supports_simulcast;
|
|
oss << ", preferred_pixel_formats = [";
|
|
for (size_t i = 0; i < preferred_pixel_formats.size(); ++i) {
|
|
if (i > 0)
|
|
oss << ", ";
|
|
oss << VideoFrameBufferTypeToString(preferred_pixel_formats.at(i));
|
|
}
|
|
oss << "]";
|
|
oss << "}";
|
|
return oss.str();
|
|
}
|
|
|
|
bool VideoEncoder::EncoderInfo::operator==(const EncoderInfo& rhs) const {
|
|
if (scaling_settings.thresholds.has_value() !=
|
|
rhs.scaling_settings.thresholds.has_value()) {
|
|
return false;
|
|
}
|
|
if (scaling_settings.thresholds.has_value()) {
|
|
QpThresholds l = *scaling_settings.thresholds;
|
|
QpThresholds r = *rhs.scaling_settings.thresholds;
|
|
if (l.low != r.low || l.high != r.high) {
|
|
return false;
|
|
}
|
|
}
|
|
if (scaling_settings.min_pixels_per_frame !=
|
|
rhs.scaling_settings.min_pixels_per_frame) {
|
|
return false;
|
|
}
|
|
|
|
if (supports_native_handle != rhs.supports_native_handle ||
|
|
implementation_name != rhs.implementation_name ||
|
|
has_trusted_rate_controller != rhs.has_trusted_rate_controller ||
|
|
is_hardware_accelerated != rhs.is_hardware_accelerated ||
|
|
has_internal_source != rhs.has_internal_source) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < kMaxSpatialLayers; ++i) {
|
|
if (fps_allocation[i] != rhs.fps_allocation[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (resolution_bitrate_limits != rhs.resolution_bitrate_limits ||
|
|
supports_simulcast != rhs.supports_simulcast) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
absl::optional<VideoEncoder::ResolutionBitrateLimits>
|
|
VideoEncoder::EncoderInfo::GetEncoderBitrateLimitsForResolution(
|
|
int frame_size_pixels) const {
|
|
std::vector<ResolutionBitrateLimits> bitrate_limits =
|
|
resolution_bitrate_limits;
|
|
|
|
// Sort the list of bitrate limits by resolution.
|
|
sort(bitrate_limits.begin(), bitrate_limits.end(),
|
|
[](const ResolutionBitrateLimits& lhs,
|
|
const ResolutionBitrateLimits& rhs) {
|
|
return lhs.frame_size_pixels < rhs.frame_size_pixels;
|
|
});
|
|
|
|
for (size_t i = 0; i < bitrate_limits.size(); ++i) {
|
|
RTC_DCHECK_GE(bitrate_limits[i].min_bitrate_bps, 0);
|
|
RTC_DCHECK_GE(bitrate_limits[i].min_start_bitrate_bps, 0);
|
|
RTC_DCHECK_GE(bitrate_limits[i].max_bitrate_bps,
|
|
bitrate_limits[i].min_bitrate_bps);
|
|
if (i > 0) {
|
|
// The bitrate limits aren't expected to decrease with resolution.
|
|
RTC_DCHECK_GE(bitrate_limits[i].min_bitrate_bps,
|
|
bitrate_limits[i - 1].min_bitrate_bps);
|
|
RTC_DCHECK_GE(bitrate_limits[i].min_start_bitrate_bps,
|
|
bitrate_limits[i - 1].min_start_bitrate_bps);
|
|
RTC_DCHECK_GE(bitrate_limits[i].max_bitrate_bps,
|
|
bitrate_limits[i - 1].max_bitrate_bps);
|
|
}
|
|
|
|
if (bitrate_limits[i].frame_size_pixels >= frame_size_pixels) {
|
|
return absl::optional<ResolutionBitrateLimits>(bitrate_limits[i]);
|
|
}
|
|
}
|
|
|
|
return absl::nullopt;
|
|
}
|
|
|
|
VideoEncoder::RateControlParameters::RateControlParameters()
|
|
: bitrate(VideoBitrateAllocation()),
|
|
framerate_fps(0.0),
|
|
bandwidth_allocation(DataRate::Zero()) {}
|
|
|
|
VideoEncoder::RateControlParameters::RateControlParameters(
|
|
const VideoBitrateAllocation& bitrate,
|
|
double framerate_fps)
|
|
: bitrate(bitrate),
|
|
framerate_fps(framerate_fps),
|
|
bandwidth_allocation(DataRate::BitsPerSec(bitrate.get_sum_bps())) {}
|
|
|
|
VideoEncoder::RateControlParameters::RateControlParameters(
|
|
const VideoBitrateAllocation& bitrate,
|
|
double framerate_fps,
|
|
DataRate bandwidth_allocation)
|
|
: bitrate(bitrate),
|
|
framerate_fps(framerate_fps),
|
|
bandwidth_allocation(bandwidth_allocation) {}
|
|
|
|
bool VideoEncoder::RateControlParameters::operator==(
|
|
const VideoEncoder::RateControlParameters& rhs) const {
|
|
return std::tie(bitrate, framerate_fps, bandwidth_allocation) ==
|
|
std::tie(rhs.bitrate, rhs.framerate_fps, rhs.bandwidth_allocation);
|
|
}
|
|
|
|
bool VideoEncoder::RateControlParameters::operator!=(
|
|
const VideoEncoder::RateControlParameters& rhs) const {
|
|
return !(rhs == *this);
|
|
}
|
|
|
|
VideoEncoder::RateControlParameters::~RateControlParameters() = default;
|
|
|
|
void VideoEncoder::SetFecControllerOverride(
|
|
FecControllerOverride* fec_controller_override) {}
|
|
|
|
int32_t VideoEncoder::InitEncode(const VideoCodec* codec_settings,
|
|
int32_t number_of_cores,
|
|
size_t max_payload_size) {
|
|
const VideoEncoder::Capabilities capabilities(/* loss_notification= */ false);
|
|
const VideoEncoder::Settings settings(capabilities, number_of_cores,
|
|
max_payload_size);
|
|
// In theory, this and the other version of InitEncode() could end up calling
|
|
// each other in a loop until we get a stack overflow.
|
|
// In practice, any subclass of VideoEncoder would overload at least one
|
|
// of these, and we have a TODO in the header file to make this pure virtual.
|
|
return InitEncode(codec_settings, settings);
|
|
}
|
|
|
|
int VideoEncoder::InitEncode(const VideoCodec* codec_settings,
|
|
const VideoEncoder::Settings& settings) {
|
|
// In theory, this and the other version of InitEncode() could end up calling
|
|
// each other in a loop until we get a stack overflow.
|
|
// In practice, any subclass of VideoEncoder would overload at least one
|
|
// of these, and we have a TODO in the header file to make this pure virtual.
|
|
return InitEncode(codec_settings, settings.number_of_cores,
|
|
settings.max_payload_size);
|
|
}
|
|
|
|
void VideoEncoder::OnPacketLossRateUpdate(float packet_loss_rate) {}
|
|
|
|
void VideoEncoder::OnRttUpdate(int64_t rtt_ms) {}
|
|
|
|
void VideoEncoder::OnLossNotification(
|
|
const LossNotification& loss_notification) {}
|
|
|
|
// TODO(webrtc:9722): Remove and make pure virtual.
|
|
VideoEncoder::EncoderInfo VideoEncoder::GetEncoderInfo() const {
|
|
return EncoderInfo();
|
|
}
|
|
|
|
} // namespace webrtc
|