| // Copyright 2013 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/cast/cast_sender_impl.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "media/base/video_frame.h" |
| #include "media/cast/sender/video_frame_factory.h" |
| |
| namespace media { |
| namespace cast { |
| |
| // The LocalVideoFrameInput class posts all incoming video frames to the main |
| // cast thread for processing. |
| class LocalVideoFrameInput final : public VideoFrameInput { |
| public: |
| LocalVideoFrameInput(scoped_refptr<CastEnvironment> cast_environment, |
| base::WeakPtr<VideoSender> video_sender) |
| : cast_environment_(cast_environment), |
| video_sender_(video_sender), |
| video_frame_factory_( |
| video_sender.get() ? |
| video_sender->CreateVideoFrameFactory().release() : nullptr) {} |
| |
| void InsertRawVideoFrame(scoped_refptr<media::VideoFrame> video_frame, |
| base::TimeTicks capture_time) final { |
| cast_environment_->PostTask( |
| CastEnvironment::MAIN, FROM_HERE, |
| base::BindOnce(&VideoSender::InsertRawVideoFrame, video_sender_, |
| std::move(video_frame), capture_time)); |
| } |
| |
| scoped_refptr<VideoFrame> MaybeCreateOptimizedFrame( |
| const gfx::Size& frame_size, |
| base::TimeDelta timestamp) final { |
| return video_frame_factory_ ? |
| video_frame_factory_->MaybeCreateFrame(frame_size, timestamp) : nullptr; |
| } |
| |
| bool CanCreateOptimizedFrames() const final { |
| return video_frame_factory_.get() != nullptr; |
| } |
| |
| protected: |
| ~LocalVideoFrameInput() final = default; |
| |
| private: |
| friend class base::RefCountedThreadSafe<LocalVideoFrameInput>; |
| |
| const scoped_refptr<CastEnvironment> cast_environment_; |
| const base::WeakPtr<VideoSender> video_sender_; |
| const std::unique_ptr<VideoFrameFactory> video_frame_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LocalVideoFrameInput); |
| }; |
| |
| // The LocalAudioFrameInput class posts all incoming audio frames to the main |
| // cast thread for processing. Therefore frames can be inserted from any thread. |
| class LocalAudioFrameInput final : public AudioFrameInput { |
| public: |
| LocalAudioFrameInput(scoped_refptr<CastEnvironment> cast_environment, |
| base::WeakPtr<AudioSender> audio_sender) |
| : cast_environment_(cast_environment), audio_sender_(audio_sender) {} |
| |
| void InsertAudio(std::unique_ptr<AudioBus> audio_bus, |
| const base::TimeTicks& recorded_time) final { |
| cast_environment_->PostTask( |
| CastEnvironment::MAIN, FROM_HERE, |
| base::BindOnce(&AudioSender::InsertAudio, audio_sender_, |
| std::move(audio_bus), recorded_time)); |
| } |
| |
| protected: |
| ~LocalAudioFrameInput() final = default; |
| |
| private: |
| friend class base::RefCountedThreadSafe<LocalAudioFrameInput>; |
| |
| scoped_refptr<CastEnvironment> cast_environment_; |
| base::WeakPtr<AudioSender> audio_sender_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LocalAudioFrameInput); |
| }; |
| |
| std::unique_ptr<CastSender> CastSender::Create( |
| scoped_refptr<CastEnvironment> cast_environment, |
| CastTransport* const transport_sender) { |
| CHECK(cast_environment.get()); |
| return std::unique_ptr<CastSender>( |
| new CastSenderImpl(cast_environment, transport_sender)); |
| } |
| |
| CastSenderImpl::CastSenderImpl(scoped_refptr<CastEnvironment> cast_environment, |
| CastTransport* const transport_sender) |
| : cast_environment_(cast_environment), transport_sender_(transport_sender) { |
| CHECK(cast_environment.get()); |
| } |
| |
| void CastSenderImpl::InitializeAudio( |
| const FrameSenderConfig& audio_config, |
| StatusChangeOnceCallback status_change_cb) { |
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| CHECK(audio_config.use_external_encoder || |
| cast_environment_->HasAudioThread()); |
| |
| VLOG(1) << "CastSenderImpl@" << this << "::InitializeAudio()"; |
| |
| audio_sender_ = std::make_unique<AudioSender>( |
| cast_environment_, audio_config, |
| base::BindOnce(&CastSenderImpl::OnAudioStatusChange, |
| weak_factory_.GetWeakPtr(), std::move(status_change_cb)), |
| transport_sender_); |
| if (video_sender_) { |
| DCHECK(audio_sender_->GetTargetPlayoutDelay() == |
| video_sender_->GetTargetPlayoutDelay()); |
| } |
| } |
| |
| void CastSenderImpl::InitializeVideo( |
| const FrameSenderConfig& video_config, |
| const StatusChangeCallback& status_change_cb, |
| const CreateVideoEncodeAcceleratorCallback& create_vea_cb) { |
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| |
| VLOG(1) << "CastSenderImpl@" << this << "::InitializeVideo()"; |
| |
| // No feedback callback, since it's ignored for CastSender. |
| video_sender_ = std::make_unique<VideoSender>( |
| cast_environment_, video_config, |
| base::BindRepeating(&CastSenderImpl::OnVideoStatusChange, |
| weak_factory_.GetWeakPtr(), status_change_cb), |
| create_vea_cb, transport_sender_, |
| base::BindRepeating(&CastSenderImpl::SetTargetPlayoutDelay, |
| weak_factory_.GetWeakPtr()), |
| media::VideoCaptureFeedbackCB()); |
| if (audio_sender_) { |
| DCHECK(audio_sender_->GetTargetPlayoutDelay() == |
| video_sender_->GetTargetPlayoutDelay()); |
| } |
| } |
| |
| CastSenderImpl::~CastSenderImpl() { |
| VLOG(1) << "CastSenderImpl@" << this << "::~CastSenderImpl()"; |
| } |
| |
| scoped_refptr<AudioFrameInput> CastSenderImpl::audio_frame_input() { |
| return audio_frame_input_; |
| } |
| |
| scoped_refptr<VideoFrameInput> CastSenderImpl::video_frame_input() { |
| return video_frame_input_; |
| } |
| |
| void CastSenderImpl::SetTargetPlayoutDelay( |
| base::TimeDelta new_target_playout_delay) { |
| VLOG(1) << "CastSenderImpl@" << this << "::SetTargetPlayoutDelay(" |
| << new_target_playout_delay.InMilliseconds() << " ms)"; |
| if (audio_sender_) { |
| audio_sender_->SetTargetPlayoutDelay(new_target_playout_delay); |
| } |
| if (video_sender_) { |
| video_sender_->SetTargetPlayoutDelay(new_target_playout_delay); |
| } |
| } |
| |
| void CastSenderImpl::OnAudioStatusChange( |
| StatusChangeOnceCallback status_change_cb, |
| OperationalStatus status) { |
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| if (status == STATUS_INITIALIZED && !audio_frame_input_) { |
| audio_frame_input_ = |
| new LocalAudioFrameInput(cast_environment_, audio_sender_->AsWeakPtr()); |
| } |
| std::move(status_change_cb).Run(status); |
| } |
| |
| void CastSenderImpl::OnVideoStatusChange( |
| const StatusChangeCallback& status_change_cb, |
| OperationalStatus status) { |
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| if (status == STATUS_INITIALIZED && !video_frame_input_) { |
| video_frame_input_ = |
| new LocalVideoFrameInput(cast_environment_, video_sender_->AsWeakPtr()); |
| } |
| status_change_cb.Run(status); |
| } |
| |
| } // namespace cast |
| } // namespace media |