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