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

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

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

Powered by Google App Engine
This is Rietveld 408576698