| /* | |
| * Copyright (c) 2024 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/simple_encoder_wrapper.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <span> | |
| #include <string> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "absl/algorithm/container.h" | |
| #include "absl/strings/string_view.h" | |
| #include "api/scoped_refptr.h" | |
| #include "api/units/data_size.h" | |
| #include "api/units/frequency.h" | |
| #include "api/video/video_frame_buffer.h" | |
| #include "api/video_codecs/scalability_mode.h" | |
| #include "api/video_codecs/scalability_mode_helper.h" | |
| #include "api/video_codecs/video_encoder_factory_interface.h" | |
| #include "api/video_codecs/video_encoder_interface.h" | |
| #include "common_video/generic_frame_descriptor/generic_frame_info.h" | |
| #include "modules/video_coding/svc/create_scalability_structure.h" | |
| #include "modules/video_coding/svc/scalable_video_controller.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/numerics/rational.h" | |
| namespace webrtc { | |
| using PredictionConstraints = | |
| VideoEncoderFactoryInterface::Capabilities::PredictionConstraints; | |
| using FrameEncodeSettings = VideoEncoderInterface::FrameEncodeSettings; | |
| namespace { | |
| enum class Inter { kS, kL, kKey }; | |
| enum class Scaling { k1_2, k2_3 }; | |
| std::string SvcToString(int spatial_layers, | |
| int temporal_layers, | |
| Inter inter, | |
| Scaling scaling) { | |
| RTC_CHECK(spatial_layers > 1 || inter == Inter::kL); | |
| std::string res; | |
| res += inter == Inter::kS ? "S" : "L"; | |
| res += std::to_string(spatial_layers); | |
| res += "T"; | |
| res += std::to_string(temporal_layers); | |
| if (scaling == Scaling::k2_3) { | |
| res += "h"; | |
| } | |
| if (inter == Inter::kKey) { | |
| res += "_KEY"; | |
| } | |
| return res; | |
| } | |
| } // namespace | |
| // static | |
| std::vector<std::string> SimpleEncoderWrapper::SupportedWebrtcSvcModes( | |
| const PredictionConstraints& prediction_constraints) { | |
| std::vector<std::string> res; | |
| const int max_spatial_layers = | |
| std::min(3, prediction_constraints.max_spatial_layers); | |
| const int max_temporal_layers = | |
| std::min(3, prediction_constraints.max_temporal_layers); | |
| const bool scale_by_half = | |
| absl::c_linear_search(prediction_constraints.scaling_factors, | |
| Rational{.numerator = 1, .denominator = 2}); | |
| const bool scale_by_two_thirds = | |
| absl::c_linear_search(prediction_constraints.scaling_factors, | |
| Rational{.numerator = 2, .denominator = 3}); | |
| const bool inter_layer = | |
| prediction_constraints.max_references > 1 && | |
| prediction_constraints.buffer_space_type != | |
| PredictionConstraints::BufferSpaceType::kMultiInstance; | |
| for (int s = 1; s <= max_spatial_layers; ++s) { | |
| for (int t = 1; t <= max_temporal_layers; ++t) { | |
| if (prediction_constraints.num_buffers > ((std::max(1, t - 1) * s) - 1)) { | |
| if (s == 1 || inter_layer) { | |
| res.push_back(SvcToString(s, t, Inter::kL, Scaling::k1_2)); | |
| if (s == 1) { | |
| continue; | |
| } | |
| } | |
| if (scale_by_half) { | |
| res.push_back(SvcToString(s, t, Inter::kS, Scaling::k1_2)); | |
| if (inter_layer) { | |
| res.push_back(SvcToString(s, t, Inter::kKey, Scaling::k1_2)); | |
| } | |
| } | |
| if (scale_by_two_thirds) { | |
| res.push_back(SvcToString(s, t, Inter::kS, Scaling::k2_3)); | |
| if (inter_layer) { | |
| res.push_back(SvcToString(s, t, Inter::kKey, Scaling::k2_3)); | |
| res.push_back(SvcToString(s, t, Inter::kL, Scaling::k2_3)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return res; | |
| } | |
| // static | |
| std::unique_ptr<SimpleEncoderWrapper> SimpleEncoderWrapper::Create( | |
| std::unique_ptr<VideoEncoderInterface> encoder, | |
| absl::string_view scalability_mode) { | |
| if (!encoder) { | |
| return nullptr; | |
| } | |
| std::optional<ScalabilityMode> sm = | |
| ScalabilityModeStringToEnum(scalability_mode); | |
| if (!sm) { | |
| return nullptr; | |
| } | |
| std::unique_ptr<ScalableVideoController> svc_controller = | |
| CreateScalabilityStructure(*sm); | |
| if (!svc_controller) { | |
| return nullptr; | |
| } | |
| return std::make_unique<SimpleEncoderWrapper>(std::move(encoder), | |
| std::move(svc_controller)); | |
| } | |
| SimpleEncoderWrapper::SimpleEncoderWrapper( | |
| std::unique_ptr<VideoEncoderInterface> encoder, | |
| std::unique_ptr<ScalableVideoController> svc_controller) | |
| : encoder_(std::move(encoder)), | |
| svc_controller_(std::move(svc_controller)), | |
| layer_configs_(svc_controller_->StreamConfig()) {} | |
| void SimpleEncoderWrapper::SetEncodeQp(int qp) { | |
| target_qp_ = qp; | |
| } | |
| void SimpleEncoderWrapper::SetEncodeFps(int fps) { | |
| fps_ = fps; | |
| } | |
| void SimpleEncoderWrapper::Encode(scoped_refptr<VideoFrameBuffer> frame_buffer, | |
| bool force_keyframe, | |
| EncodeResultCallback callback) { | |
| std::vector<ScalableVideoController::LayerFrameConfig> configs = | |
| svc_controller_->NextFrameConfig(force_keyframe); | |
| std::vector<FrameEncodeSettings> encode_settings; | |
| std::vector<GenericFrameInfo> frame_infos; | |
| for (size_t s = 0; s < configs.size(); ++s) { | |
| const ScalableVideoController::LayerFrameConfig& config = configs[s]; | |
| frame_infos.push_back(svc_controller_->OnEncodeDone(config)); | |
| FrameEncodeSettings& settings = encode_settings.emplace_back(); | |
| settings.rate_options = VideoEncoderInterface::FrameEncodeSettings::Cqp{ | |
| .target_qp = target_qp_}; | |
| settings.spatial_id = config.SpatialId(); | |
| settings.temporal_id = config.TemporalId(); | |
| const int num = layer_configs_.scaling_factor_num[s]; | |
| const int den = layer_configs_.scaling_factor_den[s]; | |
| settings.resolution = {.width = (frame_buffer->width() * num / den), | |
| .height = (frame_buffer->height() * num / den)}; | |
| bool buffer_updated = false; | |
| for (const CodecBufferUsage& buffer : config.Buffers()) { | |
| if (buffer.referenced) { | |
| settings.reference_buffers.push_back(buffer.id); | |
| } | |
| if (buffer.updated) { | |
| RTC_CHECK(!buffer_updated); | |
| settings.update_buffer = buffer.id; | |
| buffer_updated = true; | |
| } | |
| } | |
| if (settings.reference_buffers.empty()) { | |
| settings.frame_type = FrameType::kKeyframe; | |
| } | |
| struct FrameOut : public VideoEncoderInterface::FrameOutput { | |
| std::span<uint8_t> GetBitstreamOutputBuffer(DataSize size) override { | |
| bitstream.resize(size.bytes()); | |
| return bitstream; | |
| } | |
| void EncodeComplete( | |
| const VideoEncoderInterface::EncodeResult& result) override { | |
| auto* data = std::get_if<VideoEncoderInterface::EncodedData>(&result); | |
| SimpleEncoderWrapper::EncodeResult res; | |
| if (!data) { | |
| res.oh_no = true; | |
| callback(res); | |
| return; | |
| } | |
| res.frame_type = data->frame_type; | |
| res.bitstream_data = std::move(bitstream); | |
| res.generic_frame_info = frame_info; | |
| if (res.frame_type == FrameType::kKeyframe) { | |
| res.dependency_structure = svc_controller->DependencyStructure(); | |
| } | |
| callback(res); | |
| } | |
| std::vector<uint8_t> bitstream; | |
| EncodeResultCallback callback; | |
| GenericFrameInfo frame_info; | |
| ScalableVideoController* svc_controller; | |
| }; | |
| auto out = std::make_unique<FrameOut>(); | |
| out->callback = callback; | |
| out->frame_info = std::move(frame_infos[settings.spatial_id]); | |
| out->svc_controller = svc_controller_.get(); | |
| settings.frame_output = std::move(out); | |
| } | |
| encoder_->Encode(std::move(frame_buffer), | |
| {.presentation_timestamp = presentation_timestamp_}, | |
| std::move(encode_settings)); | |
| presentation_timestamp_ += 1 / Frequency::Hertz(fps_); | |
| } | |
| } // namespace webrtc |