| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/video/video_encoder_fallback.h" |
| |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/sequence_checker.h" |
| #include "media/base/video_frame.h" |
| |
| namespace media { |
| |
| VideoEncoderFallback::VideoEncoderFallback( |
| std::unique_ptr<VideoEncoder> main_encoder, |
| CreateFallbackCB create_fallback_cb) |
| : encoder_(std::move(main_encoder)), |
| create_fallback_cb_(std::move(create_fallback_cb)) { |
| DCHECK(encoder_); |
| DCHECK(!create_fallback_cb_.is_null()); |
| } |
| |
| VideoEncoderFallback::~VideoEncoderFallback() = default; |
| |
| void VideoEncoderFallback::Initialize(VideoCodecProfile profile, |
| const Options& options, |
| OutputCB output_cb, |
| StatusCB done_cb) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| init_done_cb_ = std::move(done_cb); |
| output_cb_ = std::move(output_cb); |
| profile_ = profile; |
| options_ = options; |
| auto done_callback = [](base::WeakPtr<VideoEncoderFallback> self, |
| Status status) { |
| if (!self) |
| return; |
| if (status.is_ok()) { |
| std::move(self->init_done_cb_).Run(std::move(status)); |
| return; |
| } |
| self->FallbackInitialize(); |
| }; |
| |
| encoder_->Initialize( |
| profile, options, |
| base::BindRepeating(&VideoEncoderFallback::CallOutput, |
| weak_factory_.GetWeakPtr()), |
| base::BindOnce(done_callback, weak_factory_.GetWeakPtr())); |
| } |
| |
| VideoEncoder::PendingEncode VideoEncoderFallback::MakePendingEncode( |
| scoped_refptr<VideoFrame> frame, |
| bool key_frame, |
| StatusCB done_cb) { |
| PendingEncode result; |
| result.done_callback = std::move(done_cb); |
| result.frame = std::move(frame); |
| result.key_frame = key_frame; |
| return result; |
| } |
| |
| void VideoEncoderFallback::Encode(scoped_refptr<VideoFrame> frame, |
| bool key_frame, |
| StatusCB done_cb) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (use_fallback_) { |
| if (fallback_initialized_) { |
| encoder_->Encode(std::move(frame), key_frame, std::move(done_cb)); |
| } else { |
| encodes_to_retry_.push_back(std::make_unique<PendingEncode>( |
| MakePendingEncode(std::move(frame), key_frame, std::move(done_cb)))); |
| } |
| return; |
| } |
| |
| auto done_callback = [](base::WeakPtr<VideoEncoderFallback> self, |
| PendingEncode args, Status status) { |
| if (!self) |
| return; |
| DCHECK(self->encoder_); |
| if (status.is_ok()) { |
| std::move(args.done_callback).Run(std::move(status)); |
| return; |
| } |
| self->FallbackEncode(std::move(args)); |
| }; |
| |
| encoder_->Encode( |
| frame, key_frame, |
| base::BindOnce(done_callback, weak_factory_.GetWeakPtr(), |
| MakePendingEncode(frame, key_frame, std::move(done_cb)))); |
| } |
| |
| void VideoEncoderFallback::ChangeOptions(const Options& options, |
| OutputCB output_cb, |
| StatusCB done_cb) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| options_ = options; |
| if (encoder_) |
| encoder_->ChangeOptions(options, std::move(output_cb), std::move(done_cb)); |
| } |
| |
| void VideoEncoderFallback::Flush(StatusCB done_cb) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (encoder_) |
| encoder_->Flush(std::move(done_cb)); |
| } |
| |
| void VideoEncoderFallback::FallbackInitCompleted(Status status) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(encoder_); |
| if (init_done_cb_) |
| std::move(init_done_cb_).Run(status); |
| fallback_initialized_ = true; |
| |
| if (status.is_ok()) { |
| for (auto& encode : encodes_to_retry_) { |
| encoder_->Encode(std::move(encode->frame), encode->key_frame, |
| std::move(encode->done_callback)); |
| } |
| } else { |
| for (auto& encode : encodes_to_retry_) |
| std::move(encode->done_callback).Run(status); |
| } |
| encodes_to_retry_.clear(); |
| } |
| |
| void VideoEncoderFallback::FallbackInitialize() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| use_fallback_ = true; |
| encoder_ = std::move(create_fallback_cb_).Run(); |
| if (!encoder_) { |
| std::move(init_done_cb_).Run(StatusCode::kEncoderInitializationError); |
| FallbackInitCompleted(StatusCode::kEncoderInitializationError); |
| return; |
| } |
| |
| encoder_->Initialize( |
| profile_, options_, |
| base::BindRepeating(&VideoEncoderFallback::CallOutput, |
| weak_factory_.GetWeakPtr()), |
| base::BindOnce(&VideoEncoderFallback::FallbackInitCompleted, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void VideoEncoderFallback::FallbackEncode(PendingEncode args) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!use_fallback_) { |
| use_fallback_ = true; |
| encoder_ = std::move(create_fallback_cb_).Run(); |
| if (!encoder_) { |
| std::move(args.done_callback) |
| .Run(StatusCode::kEncoderInitializationError); |
| return; |
| } |
| |
| encoder_->Initialize( |
| profile_, options_, |
| base::BindRepeating(&VideoEncoderFallback::CallOutput, |
| weak_factory_.GetWeakPtr()), |
| base::BindOnce(&VideoEncoderFallback::FallbackInitCompleted, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| if (fallback_initialized_) { |
| encoder_->Encode(std::move(args.frame), args.key_frame, |
| std::move(args.done_callback)); |
| } else { |
| encodes_to_retry_.push_back( |
| std::make_unique<PendingEncode>(std::move(args))); |
| } |
| } |
| |
| void VideoEncoderFallback::CallOutput(VideoEncoderOutput output, |
| absl::optional<CodecDescription> desc) { |
| output_cb_.Run(std::move(output), std::move(desc)); |
| } |
| |
| } // namespace media |