summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorposciak@chromium.org <posciak@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-10 23:23:12 +0000
committerposciak@chromium.org <posciak@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-10 23:23:12 +0000
commit787e796ec9a318f95c6e6bc30df1738dcc4318f1 (patch)
tree977f70cbbc3bb610a310ed98e274a1a5825d78a5 /content
parent0489d0f1bf0c50ff66c9f0fef9bc5b89beba68e6 (diff)
downloadchromium_src-787e796ec9a318f95c6e6bc30df1738dcc4318f1.zip
chromium_src-787e796ec9a318f95c6e6bc30df1738dcc4318f1.tar.gz
chromium_src-787e796ec9a318f95c6e6bc30df1738dcc4318f1.tar.bz2
VAVDA: Add support for mid-stream resolution change.
This adds detection and handling of in-SPS resolution change to VAVDA, instead of failing if one is detected, as has been the case until now. Note that until the VDA API and clients are updated to allow returning crop information to the them from decoders (crbug.com/247429), the clients have to parse SPSes themselves as well to properly update visible rect (cropping) information for this to work properly. If they don't, the pictures will be scaled to previous resolution (before the switch). The playback will still continue though and it's still better than the current situation, where we just cannot continue at all. BUG=177422 TEST=flash, html5, memory leak tests, vdatest, seeks, on switch test (http://yt-dash-mse-test.commondatastorage.googleapis.com/switchtest/switch_1080p_720p.mp4) and regular streams Review URL: https://chromiumcodereview.appspot.com/16657003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@210964 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/common/gpu/media/vaapi_h264_decoder.cc35
-rw-r--r--content/common/gpu/media/vaapi_video_decode_accelerator.cc120
-rw-r--r--content/common/gpu/media/vaapi_video_decode_accelerator.h20
-rw-r--r--content/common/gpu/media/vaapi_wrapper.cc2
4 files changed, 132 insertions, 45 deletions
diff --git a/content/common/gpu/media/vaapi_h264_decoder.cc b/content/common/gpu/media/vaapi_h264_decoder.cc
index 4753cd1..5085ac6 100644
--- a/content/common/gpu/media/vaapi_h264_decoder.cc
+++ b/content/common/gpu/media/vaapi_h264_decoder.cc
@@ -1039,6 +1039,8 @@ bool VaapiH264Decoder::OutputAllRemainingPics() {
}
bool VaapiH264Decoder::Flush() {
+ DVLOG(2) << "Decoder flush";
+
if (!OutputAllRemainingPics())
return false;
@@ -1387,6 +1389,7 @@ static int LevelToMaxDpbMbs(int level) {
bool VaapiH264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) {
const H264SPS* sps = parser_.GetSPS(sps_id);
DCHECK(sps);
+ DVLOG(4) << "Processing SPS";
*need_new_buffers = false;
@@ -1410,26 +1413,20 @@ bool VaapiH264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) {
int height_mb = (2 - sps->frame_mbs_only_flag) *
(sps->pic_height_in_map_units_minus1 + 1);
- int width = 16 * width_mb;
- int height = 16 * height_mb;
-
- if (width == 0 || height == 0) {
- DVLOG(1) << "Invalid picture size!";
+ gfx::Size new_pic_size(16 * width_mb, 16 * height_mb);
+ if (new_pic_size.IsEmpty()) {
+ DVLOG(1) << "Invalid picture size: " << new_pic_size.ToString();
return false;
}
- if (!pic_size_.IsEmpty()) {
- if (width == pic_size_.width() && height == pic_size_.height()) {
- return true;
- } else {
- DVLOG(1) << "Picture size changed mid-stream";
- report_error_to_uma_cb_.Run(MID_STREAM_RESOLUTION_CHANGE);
- return false;
- }
+ if (!pic_size_.IsEmpty() && new_pic_size == pic_size_) {
+ // Already have surfaces and this SPS keeps the same resolution,
+ // no need to request a new set.
+ return true;
}
- pic_size_.SetSize(width, height);
- DVLOG(1) << "New picture size: " << width << "x" << height;
+ pic_size_ = new_pic_size;
+ DVLOG(1) << "New picture size: " << pic_size_.ToString();
max_pic_order_cnt_lsb_ = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
max_frame_num_ = 1 << (sps->log2_max_frame_num_minus4 + 4);
@@ -1588,9 +1585,13 @@ VaapiH264Decoder::DecResult VaapiH264Decoder::Decode() {
state_ = kDecoding;
- if (need_new_buffers)
- return kAllocateNewSurfaces;
+ if (need_new_buffers) {
+ if (!Flush())
+ return kDecodeError;
+ available_va_surfaces_.clear();
+ return kAllocateNewSurfaces;
+ }
break;
}
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.cc b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
index 8a318071..cd18574 100644
--- a/content/common/gpu/media/vaapi_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
@@ -256,7 +256,9 @@ VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
decoder_thread_("VaapiDecoderThread"),
num_frames_at_client_(0),
num_stream_bufs_at_decoder_(0),
- finish_flush_pending_(false) {
+ finish_flush_pending_(false),
+ awaiting_va_surfaces_recycle_(false),
+ requested_num_pics_(0) {
DCHECK(client);
}
@@ -338,6 +340,7 @@ void VaapiVideoDecodeAccelerator::SurfaceReady(
int32 input_id,
const scoped_refptr<VASurface>& va_surface) {
DCHECK_EQ(message_loop_, base::MessageLoop::current());
+ DCHECK(!awaiting_va_surfaces_recycle_);
// Drop any requests to output if we are resetting or being destroyed.
if (state_ == kResetting || state_ == kDestroying)
@@ -528,6 +531,9 @@ void VaapiVideoDecodeAccelerator::DecodeTask() {
TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask");
base::AutoLock auto_lock(lock_);
+ if (state_ != kDecoding)
+ return;
+
// Main decode task.
DVLOG(4) << "Decode task";
@@ -549,14 +555,11 @@ void VaapiVideoDecodeAccelerator::DecodeTask() {
switch (res) {
case VaapiH264Decoder::kAllocateNewSurfaces:
- state_ = kPicturesRequested;
- num_pics_ = decoder_->GetRequiredNumOfPictures();
- pic_size_ = decoder_->GetPicSize();
- DVLOG(1) << "Requesting " << num_pics_ << " pictures of size: "
- << pic_size_.width() << "x" << pic_size_.height();
+ DVLOG(1) << "Decoder requesting a new set of surfaces";
message_loop_->PostTask(FROM_HERE, base::Bind(
- &Client::ProvidePictureBuffers, client_,
- num_pics_, pic_size_, GL_TEXTURE_2D));
+ &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_,
+ decoder_->GetRequiredNumOfPictures(),
+ decoder_->GetPicSize()));
// We'll get rescheduled once ProvidePictureBuffers() finishes.
return;
@@ -580,6 +583,67 @@ void VaapiVideoDecodeAccelerator::DecodeTask() {
}
}
+void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics,
+ gfx::Size size) {
+ DCHECK_EQ(message_loop_, base::MessageLoop::current());
+ DCHECK(!awaiting_va_surfaces_recycle_);
+
+ // At this point decoder has stopped running and has already posted onto our
+ // loop any remaining output request callbacks, which executed before we got
+ // here. Some of them might have been pended though, because we might not
+ // have had enough TFPictures to output surfaces to. Initiate a wait cycle,
+ // which will wait for client to return enough PictureBuffers to us, so that
+ // we can finish all pending output callbacks, releasing associated surfaces.
+ DVLOG(1) << "Initiating surface set change";
+ awaiting_va_surfaces_recycle_ = true;
+
+ requested_num_pics_ = num_pics;
+ requested_pic_size_ = size;
+
+ TryFinishSurfaceSetChange();
+}
+
+void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
+ DCHECK_EQ(message_loop_, base::MessageLoop::current());
+
+ if (!awaiting_va_surfaces_recycle_)
+ return;
+
+ if (!pending_output_cbs_.empty() ||
+ tfp_pictures_.size() != available_va_surfaces_.size()) {
+ // Either:
+ // 1. Not all pending pending output callbacks have been executed yet.
+ // Wait for the client to return enough pictures and retry later.
+ // 2. The above happened and all surface release callbacks have been posted
+ // as the result, but not all have executed yet. Post ourselves after them
+ // to let them release surfaces.
+ DVLOG(2) << "Awaiting pending output/surface release callbacks to finish";
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_));
+ return;
+ }
+
+ // All surfaces released, destroy them and dismiss all PictureBuffers.
+ awaiting_va_surfaces_recycle_ = false;
+ available_va_surfaces_.clear();
+ vaapi_wrapper_->DestroySurfaces();
+
+ for (TFPPictures::iterator iter = tfp_pictures_.begin();
+ iter != tfp_pictures_.end(); ++iter) {
+ DVLOG(2) << "Dismissing picture id: " << iter->first;
+ client_->DismissPictureBuffer(iter->first);
+ }
+ tfp_pictures_.clear();
+
+ // And ask for a new set as requested.
+ DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: "
+ << requested_pic_size_.ToString();
+
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &Client::ProvidePictureBuffers, client_,
+ requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D));
+}
+
void VaapiVideoDecodeAccelerator::Decode(
const media::BitstreamBuffer& bitstream_buffer) {
DCHECK_EQ(message_loop_, base::MessageLoop::current());
@@ -599,8 +663,6 @@ void VaapiVideoDecodeAccelerator::Decode(
base::Unretained(this)));
break;
- case kPicturesRequested:
- // Waiting for pictures, fallthrough.
case kDecoding:
// Decoder already running, fallthrough.
case kResetting:
@@ -613,14 +675,13 @@ void VaapiVideoDecodeAccelerator::Decode(
RETURN_AND_NOTIFY_ON_FAILURE(false,
"Decode request from client in invalid state: " << state_,
PLATFORM_FAILURE, );
- return;
+ break;
}
}
void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
VASurfaceID va_surface_id) {
DCHECK_EQ(message_loop_, base::MessageLoop::current());
-
base::AutoLock auto_lock(lock_);
available_va_surfaces_.push_back(va_surface_id);
@@ -632,17 +693,20 @@ void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
DCHECK_EQ(message_loop_, base::MessageLoop::current());
base::AutoLock auto_lock(lock_);
- DCHECK_EQ(state_, kPicturesRequested);
DCHECK(tfp_pictures_.empty());
+ while (!output_buffers_.empty())
+ output_buffers_.pop();
+
RETURN_AND_NOTIFY_ON_FAILURE(
- buffers.size() == num_pics_,
+ buffers.size() == requested_num_pics_,
"Got an invalid number of picture buffers. (Got " << buffers.size()
- << ", requested " << num_pics_ << ")", INVALID_ARGUMENT, );
+ << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, );
+ DCHECK(requested_pic_size_ == buffers[0].size());
std::vector<VASurfaceID> va_surface_ids;
RETURN_AND_NOTIFY_ON_FAILURE(
- vaapi_wrapper_->CreateSurfaces(pic_size_,
+ vaapi_wrapper_->CreateSurfaces(requested_pic_size_,
buffers.size(),
&va_surface_ids),
"Failed creating VA Surfaces", PLATFORM_FAILURE, );
@@ -656,7 +720,7 @@ void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
linked_ptr<TFPPicture> tfp_picture(
TFPPicture::Create(make_context_current_, fb_config_, x_display_,
buffers[i].id(), buffers[i].texture_id(),
- pic_size_));
+ requested_pic_size_));
RETURN_AND_NOTIFY_ON_FAILURE(
tfp_picture.get(), "Failed assigning picture buffer to a texture.",
@@ -747,6 +811,7 @@ void VaapiVideoDecodeAccelerator::FinishFlush() {
void VaapiVideoDecodeAccelerator::ResetTask() {
DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
+ DVLOG(1) << "ResetTask";
// All the decoding tasks from before the reset request from client are done
// by now, as this task was scheduled after them and client is expected not
@@ -790,19 +855,30 @@ void VaapiVideoDecodeAccelerator::Reset() {
void VaapiVideoDecodeAccelerator::FinishReset() {
DCHECK_EQ(message_loop_, base::MessageLoop::current());
-
+ DVLOG(1) << "FinishReset";
base::AutoLock auto_lock(lock_);
+
if (state_ != kResetting) {
DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_;
return; // We could've gotten destroyed already.
}
- state_ = kIdle;
- num_stream_bufs_at_decoder_ = 0;
-
- while(!pending_output_cbs_.empty())
+ // Drop pending outputs.
+ while (!pending_output_cbs_.empty())
pending_output_cbs_.pop();
+ if (awaiting_va_surfaces_recycle_) {
+ // Decoder requested a new surface set while we were waiting for it to
+ // finish the last DecodeTask, running at the time of Reset().
+ // Let the surface set change finish first before resetting.
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
+ return;
+ }
+
+ num_stream_bufs_at_decoder_ = 0;
+ state_ = kIdle;
+
message_loop_->PostTask(FROM_HERE, base::Bind(
&Client::NotifyResetDone, client_));
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.h b/content/common/gpu/media/vaapi_video_decode_accelerator.h
index a208d03..85ce0aa 100644
--- a/content/common/gpu/media/vaapi_video_decode_accelerator.h
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.h
@@ -144,6 +144,12 @@ private:
// pool.
void RecycleVASurfaceID(VASurfaceID va_surface_id);
+ // Initiate wait cycle for surfaces to be released before we release them
+ // and allocate new ones, as requested by the decoder.
+ void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size);
+ // Check if the surfaces have been released or post ourselves for later.
+ void TryFinishSurfaceSetChange();
+
// Client-provided X/GLX state.
Display* x_display_;
GLXContext glx_context_;
@@ -154,8 +160,6 @@ private:
enum State {
// Initialize() not called yet or failed.
kUninitialized,
- // Requested a new set of pictures and waiting for them.
- kPicturesRequested,
// DecodeTask running.
kDecoding,
// Resetting, waiting for decoder to finish current task and cleanup.
@@ -203,10 +207,6 @@ private:
// Return a TFPPicture associated with given client-provided id.
TFPPicture* TFPPictureById(int32 picture_buffer_id);
- // Number/resolution of output picture buffers.
- size_t num_pics_;
- gfx::Size pic_size_;
-
// VA Surfaces no longer in use that can be passed back to the decoder for
// reuse, once it requests them.
std::list<VASurfaceID> available_va_surfaces_;
@@ -256,6 +256,14 @@ private:
// NotifyingFlushDone.
bool finish_flush_pending_;
+ // Decoder requested a new surface set and we are waiting for all the surfaces
+ // to be returned before we can free them.
+ bool awaiting_va_surfaces_recycle_;
+
+ // Last requested number/resolution of output picture buffers.
+ size_t requested_num_pics_;
+ gfx::Size requested_pic_size_;
+
DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator);
};
diff --git a/content/common/gpu/media/vaapi_wrapper.cc b/content/common/gpu/media/vaapi_wrapper.cc
index 4734200..5d22e4b 100644
--- a/content/common/gpu/media/vaapi_wrapper.cc
+++ b/content/common/gpu/media/vaapi_wrapper.cc
@@ -253,6 +253,7 @@ bool VaapiWrapper::CreateSurfaces(gfx::Size size,
size_t num_surfaces,
std::vector<VASurfaceID>* va_surfaces) {
base::AutoLock auto_lock(va_lock_);
+ DVLOG(2) << "Creating " << num_surfaces << " surfaces";
DCHECK(va_surfaces->empty());
DCHECK(va_surface_ids_.empty());
@@ -288,6 +289,7 @@ bool VaapiWrapper::CreateSurfaces(gfx::Size size,
void VaapiWrapper::DestroySurfaces() {
base::AutoLock auto_lock(va_lock_);
+ DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces";
if (va_context_id_ != VA_INVALID_ID) {
VAStatus va_res = VAAPI_DestroyContext(va_display_, va_context_id_);