|  | /* | 
|  | *  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 <cstdint> | 
|  | #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() {} | 
|  |  | 
|  | // 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), | 
|  | 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 | 
|  | << ", 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 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 << "]"; | 
|  | if (is_qp_trusted.has_value()) { | 
|  | oss << ", is_qp_trusted = " << is_qp_trusted.value(); | 
|  | } | 
|  | 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) { | 
|  | 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 |