/* * 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 "modules/video_coding/codecs/multiplex/include/multiplex_encoder_adapter.h" #include #include "api/video/encoded_image.h" #include "api/video_codecs/video_encoder.h" #include "common_video/include/video_frame_buffer.h" #include "common_video/libyuv/include/webrtc_libyuv.h" #include "media/base/video_common.h" #include "modules/video_coding/codecs/multiplex/include/augmented_video_frame_buffer.h" #include "rtc_base/logging.h" namespace webrtc { // Callback wrapper that helps distinguish returned results from `encoders_` // instances. class MultiplexEncoderAdapter::AdapterEncodedImageCallback : public webrtc::EncodedImageCallback { public: AdapterEncodedImageCallback(webrtc::MultiplexEncoderAdapter* adapter, AlphaCodecStream stream_idx) : adapter_(adapter), stream_idx_(stream_idx) {} EncodedImageCallback::Result OnEncodedImage( const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info) override { if (!adapter_) return Result(Result::OK); return adapter_->OnEncodedImage(stream_idx_, encoded_image, codec_specific_info); } private: MultiplexEncoderAdapter* adapter_; const AlphaCodecStream stream_idx_; }; MultiplexEncoderAdapter::MultiplexEncoderAdapter( VideoEncoderFactory* factory, const SdpVideoFormat& associated_format, bool supports_augmented_data) : factory_(factory), associated_format_(associated_format), encoded_complete_callback_(nullptr), key_frame_interval_(0), supports_augmented_data_(supports_augmented_data) {} MultiplexEncoderAdapter::~MultiplexEncoderAdapter() { Release(); } void MultiplexEncoderAdapter::SetFecControllerOverride( FecControllerOverride* fec_controller_override) { // Ignored. } int MultiplexEncoderAdapter::InitEncode( const VideoCodec* inst, const VideoEncoder::Settings& settings) { const size_t buffer_size = CalcBufferSize(VideoType::kI420, inst->width, inst->height); multiplex_dummy_planes_.resize(buffer_size); // It is more expensive to encode 0x00, so use 0x80 instead. std::fill(multiplex_dummy_planes_.begin(), multiplex_dummy_planes_.end(), 0x80); RTC_DCHECK_EQ(kVideoCodecMultiplex, inst->codecType); VideoCodec video_codec = *inst; video_codec.codecType = PayloadStringToCodecType(associated_format_.name); // Take over the key frame interval at adapter level, because we have to // sync the key frames for both sub-encoders. switch (video_codec.codecType) { case kVideoCodecVP8: key_frame_interval_ = video_codec.VP8()->keyFrameInterval; video_codec.VP8()->keyFrameInterval = 0; break; case kVideoCodecVP9: key_frame_interval_ = video_codec.VP9()->keyFrameInterval; video_codec.VP9()->keyFrameInterval = 0; break; case kVideoCodecH264: key_frame_interval_ = video_codec.H264()->keyFrameInterval; video_codec.H264()->keyFrameInterval = 0; break; default: break; } encoder_info_ = EncoderInfo(); encoder_info_.implementation_name = "MultiplexEncoderAdapter ("; encoder_info_.requested_resolution_alignment = 1; encoder_info_.apply_alignment_to_all_simulcast_layers = false; // This needs to be false so that we can do the split in Encode(). encoder_info_.supports_native_handle = false; for (size_t i = 0; i < kAlphaCodecStreams; ++i) { std::unique_ptr encoder = factory_->CreateVideoEncoder(associated_format_); const int rv = encoder->InitEncode(&video_codec, settings); if (rv) { RTC_LOG(LS_ERROR) << "Failed to create multiplex codec index " << i; return rv; } adapter_callbacks_.emplace_back(new AdapterEncodedImageCallback( this, static_cast(i))); encoder->RegisterEncodeCompleteCallback(adapter_callbacks_.back().get()); const EncoderInfo& encoder_impl_info = encoder->GetEncoderInfo(); encoder_info_.implementation_name += encoder_impl_info.implementation_name; if (i != kAlphaCodecStreams - 1) { encoder_info_.implementation_name += ", "; } // Uses hardware support if any of the encoders uses it. // For example, if we are having issues with down-scaling due to // pipelining delay in HW encoders we need higher encoder usage // thresholds in CPU adaptation. if (i == 0) { encoder_info_.is_hardware_accelerated = encoder_impl_info.is_hardware_accelerated; } else { encoder_info_.is_hardware_accelerated |= encoder_impl_info.is_hardware_accelerated; } encoder_info_.requested_resolution_alignment = cricket::LeastCommonMultiple( encoder_info_.requested_resolution_alignment, encoder_impl_info.requested_resolution_alignment); if (encoder_impl_info.apply_alignment_to_all_simulcast_layers) { encoder_info_.apply_alignment_to_all_simulcast_layers = true; } encoder_info_.has_internal_source = false; encoders_.emplace_back(std::move(encoder)); } encoder_info_.implementation_name += ")"; return WEBRTC_VIDEO_CODEC_OK; } int MultiplexEncoderAdapter::Encode( const VideoFrame& input_image, const std::vector* frame_types) { if (!encoded_complete_callback_) { return WEBRTC_VIDEO_CODEC_UNINITIALIZED; } // The input image is forwarded as-is, unless it is a native buffer and // `supports_augmented_data_` is true in which case we need to map it in order // to access the underlying AugmentedVideoFrameBuffer. VideoFrame forwarded_image = input_image; if (supports_augmented_data_ && forwarded_image.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative) { auto info = GetEncoderInfo(); rtc::scoped_refptr mapped_buffer = forwarded_image.video_frame_buffer()->GetMappedFrameBuffer( info.preferred_pixel_formats); if (!mapped_buffer) { // Unable to map the buffer. return WEBRTC_VIDEO_CODEC_ERROR; } forwarded_image.set_video_frame_buffer(std::move(mapped_buffer)); } std::vector adjusted_frame_types; if (key_frame_interval_ > 0 && picture_index_ % key_frame_interval_ == 0) { adjusted_frame_types.push_back(VideoFrameType::kVideoFrameKey); } else { adjusted_frame_types.push_back(VideoFrameType::kVideoFrameDelta); } const bool has_alpha = forwarded_image.video_frame_buffer()->type() == VideoFrameBuffer::Type::kI420A; std::unique_ptr augmenting_data = nullptr; uint16_t augmenting_data_length = 0; AugmentedVideoFrameBuffer* augmented_video_frame_buffer = nullptr; if (supports_augmented_data_) { augmented_video_frame_buffer = static_cast( forwarded_image.video_frame_buffer().get()); augmenting_data_length = augmented_video_frame_buffer->GetAugmentingDataSize(); augmenting_data = std::unique_ptr(new uint8_t[augmenting_data_length]); memcpy(augmenting_data.get(), augmented_video_frame_buffer->GetAugmentingData(), augmenting_data_length); augmenting_data_size_ = augmenting_data_length; } { MutexLock lock(&mutex_); stashed_images_.emplace( std::piecewise_construct, std::forward_as_tuple(forwarded_image.timestamp()), std::forward_as_tuple( picture_index_, has_alpha ? kAlphaCodecStreams : 1, std::move(augmenting_data), augmenting_data_length)); } ++picture_index_; // Encode YUV int rv = encoders_[kYUVStream]->Encode(forwarded_image, &adjusted_frame_types); // If we do not receive an alpha frame, we send a single frame for this // `picture_index_`. The receiver will receive `frame_count` as 1 which // specifies this case. if (rv || !has_alpha) return rv; // Encode AXX rtc::scoped_refptr frame_buffer = supports_augmented_data_ ? augmented_video_frame_buffer->GetVideoFrameBuffer() : forwarded_image.video_frame_buffer(); const I420ABufferInterface* yuva_buffer = frame_buffer->GetI420A(); rtc::scoped_refptr alpha_buffer = WrapI420Buffer(forwarded_image.width(), forwarded_image.height(), yuva_buffer->DataA(), yuva_buffer->StrideA(), multiplex_dummy_planes_.data(), yuva_buffer->StrideU(), multiplex_dummy_planes_.data(), yuva_buffer->StrideV(), // To keep reference alive. [frame_buffer] {}); VideoFrame alpha_image = VideoFrame::Builder() .set_video_frame_buffer(alpha_buffer) .set_timestamp_rtp(forwarded_image.timestamp()) .set_timestamp_ms(forwarded_image.render_time_ms()) .set_rotation(forwarded_image.rotation()) .set_id(forwarded_image.id()) .set_packet_infos(forwarded_image.packet_infos()) .build(); rv = encoders_[kAXXStream]->Encode(alpha_image, &adjusted_frame_types); return rv; } int MultiplexEncoderAdapter::RegisterEncodeCompleteCallback( EncodedImageCallback* callback) { encoded_complete_callback_ = callback; return WEBRTC_VIDEO_CODEC_OK; } void MultiplexEncoderAdapter::SetRates( const RateControlParameters& parameters) { VideoBitrateAllocation bitrate_allocation(parameters.bitrate); bitrate_allocation.SetBitrate( 0, 0, parameters.bitrate.GetBitrate(0, 0) - augmenting_data_size_); for (auto& encoder : encoders_) { // TODO(emircan): `framerate` is used to calculate duration in encoder // instances. We report the total frame rate to keep real time for now. // Remove this after refactoring duration logic. encoder->SetRates(RateControlParameters( bitrate_allocation, static_cast(encoders_.size() * parameters.framerate_fps), parameters.bandwidth_allocation - DataRate::BitsPerSec(augmenting_data_size_))); } } void MultiplexEncoderAdapter::OnPacketLossRateUpdate(float packet_loss_rate) { for (auto& encoder : encoders_) { encoder->OnPacketLossRateUpdate(packet_loss_rate); } } void MultiplexEncoderAdapter::OnRttUpdate(int64_t rtt_ms) { for (auto& encoder : encoders_) { encoder->OnRttUpdate(rtt_ms); } } void MultiplexEncoderAdapter::OnLossNotification( const LossNotification& loss_notification) { for (auto& encoder : encoders_) { encoder->OnLossNotification(loss_notification); } } int MultiplexEncoderAdapter::Release() { for (auto& encoder : encoders_) { const int rv = encoder->Release(); if (rv) return rv; } encoders_.clear(); adapter_callbacks_.clear(); MutexLock lock(&mutex_); stashed_images_.clear(); return WEBRTC_VIDEO_CODEC_OK; } VideoEncoder::EncoderInfo MultiplexEncoderAdapter::GetEncoderInfo() const { return encoder_info_; } EncodedImageCallback::Result MultiplexEncoderAdapter::OnEncodedImage( AlphaCodecStream stream_idx, const EncodedImage& encodedImage, const CodecSpecificInfo* codecSpecificInfo) { // Save the image MultiplexImageComponent image_component; image_component.component_index = stream_idx; image_component.codec_type = PayloadStringToCodecType(associated_format_.name); image_component.encoded_image = encodedImage; MutexLock lock(&mutex_); const auto& stashed_image_itr = stashed_images_.find(encodedImage.Timestamp()); const auto& stashed_image_next_itr = std::next(stashed_image_itr, 1); RTC_DCHECK(stashed_image_itr != stashed_images_.end()); MultiplexImage& stashed_image = stashed_image_itr->second; const uint8_t frame_count = stashed_image.component_count; stashed_image.image_components.push_back(image_component); if (stashed_image.image_components.size() == frame_count) { // Complete case for (auto iter = stashed_images_.begin(); iter != stashed_images_.end() && iter != stashed_image_next_itr; iter++) { // No image at all, skip. if (iter->second.image_components.size() == 0) continue; // We have to send out those stashed frames, otherwise the delta frame // dependency chain is broken. combined_image_ = MultiplexEncodedImagePacker::PackAndRelease(iter->second); CodecSpecificInfo codec_info = *codecSpecificInfo; codec_info.codecType = kVideoCodecMultiplex; encoded_complete_callback_->OnEncodedImage(combined_image_, &codec_info); } stashed_images_.erase(stashed_images_.begin(), stashed_image_next_itr); } return EncodedImageCallback::Result(EncodedImageCallback::Result::OK); } } // namespace webrtc