blob: 20411431b3d649e32127b2e81f9e829f26355465 [file] [log] [blame]
// 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 "base/bind.h"
#include "base/callback.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 : 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(const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& capture_time) final {
cast_environment_->PostTask(CastEnvironment::MAIN,
FROM_HERE,
base::Bind(&VideoSender::InsertRawVideoFrame,
video_sender_,
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 : 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::Bind(&AudioSender::InsertAudio,
audio_sender_,
base::Passed(&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),
weak_factory_(this) {
CHECK(cast_environment.get());
}
void CastSenderImpl::InitializeAudio(
const FrameSenderConfig& audio_config,
const StatusChangeCallback& 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_.reset(
new AudioSender(cast_environment_,
audio_config,
base::Bind(&CastSenderImpl::OnAudioStatusChange,
weak_factory_.GetWeakPtr(),
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,
const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
VLOG(1) << "CastSenderImpl@" << this << "::InitializeVideo()";
video_sender_.reset(new VideoSender(
cast_environment_,
video_config,
base::Bind(&CastSenderImpl::OnVideoStatusChange,
weak_factory_.GetWeakPtr(),
status_change_cb),
create_vea_cb,
create_video_encode_mem_cb,
transport_sender_,
base::Bind(&CastSenderImpl::SetTargetPlayoutDelay,
weak_factory_.GetWeakPtr())));
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(
const StatusChangeCallback& 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());
}
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