diff options
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, |