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