Chromium Code Reviews| Index: content/common/gpu/media/v4l2_slice_video_decode_accelerator.cc |
| diff --git a/content/common/gpu/media/v4l2_slice_video_decode_accelerator.cc b/content/common/gpu/media/v4l2_slice_video_decode_accelerator.cc |
| index 4c3b724daa5bb5e6b1bc7d8bfeaf332023054bd7..fb246db6b53f146e74cc4e1d754ba7ffe1eaf42f 100644 |
| --- a/content/common/gpu/media/v4l2_slice_video_decode_accelerator.cc |
| +++ b/content/common/gpu/media/v4l2_slice_video_decode_accelerator.cc |
| @@ -405,7 +405,6 @@ V4L2SliceVideoDecodeAccelerator::V4L2SliceVideoDecodeAccelerator( |
| decoder_resetting_(false), |
| surface_set_change_pending_(false), |
| picture_clearing_count_(0), |
| - pictures_assigned_(false, false), |
| make_context_current_(make_context_current), |
| egl_display_(egl_display), |
| egl_context_(egl_context), |
| @@ -547,11 +546,6 @@ void V4L2SliceVideoDecodeAccelerator::Destroy() { |
| FROM_HERE, base::Bind(&V4L2SliceVideoDecodeAccelerator::DestroyTask, |
| base::Unretained(this))); |
| - // Wake up decoder thread in case we are waiting in CreateOutputBuffers |
| - // for client to provide pictures. Since this is Destroy, we won't be |
| - // getting them anymore (AssignPictureBuffers won't be called). |
| - pictures_assigned_.Signal(); |
| - |
| // Wait for tasks to finish/early-exit. |
| decoder_thread_.Stop(); |
| } |
| @@ -753,12 +747,6 @@ bool V4L2SliceVideoDecodeAccelerator::CreateOutputBuffers() { |
| client_, num_pictures, coded_size_, |
| device_->GetTextureTarget())); |
| - // Wait for the client to call AssignPictureBuffers() on the Child thread. |
| - // We do this, because if we continue decoding without finishing buffer |
| - // allocation, we may end up Resetting before AssignPictureBuffers arrives, |
| - // resulting in unnecessary complications and subtle bugs. |
| - pictures_assigned_.Wait(); |
| - |
| return true; |
| } |
| @@ -785,7 +773,7 @@ void V4L2SliceVideoDecodeAccelerator::DestroyInputBuffers() { |
| } |
| void V4L2SliceVideoDecodeAccelerator::DismissPictures( |
| - std::vector<int32_t> picture_buffer_ids, |
| + const std::vector<int32_t>& picture_buffer_ids, |
| base::WaitableEvent* done) { |
| DVLOGF(3); |
| DCHECK(child_task_runner_->BelongsToCurrentThread()); |
| @@ -966,11 +954,29 @@ void V4L2SliceVideoDecodeAccelerator::ProcessPendingEventsIfNeeded() { |
| // We always first process the surface set change, as it is an internal |
| // event from the decoder and interleaving it with external requests would |
| // put the decoder in an undefined state. |
| - FinishSurfaceSetChangeIfNeeded(); |
| + if (surface_set_change_pending_) { |
| + if (!FinishSurfaceSetChange()) |
| + return; |
| + } |
| + DCHECK(!surface_set_change_pending_); |
| // Process external (client) requests. |
| - FinishFlushIfNeeded(); |
| - FinishResetIfNeeded(); |
| + if (decoder_flushing_) { |
| + if (!FinishFlush()) |
| + return; |
| + } |
| + DCHECK(!decoder_flushing_); |
| + |
| + if (decoder_resetting_) { |
| + if (!FinishReset()) |
| + return; |
| + } |
| + DCHECK(!decoder_resetting_); |
| + |
| + if (state_ == kIdle) |
| + state_ = kDecoding; |
| + |
| + ScheduleDecodeBufferTaskIfNeeded(); |
|
wuchengli
2016/02/18 08:06:47
I asked offline about why we need line 976-99 now
|
| } |
| void V4L2SliceVideoDecodeAccelerator::ReuseInputBuffer(int index) { |
| @@ -1289,21 +1295,21 @@ void V4L2SliceVideoDecodeAccelerator::InitiateSurfaceSetChange() { |
| DVLOGF(2); |
| DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); |
| - DCHECK_EQ(state_, kDecoding); |
| state_ = kIdle; |
| DCHECK(!surface_set_change_pending_); |
| surface_set_change_pending_ = true; |
| - FinishSurfaceSetChangeIfNeeded(); |
| + ProcessPendingEventsIfNeeded(); |
| } |
| -void V4L2SliceVideoDecodeAccelerator::FinishSurfaceSetChangeIfNeeded() { |
| +bool V4L2SliceVideoDecodeAccelerator::FinishSurfaceSetChange() { |
| DVLOGF(2); |
| DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); |
| - if (!surface_set_change_pending_ || !surfaces_at_device_.empty()) |
| - return; |
| + DCHECK(surface_set_change_pending_); |
| + if (!surfaces_at_device_.empty()) |
| + return false; |
| DCHECK_EQ(state_, kIdle); |
| DCHECK(decoder_display_queue_.empty()); |
| @@ -1316,7 +1322,7 @@ void V4L2SliceVideoDecodeAccelerator::FinishSurfaceSetChangeIfNeeded() { |
| // Keep input queue running while we switch outputs. |
| if (!StopDevicePoll(true)) { |
| NOTIFY_ERROR(PLATFORM_FAILURE); |
| - return; |
| + return false; |
| } |
| // This will return only once all buffers are dismissed and destroyed. |
| @@ -1325,24 +1331,21 @@ void V4L2SliceVideoDecodeAccelerator::FinishSurfaceSetChangeIfNeeded() { |
| // after displaying. |
| if (!DestroyOutputs(true)) { |
| NOTIFY_ERROR(PLATFORM_FAILURE); |
| - return; |
| + return false; |
| } |
| if (!CreateOutputBuffers()) { |
| NOTIFY_ERROR(PLATFORM_FAILURE); |
| - return; |
| - } |
| - |
| - if (!StartDevicePoll()) { |
| - NOTIFY_ERROR(PLATFORM_FAILURE); |
| - return; |
| + return false; |
| } |
| - DVLOGF(3) << "Surface set change finished"; |
| - |
| + // At this point we can safely say the surface set has been changed, even |
| + // though we haven't received the actual buffers via AssignPictureBuffers() |
| + // yet. We will not start decoding without having surfaces available, |
| + // and will schedule a decode task once the client provides the buffers. |
| surface_set_change_pending_ = false; |
| - state_ = kDecoding; |
| - ScheduleDecodeBufferTaskIfNeeded(); |
| + DVLOG(3) << "Surface set change finished"; |
| + return true; |
| } |
| bool V4L2SliceVideoDecodeAccelerator::DestroyOutputs(bool dismiss) { |
| @@ -1432,6 +1435,18 @@ void V4L2SliceVideoDecodeAccelerator::AssignPictureBuffers( |
| DVLOGF(3); |
| DCHECK(child_task_runner_->BelongsToCurrentThread()); |
| + decoder_thread_task_runner_->PostTask( |
| + FROM_HERE, base::Bind( |
| + &V4L2SliceVideoDecodeAccelerator::AssignPictureBuffersTask, |
|
wuchengli
2016/02/18 08:06:47
For the record, we had an offline discussion. I as
|
| + base::Unretained(this), buffers)); |
| +} |
| + |
| +void V4L2SliceVideoDecodeAccelerator::AssignPictureBuffersTask( |
| + const std::vector<media::PictureBuffer>& buffers) { |
| + DVLOGF(3); |
| + DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kDecoding); |
| + |
| const uint32_t req_buffer_count = decoder_->GetRequiredNumOfPictures(); |
| if (buffers.size() < req_buffer_count) { |
| @@ -1442,17 +1457,6 @@ void V4L2SliceVideoDecodeAccelerator::AssignPictureBuffers( |
| return; |
| } |
| - if (!make_context_current_.Run()) { |
| - DLOG(ERROR) << "could not make context current"; |
| - NOTIFY_ERROR(PLATFORM_FAILURE); |
| - return; |
| - } |
| - |
| - gfx::ScopedTextureBinder bind_restore(GL_TEXTURE_EXTERNAL_OES, 0); |
| - |
| - // It's safe to manipulate all the buffer state here, because the decoder |
| - // thread is waiting on pictures_assigned_. |
| - |
| // Allocate the output buffers. |
| struct v4l2_requestbuffers reqbufs; |
| memset(&reqbufs, 0, sizeof(reqbufs)); |
| @@ -1467,43 +1471,89 @@ void V4L2SliceVideoDecodeAccelerator::AssignPictureBuffers( |
| return; |
| } |
| - output_buffer_map_.resize(buffers.size()); |
| + child_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&V4L2SliceVideoDecodeAccelerator::CreateEGLImages, |
| + weak_this_, buffers, output_format_fourcc_, |
| + output_planes_count_)); |
| +} |
| - DCHECK(free_output_buffers_.empty()); |
| - for (size_t i = 0; i < output_buffer_map_.size(); ++i) { |
| - DCHECK(buffers[i].size() == coded_size_); |
| +void V4L2SliceVideoDecodeAccelerator::CreateEGLImages( |
| + const std::vector<media::PictureBuffer>& buffers, |
| + uint32_t output_format_fourcc, |
| + size_t output_planes_count) { |
| + DVLOGF(3); |
| + DCHECK(child_task_runner_->BelongsToCurrentThread()); |
| - OutputRecord& output_record = output_buffer_map_[i]; |
| - DCHECK(!output_record.at_device); |
| - DCHECK(!output_record.at_client); |
| - DCHECK_EQ(output_record.egl_image, EGL_NO_IMAGE_KHR); |
| - DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR); |
| - DCHECK_EQ(output_record.picture_id, -1); |
| - DCHECK_EQ(output_record.cleared, false); |
| + if (!make_context_current_.Run()) { |
| + DLOG(ERROR) << "could not make context current"; |
| + NOTIFY_ERROR(PLATFORM_FAILURE); |
| + return; |
| + } |
| + |
| + gfx::ScopedTextureBinder bind_restore(GL_TEXTURE_EXTERNAL_OES, 0); |
| + std::vector<EGLImageKHR> egl_images; |
| + for (size_t i = 0; i < buffers.size(); ++i) { |
| EGLImageKHR egl_image = device_->CreateEGLImage(egl_display_, |
| egl_context_, |
| buffers[i].texture_id(), |
| - coded_size_, |
| + buffers[i].size(), |
| i, |
| - output_format_fourcc_, |
| - output_planes_count_); |
| + output_format_fourcc, |
| + output_planes_count); |
| if (egl_image == EGL_NO_IMAGE_KHR) { |
| LOGF(ERROR) << "Could not create EGLImageKHR"; |
| - // Ownership of EGLImages allocated in previous iterations of this loop |
| - // has been transferred to output_buffer_map_. After we error-out here |
| - // the destructor will handle their cleanup. |
| + for (const auto& image_to_destroy : egl_images) |
| + device_->DestroyEGLImage(egl_display_, image_to_destroy); |
| + |
| NOTIFY_ERROR(PLATFORM_FAILURE); |
| return; |
| } |
| - output_record.egl_image = egl_image; |
| + egl_images.push_back(egl_image); |
| + } |
| + |
| + decoder_thread_task_runner_->PostTask( |
| + FROM_HERE, base::Bind( |
| + &V4L2SliceVideoDecodeAccelerator::AssignEGLImages, |
| + base::Unretained(this), buffers, egl_images)); |
| +} |
| + |
| +void V4L2SliceVideoDecodeAccelerator::AssignEGLImages( |
| + const std::vector<media::PictureBuffer>& buffers, |
| + const std::vector<EGLImageKHR>& egl_images) { |
| + DVLOGF(3); |
| + DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); |
| + DCHECK_EQ(buffers.size(), egl_images.size()); |
| + |
| + DCHECK(free_output_buffers_.empty()); |
| + DCHECK(output_buffer_map_.empty()); |
| + |
| + output_buffer_map_.resize(buffers.size()); |
| + |
| + for (size_t i = 0; i < output_buffer_map_.size(); ++i) { |
| + DCHECK(buffers[i].size() == coded_size_); |
| + |
| + OutputRecord& output_record = output_buffer_map_[i]; |
| + DCHECK(!output_record.at_device); |
| + DCHECK(!output_record.at_client); |
| + DCHECK_EQ(output_record.egl_image, EGL_NO_IMAGE_KHR); |
| + DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR); |
| + DCHECK_EQ(output_record.picture_id, -1); |
| + DCHECK_EQ(output_record.cleared, false); |
| + |
| + output_record.egl_image = egl_images[i]; |
| output_record.picture_id = buffers[i].id(); |
| free_output_buffers_.push_back(i); |
| DVLOGF(3) << "buffer[" << i << "]: picture_id=" << output_record.picture_id; |
| } |
| - pictures_assigned_.Signal(); |
| + if (!StartDevicePoll()) { |
| + NOTIFY_ERROR(PLATFORM_FAILURE); |
| + return; |
| + } |
| + |
| + ProcessPendingEventsIfNeeded(); |
| } |
| void V4L2SliceVideoDecodeAccelerator::ReusePictureBuffer( |
| @@ -1617,18 +1667,17 @@ void V4L2SliceVideoDecodeAccelerator::InitiateFlush() { |
| decoder_flushing_ = true; |
| - decoder_thread_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&V4L2SliceVideoDecodeAccelerator::FinishFlushIfNeeded, |
| - base::Unretained(this))); |
| + ProcessPendingEventsIfNeeded(); |
| } |
| -void V4L2SliceVideoDecodeAccelerator::FinishFlushIfNeeded() { |
| +bool V4L2SliceVideoDecodeAccelerator::FinishFlush() { |
| DVLOGF(3); |
| DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); |
| - if (!decoder_flushing_ || !surfaces_at_device_.empty()) |
| - return; |
| + DCHECK(decoder_flushing_); |
| + |
| + if (!surfaces_at_device_.empty()) |
| + return false; |
| DCHECK_EQ(state_, kIdle); |
| @@ -1646,14 +1695,13 @@ void V4L2SliceVideoDecodeAccelerator::FinishFlushIfNeeded() { |
| SendPictureReady(); |
| + decoder_flushing_ = false; |
| + DVLOGF(3) << "Flush finished"; |
| + |
| child_task_runner_->PostTask(FROM_HERE, |
| base::Bind(&Client::NotifyFlushDone, client_)); |
| - decoder_flushing_ = false; |
| - |
| - DVLOGF(3) << "Flush finished"; |
| - state_ = kDecoding; |
| - ScheduleDecodeBufferTaskIfNeeded(); |
| + return true; |
| } |
| void V4L2SliceVideoDecodeAccelerator::Reset() { |
| @@ -1689,15 +1737,16 @@ void V4L2SliceVideoDecodeAccelerator::ResetTask() { |
| while (!decoder_input_queue_.empty()) |
| decoder_input_queue_.pop(); |
| - FinishResetIfNeeded(); |
| + ProcessPendingEventsIfNeeded(); |
| } |
| -void V4L2SliceVideoDecodeAccelerator::FinishResetIfNeeded() { |
| +bool V4L2SliceVideoDecodeAccelerator::FinishReset() { |
| DVLOGF(3); |
| DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); |
| - if (!decoder_resetting_ || !surfaces_at_device_.empty()) |
| - return; |
| + DCHECK(decoder_resetting_); |
| + if (!surfaces_at_device_.empty()) |
| + return false; |
| DCHECK_EQ(state_, kIdle); |
| DCHECK(!decoder_flushing_); |
| @@ -1721,14 +1770,12 @@ void V4L2SliceVideoDecodeAccelerator::FinishResetIfNeeded() { |
| } |
| decoder_resetting_ = false; |
| + DVLOGF(3) << "Reset finished"; |
| child_task_runner_->PostTask(FROM_HERE, |
| base::Bind(&Client::NotifyResetDone, client_)); |
| - DVLOGF(3) << "Reset finished"; |
| - |
| - state_ = kDecoding; |
| - ScheduleDecodeBufferTaskIfNeeded(); |
| + return true; |
| } |
| void V4L2SliceVideoDecodeAccelerator::SetErrorState(Error error) { |