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 "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 | |
OLD | NEW |