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

Side by Side Diff: media/filters/gpu_video_decoder.cc

Issue 8922010: <video> decode in hardware! (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 9 years 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/gpu_video_decoder.h ('k') | media/filters/video_renderer_base.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/filters/gpu_video_decoder.h"
6
7 #include "base/bind.h"
8 #include "base/message_loop.h"
9 #include "base/stl_util.h"
10 #include "media/base/demuxer_stream.h"
11 #include "media/base/filter_host.h"
12 #include "media/base/video_decoder_config.h"
13 #include "media/ffmpeg/ffmpeg_common.h"
14
15 namespace media {
16
17 GpuVideoDecoder::Factories::~Factories() {}
18
19 // Size of shared-memory segments we allocate. Since we reuse them we let them
20 // be on the beefy side.
21 static const size_t kSharedMemorySegmentBytes = 100 << 10;
22
23 GpuVideoDecoder::SHMBuffer::SHMBuffer(base::SharedMemory* m, size_t s)
24 : shm(m), size(s) {
25 }
26
27 GpuVideoDecoder::SHMBuffer::~SHMBuffer() {}
28
29 GpuVideoDecoder::BufferPair::BufferPair(
30 SHMBuffer* s, const scoped_refptr<Buffer>& b) : shm_buffer(s), buffer(b) {
31 }
32
33 GpuVideoDecoder::BufferPair::~BufferPair() {}
34
35 GpuVideoDecoder::GpuVideoDecoder(
36 MessageLoop* message_loop,
37 Factories* factories)
38 : message_loop_(message_loop),
39 factories_(factories),
40 flush_in_progress_(false),
41 demuxer_read_in_progress_(false),
42 next_picture_buffer_id_(0),
43 next_bitstream_buffer_id_(0) {
44 DCHECK(message_loop_ && factories_.get());
45 }
46
47 GpuVideoDecoder::~GpuVideoDecoder() {
48 DCHECK(!vda_); // Stop should have been already called.
49 STLDeleteElements(&available_shm_segments_);
50 for (std::map<int32, BufferPair>::iterator it =
51 bitstream_buffers_in_decoder_.begin();
52 it != bitstream_buffers_in_decoder_.end(); ++it) {
53 it->second.shm_buffer->shm->Close();
54 }
55 bitstream_buffers_in_decoder_.clear();
56 }
57
58 void GpuVideoDecoder::Stop(const base::Closure& callback) {
59 if (MessageLoop::current() != message_loop_) {
60 message_loop_->PostTask(FROM_HERE, base::Bind(
61 &GpuVideoDecoder::Stop, this, callback));
62 return;
63 }
64 if (!vda_) {
65 callback.Run();
66 return;
67 }
68 vda_->Destroy();
69 vda_ = NULL;
70 callback.Run();
71 }
72
73 void GpuVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
74 if (MessageLoop::current() != message_loop_) {
75 message_loop_->PostTask(FROM_HERE, base::Bind(
76 &GpuVideoDecoder::Seek, this, time, cb));
77 return;
78 }
79 pts_stream_.Seek(time);
80 cb.Run(PIPELINE_OK);
81 }
82
83 void GpuVideoDecoder::Pause(const base::Closure& callback) {
84 if (MessageLoop::current() != message_loop_) {
85 message_loop_->PostTask(FROM_HERE, base::Bind(
86 &GpuVideoDecoder::Pause, this, callback));
87 return;
88 }
89 callback.Run();
90 }
91
92 void GpuVideoDecoder::Flush(const base::Closure& callback) {
93 if (MessageLoop::current() != message_loop_) {
94 message_loop_->PostTask(FROM_HERE, base::Bind(
95 &GpuVideoDecoder::Flush, this, callback));
96 return;
97 }
98 // Pipeline should have quiesced (via Pause() to all filters) before calling
99 // us, so there should be nothing pending.
100 DCHECK(pending_read_cb_.is_null());
101
102 // Throw away any already-decoded frames.
103 ready_video_frames_.clear();
104
105 if (!vda_) {
106 callback.Run();
107 return;
108 }
109 DCHECK(pending_flush_cb_.is_null());
110 pending_flush_cb_ = callback;
111 pts_stream_.Flush();
112 vda_->Reset();
113 }
114
115 void GpuVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
116 const PipelineStatusCB& callback,
117 const StatisticsCallback& stats_callback) {
118 if (MessageLoop::current() != message_loop_) {
119 message_loop_->PostTask(FROM_HERE, base::Bind(
120 &GpuVideoDecoder::Initialize, this,
121 make_scoped_refptr(demuxer_stream), callback, stats_callback));
122 return;
123 }
124
125 DCHECK(!demuxer_stream_);
126 if (!demuxer_stream) {
127 callback.Run(PIPELINE_ERROR_DECODE);
128 return;
129 }
130
131 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config();
132 // TODO(scherkus): this check should go in PipelineImpl prior to creating
133 // decoder objects.
134 if (!config.IsValidConfig()) {
135 DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString();
136 callback.Run(PIPELINE_ERROR_DECODE);
137 return;
138 }
139
140 vda_ = factories_->CreateVideoDecodeAccelerator(config.profile(), this);
141 if (!vda_) {
142 callback.Run(DECODER_ERROR_NOT_SUPPORTED);
143 return;
144 }
145
146 demuxer_stream_ = demuxer_stream;
147 statistics_callback_ = stats_callback;
148
149 demuxer_stream_->EnableBitstreamConverter();
150
151 pts_stream_.Initialize(GetFrameDuration(config));
152 natural_size_ = config.natural_size();
153
154 callback.Run(PIPELINE_OK);
155 }
156
157 void GpuVideoDecoder::Read(const ReadCB& callback) {
158 if (MessageLoop::current() != message_loop_) {
159 message_loop_->PostTask(FROM_HERE, base::Bind(
160 &GpuVideoDecoder::Read, this, callback));
161 return;
162 }
163
164 if (!vda_) {
165 callback.Run(VideoFrame::CreateEmptyFrame());
166 return;
167 }
168
169 DCHECK(pending_read_cb_.is_null());
170 pending_read_cb_ = callback;
171
172 if (!ready_video_frames_.empty()) {
173 DeliverFrame(ready_video_frames_.front());
174 ready_video_frames_.pop_front();
175 return;
176 }
177 EnsureDemuxOrDecode();
178 }
179
180 void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) {
181 if (MessageLoop::current() != message_loop_) {
182 message_loop_->PostTask(FROM_HERE, base::Bind(
183 &GpuVideoDecoder::RequestBufferDecode, this, buffer));
184 return;
185 }
186 demuxer_read_in_progress_ = false;
187
188 if (!vda_) {
189 DeliverFrame(VideoFrame::CreateEmptyFrame());
190 return;
191 }
192
193 if (buffer->IsEndOfStream()) {
194 if (!flush_in_progress_) {
195 flush_in_progress_ = true;
196 vda_->Flush();
197 }
198 return;
199 }
200
201 size_t size = buffer->GetDataSize();
202 SHMBuffer* shm_buffer = GetSHM(size);
203 memcpy(shm_buffer->shm->memory(), buffer->GetData(), size);
204 BitstreamBuffer bitstream_buffer(
205 next_bitstream_buffer_id_++, shm_buffer->shm->handle(), size);
206 bool inserted = bitstream_buffers_in_decoder_.insert(std::make_pair(
207 bitstream_buffer.id(), BufferPair(shm_buffer, buffer))).second;
208 DCHECK(inserted);
209 pts_stream_.EnqueuePts(buffer.get());
210
211 vda_->Decode(bitstream_buffer);
212 }
213
214 const gfx::Size& GpuVideoDecoder::natural_size() {
215 return natural_size_;
216 }
217
218 void GpuVideoDecoder::NotifyInitializeDone() {
219 NOTREACHED() << "GpuVideoDecodeAcceleratorHost::Initialize is synchronous!";
220 }
221
222 void GpuVideoDecoder::ProvidePictureBuffers(uint32 count,
223 const gfx::Size& size) {
224 if (MessageLoop::current() != message_loop_) {
225 message_loop_->PostTask(FROM_HERE, base::Bind(
226 &GpuVideoDecoder::ProvidePictureBuffers, this, count, size));
227 return;
228 }
229
230 std::vector<uint32> texture_ids;
231 if (!factories_->CreateTextures(count, size, &texture_ids)) {
232 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
233 return;
234 }
235
236 if (!vda_)
237 return;
238
239 std::vector<PictureBuffer> picture_buffers;
240 for (size_t i = 0; i < texture_ids.size(); ++i) {
241 picture_buffers.push_back(PictureBuffer(
242 next_picture_buffer_id_++, size, texture_ids[i]));
243 bool inserted = picture_buffers_in_decoder_.insert(std::make_pair(
244 picture_buffers.back().id(), picture_buffers.back())).second;
245 DCHECK(inserted);
246 }
247 vda_->AssignPictureBuffers(picture_buffers);
248 }
249
250 void GpuVideoDecoder::DismissPictureBuffer(int32 id) {
251 if (MessageLoop::current() != message_loop_) {
252 message_loop_->PostTask(FROM_HERE, base::Bind(
253 &GpuVideoDecoder::DismissPictureBuffer, this, id));
254 return;
255 }
256 std::map<int32, PictureBuffer>::iterator it =
257 picture_buffers_in_decoder_.find(id);
258 if (it == picture_buffers_in_decoder_.end()) {
259 NOTREACHED() << "Missing picture buffer: " << id;
260 return;
261 }
262 if (!factories_->DeleteTexture(it->second.texture_id())) {
263 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
264 return;
265 }
266 picture_buffers_in_decoder_.erase(it);
267 }
268
269 static void ResetAndRunCB(VideoDecoder::ReadCB* cb,
270 scoped_refptr<VideoFrame> frame) {
271 DCHECK(!cb->is_null());
272 VideoDecoder::ReadCB tmp_cb(*cb);
273 cb->Reset();
274 tmp_cb.Run(frame);
275 }
276
277 void GpuVideoDecoder::PictureReady(const media::Picture& picture) {
278 if (MessageLoop::current() != message_loop_) {
279 message_loop_->PostTask(FROM_HERE, base::Bind(
280 &GpuVideoDecoder::PictureReady, this, picture));
281 return;
282 }
283 std::map<int32, PictureBuffer>::iterator it =
284 picture_buffers_in_decoder_.find(picture.picture_buffer_id());
285 if (it == picture_buffers_in_decoder_.end()) {
286 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id();
287 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
288 return;
289 }
290 const PictureBuffer& pb = it->second;
291
292 // Update frame's timestamp.
293 base::TimeDelta timestamp;
294 base::TimeDelta duration;
295 std::map<int32, BufferPair>::const_iterator buf_it =
296 bitstream_buffers_in_decoder_.find(picture.bitstream_buffer_id());
297 if (buf_it != bitstream_buffers_in_decoder_.end()) {
298 // Sufficiently out-of-order decoding could have already called
299 // NotifyEndOfBitstreamBuffer on this buffer, but that's ok since we only
300 // need the buffer's time info for best-effort PTS updating.
301 timestamp = buf_it->second.buffer->GetTimestamp();
302 duration = buf_it->second.buffer->GetDuration();
303 }
304
305 scoped_refptr<VideoFrame> frame(VideoFrame::WrapNativeTexture(
306 pb.texture_id(), pb.size().width(),
307 pb.size().height(), timestamp, duration,
308 base::Bind(&GpuVideoDecoder::ReusePictureBuffer, this,
309 picture.picture_buffer_id())));
310 pts_stream_.UpdatePtsAndDuration(frame.get());
311 frame->SetTimestamp(pts_stream_.current_pts());
312 frame->SetDuration(pts_stream_.current_duration());
313
314 // Deliver the frame.
315 DeliverFrame(frame);
316 }
317
318 void GpuVideoDecoder::DeliverFrame(const scoped_refptr<VideoFrame>& frame) {
319 message_loop_->PostTask(FROM_HERE, base::Bind(
320 &GpuVideoDecoder::DeliverFrameOutOfLine, this, frame));
321 }
322
323 void GpuVideoDecoder::DeliverFrameOutOfLine(
324 const scoped_refptr<VideoFrame>& frame) {
325 if (pending_read_cb_.is_null()) {
326 ready_video_frames_.push_back(frame);
327 return;
328 }
329 ResetAndRunCB(&pending_read_cb_, frame);
330 }
331
332 void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) {
333 if (MessageLoop::current() != message_loop_) {
334 message_loop_->PostTask(FROM_HERE, base::Bind(
335 &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id));
336 return;
337 }
338 if (!vda_)
339 return;
340 vda_->ReusePictureBuffer(picture_buffer_id);
341 }
342
343 GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) {
344 DCHECK(MessageLoop::current() == message_loop_);
345 if (available_shm_segments_.empty() ||
346 available_shm_segments_.back()->size < min_size) {
347 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes);
348 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate);
349 DCHECK(shm);
350 return new SHMBuffer(shm, size_to_allocate);
351 }
352 SHMBuffer* ret = available_shm_segments_.back();
353 available_shm_segments_.pop_back();
354 return ret;
355 }
356
357 void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) {
358 DCHECK(MessageLoop::current() == message_loop_);
359 available_shm_segments_.push_back(shm_buffer);
360 }
361
362 void GpuVideoDecoder::NotifyEndOfStream() {
363 if (MessageLoop::current() != message_loop_) {
364 message_loop_->PostTask(FROM_HERE, base::Bind(
365 &GpuVideoDecoder::NotifyEndOfStream, this));
366 return;
367 }
368 DeliverFrame(VideoFrame::CreateEmptyFrame());
369 }
370
371 void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) {
372 if (MessageLoop::current() != message_loop_) {
373 message_loop_->PostTask(FROM_HERE, base::Bind(
374 &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id));
375 return;
376 }
377
378 std::map<int32, BufferPair>::iterator it =
379 bitstream_buffers_in_decoder_.find(id);
380 if (it == bitstream_buffers_in_decoder_.end()) {
381 NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
382 NOTREACHED() << "Missing bitstream buffer: " << id;
383 return;
384 }
385 PutSHM(it->second.shm_buffer);
386 const scoped_refptr<Buffer>& buffer = it->second.buffer;
387 if (buffer->GetDataSize()) {
388 PipelineStatistics statistics;
389 statistics.video_bytes_decoded = buffer->GetDataSize();
390 statistics_callback_.Run(statistics);
391 }
392 bitstream_buffers_in_decoder_.erase(it);
393
394 if (!pending_read_cb_.is_null()) {
395 DCHECK(ready_video_frames_.empty());
396 EnsureDemuxOrDecode();
397 }
398 }
399
400 void GpuVideoDecoder::EnsureDemuxOrDecode() {
401 DCHECK(MessageLoop::current() == message_loop_);
402 if (demuxer_read_in_progress_ || !bitstream_buffers_in_decoder_.empty())
403 return;
404 demuxer_read_in_progress_ = true;
405 demuxer_stream_->Read(base::Bind(
406 &GpuVideoDecoder::RequestBufferDecode, this));
407 }
408
409 void GpuVideoDecoder::NotifyFlushDone() {
410 if (MessageLoop::current() != message_loop_) {
411 message_loop_->PostTask(FROM_HERE, base::Bind(
412 &GpuVideoDecoder::NotifyFlushDone, this));
413 return;
414 }
415 DCHECK(flush_in_progress_);
416 flush_in_progress_ = false;
417 DeliverFrame(VideoFrame::CreateEmptyFrame());
418 }
419
420 void GpuVideoDecoder::NotifyResetDone() {
421 if (MessageLoop::current() != message_loop_) {
422 message_loop_->PostTask(FROM_HERE, base::Bind(
423 &GpuVideoDecoder::NotifyResetDone, this));
424 return;
425 }
426 // Throw away any already-decoded frames that have come in during the reset.
427 ready_video_frames_.clear();
428 ResetAndRunCB(&pending_flush_cb_);
429 }
430
431 void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) {
432 if (MessageLoop::current() != message_loop_) {
433 message_loop_->PostTask(FROM_HERE, base::Bind(
434 &GpuVideoDecoder::NotifyError, this, error));
435 return;
436 }
437 vda_ = NULL;
438 DLOG(ERROR) << "VDA Error: " << error;
439 if (host())
440 host()->SetError(PIPELINE_ERROR_DECODE);
441 }
442
443 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/gpu_video_decoder.h ('k') | media/filters/video_renderer_base.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698