Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1696)

Unified Diff: content/common/gpu/media/vt_video_decode_accelerator.cc

Issue 727893002: Implement a reorder queue in VTVideoDecodeAccelerator. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@vt_queue_frames
Patch Set: Rebase. Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/common/gpu/media/vt_video_decode_accelerator.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/common/gpu/media/vt_video_decode_accelerator.cc
diff --git a/content/common/gpu/media/vt_video_decode_accelerator.cc b/content/common/gpu/media/vt_video_decode_accelerator.cc
index 1cae8f255a43e391096088d820713abd00400fcf..dedb15d5e215baae0ef923a901b1bfe0069b294d 100644
--- a/content/common/gpu/media/vt_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vt_video_decode_accelerator.cc
@@ -38,6 +38,10 @@ static const int kNALUHeaderLength = 4;
// requirements are low, as we don't need the textures to be backed by storage.
static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1;
+// TODO(sandersd): Use the configured reorder window instead.
+static const int kMinReorderQueueSize = 4;
+static const int kMaxReorderQueueSize = 16;
+
// Route decoded frame callbacks back into the VTVideoDecodeAccelerator.
static void OutputThunk(
void* decompression_output_refcon,
@@ -59,12 +63,23 @@ VTVideoDecodeAccelerator::Task::~Task() {
}
VTVideoDecodeAccelerator::Frame::Frame(int32_t bitstream_id)
- : bitstream_id(bitstream_id) {
+ : bitstream_id(bitstream_id), pic_order_cnt(0) {
}
VTVideoDecodeAccelerator::Frame::~Frame() {
}
+bool VTVideoDecodeAccelerator::FrameOrder::operator()(
+ const linked_ptr<Frame>& lhs,
+ const linked_ptr<Frame>& rhs) const {
+ if (lhs->pic_order_cnt != rhs->pic_order_cnt)
+ return lhs->pic_order_cnt > rhs->pic_order_cnt;
+ // If the pic_order is the same, fallback on using the bitstream order.
+ // TODO(sandersd): Assign a sequence number in Decode().
+ return lhs->bitstream_id > rhs->bitstream_id;
+}
+
+
VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(
CGLContextObj cgl_context,
const base::Callback<bool(void)>& make_context_current)
@@ -74,6 +89,8 @@ VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(
state_(STATE_DECODING),
format_(NULL),
session_(NULL),
+ last_sps_id_(-1),
+ last_pps_id_(-1),
gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()),
decoder_thread_("VTDecoderThread"),
weak_this_factory_(this) {
@@ -262,7 +279,7 @@ void VTVideoDecodeAccelerator::DecodeTask(
break;
if (result != media::H264Parser::kOk) {
DLOG(ERROR) << "Failed to find H.264 NALU";
- NotifyError(PLATFORM_FAILURE);
+ NotifyError(UNREADABLE_INPUT);
return;
}
switch (nalu.nal_unit_type) {
@@ -270,25 +287,75 @@ void VTVideoDecodeAccelerator::DecodeTask(
last_sps_.assign(nalu.data, nalu.data + nalu.size);
last_spsext_.clear();
config_changed = true;
+ if (parser_.ParseSPS(&last_sps_id_) != media::H264Parser::kOk) {
+ DLOG(ERROR) << "Could not parse SPS";
+ NotifyError(UNREADABLE_INPUT);
+ return;
+ }
break;
+
case media::H264NALU::kSPSExt:
// TODO(sandersd): Check that the previous NALU was an SPS.
last_spsext_.assign(nalu.data, nalu.data + nalu.size);
config_changed = true;
break;
+
case media::H264NALU::kPPS:
last_pps_.assign(nalu.data, nalu.data + nalu.size);
config_changed = true;
+ if (parser_.ParsePPS(&last_pps_id_) != media::H264Parser::kOk) {
+ DLOG(ERROR) << "Could not parse PPS";
+ NotifyError(UNREADABLE_INPUT);
+ return;
+ }
break;
+
case media::H264NALU::kSliceDataA:
case media::H264NALU::kSliceDataB:
case media::H264NALU::kSliceDataC:
DLOG(ERROR) << "Coded slide data partitions not implemented.";
NotifyError(PLATFORM_FAILURE);
return;
- case media::H264NALU::kIDRSlice:
+
case media::H264NALU::kNonIDRSlice:
- // TODO(sandersd): Compute pic_order_count.
+ // TODO(sandersd): Check that there has been an SPS or IDR slice since
+ // the last reset.
+ case media::H264NALU::kIDRSlice:
+ {
+ // TODO(sandersd): Make sure this only happens once per frame.
+ DCHECK_EQ(frame->pic_order_cnt, 0);
+
+ media::H264SliceHeader slice_hdr;
+ result = parser_.ParseSliceHeader(nalu, &slice_hdr);
+ if (result != media::H264Parser::kOk) {
+ DLOG(ERROR) << "Could not parse slice header";
+ NotifyError(UNREADABLE_INPUT);
+ return;
+ }
+
+ // TODO(sandersd): Keep a cache of recent SPS/PPS units instead of
+ // only the most recent ones.
+ DCHECK_EQ(slice_hdr.pic_parameter_set_id, last_pps_id_);
+ const media::H264PPS* pps =
+ parser_.GetPPS(slice_hdr.pic_parameter_set_id);
+ if (!pps) {
+ DLOG(ERROR) << "Mising PPS referenced by slice";
+ NotifyError(UNREADABLE_INPUT);
+ return;
+ }
+
+ DCHECK_EQ(pps->seq_parameter_set_id, last_sps_id_);
+ const media::H264SPS* sps = parser_.GetSPS(pps->seq_parameter_set_id);
+ if (!sps) {
+ DLOG(ERROR) << "Mising SPS referenced by PPS";
+ NotifyError(UNREADABLE_INPUT);
+ return;
+ }
+
+ // TODO(sandersd): Compute pic_order_cnt.
+ DCHECK(!slice_hdr.field_pic_flag);
+ frame->pic_order_cnt = 0;
+ }
default:
nalus.push_back(nalu);
data_size += kNALUHeaderLength + nalu.size;
@@ -327,6 +394,9 @@ void VTVideoDecodeAccelerator::DecodeTask(
return;
}
+ // Update the frame metadata with configuration data.
+ frame->coded_size = coded_size_;
+
// Create a memory-backed CMBlockBuffer for the translated data.
// TODO(sandersd): Pool of memory blocks.
base::ScopedCFTypeRef<CMBlockBufferRef> data;
@@ -385,9 +455,6 @@ void VTVideoDecodeAccelerator::DecodeTask(
return;
}
- // Update the frame data.
- frame->coded_size = coded_size_;
-
// Send the frame for decoding.
// Asynchronous Decompression allows for parallel submission of frames
// (without it, DecodeFrame() does not return until the frame has been
@@ -431,8 +498,8 @@ void VTVideoDecodeAccelerator::DecodeDone(Frame* frame) {
Task task(TASK_FRAME);
task.frame = pending_frames_.front();
pending_frames_.pop();
- pending_tasks_.push(task);
- ProcessTasks();
+ task_queue_.push(task);
+ ProcessWorkQueues();
}
void VTVideoDecodeAccelerator::FlushTask(TaskType type) {
@@ -447,8 +514,8 @@ void VTVideoDecodeAccelerator::FlushTask(TaskType type) {
void VTVideoDecodeAccelerator::FlushDone(TaskType type) {
DCHECK(gpu_thread_checker_.CalledOnValidThread());
- pending_tasks_.push(Task(type));
- ProcessTasks();
+ task_queue_.push(Task(type));
+ ProcessWorkQueues();
}
void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) {
@@ -477,7 +544,7 @@ void VTVideoDecodeAccelerator::AssignPictureBuffers(
// they will be broken if they are used before that happens. So, schedule
// future work after that happens.
gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
- &VTVideoDecodeAccelerator::ProcessTasks, weak_this_));
+ &VTVideoDecodeAccelerator::ProcessWorkQueues, weak_this_));
}
void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) {
@@ -486,60 +553,77 @@ void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) {
picture_bindings_.erase(picture_id);
if (assigned_picture_ids_.count(picture_id) != 0) {
available_picture_ids_.push_back(picture_id);
- ProcessTasks();
+ ProcessWorkQueues();
} else {
client_->DismissPictureBuffer(picture_id);
}
}
-void VTVideoDecodeAccelerator::ProcessTasks() {
+void VTVideoDecodeAccelerator::ProcessWorkQueues() {
DCHECK(gpu_thread_checker_.CalledOnValidThread());
+ switch (state_) {
+ case STATE_DECODING:
+ // TODO(sandersd): Batch where possible.
+ while (ProcessReorderQueue() || ProcessTaskQueue());
+ return;
- while (!pending_tasks_.empty()) {
- const Task& task = pending_tasks_.front();
-
- switch (state_) {
- case STATE_DECODING:
- if (!ProcessTask(task))
- return;
- pending_tasks_.pop();
- break;
-
- case STATE_ERROR:
- // Do nothing until Destroy() is called.
- return;
+ case STATE_ERROR:
+ // Do nothing until Destroy() is called.
+ return;
- case STATE_DESTROYING:
- // Discard tasks until destruction is complete.
- if (task.type == TASK_DESTROY) {
+ case STATE_DESTROYING:
+ // Drop tasks until we are ready to destruct.
+ while (!task_queue_.empty()) {
+ if (task_queue_.front().type == TASK_DESTROY) {
delete this;
return;
}
- pending_tasks_.pop();
- break;
- }
+ task_queue_.pop();
+ }
+ return;
}
}
-bool VTVideoDecodeAccelerator::ProcessTask(const Task& task) {
+bool VTVideoDecodeAccelerator::ProcessTaskQueue() {
DCHECK(gpu_thread_checker_.CalledOnValidThread());
DCHECK_EQ(state_, STATE_DECODING);
+ if (task_queue_.empty())
+ return false;
+
+ const Task& task = task_queue_.front();
switch (task.type) {
case TASK_FRAME:
- return ProcessFrame(*task.frame);
+ // TODO(sandersd): Signal IDR explicitly (not using pic_order_cnt == 0).
+ if (reorder_queue_.size() < kMaxReorderQueueSize &&
+ (task.frame->pic_order_cnt != 0 || reorder_queue_.empty())) {
+ assigned_bitstream_ids_.erase(task.frame->bitstream_id);
+ client_->NotifyEndOfBitstreamBuffer(task.frame->bitstream_id);
+ reorder_queue_.push(task.frame);
+ task_queue_.pop();
+ return true;
+ }
+ return false;
case TASK_FLUSH:
DCHECK_EQ(task.type, pending_flush_tasks_.front());
- pending_flush_tasks_.pop();
- client_->NotifyFlushDone();
- return true;
+ if (reorder_queue_.size() == 0) {
+ pending_flush_tasks_.pop();
+ client_->NotifyFlushDone();
+ task_queue_.pop();
+ return true;
+ }
+ return false;
case TASK_RESET:
DCHECK_EQ(task.type, pending_flush_tasks_.front());
- pending_flush_tasks_.pop();
- client_->NotifyResetDone();
- return true;
+ if (reorder_queue_.size() == 0) {
+ pending_flush_tasks_.pop();
+ client_->NotifyResetDone();
+ task_queue_.pop();
+ return true;
+ }
+ return false;
case TASK_DESTROY:
NOTREACHED() << "Can't destroy while in STATE_DECODING.";
@@ -548,12 +632,37 @@ bool VTVideoDecodeAccelerator::ProcessTask(const Task& task) {
}
}
+bool VTVideoDecodeAccelerator::ProcessReorderQueue() {
+ DCHECK(gpu_thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(state_, STATE_DECODING);
+
+ if (reorder_queue_.empty())
+ return false;
+
+ // If the next task is a flush (because there is a pending flush or becuase
+ // the next frame is an IDR), then we don't need a full reorder buffer to send
+ // the next frame.
+ bool flushing = !task_queue_.empty() &&
+ (task_queue_.front().type != TASK_FRAME ||
+ task_queue_.front().frame->pic_order_cnt == 0);
+ if (flushing || reorder_queue_.size() >= kMinReorderQueueSize) {
+ if (ProcessFrame(*reorder_queue_.top())) {
+ reorder_queue_.pop();
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool VTVideoDecodeAccelerator::ProcessFrame(const Frame& frame) {
DCHECK(gpu_thread_checker_.CalledOnValidThread());
DCHECK_EQ(state_, STATE_DECODING);
+
// If the next pending flush is for a reset, then the frame will be dropped.
bool resetting = !pending_flush_tasks_.empty() &&
pending_flush_tasks_.front() == TASK_RESET;
+
if (!resetting && frame.image.get()) {
// If the |coded_size| has changed, request new picture buffers and then
// wait for them.
@@ -577,8 +686,7 @@ bool VTVideoDecodeAccelerator::ProcessFrame(const Frame& frame) {
if (!SendFrame(frame))
return false;
}
- assigned_bitstream_ids_.erase(frame.bitstream_id);
- client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id);
+
return true;
}
@@ -643,7 +751,7 @@ void VTVideoDecodeAccelerator::QueueFlush(TaskType type) {
// If this is a new flush request, see if we can make progress.
if (pending_flush_tasks_.size() == 1)
- ProcessTasks();
+ ProcessWorkQueues();
}
void VTVideoDecodeAccelerator::Flush() {
@@ -667,6 +775,8 @@ void VTVideoDecodeAccelerator::Destroy() {
// For a graceful shutdown, return assigned buffers and flush before
// destructing |this|.
+ // TODO(sandersd): Make sure the decoder won't try to read the buffers again
+ // before discarding them.
for (int32_t bitstream_id : assigned_bitstream_ids_)
client_->NotifyEndOfBitstreamBuffer(bitstream_id);
assigned_bitstream_ids_.clear();
« no previous file with comments | « content/common/gpu/media/vt_video_decode_accelerator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698