| /* | |
| * 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 <algorithm> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <optional> | |
| #include <string> | |
| #include <tuple> | |
| #include <vector> | |
| #include "absl/container/inlined_vector.h" | |
| #include "api/fec_controller_override.h" | |
| #include "api/units/data_rate.h" | |
| #include "api/video/video_bitrate_allocation.h" | |
| #include "api/video/video_codec_constants.h" | |
| #include "api/video/video_frame_buffer.h" | |
| #include "api/video_codecs/video_codec.h" | |
| #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.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.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.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() {} | |
| 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), | |
| enable_cpu_overuse_detection(true), | |
| 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 { | |
| StringBuilder oss; | |
| 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 | |
| << ", enable_cpu_overuse_detection = " << enable_cpu_overuse_detection | |
| << ", fps_allocation = ["; | |
| size_t num_spatial_layer_with_fps_allocation = 0; | |
| for (size_t i = 0; i < kMaxSpatialLayers; ++i) { | |
| if (!fps_allocation[i].empty()) { | |
| num_spatial_layer_with_fps_allocation = i + 1; | |
| } | |
| } | |
| bool first = true; | |
| for (size_t i = 0; i < num_spatial_layer_with_fps_allocation; ++i) { | |
| if (fps_allocation[i].empty()) { | |
| break; | |
| } | |
| if (!first) { | |
| oss << ", "; | |
| } | |
| const absl::InlinedVector<uint8_t, kMaxTemporalStreams>& fractions = | |
| fps_allocation[i]; | |
| if (!fractions.empty()) { | |
| first = false; | |
| oss << "[ "; | |
| for (size_t j = 0; j < fractions.size(); ++j) { | |
| if (j > 0) { | |
| oss << ", "; | |
| } | |
| oss << (static_cast<double>(fractions[j]) / 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 << "]"; | |
| if (is_qp_trusted.has_value()) { | |
| oss << ", is_qp_trusted = " << is_qp_trusted.value(); | |
| } | |
| if (mapped_resolution.has_value()) { | |
| oss << ", mapped_resolution = " << mapped_resolution->width << " x " | |
| << mapped_resolution->height; | |
| } | |
| 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 || | |
| enable_cpu_overuse_detection != rhs.enable_cpu_overuse_detection) { | |
| 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; | |
| } | |
| std::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 std::optional<ResolutionBitrateLimits>(bitrate_limits[i]); | |
| } | |
| } | |
| return std::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 */) {} | |
| } // namespace webrtc |