Index: media/gpu/ipc/service/gpu_video_encode_accelerator.cc |
diff --git a/media/gpu/ipc/service/gpu_video_encode_accelerator.cc b/media/gpu/ipc/service/gpu_video_encode_accelerator.cc |
index 26d2aaa55d5f2c2dea38d33181dd7e3f5944d6dc..5c61111e24f3b2077597a03a986b58c1b89db948 100644 |
--- a/media/gpu/ipc/service/gpu_video_encode_accelerator.cc |
+++ b/media/gpu/ipc/service/gpu_video_encode_accelerator.cc |
@@ -59,6 +59,10 @@ bool MakeDecoderContextCurrent( |
return true; |
} |
+void DropSharedMemory(std::unique_ptr<base::SharedMemory> shm) { |
+ // Just let |shm| fall out of scope. |
+} |
+ |
#if defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC) |
std::unique_ptr<VideoEncodeAccelerator> CreateV4L2VEA() { |
scoped_refptr<V4L2Device> device = V4L2Device::Create(); |
@@ -99,13 +103,73 @@ std::unique_ptr<VideoEncodeAccelerator> CreateMediaFoundationVEA() { |
} // anonymous namespace |
+class GpuVideoEncodeAccelerator::MessageFilter : public IPC::MessageFilter { |
+ public: |
+ MessageFilter(GpuVideoEncodeAccelerator* owner, int32_t host_route_id) |
+ : owner_(owner), host_route_id_(host_route_id) {} |
+ |
+ void OnChannelError() override { sender_ = nullptr; } |
+ |
+ void OnChannelClosing() override { sender_ = nullptr; } |
+ |
+ void OnFilterAdded(IPC::Channel* channel) override { sender_ = channel; } |
+ |
+ void OnFilterRemoved() override { owner_->OnFilterRemoved(); } |
+ |
+ bool OnMessageReceived(const IPC::Message& msg) override { |
+ if (msg.routing_id() != host_route_id_) |
+ return false; |
+ |
+ IPC_BEGIN_MESSAGE_MAP(MessageFilter, msg) |
+ IPC_MESSAGE_FORWARD(AcceleratedVideoEncoderMsg_Encode, owner_, |
+ GpuVideoEncodeAccelerator::OnEncode) |
+ IPC_MESSAGE_FORWARD(AcceleratedVideoEncoderMsg_UseOutputBitstreamBuffer, |
+ owner_, |
+ GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer) |
+ IPC_MESSAGE_FORWARD( |
+ AcceleratedVideoEncoderMsg_RequestEncodingParametersChange, owner_, |
+ GpuVideoEncodeAccelerator::OnRequestEncodingParametersChange) |
+ IPC_MESSAGE_UNHANDLED(return false) |
+ IPC_END_MESSAGE_MAP() |
+ return true; |
+ } |
+ |
+ bool SendOnIOThread(IPC::Message* message) { |
+ if (!sender_ || message->is_sync()) { |
+ DCHECK(!message->is_sync()); |
+ delete message; |
+ return false; |
+ } |
+ return sender_->Send(message); |
+ } |
+ |
+ protected: |
+ ~MessageFilter() override {} |
+ |
+ private: |
+ GpuVideoEncodeAccelerator* const owner_; |
+ const int32_t host_route_id_; |
+ // The sender to which this filter was added. |
+ IPC::Sender* sender_ = nullptr; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MessageFilter); |
+}; |
+ |
GpuVideoEncodeAccelerator::GpuVideoEncodeAccelerator( |
int32_t host_route_id, |
- gpu::GpuCommandBufferStub* stub) |
+ gpu::GpuCommandBufferStub* stub, |
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) |
: host_route_id_(host_route_id), |
stub_(stub), |
input_format_(PIXEL_FORMAT_UNKNOWN), |
output_buffer_size_(0), |
+ filter_removed_(base::WaitableEvent::ResetPolicy::MANUAL, |
+ base::WaitableEvent::InitialState::NOT_SIGNALED), |
+ encoder_worker_thread_("EncoderWorkerThread"), |
+ main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ io_task_runner_(io_task_runner), |
+ encode_task_runner_(main_task_runner_), |
+ weak_this_factory_for_encoder_worker_(this), |
weak_this_factory_(this) { |
stub_->AddDestructionObserver(this); |
make_context_current_ = |
@@ -116,13 +180,21 @@ GpuVideoEncodeAccelerator::~GpuVideoEncodeAccelerator() { |
// This class can only be self-deleted from OnWillDestroyStub(), which means |
// the VEA has already been destroyed in there. |
DCHECK(!encoder_); |
+ if (encoder_worker_thread_.IsRunning()) { |
+ encoder_worker_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&GpuVideoEncodeAccelerator::DestroyOnEncoderWorker, |
+ weak_this_factory_for_encoder_worker_.GetWeakPtr())); |
+ encoder_worker_thread_.Stop(); |
+ } |
} |
bool GpuVideoEncodeAccelerator::Initialize(VideoPixelFormat input_format, |
const gfx::Size& input_visible_size, |
VideoCodecProfile output_profile, |
uint32_t initial_bitrate) { |
- DVLOG(1) << __FUNCTION__ |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ DVLOG(1) << __func__ |
<< " input_format=" << VideoPixelFormatToString(input_format) |
<< ", input_visible_size=" << input_visible_size.ToString() |
<< ", output_profile=" << GetProfileName(output_profile) |
@@ -130,14 +202,14 @@ bool GpuVideoEncodeAccelerator::Initialize(VideoPixelFormat input_format, |
DCHECK(!encoder_); |
if (!stub_->channel()->AddRoute(host_route_id_, stub_->stream_id(), this)) { |
- DLOG(ERROR) << __FUNCTION__ << " failed to add route"; |
+ DLOG(ERROR) << __func__ << " failed to add route"; |
return false; |
} |
if (input_visible_size.width() > limits::kMaxDimension || |
input_visible_size.height() > limits::kMaxDimension || |
input_visible_size.GetArea() > limits::kMaxCanvas) { |
- DLOG(ERROR) << __FUNCTION__ << "too large input_visible_size " |
+ DLOG(ERROR) << __func__ << "too large input_visible_size " |
<< input_visible_size.ToString(); |
return false; |
} |
@@ -153,11 +225,26 @@ bool GpuVideoEncodeAccelerator::Initialize(VideoPixelFormat input_format, |
initial_bitrate, this)) { |
input_format_ = input_format; |
input_visible_size_ = input_visible_size; |
+ // Attempt to set up performing encoding tasks on IO thread, if supported |
+ // by the VEA. |
+ if (encoder_->TryToSetupEncodeOnSeparateThread( |
+ weak_this_factory_.GetWeakPtr(), io_task_runner_)) { |
+ filter_ = new MessageFilter(this, host_route_id_); |
+ stub_->channel()->AddFilter(filter_.get()); |
+ encode_task_runner_ = io_task_runner_; |
+ } |
+ |
+ if (!encoder_worker_thread_.Start()) { |
+ DLOG(ERROR) << "Failed spawning encoder worker thread."; |
+ return false; |
+ } |
+ encoder_worker_task_runner_ = encoder_worker_thread_.task_runner(); |
+ |
return true; |
} |
} |
encoder_.reset(); |
- DLOG(ERROR) << __FUNCTION__ << " VEA initialization failed"; |
+ DLOG(ERROR) << __func__ << " VEA initialization failed"; |
return false; |
} |
@@ -176,12 +263,24 @@ bool GpuVideoEncodeAccelerator::OnMessageReceived(const IPC::Message& message) { |
return handled; |
} |
+bool GpuVideoEncodeAccelerator::Send(IPC::Message* message) { |
+ if (filter_ && io_task_runner_->BelongsToCurrentThread()) { |
+ return filter_->SendOnIOThread(message); |
+ } |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ return stub_->channel()->Send(message); |
+} |
+ |
void GpuVideoEncodeAccelerator::RequireBitstreamBuffers( |
unsigned int input_count, |
const gfx::Size& input_coded_size, |
size_t output_buffer_size) { |
- Send(new AcceleratedVideoEncoderHostMsg_RequireBitstreamBuffers( |
- host_route_id_, input_count, input_coded_size, output_buffer_size)); |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
+ if (!Send(new AcceleratedVideoEncoderHostMsg_RequireBitstreamBuffers( |
+ host_route_id_, input_count, input_coded_size, output_buffer_size))) { |
+ DLOG(ERROR) << __func__ << " failed."; |
+ return; |
+ } |
input_coded_size_ = input_coded_size; |
output_buffer_size_ = output_buffer_size; |
} |
@@ -191,17 +290,39 @@ void GpuVideoEncodeAccelerator::BitstreamBufferReady( |
size_t payload_size, |
bool key_frame, |
base::TimeDelta timestamp) { |
- Send(new AcceleratedVideoEncoderHostMsg_BitstreamBufferReady( |
- host_route_id_, bitstream_buffer_id, payload_size, key_frame, timestamp)); |
+ DCHECK(CheckIfCalledOnCorrectThread()); |
+ if (!Send(new AcceleratedVideoEncoderHostMsg_BitstreamBufferReady( |
+ host_route_id_, bitstream_buffer_id, payload_size, key_frame, |
+ timestamp))) { |
+ DLOG(ERROR) << __func__ << " failed."; |
+ } |
} |
void GpuVideoEncodeAccelerator::NotifyError( |
VideoEncodeAccelerator::Error error) { |
- Send(new AcceleratedVideoEncoderHostMsg_NotifyError(host_route_id_, error)); |
+ if (!Send(new AcceleratedVideoEncoderHostMsg_NotifyError(host_route_id_, |
+ error))) { |
+ DLOG(ERROR) << __func__ << " failed."; |
+ } |
} |
void GpuVideoEncodeAccelerator::OnWillDestroyStub() { |
+ DVLOG(2) << __func__; |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
DCHECK(stub_); |
+ |
+ // The stub is going away, so we have to stop and destroy VEA here before |
+ // returning. We cannot destroy the VEA before the IO thread message filter is |
+ // removed however, since we cannot service incoming messages with VEA gone. |
+ // We cannot simply check for existence of VEA on IO thread though, because |
+ // we don't want to synchronize the IO thread with the ChildThread. |
+ // So we have to wait for the RemoveFilter callback here instead and remove |
+ // the VEA after it arrives and before returning. |
+ if (filter_) { |
+ stub_->channel()->RemoveFilter(filter_.get()); |
+ filter_removed_.Wait(); |
+ } |
+ |
stub_->channel()->RemoveRoute(host_route_id_); |
stub_->RemoveDestructionObserver(this); |
encoder_.reset(); |
@@ -252,80 +373,54 @@ GpuVideoEncodeAccelerator::GetVEAFactoryFunctions( |
return vea_factory_functions; |
} |
+void GpuVideoEncodeAccelerator::OnFilterRemoved() { |
+ DVLOG(2) << __func__; |
+ DCHECK(io_task_runner_->BelongsToCurrentThread()); |
+ |
+ // We're destroying; cancel all callbacks. |
+ weak_this_factory_.InvalidateWeakPtrs(); |
+ filter_removed_.Signal(); |
+} |
+ |
void GpuVideoEncodeAccelerator::OnEncode( |
const AcceleratedVideoEncoderMsg_Encode_Params& params) { |
- DVLOG(3) << __FUNCTION__ << " frame_id = " << params.frame_id |
+ DVLOG(3) << __func__ << " frame_id = " << params.frame_id |
<< ", buffer_size=" << params.buffer_size |
<< ", force_keyframe=" << params.force_keyframe; |
+ DCHECK(CheckIfCalledOnCorrectThread()); |
DCHECK_EQ(PIXEL_FORMAT_I420, input_format_); |
- // Wrap into a SharedMemory in the beginning, so that |params.buffer_handle| |
- // is cleaned properly in case of an early return. |
- std::unique_ptr<base::SharedMemory> shm( |
- new base::SharedMemory(params.buffer_handle, true)); |
- |
if (!encoder_) |
return; |
if (params.frame_id < 0) { |
- DLOG(ERROR) << __FUNCTION__ << " invalid frame_id=" << params.frame_id; |
+ DLOG(ERROR) << __func__ << " invalid frame_id=" << params.frame_id; |
NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
return; |
} |
- const uint32_t aligned_offset = |
- params.buffer_offset % base::SysInfo::VMAllocationGranularity(); |
- base::CheckedNumeric<off_t> map_offset = params.buffer_offset; |
- map_offset -= aligned_offset; |
- base::CheckedNumeric<size_t> map_size = params.buffer_size; |
- map_size += aligned_offset; |
- |
- if (!map_offset.IsValid() || !map_size.IsValid()) { |
- DLOG(ERROR) << __FUNCTION__ << " invalid map_offset or map_size"; |
- NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
- return; |
- } |
- |
- if (!shm->MapAt(map_offset.ValueOrDie(), map_size.ValueOrDie())) { |
- DLOG(ERROR) << __FUNCTION__ |
- << " could not map frame_id=" << params.frame_id; |
- NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
- return; |
- } |
- |
- uint8_t* shm_memory = |
- reinterpret_cast<uint8_t*>(shm->memory()) + aligned_offset; |
- scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalSharedMemory( |
- input_format_, input_coded_size_, gfx::Rect(input_visible_size_), |
- input_visible_size_, shm_memory, params.buffer_size, params.buffer_handle, |
- params.buffer_offset, params.timestamp); |
- if (!frame) { |
- DLOG(ERROR) << __FUNCTION__ << " could not create a frame"; |
- NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
- return; |
- } |
- frame->AddDestructionObserver(BindToCurrentLoop(base::Bind( |
- &GpuVideoEncodeAccelerator::EncodeFrameFinished, |
- weak_this_factory_.GetWeakPtr(), params.frame_id, base::Passed(&shm)))); |
- encoder_->Encode(frame, params.force_keyframe); |
+ encoder_worker_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&GpuVideoEncodeAccelerator::CreateEncodeFrameOnEncoderWorker, |
+ weak_this_factory_for_encoder_worker_.GetWeakPtr(), params)); |
} |
void GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer( |
int32_t buffer_id, |
base::SharedMemoryHandle buffer_handle, |
uint32_t buffer_size) { |
- DVLOG(3) << __FUNCTION__ << " buffer_id=" << buffer_id |
+ DVLOG(3) << __func__ << " buffer_id=" << buffer_id |
<< ", buffer_size=" << buffer_size; |
+ DCHECK(CheckIfCalledOnCorrectThread()); |
if (!encoder_) |
return; |
if (buffer_id < 0) { |
- DLOG(ERROR) << __FUNCTION__ << " invalid buffer_id=" << buffer_id; |
+ DLOG(ERROR) << __func__ << " invalid buffer_id=" << buffer_id; |
NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
return; |
} |
if (buffer_size < output_buffer_size_) { |
- DLOG(ERROR) << __FUNCTION__ |
- << " buffer too small for buffer_id=" << buffer_id; |
+ DLOG(ERROR) << __func__ << " buffer too small for buffer_id=" << buffer_id; |
NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
return; |
} |
@@ -334,30 +429,115 @@ void GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer( |
} |
void GpuVideoEncodeAccelerator::OnDestroy() { |
- DVLOG(2) << __FUNCTION__; |
+ DVLOG(2) << __func__; |
+ DCHECK(main_task_runner_->BelongsToCurrentThread()); |
OnWillDestroyStub(); |
} |
void GpuVideoEncodeAccelerator::OnRequestEncodingParametersChange( |
uint32_t bitrate, |
uint32_t framerate) { |
- DVLOG(2) << __FUNCTION__ << " bitrate=" << bitrate |
- << ", framerate=" << framerate; |
+ DVLOG(2) << __func__ << " bitrate=" << bitrate << ", framerate=" << framerate; |
+ DCHECK(CheckIfCalledOnCorrectThread()); |
if (!encoder_) |
return; |
encoder_->RequestEncodingParametersChange(bitrate, framerate); |
} |
-void GpuVideoEncodeAccelerator::EncodeFrameFinished( |
+void GpuVideoEncodeAccelerator::CreateEncodeFrameOnEncoderWorker( |
+ const AcceleratedVideoEncoderMsg_Encode_Params& params) { |
+ DVLOG(3) << __func__; |
+ DCHECK(encoder_worker_task_runner_->BelongsToCurrentThread()); |
+ |
+ // Wrap into a SharedMemory in the beginning, so that |params.buffer_handle| |
+ // is cleaned properly in case of an early return. |
+ std::unique_ptr<base::SharedMemory> shm( |
+ new base::SharedMemory(params.buffer_handle, true)); |
+ const uint32_t aligned_offset = |
+ params.buffer_offset % base::SysInfo::VMAllocationGranularity(); |
+ base::CheckedNumeric<off_t> map_offset = params.buffer_offset; |
+ map_offset -= aligned_offset; |
+ base::CheckedNumeric<size_t> map_size = params.buffer_size; |
+ map_size += aligned_offset; |
+ |
+ if (!map_offset.IsValid() || !map_size.IsValid()) { |
+ DLOG(ERROR) << __func__ << " invalid map_offset or map_size"; |
+ encode_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuVideoEncodeAccelerator::NotifyError, |
+ weak_this_factory_.GetWeakPtr(), |
+ VideoEncodeAccelerator::kPlatformFailureError)); |
+ return; |
+ } |
+ |
+ if (!shm->MapAt(map_offset.ValueOrDie(), map_size.ValueOrDie())) { |
+ DLOG(ERROR) << __func__ << " could not map frame_id=" << params.frame_id; |
+ encode_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuVideoEncodeAccelerator::NotifyError, |
+ weak_this_factory_.GetWeakPtr(), |
+ VideoEncodeAccelerator::kPlatformFailureError)); |
+ return; |
+ } |
+ |
+ uint8_t* shm_memory = |
+ reinterpret_cast<uint8_t*>(shm->memory()) + aligned_offset; |
+ scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalSharedMemory( |
+ input_format_, input_coded_size_, gfx::Rect(input_visible_size_), |
+ input_visible_size_, shm_memory, params.buffer_size, params.buffer_handle, |
+ params.buffer_offset, params.timestamp); |
+ if (!frame) { |
+ DLOG(ERROR) << __func__ << " could not create a frame"; |
+ encode_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuVideoEncodeAccelerator::NotifyError, |
+ weak_this_factory_.GetWeakPtr(), |
+ VideoEncodeAccelerator::kPlatformFailureError)); |
+ return; |
+ } |
+ |
+ // We wrap |shm| in a callback and add it as a destruction observer, so it |
+ // stays alive and mapped until |frame| goes out of scope. |
+ frame->AddDestructionObserver( |
+ base::Bind(&DropSharedMemory, base::Passed(&shm))); |
+ encode_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&GpuVideoEncodeAccelerator::OnEncodeFrameCreated, |
+ weak_this_factory_.GetWeakPtr(), params.frame_id, |
+ params.force_keyframe, frame)); |
+} |
+ |
+void GpuVideoEncodeAccelerator::DestroyOnEncoderWorker() { |
+ DCHECK(encoder_worker_task_runner_->BelongsToCurrentThread()); |
+ weak_this_factory_for_encoder_worker_.InvalidateWeakPtrs(); |
+} |
+ |
+void GpuVideoEncodeAccelerator::OnEncodeFrameCreated( |
int32_t frame_id, |
- std::unique_ptr<base::SharedMemory> shm) { |
- Send(new AcceleratedVideoEncoderHostMsg_NotifyInputDone(host_route_id_, |
- frame_id)); |
- // Just let |shm| fall out of scope. |
+ bool force_keyframe, |
+ const scoped_refptr<media::VideoFrame>& frame) { |
+ DVLOG(3) << __func__; |
+ DCHECK(CheckIfCalledOnCorrectThread()); |
+ |
+ if (!frame) { |
+ DLOG(ERROR) << __func__ << " could not create a frame"; |
+ NotifyError(VideoEncodeAccelerator::kPlatformFailureError); |
+ return; |
+ } |
+ |
+ frame->AddDestructionObserver(BindToCurrentLoop( |
+ base::Bind(&GpuVideoEncodeAccelerator::EncodeFrameFinished, |
+ weak_this_factory_.GetWeakPtr(), frame_id))); |
+ encoder_->Encode(frame, force_keyframe); |
+} |
+ |
+void GpuVideoEncodeAccelerator::EncodeFrameFinished(int32_t frame_id) { |
+ DCHECK(CheckIfCalledOnCorrectThread()); |
+ if (!Send(new AcceleratedVideoEncoderHostMsg_NotifyInputDone(host_route_id_, |
+ frame_id))) { |
+ DLOG(ERROR) << __func__ << " failed."; |
+ } |
} |
-void GpuVideoEncodeAccelerator::Send(IPC::Message* message) { |
- stub_->channel()->Send(message); |
+bool GpuVideoEncodeAccelerator::CheckIfCalledOnCorrectThread() { |
+ return (filter_ && io_task_runner_->BelongsToCurrentThread()) || |
+ (!filter_ && main_task_runner_->BelongsToCurrentThread()); |
} |
} // namespace media |