diff options
author | miu <miu@chromium.org> | 2015-01-29 17:24:28 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-30 01:25:07 +0000 |
commit | 7ccf0ef39c36bed75b19ad08a87a0e93c60893ba (patch) | |
tree | 19b275d726c0407d449ea85b5777ff74cbdbd770 /media/cast | |
parent | b4fd13cb9dca2d60333e6e9211dd5b88b0ca9739 (diff) | |
download | chromium_src-7ccf0ef39c36bed75b19ad08a87a0e93c60893ba.zip chromium_src-7ccf0ef39c36bed75b19ad08a87a0e93c60893ba.tar.gz chromium_src-7ccf0ef39c36bed75b19ad08a87a0e93c60893ba.tar.bz2 |
[Cast] Refactor ExternalVideoEncoder for cleaner/simpler encapsulation.
There was a lot of hopping around between two classes, each class having
code that executes on two threads. This change makes
ExternalVideoEncoder live entirely on the cast MAIN thread, and the VEA
client live entirely on the VEA's own thread. This greatly simplified
the code and improved readability (especially the create/init sequence).
Also added a new FakeVideoEncodeAcceleratorFactory class to provide
common functionality for multiple unit test modules. In a soon-upcoming
change, the video_sender_unittest.cc code will start using it.
BUG=325998, 451277
Review URL: https://codereview.chromium.org/868803007
Cr-Commit-Position: refs/heads/master@{#313852}
Diffstat (limited to 'media/cast')
-rw-r--r-- | media/cast/BUILD.gn | 2 | ||||
-rw-r--r-- | media/cast/cast_testing.gypi | 2 | ||||
-rw-r--r-- | media/cast/sender/external_video_encoder.cc | 418 | ||||
-rw-r--r-- | media/cast/sender/external_video_encoder.h | 54 | ||||
-rw-r--r-- | media/cast/sender/external_video_encoder_unittest.cc | 140 | ||||
-rw-r--r-- | media/cast/sender/fake_video_encode_accelerator_factory.cc | 81 | ||||
-rw-r--r-- | media/cast/sender/fake_video_encode_accelerator_factory.h | 90 | ||||
-rw-r--r-- | media/cast/sender/video_sender.cc | 1 |
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, |