summaryrefslogtreecommitdiffstats
path: root/media/cast
diff options
context:
space:
mode:
Diffstat (limited to 'media/cast')
-rw-r--r--media/cast/BUILD.gn2
-rw-r--r--media/cast/cast_testing.gypi2
-rw-r--r--media/cast/sender/external_video_encoder.cc418
-rw-r--r--media/cast/sender/external_video_encoder.h54
-rw-r--r--media/cast/sender/external_video_encoder_unittest.cc140
-rw-r--r--media/cast/sender/fake_video_encode_accelerator_factory.cc81
-rw-r--r--media/cast/sender/fake_video_encode_accelerator_factory.h90
-rw-r--r--media/cast/sender/video_sender.cc1
8 files changed, 431 insertions, 357 deletions
diff --git a/media/cast/BUILD.gn b/media/cast/BUILD.gn
index c10a990..2166953 100644
--- a/media/cast/BUILD.gn
+++ b/media/cast/BUILD.gn
@@ -296,6 +296,8 @@ test("cast_unittests") {
"sender/audio_sender_unittest.cc",
"sender/congestion_control_unittest.cc",
"sender/external_video_encoder_unittest.cc",
+ "sender/fake_video_encode_accelerator_factory.cc",
+ "sender/fake_video_encode_accelerator_factory.h",
"sender/video_encoder_impl_unittest.cc",
"sender/video_sender_unittest.cc",
"test/end2end_unittest.cc",
diff --git a/media/cast/cast_testing.gypi b/media/cast/cast_testing.gypi
index 12199b5..67a0f7d 100644
--- a/media/cast/cast_testing.gypi
+++ b/media/cast/cast_testing.gypi
@@ -116,6 +116,8 @@
'sender/audio_sender_unittest.cc',
'sender/congestion_control_unittest.cc',
'sender/external_video_encoder_unittest.cc',
+ 'sender/fake_video_encode_accelerator_factory.cc',
+ 'sender/fake_video_encode_accelerator_factory.h',
'sender/video_encoder_impl_unittest.cc',
'sender/video_sender_unittest.cc',
'test/end2end_unittest.cc',
diff --git a/media/cast/sender/external_video_encoder.cc b/media/cast/sender/external_video_encoder.cc
index 6dec102..9cc2e8c 100644
--- a/media/cast/sender/external_video_encoder.cc
+++ b/media/cast/sender/external_video_encoder.cc
@@ -17,13 +17,8 @@
#include "media/cast/net/cast_transport_config.h"
#include "media/video/video_encode_accelerator.h"
-namespace media {
-namespace cast {
-class LocalVideoEncodeAcceleratorClient;
-} // namespace cast
-} // namespace media
-
namespace {
+
static const size_t kOutputBufferCount = 3;
void LogFrameEncodedEvent(
@@ -35,6 +30,7 @@ void LogFrameEncodedEvent(
event_time, media::cast::FRAME_ENCODED, media::cast::VIDEO_EVENT,
rtp_timestamp, frame_id);
}
+
} // namespace
namespace media {
@@ -54,102 +50,63 @@ struct InProgressFrameEncode {
frame_encoded_callback(callback) {}
};
-// The ExternalVideoEncoder class can be deleted directly by cast, while
-// LocalVideoEncodeAcceleratorClient stays around long enough to properly shut
-// down the VideoEncodeAccelerator.
-class LocalVideoEncodeAcceleratorClient
+// Owns a VideoEncoderAccelerator instance and provides the necessary adapters
+// to encode media::VideoFrames and emit media::cast::EncodedFrames. All
+// methods must be called on the thread associated with the given
+// SingleThreadTaskRunner, except for the task_runner() accessor.
+class ExternalVideoEncoder::VEAClientImpl
: public VideoEncodeAccelerator::Client,
- public base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient> {
+ public base::RefCountedThreadSafe<VEAClientImpl> {
public:
- // Create an instance of this class and post a task to create
- // video_encode_accelerator_. A ref to |this| will be kept, awaiting reply
- // via ProxyCreateVideoEncodeAccelerator, which will provide us with the
- // encoder task runner and vea instance. We cannot be destroyed until we
- // receive the reply, otherwise the VEA object created may leak.
- static scoped_refptr<LocalVideoEncodeAcceleratorClient> Create(
- scoped_refptr<CastEnvironment> cast_environment,
- const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
- const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
- const base::WeakPtr<ExternalVideoEncoder>& weak_owner) {
- scoped_refptr<LocalVideoEncodeAcceleratorClient> client(
- new LocalVideoEncodeAcceleratorClient(
- cast_environment, create_video_encode_mem_cb, weak_owner));
-
- // This will keep a ref to |client|, if weak_owner is destroyed before
- // ProxyCreateVideoEncodeAccelerator is called, we will stay alive until
- // we can properly destroy the VEA.
- create_vea_cb.Run(base::Bind(
- &LocalVideoEncodeAcceleratorClient::OnCreateVideoEncodeAcceleratorProxy,
- client));
-
- return client;
+ VEAClientImpl(
+ const scoped_refptr<CastEnvironment>& cast_environment,
+ const scoped_refptr<base::SingleThreadTaskRunner>& encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea,
+ int max_frame_rate,
+ const CreateVideoEncodeMemoryCallback& create_video_encode_memory_cb)
+ : cast_environment_(cast_environment),
+ task_runner_(encoder_task_runner),
+ max_frame_rate_(max_frame_rate),
+ create_video_encode_memory_cb_(create_video_encode_memory_cb),
+ video_encode_accelerator_(vea.Pass()),
+ encoder_active_(false),
+ last_encoded_frame_id_(kStartFrameId),
+ key_frame_encountered_(false) {
}
- // Initialize the real HW encoder.
- void Initialize(const VideoSenderConfig& video_config) {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
-
- VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
- switch (video_config.codec) {
- case CODEC_VIDEO_VP8:
- output_profile = media::VP8PROFILE_ANY;
- break;
- case CODEC_VIDEO_H264:
- output_profile = media::H264PROFILE_MAIN;
- break;
- case CODEC_VIDEO_FAKE:
- NOTREACHED() << "Fake software video encoder cannot be external";
- break;
- default:
- NOTREACHED() << "Video codec not specified or not supported";
- break;
- }
- max_frame_rate_ = video_config.max_frame_rate;
+ base::SingleThreadTaskRunner* task_runner() const {
+ return task_runner_.get();
+ }
+
+ void Initialize(const gfx::Size& frame_size,
+ VideoCodecProfile codec_profile,
+ int start_bit_rate,
+ const CastInitializationCallback& initialization_cb) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(!frame_size.IsEmpty());
- bool result = video_encode_accelerator_->Initialize(
+ encoder_active_ = video_encode_accelerator_->Initialize(
media::VideoFrame::I420,
- gfx::Size(video_config.width, video_config.height),
- output_profile,
- video_config.start_bitrate,
+ frame_size,
+ codec_profile,
+ start_bit_rate,
this);
UMA_HISTOGRAM_BOOLEAN("Cast.Sender.VideoEncodeAcceleratorInitializeSuccess",
- result);
- if (!result) {
+ encoder_active_);
+
+ if (!initialization_cb.is_null()) {
cast_environment_->PostTask(
CastEnvironment::MAIN,
FROM_HERE,
- base::Bind(&ExternalVideoEncoder::EncoderInitialized, weak_owner_,
- false));
- return;
- }
-
- // Wait until shared memory is allocated to indicate that encoder is
- // initialized.
- }
-
- // Destroy the VEA on the correct thread.
- void Destroy() {
- DCHECK(encoder_task_runner_.get());
- if (!video_encode_accelerator_)
- return;
-
- if (encoder_task_runner_->RunsTasksOnCurrentThread()) {
- video_encode_accelerator_.reset();
- } else {
- // We do this instead of just reposting to encoder_task_runner_, because
- // we are called from the destructor.
- encoder_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&DestroyVideoEncodeAcceleratorOnEncoderThread,
- base::Passed(&video_encode_accelerator_)));
+ base::Bind(initialization_cb,
+ encoder_active_ ? STATUS_VIDEO_INITIALIZED :
+ STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED));
}
}
- void SetBitRate(uint32 bit_rate) {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ void SetBitRate(int bit_rate) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
video_encode_accelerator_->RequestEncodingParametersChange(bit_rate,
max_frame_rate_);
@@ -160,8 +117,10 @@ class LocalVideoEncodeAcceleratorClient
const base::TimeTicks& reference_time,
bool key_frame_requested,
const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+
+ if (!encoder_active_)
+ return;
in_progress_frame_encodes_.push_back(InProgressFrameEncode(
TimeDeltaToRtpDelta(video_frame->timestamp(), kVideoFrequency),
@@ -174,39 +133,38 @@ class LocalVideoEncodeAcceleratorClient
protected:
void NotifyError(VideoEncodeAccelerator::Error error) override {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
VLOG(1) << "ExternalVideoEncoder NotifyError: " << error;
- cast_environment_->PostTask(
- CastEnvironment::MAIN,
- FROM_HERE,
- base::Bind(&ExternalVideoEncoder::EncoderError, weak_owner_));
+ encoder_active_ = false;
+ // TODO(miu): Plumbing is required to bubble this up to the CastSession and
+ // beyond.
+ // TODO(miu): Force-flush all |in_progress_frame_encodes_| immediately so
+ // pending frames do not become stuck, freezing VideoSender.
}
// Called to allocate the input and output buffers.
void RequireBitstreamBuffers(unsigned int input_count,
const gfx::Size& input_coded_size,
size_t output_buffer_size) override {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
- DCHECK(video_encode_accelerator_);
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ // TODO(miu): Investigate why we are ignoring |input_count| (4) and instead
+ // using |kOutputBufferCount| (3) here.
for (size_t j = 0; j < kOutputBufferCount; ++j) {
create_video_encode_memory_cb_.Run(
output_buffer_size,
- base::Bind(&LocalVideoEncodeAcceleratorClient::OnCreateSharedMemory,
- this));
+ base::Bind(&VEAClientImpl::OnCreateSharedMemory, this));
}
}
- // Encoder has encoded a frame and it's available in one of out output
- // buffers.
+ // Encoder has encoded a frame and it's available in one of the output
+ // buffers. Package the result in a media::cast::EncodedFrame and post it
+ // to the Cast MAIN thread via the supplied callback.
void BitstreamBufferReady(int32 bitstream_buffer_id,
size_t payload_size,
bool key_frame) override {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
if (bitstream_buffer_id < 0 ||
bitstream_buffer_id >= static_cast<int32>(output_buffers_.size())) {
NOTREACHED();
@@ -229,6 +187,9 @@ class LocalVideoEncodeAcceleratorClient
// Do not send video until we have encountered the first key frame.
// Save the bitstream buffer in |stream_header_| to be sent later along
// with the first key frame.
+ //
+ // TODO(miu): Should |stream_header_| be an std::ostringstream for
+ // performance reasons?
stream_header_.append(static_cast<const char*>(output_buffer->memory()),
payload_size);
} else if (!in_progress_frame_encodes_.empty()) {
@@ -280,58 +241,27 @@ class LocalVideoEncodeAcceleratorClient
}
private:
- LocalVideoEncodeAcceleratorClient(
- scoped_refptr<CastEnvironment> cast_environment,
- const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
- const base::WeakPtr<ExternalVideoEncoder>& weak_owner)
- : cast_environment_(cast_environment),
- create_video_encode_memory_cb_(create_video_encode_mem_cb),
- weak_owner_(weak_owner),
- last_encoded_frame_id_(kStartFrameId),
- key_frame_encountered_(false) {}
-
- // Trampoline VEA creation callback to OnCreateVideoEncodeAccelerator()
- // on encoder_task_runner. Normally we would just repost the same method to
- // it, and would not need a separate proxy method, but we can't
- // ThreadTaskRunnerHandle::Get() in unittests just yet.
- void OnCreateVideoEncodeAcceleratorProxy(
- scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
- scoped_ptr<media::VideoEncodeAccelerator> vea) {
- encoder_task_runner->PostTask(
- FROM_HERE,
- base::Bind(&media::cast::LocalVideoEncodeAcceleratorClient::
- OnCreateVideoEncodeAccelerator,
- this,
- encoder_task_runner,
- base::Passed(&vea)));
- }
+ friend class base::RefCountedThreadSafe<VEAClientImpl>;
- void OnCreateVideoEncodeAccelerator(
- scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
- scoped_ptr<media::VideoEncodeAccelerator> vea) {
- encoder_task_runner_ = encoder_task_runner;
- video_encode_accelerator_.reset(vea.release());
-
- cast_environment_->PostTask(
- CastEnvironment::MAIN,
+ ~VEAClientImpl() override {
+ // According to the media::VideoEncodeAccelerator interface, Destroy()
+ // should be called instead of invoking its private destructor.
+ task_runner_->PostTask(
FROM_HERE,
- base::Bind(&ExternalVideoEncoder::OnCreateVideoEncodeAccelerator,
- weak_owner_,
- encoder_task_runner_));
+ base::Bind(&media::VideoEncodeAccelerator::Destroy,
+ base::Unretained(video_encode_accelerator_.release())));
}
// Note: This method can be called on any thread.
void OnCreateSharedMemory(scoped_ptr<base::SharedMemory> memory) {
- encoder_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&LocalVideoEncodeAcceleratorClient::ReceivedSharedMemory,
- this,
- base::Passed(&memory)));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&VEAClientImpl::OnReceivedSharedMemory,
+ this,
+ base::Passed(&memory)));
}
- void ReceivedSharedMemory(scoped_ptr<base::SharedMemory> memory) {
- DCHECK(encoder_task_runner_.get());
- DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread());
+ void OnReceivedSharedMemory(scoped_ptr<base::SharedMemory> memory) {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
output_buffers_.push_back(memory.release());
@@ -346,32 +276,14 @@ class LocalVideoEncodeAcceleratorClient
output_buffers_[i]->handle(),
output_buffers_[i]->mapped_size()));
}
-
- cast_environment_->PostTask(
- CastEnvironment::MAIN,
- FROM_HERE,
- base::Bind(&ExternalVideoEncoder::EncoderInitialized, weak_owner_,
- true));
- }
-
- static void DestroyVideoEncodeAcceleratorOnEncoderThread(
- scoped_ptr<media::VideoEncodeAccelerator> vea) {
- // VEA::~VEA specialization takes care of calling Destroy() on the VEA impl.
- }
-
- friend class base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient>;
-
- ~LocalVideoEncodeAcceleratorClient() override {
- Destroy();
- DCHECK(!video_encode_accelerator_);
}
const scoped_refptr<CastEnvironment> cast_environment_;
- scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
- scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_;
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ const int max_frame_rate_;
const CreateVideoEncodeMemoryCallback create_video_encode_memory_cb_;
- const base::WeakPtr<ExternalVideoEncoder> weak_owner_;
- int max_frame_rate_;
+ scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_;
+ bool encoder_active_;
uint32 last_encoded_frame_id_;
bool key_frame_encountered_;
std::string stream_header_;
@@ -382,109 +294,137 @@ class LocalVideoEncodeAcceleratorClient
// FIFO list.
std::list<InProgressFrameEncode> in_progress_frame_encodes_;
- DISALLOW_COPY_AND_ASSIGN(LocalVideoEncodeAcceleratorClient);
+ DISALLOW_COPY_AND_ASSIGN(VEAClientImpl);
};
ExternalVideoEncoder::ExternalVideoEncoder(
- scoped_refptr<CastEnvironment> cast_environment,
+ const scoped_refptr<CastEnvironment>& cast_environment,
const VideoSenderConfig& video_config,
+ const gfx::Size& frame_size,
const CastInitializationCallback& initialization_cb,
const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
- const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb)
- : video_config_(video_config),
- cast_environment_(cast_environment),
- encoder_active_(false),
+ const CreateVideoEncodeMemoryCallback& create_video_encode_memory_cb)
+ : cast_environment_(cast_environment),
+ create_video_encode_memory_cb_(create_video_encode_memory_cb),
+ bit_rate_(video_config.start_bitrate),
key_frame_requested_(false),
- initialization_cb_(initialization_cb),
weak_factory_(this) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK_GT(video_config.max_frame_rate, 0);
+ DCHECK(!frame_size.IsEmpty());
+ DCHECK(!create_vea_cb.is_null());
+ DCHECK(!create_video_encode_memory_cb_.is_null());
+ DCHECK_GT(bit_rate_, 0);
+
+ VideoCodecProfile codec_profile;
+ switch (video_config.codec) {
+ case CODEC_VIDEO_VP8:
+ codec_profile = media::VP8PROFILE_ANY;
+ break;
+ case CODEC_VIDEO_H264:
+ codec_profile = media::H264PROFILE_MAIN;
+ break;
+ case CODEC_VIDEO_FAKE:
+ NOTREACHED() << "Fake software video encoder cannot be external";
+ // ...flow through to next case...
+ default:
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(initialization_cb, STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED));
+ return;
+ }
- video_accelerator_client_ =
- LocalVideoEncodeAcceleratorClient::Create(cast_environment_,
- create_vea_cb,
- create_video_encode_mem_cb,
- weak_factory_.GetWeakPtr());
- DCHECK(video_accelerator_client_.get());
+ create_vea_cb.Run(
+ base::Bind(&ExternalVideoEncoder::OnCreateVideoEncodeAccelerator,
+ weak_factory_.GetWeakPtr(),
+ frame_size,
+ codec_profile,
+ video_config.max_frame_rate,
+ initialization_cb));
}
ExternalVideoEncoder::~ExternalVideoEncoder() {
}
-void ExternalVideoEncoder::EncoderInitialized(bool success) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- encoder_active_ = success;
- DCHECK(!initialization_cb_.is_null());
- initialization_cb_.Run(
- success ?
- STATUS_VIDEO_INITIALIZED : STATUS_HW_VIDEO_ENCODER_NOT_SUPPORTED);
- initialization_cb_.Reset();
-}
-
-void ExternalVideoEncoder::EncoderError() {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- encoder_active_ = false;
-}
-
-void ExternalVideoEncoder::OnCreateVideoEncodeAccelerator(
- scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner) {
- DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- encoder_task_runner_ = encoder_task_runner;
-
- encoder_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize,
- video_accelerator_client_,
- video_config_));
-}
-
bool ExternalVideoEncoder::EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time,
const FrameEncodedCallback& frame_encoded_callback) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
-
- if (!encoder_active_)
- return false;
-
- encoder_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&LocalVideoEncodeAcceleratorClient::EncodeVideoFrame,
- video_accelerator_client_,
- video_frame,
- reference_time,
- key_frame_requested_,
- frame_encoded_callback));
-
+ DCHECK(!video_frame->visible_rect().IsEmpty());
+ DCHECK(!frame_encoded_callback.is_null());
+
+ if (!client_)
+ return false; // Not ready.
+
+ client_->task_runner()->PostTask(FROM_HERE,
+ base::Bind(&VEAClientImpl::EncodeVideoFrame,
+ client_,
+ video_frame,
+ reference_time,
+ key_frame_requested_,
+ frame_encoded_callback));
key_frame_requested_ = false;
return true;
}
-// Inform the encoder about the new target bit rate.
void ExternalVideoEncoder::SetBitRate(int new_bit_rate) {
- if (!encoder_active_) {
- // If we receive SetBitRate() before VEA creation callback is invoked,
- // cache the new bit rate in the encoder config and use the new settings
- // to initialize VEA.
- video_config_.start_bitrate = new_bit_rate;
- return;
- }
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+ DCHECK_GT(new_bit_rate, 0);
- encoder_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&LocalVideoEncodeAcceleratorClient::SetBitRate,
- video_accelerator_client_,
- new_bit_rate));
+ bit_rate_ = new_bit_rate;
+ if (!client_)
+ return;
+ client_->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&VEAClientImpl::SetBitRate, client_, bit_rate_));
}
-// Inform the encoder to encode the next frame as a key frame.
void ExternalVideoEncoder::GenerateKeyFrame() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
key_frame_requested_ = true;
}
-// Inform the encoder to only reference frames older or equal to frame_id;
void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
- // Do nothing not supported.
+ // Do nothing. Not supported.
+}
+
+void ExternalVideoEncoder::OnCreateVideoEncodeAccelerator(
+ const gfx::Size& frame_size,
+ VideoCodecProfile codec_profile,
+ int max_frame_rate,
+ const CastInitializationCallback& initialization_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea) {
+ DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
+
+ // The callback will be invoked with null pointers in the case where the
+ // system does not support or lacks the resources to provide GPU-accelerated
+ // video encoding.
+ if (!encoder_task_runner || !vea) {
+ if (!initialization_cb.is_null()) {
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(initialization_cb, STATUS_INVALID_VIDEO_CONFIGURATION));
+ }
+ return;
+ }
+
+ DCHECK(!client_);
+ client_ = new VEAClientImpl(cast_environment_,
+ encoder_task_runner,
+ vea.Pass(),
+ max_frame_rate,
+ create_video_encode_memory_cb_);
+ client_->task_runner()->PostTask(FROM_HERE,
+ base::Bind(&VEAClientImpl::Initialize,
+ client_,
+ frame_size,
+ codec_profile,
+ bit_rate_,
+ initialization_cb));
}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/sender/external_video_encoder.h b/media/cast/sender/external_video_encoder.h
index 9012555..f647421 100644
--- a/media/cast/sender/external_video_encoder.h
+++ b/media/cast/sender/external_video_encoder.h
@@ -11,26 +11,23 @@
#include "media/cast/cast_environment.h"
#include "media/cast/sender/video_encoder.h"
#include "media/video/video_encode_accelerator.h"
-
-namespace media {
-class VideoFrame;
-}
+#include "ui/gfx/geometry/size.h"
namespace media {
namespace cast {
-class LocalVideoEncodeAcceleratorClient;
-
-// This object is called external from the main cast thread and internally from
-// the video encoder thread.
+// Cast MAIN thread proxy to the internal media::VideoEncodeAccelerator
+// implementation running on a separate thread. Encodes media::VideoFrames and
+// emits media::cast::EncodedFrames.
class ExternalVideoEncoder : public VideoEncoder {
public:
ExternalVideoEncoder(
- scoped_refptr<CastEnvironment> cast_environment,
+ const scoped_refptr<CastEnvironment>& cast_environment,
const VideoSenderConfig& video_config,
+ const gfx::Size& frame_size,
const CastInitializationCallback& initialization_cb,
const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
- const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb);
+ const CreateVideoEncodeMemoryCallback& create_video_encode_memory_cb);
~ExternalVideoEncoder() override;
@@ -43,33 +40,28 @@ class ExternalVideoEncoder : public VideoEncoder {
void GenerateKeyFrame() override;
void LatestFrameIdToReference(uint32 frame_id) override;
- // Called when video_accelerator_client_ has finished creating the VEA and
- // is ready for use.
- void OnCreateVideoEncodeAccelerator(
- scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner);
-
- protected:
- // If |success| is true then encoder is initialized successfully.
- // Otherwise encoder initialization failed.
- void EncoderInitialized(bool success);
- void EncoderError();
-
private:
- friend class LocalVideoEncodeAcceleratorClient;
+ class VEAClientImpl;
- VideoSenderConfig video_config_;
- scoped_refptr<CastEnvironment> cast_environment_;
+ // Method invoked by the CreateVideoEncodeAcceleratorCallback to construct a
+ // VEAClientImpl to own and interface with a new |vea|. Upon return,
+ // |client_| holds a reference to the new VEAClientImpl.
+ void OnCreateVideoEncodeAccelerator(
+ const gfx::Size& frame_size,
+ VideoCodecProfile codec_profile,
+ int max_frame_rate,
+ const CastInitializationCallback& initialization_cb,
+ scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner,
+ scoped_ptr<media::VideoEncodeAccelerator> vea);
- bool encoder_active_;
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ const CreateVideoEncodeMemoryCallback create_video_encode_memory_cb_;
+ int bit_rate_;
bool key_frame_requested_;
- scoped_refptr<LocalVideoEncodeAcceleratorClient> video_accelerator_client_;
- scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_;
-
- CastInitializationCallback initialization_cb_;
+ scoped_refptr<VEAClientImpl> client_;
- // Weak pointer factory for posting back LocalVideoEncodeAcceleratorClient
- // notifications to ExternalVideoEncoder.
+ // Provides a weak pointer for the OnCreateVideoEncoderAccelerator() callback.
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<ExternalVideoEncoder> weak_factory_;
diff --git a/media/cast/sender/external_video_encoder_unittest.cc b/media/cast/sender/external_video_encoder_unittest.cc
index 6cc1911..2518a40 100644
--- a/media/cast/sender/external_video_encoder_unittest.cc
+++ b/media/cast/sender/external_video_encoder_unittest.cc
@@ -11,6 +11,7 @@
#include "media/cast/cast_defines.h"
#include "media/cast/cast_environment.h"
#include "media/cast/sender/external_video_encoder.h"
+#include "media/cast/sender/fake_video_encode_accelerator_factory.h"
#include "media/cast/test/fake_single_thread_task_runner.h"
#include "media/cast/test/utility/video_utility.h"
#include "media/video/fake_video_encode_accelerator.h"
@@ -23,39 +24,6 @@ using testing::_;
namespace {
-void IgnoreInitializationStatus(CastInitializationStatus status) {}
-
-class VEAFactory {
- public:
- VEAFactory(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- scoped_ptr<VideoEncodeAccelerator> vea)
- : task_runner_(task_runner), vea_(vea.Pass()) {}
-
- void CreateVideoEncodeAccelerator(
- const ReceiveVideoEncodeAcceleratorCallback& callback) {
- create_cb_ = callback;
- }
-
- void FinishCreatingVideoEncodeAccelerator() {
- create_cb_.Run(task_runner_, vea_.Pass());
- }
-
- private:
- const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- scoped_ptr<VideoEncodeAccelerator> vea_;
- ReceiveVideoEncodeAcceleratorCallback create_cb_;
-};
-
-void CreateSharedMemory(
- size_t size, const ReceiveVideoEncodeMemoryCallback& callback) {
- scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
- if (!shm->CreateAndMapAnonymous(size)) {
- NOTREACHED();
- return;
- }
- callback.Run(shm.Pass());
-}
-
class TestVideoEncoderCallback
: public base::RefCountedThreadSafe<TestVideoEncoderCallback> {
public:
@@ -104,13 +72,22 @@ class TestVideoEncoderCallback
class ExternalVideoEncoderTest : public ::testing::Test {
protected:
ExternalVideoEncoderTest()
- : test_video_encoder_callback_(new TestVideoEncoderCallback()) {
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
+ cast_environment_(new CastEnvironment(
+ scoped_ptr<base::TickClock>(testing_clock_).Pass(),
+ task_runner_,
+ task_runner_,
+ task_runner_)),
+ vea_factory_(task_runner_),
+ init_status_(STATUS_VIDEO_UNINITIALIZED),
+ test_video_encoder_callback_(new TestVideoEncoderCallback()) {
+ testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
+
video_config_.ssrc = 1;
video_config_.receiver_ssrc = 2;
video_config_.rtp_payload_type = 127;
video_config_.use_external_encoder = true;
- video_config_.width = 320;
- video_config_.height = 240;
video_config_.max_bitrate = 5000000;
video_config_.min_bitrate = 1000000;
video_config_.start_bitrate = 2000000;
@@ -119,31 +96,22 @@ class ExternalVideoEncoderTest : public ::testing::Test {
video_config_.max_frame_rate = 30;
video_config_.max_number_of_video_buffers_used = 3;
video_config_.codec = CODEC_VIDEO_VP8;
- gfx::Size size(video_config_.width, video_config_.height);
+ const gfx::Size size(320, 240);
video_frame_ = media::VideoFrame::CreateFrame(
VideoFrame::I420, size, gfx::Rect(size), size, base::TimeDelta());
PopulateVideoFrame(video_frame_.get(), 123);
- testing_clock_ = new base::SimpleTestTickClock();
- testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
- task_runner_ = new test::FakeSingleThreadTaskRunner(testing_clock_);
- cast_environment_ =
- new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_).Pass(),
- task_runner_,
- task_runner_,
- task_runner_);
-
- fake_vea_ = new media::FakeVideoEncodeAccelerator(task_runner_);
- scoped_ptr<VideoEncodeAccelerator> fake_vea(fake_vea_);
- VEAFactory vea_factory(task_runner_, fake_vea.Pass());
video_encoder_.reset(new ExternalVideoEncoder(
cast_environment_,
video_config_,
- base::Bind(&IgnoreInitializationStatus),
- base::Bind(&VEAFactory::CreateVideoEncodeAccelerator,
- base::Unretained(&vea_factory)),
- base::Bind(&CreateSharedMemory)));
- vea_factory.FinishCreatingVideoEncodeAccelerator();
+ size,
+ base::Bind(&ExternalVideoEncoderTest::SaveInitializationStatus,
+ base::Unretained(this)),
+ base::Bind(
+ &FakeVideoEncodeAcceleratorFactory::CreateVideoEncodeAccelerator,
+ base::Unretained(&vea_factory_)),
+ base::Bind(&FakeVideoEncodeAcceleratorFactory::CreateSharedMemory,
+ base::Unretained(&vea_factory_))));
}
~ExternalVideoEncoderTest() override {}
@@ -154,20 +122,28 @@ class ExternalVideoEncoderTest : public ::testing::Test {
video_frame_->timestamp() + base::TimeDelta::FromMilliseconds(33));
}
- base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
- media::FakeVideoEncodeAccelerator* fake_vea_; // Owned by video_encoder_.
+ void SaveInitializationStatus(CastInitializationStatus result) {
+ EXPECT_EQ(STATUS_VIDEO_UNINITIALIZED, init_status_);
+ init_status_ = result;
+ }
+
+ base::SimpleTestTickClock* const testing_clock_; // Owned by CastEnvironment.
+ const scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ FakeVideoEncodeAcceleratorFactory vea_factory_;
+ CastInitializationStatus init_status_;
scoped_refptr<TestVideoEncoderCallback> test_video_encoder_callback_;
VideoSenderConfig video_config_;
- scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
scoped_ptr<VideoEncoder> video_encoder_;
scoped_refptr<media::VideoFrame> video_frame_;
- scoped_refptr<CastEnvironment> cast_environment_;
DISALLOW_COPY_AND_ASSIGN(ExternalVideoEncoderTest);
};
TEST_F(ExternalVideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
+ vea_factory_.SetAutoRespond(true);
task_runner_->RunTasks(); // Run the initializer on the correct thread.
+ EXPECT_EQ(STATUS_VIDEO_INITIALIZED, init_status_);
VideoEncoder::FrameEncodedCallback frame_encoded_callback =
base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
@@ -192,23 +168,29 @@ TEST_F(ExternalVideoEncoderTest, EncodePattern30fpsRunningOutOfAck) {
video_frame_, testing_clock_->NowTicks(), frame_encoded_callback));
task_runner_->RunTasks();
}
- ASSERT_EQ(1u, fake_vea_->stored_bitrates().size());
- EXPECT_EQ(2000u, fake_vea_->stored_bitrates()[0]);
+
+ ASSERT_EQ(1u, vea_factory_.last_response_vea()->stored_bitrates().size());
+ EXPECT_EQ(2000u, vea_factory_.last_response_vea()->stored_bitrates()[0]);
// We need to run the task to cleanup the GPU instance.
video_encoder_.reset(NULL);
task_runner_->RunTasks();
+
+ EXPECT_EQ(1, vea_factory_.vea_response_count());
+ EXPECT_EQ(3, vea_factory_.shm_response_count());
}
TEST_F(ExternalVideoEncoderTest, StreamHeader) {
+ vea_factory_.SetAutoRespond(true);
task_runner_->RunTasks(); // Run the initializer on the correct thread.
+ EXPECT_EQ(STATUS_VIDEO_INITIALIZED, init_status_);
VideoEncoder::FrameEncodedCallback frame_encoded_callback =
base::Bind(&TestVideoEncoderCallback::DeliverEncodedVideoFrame,
test_video_encoder_callback_.get());
// Force the FakeVideoEncodeAccelerator to return a dummy non-key frame first.
- fake_vea_->SendDummyFrameForTesting(false);
+ vea_factory_.last_response_vea()->SendDummyFrameForTesting(false);
// Verify the first returned bitstream buffer is still a key frame.
test_video_encoder_callback_->SetExpectedResult(
@@ -221,36 +203,20 @@ TEST_F(ExternalVideoEncoderTest, StreamHeader) {
// We need to run the task to cleanup the GPU instance.
video_encoder_.reset(NULL);
task_runner_->RunTasks();
+
+ EXPECT_EQ(1, vea_factory_.vea_response_count());
+ EXPECT_EQ(3, vea_factory_.shm_response_count());
}
// Verify that everything goes well even if ExternalVideoEncoder is destroyed
// before it has a chance to receive the VEA creation callback.
-TEST(ExternalVideoEncoderEarlyDestroyTest, DestroyBeforeVEACreatedCallback) {
- VideoSenderConfig video_config;
- base::SimpleTestTickClock* testing_clock = new base::SimpleTestTickClock();
- scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner(
- new test::FakeSingleThreadTaskRunner(testing_clock));
- scoped_refptr<CastEnvironment> cast_environment(
- new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock).Pass(),
- task_runner,
- task_runner,
- task_runner));
-
- scoped_ptr<VideoEncodeAccelerator> fake_vea(
- new media::FakeVideoEncodeAccelerator(task_runner));
- VEAFactory vea_factory(task_runner, fake_vea.Pass());
-
- scoped_ptr<ExternalVideoEncoder> video_encoder(new ExternalVideoEncoder(
- cast_environment,
- video_config,
- base::Bind(&IgnoreInitializationStatus),
- base::Bind(&VEAFactory::CreateVideoEncodeAccelerator,
- base::Unretained(&vea_factory)),
- base::Bind(&CreateSharedMemory)));
-
- video_encoder.reset();
- vea_factory.FinishCreatingVideoEncodeAccelerator();
- task_runner->RunTasks();
+TEST_F(ExternalVideoEncoderTest, DestroyBeforeVEACreatedCallback) {
+ video_encoder_.reset();
+ EXPECT_EQ(0, vea_factory_.vea_response_count());
+ vea_factory_.SetAutoRespond(true);
+ task_runner_->RunTasks();
+ EXPECT_EQ(1, vea_factory_.vea_response_count());
+ EXPECT_EQ(STATUS_VIDEO_UNINITIALIZED, init_status_);
}
} // namespace cast
diff --git a/media/cast/sender/fake_video_encode_accelerator_factory.cc b/media/cast/sender/fake_video_encode_accelerator_factory.cc
new file mode 100644
index 0000000..24eba4b
--- /dev/null
+++ b/media/cast/sender/fake_video_encode_accelerator_factory.cc
@@ -0,0 +1,81 @@
+// Copyright 2015 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/sender/fake_video_encode_accelerator_factory.h"
+
+#include "base/callback_helpers.h"
+
+namespace media {
+namespace cast {
+
+FakeVideoEncodeAcceleratorFactory::FakeVideoEncodeAcceleratorFactory(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+ : task_runner_(task_runner),
+ will_init_succeed_(true),
+ auto_respond_(false),
+ vea_response_count_(0),
+ shm_response_count_(0),
+ last_response_vea_(nullptr),
+ last_response_shm_(nullptr) {}
+
+FakeVideoEncodeAcceleratorFactory::~FakeVideoEncodeAcceleratorFactory() {}
+
+void FakeVideoEncodeAcceleratorFactory::SetInitializationWillSucceed(
+ bool will_init_succeed) {
+ will_init_succeed_ = will_init_succeed;
+}
+
+void FakeVideoEncodeAcceleratorFactory::SetAutoRespond(bool auto_respond) {
+ auto_respond_ = auto_respond;
+ if (auto_respond_) {
+ if (!vea_response_callback_.is_null())
+ RespondWithVideoEncodeAccelerator();
+ if (!shm_response_callback_.is_null())
+ RespondWithSharedMemory();
+ }
+}
+
+void FakeVideoEncodeAcceleratorFactory::CreateVideoEncodeAccelerator(
+ const ReceiveVideoEncodeAcceleratorCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!next_response_vea_);
+
+ FakeVideoEncodeAccelerator* const vea =
+ new FakeVideoEncodeAccelerator(task_runner_);
+ vea->SetWillInitializationSucceed(will_init_succeed_);
+ next_response_vea_.reset(vea);
+ vea_response_callback_ = callback;
+ if (auto_respond_)
+ RespondWithVideoEncodeAccelerator();
+}
+
+void FakeVideoEncodeAcceleratorFactory::CreateSharedMemory(
+ size_t size, const ReceiveVideoEncodeMemoryCallback& callback) {
+ DCHECK(!callback.is_null());
+ DCHECK(!next_response_shm_);
+
+ next_response_shm_.reset(new base::SharedMemory());
+ CHECK(next_response_shm_->CreateAndMapAnonymous(size));
+ shm_response_callback_ = callback;
+ if (auto_respond_)
+ RespondWithSharedMemory();
+}
+
+void FakeVideoEncodeAcceleratorFactory::RespondWithVideoEncodeAccelerator() {
+ DCHECK(next_response_vea_.get());
+ last_response_vea_ = next_response_vea_.get();
+ ++vea_response_count_;
+ base::ResetAndReturn(&vea_response_callback_).Run(
+ task_runner_, next_response_vea_.Pass());
+}
+
+void FakeVideoEncodeAcceleratorFactory::RespondWithSharedMemory() {
+ DCHECK(next_response_shm_.get());
+ last_response_shm_ = next_response_shm_.get();
+ ++shm_response_count_;
+ base::ResetAndReturn(&shm_response_callback_).Run(next_response_shm_.Pass());
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/sender/fake_video_encode_accelerator_factory.h b/media/cast/sender/fake_video_encode_accelerator_factory.h
new file mode 100644
index 0000000..73c8feb
--- /dev/null
+++ b/media/cast/sender/fake_video_encode_accelerator_factory.h
@@ -0,0 +1,90 @@
+// Copyright 2015 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.
+
+#ifndef MEDIA_CAST_SENDER_FAKE_VIDEO_ENCODE_ACCELERATOR_FACTORY_H_
+#define MEDIA_CAST_SENDER_FAKE_VIDEO_ENCODE_ACCELERATOR_FACTORY_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "base/single_thread_task_runner.h"
+#include "media/cast/cast_config.h"
+#include "media/video/fake_video_encode_accelerator.h"
+
+namespace media {
+namespace cast {
+
+// Used by test code to create fake VideoEncodeAccelerators. The test code
+// controls when the response callback is invoked.
+class FakeVideoEncodeAcceleratorFactory {
+ public:
+ explicit FakeVideoEncodeAcceleratorFactory(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+ ~FakeVideoEncodeAcceleratorFactory();
+
+ int vea_response_count() const {
+ return vea_response_count_;
+ }
+ int shm_response_count() const {
+ return shm_response_count_;
+ }
+
+ // These return the instance last responded. It is up to the caller to
+ // determine whether the pointer is still valid, since this factory does not
+ // own these objects anymore.
+ media::FakeVideoEncodeAccelerator* last_response_vea() const {
+ return static_cast<media::FakeVideoEncodeAccelerator*>(last_response_vea_);
+ }
+ base::SharedMemory* last_response_shm() const {
+ return last_response_shm_;
+ }
+
+ // Set whether the next created media::FakeVideoEncodeAccelerator will
+ // initialize successfully.
+ void SetInitializationWillSucceed(bool will_init_succeed);
+
+ // Enable/disable auto-respond mode. Default is disabled.
+ void SetAutoRespond(bool auto_respond);
+
+ // Creates a media::FakeVideoEncodeAccelerator. If in auto-respond mode,
+ // |callback| is run synchronously (i.e., before this method returns).
+ void CreateVideoEncodeAccelerator(
+ const ReceiveVideoEncodeAcceleratorCallback& callback);
+
+ // Creates shared memory of the requested |size|. If in auto-respond mode,
+ // |callback| is run synchronously (i.e., before this method returns).
+ void CreateSharedMemory(
+ size_t size,
+ const ReceiveVideoEncodeMemoryCallback& callback);
+
+ // Runs the |callback| provided to the last call to
+ // CreateVideoEncodeAccelerator() with the new VideoEncodeAccelerator
+ // instance.
+ void RespondWithVideoEncodeAccelerator();
+
+ // Runs the |callback| provided to the last call to
+ // CreateSharedMemory() with the new base::SharedMemory instance.
+ void RespondWithSharedMemory();
+
+ private:
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ bool will_init_succeed_;
+ bool auto_respond_;
+ scoped_ptr<media::VideoEncodeAccelerator> next_response_vea_;
+ ReceiveVideoEncodeAcceleratorCallback vea_response_callback_;
+ scoped_ptr<base::SharedMemory> next_response_shm_;
+ ReceiveVideoEncodeMemoryCallback shm_response_callback_;
+ int vea_response_count_;
+ int shm_response_count_;
+ media::VideoEncodeAccelerator* last_response_vea_;
+ base::SharedMemory* last_response_shm_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeVideoEncodeAcceleratorFactory);
+};
+
+} // namespace cast
+} // namespace media
+
+#endif // MEDIA_CAST_SENDER_FAKE_VIDEO_ENCODE_ACCELERATOR_FACTORY_H_
diff --git a/media/cast/sender/video_sender.cc b/media/cast/sender/video_sender.cc
index 3443c9d..8980a04 100644
--- a/media/cast/sender/video_sender.cc
+++ b/media/cast/sender/video_sender.cc
@@ -93,6 +93,7 @@ VideoSender::VideoSender(
video_encoder_.reset(new ExternalVideoEncoder(
cast_environment,
video_config,
+ gfx::Size(video_config.width, video_config.height),
base::Bind(&VideoSender::OnEncoderInitialized,
weak_factory_.GetWeakPtr(), initialization_cb),
create_vea_cb,