OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this | |
2 // source code is governed by a BSD-style license that can be found in the | |
3 // LICENSE file. | |
4 | |
5 | |
6 #include "chrome/renderer/media/ipc_video_decoder.h" | |
7 | |
8 #include "base/task.h" | |
9 #include "media/base/callback.h" | |
10 #include "media/base/filters.h" | |
11 #include "media/base/filter_host.h" | |
12 #include "media/base/limits.h" | |
13 #include "media/base/media_format.h" | |
14 #include "media/base/video_frame.h" | |
15 #include "media/ffmpeg/ffmpeg_common.h" | |
16 #include "media/ffmpeg/ffmpeg_util.h" | |
17 #include "media/filters/ffmpeg_interfaces.h" | |
18 | |
19 namespace media { | |
20 | |
21 IpcVideoDecoder::IpcVideoDecoder(MessageLoop* message_loop) | |
22 : width_(0), | |
23 height_(0), | |
24 state_(kUnInitialized), | |
25 pending_reads_(0), | |
26 pending_requests_(0), | |
27 renderer_thread_message_loop_(message_loop) { | |
28 } | |
29 | |
30 IpcVideoDecoder::~IpcVideoDecoder() { | |
31 } | |
32 | |
33 void IpcVideoDecoder::Initialize(DemuxerStream* demuxer_stream, | |
34 FilterCallback* callback) { | |
35 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
36 renderer_thread_message_loop_->PostTask( | |
37 FROM_HERE, | |
38 NewRunnableMethod(this, | |
39 &IpcVideoDecoder::Initialize, | |
40 demuxer_stream, | |
41 callback)); | |
42 return; | |
43 } | |
44 | |
45 CHECK(!demuxer_stream_); | |
46 demuxer_stream_ = demuxer_stream; | |
47 initialize_callback_.reset(callback); | |
48 | |
49 // Get the AVStream by querying for the provider interface. | |
50 AVStreamProvider* av_stream_provider; | |
51 if (!demuxer_stream->QueryInterface(&av_stream_provider)) { | |
52 GpuVideoDecoderInitDoneParam param; | |
53 OnInitializeDone(false, param); | |
54 return; | |
55 } | |
56 | |
57 AVStream* av_stream = av_stream_provider->GetAVStream(); | |
58 width_ = av_stream->codec->width; | |
59 height_ = av_stream->codec->height; | |
60 | |
61 // Create hardware decoder instance. | |
62 GpuVideoServiceHost* gpu_video_service_host = GpuVideoServiceHost::get(); | |
63 gpu_video_decoder_host_ = gpu_video_service_host->CreateVideoDecoder(this); | |
64 | |
65 // Initialize hardware decoder. | |
66 GpuVideoDecoderInitParam param; | |
67 param.width_ = width_; | |
68 param.height_ = height_; | |
69 if (!gpu_video_decoder_host_->Initialize(param)) { | |
70 GpuVideoDecoderInitDoneParam param; | |
71 OnInitializeDone(false, param); | |
72 } | |
73 } | |
74 | |
75 void IpcVideoDecoder::OnInitializeDone( | |
76 bool success, const GpuVideoDecoderInitDoneParam& param) { | |
77 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
78 renderer_thread_message_loop_->PostTask( | |
79 FROM_HERE, | |
80 NewRunnableMethod(this, | |
81 &IpcVideoDecoder::OnInitializeDone, | |
82 success, | |
83 param)); | |
84 return; | |
85 } | |
86 | |
87 AutoCallbackRunner done_runner(initialize_callback_.release()); | |
88 | |
89 if (success) { | |
90 media_format_.SetAsString(MediaFormat::kMimeType, | |
91 mime_type::kUncompressedVideo); | |
92 media_format_.SetAsInteger(MediaFormat::kWidth, width_); | |
93 media_format_.SetAsInteger(MediaFormat::kHeight, height_); | |
94 media_format_.SetAsInteger(MediaFormat::kSurfaceType, | |
95 static_cast<int>(param.surface_type_)); | |
96 media_format_.SetAsInteger(MediaFormat::kSurfaceFormat, | |
97 static_cast<int>(param.format_)); | |
98 state_ = kPlaying; | |
99 } else { | |
100 LOG(ERROR) << "IpcVideoDecoder initialization failed!"; | |
101 host()->SetError(PIPELINE_ERROR_DECODE); | |
102 } | |
103 } | |
104 | |
105 void IpcVideoDecoder::Stop(FilterCallback* callback) { | |
106 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
107 renderer_thread_message_loop_->PostTask( | |
108 FROM_HERE, | |
109 NewRunnableMethod(this, | |
110 &IpcVideoDecoder::Stop, | |
111 callback)); | |
112 return; | |
113 } | |
114 | |
115 stop_callback_.reset(callback); | |
116 if (!gpu_video_decoder_host_->Uninitialize()) { | |
117 LOG(ERROR) << "gpu video decoder destroy failed"; | |
118 IpcVideoDecoder::OnUninitializeDone(); | |
119 } | |
120 } | |
121 | |
122 void IpcVideoDecoder::OnUninitializeDone() { | |
123 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
124 renderer_thread_message_loop_->PostTask( | |
125 FROM_HERE, | |
126 NewRunnableMethod(this, | |
127 &IpcVideoDecoder::OnUninitializeDone)); | |
128 return; | |
129 } | |
130 | |
131 AutoCallbackRunner done_runner(stop_callback_.release()); | |
132 | |
133 state_ = kStopped; | |
134 } | |
135 | |
136 void IpcVideoDecoder::Pause(FilterCallback* callback) { | |
137 Flush(callback); // TODO(jiesun): move this to flush(). | |
138 } | |
139 | |
140 void IpcVideoDecoder::Flush(FilterCallback* callback) { | |
141 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
142 renderer_thread_message_loop_->PostTask( | |
143 FROM_HERE, | |
144 NewRunnableMethod(this, | |
145 &IpcVideoDecoder::Flush, | |
146 callback)); | |
147 return; | |
148 } | |
149 | |
150 state_ = kFlushing; | |
151 | |
152 flush_callback_.reset(callback); | |
153 | |
154 if (!gpu_video_decoder_host_->Flush()) { | |
155 LOG(ERROR) << "gpu video decoder flush failed"; | |
156 OnFlushDone(); | |
157 } | |
158 } | |
159 | |
160 void IpcVideoDecoder::OnFlushDone() { | |
161 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
162 renderer_thread_message_loop_->PostTask( | |
163 FROM_HERE, | |
164 NewRunnableMethod(this, | |
165 &IpcVideoDecoder::OnFlushDone)); | |
166 return; | |
167 } | |
168 | |
169 if (pending_reads_ == 0 && pending_requests_ == 0 && flush_callback_.get()) { | |
170 flush_callback_->Run(); | |
171 flush_callback_.reset(); | |
172 } | |
173 } | |
174 | |
175 void IpcVideoDecoder::Seek(base::TimeDelta time, FilterCallback* callback) { | |
176 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
177 renderer_thread_message_loop_->PostTask( | |
178 FROM_HERE, | |
179 NewRunnableMethod(this, | |
180 &IpcVideoDecoder::Seek, | |
181 time, | |
182 callback)); | |
183 return; | |
184 } | |
185 | |
186 OnSeekComplete(callback); | |
187 } | |
188 | |
189 void IpcVideoDecoder::OnSeekComplete(FilterCallback* callback) { | |
190 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
191 renderer_thread_message_loop_->PostTask( | |
192 FROM_HERE, | |
193 NewRunnableMethod(this, | |
194 &IpcVideoDecoder::OnSeekComplete, | |
195 callback)); | |
196 return; | |
197 } | |
198 | |
199 AutoCallbackRunner done_runner(callback); | |
200 | |
201 state_ = kPlaying; | |
202 | |
203 for (int i = 0; i < 20; ++i) { | |
204 demuxer_stream_->Read( | |
205 NewCallback(this, | |
206 &IpcVideoDecoder::OnReadComplete)); | |
207 ++pending_reads_; | |
208 } | |
209 } | |
210 | |
211 void IpcVideoDecoder::OnReadComplete(Buffer* buffer) { | |
212 scoped_refptr<Buffer> buffer_ref = buffer; | |
213 ReadCompleteTask(buffer_ref); | |
214 } | |
215 | |
216 void IpcVideoDecoder::ReadCompleteTask(scoped_refptr<Buffer> buffer) { | |
217 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
218 renderer_thread_message_loop_->PostTask( | |
219 FROM_HERE, | |
220 NewRunnableMethod(this, | |
221 &IpcVideoDecoder::ReadCompleteTask, | |
222 buffer)); | |
223 return; | |
224 } | |
225 | |
226 DCHECK_GT(pending_reads_, 0u); | |
227 --pending_reads_; | |
228 | |
229 if (state_ == kStopped || state_ == kEnded) { | |
230 // Just discard the input buffers | |
231 return; | |
232 } | |
233 | |
234 if (state_ == kFlushing) { | |
235 if (pending_reads_ == 0 && pending_requests_ == 0) { | |
236 CHECK(flush_callback_.get()); | |
237 flush_callback_->Run(); | |
238 flush_callback_.reset(); | |
239 state_ = kPlaying; | |
240 } | |
241 return; | |
242 } | |
243 // Transition to kFlushCodec on the first end of input stream buffer. | |
244 if (state_ == kPlaying && buffer->IsEndOfStream()) { | |
245 state_ = kFlushCodec; | |
246 } | |
247 | |
248 gpu_video_decoder_host_->EmptyThisBuffer(buffer); | |
249 } | |
250 | |
251 void IpcVideoDecoder::FillThisBuffer(scoped_refptr<VideoFrame> video_frame) { | |
252 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
253 renderer_thread_message_loop_->PostTask( | |
254 FROM_HERE, | |
255 NewRunnableMethod(this, | |
256 &IpcVideoDecoder::FillThisBuffer, | |
257 video_frame)); | |
258 return; | |
259 } | |
260 | |
261 // Synchronized flushing before stop should prevent this. | |
262 CHECK_NE(state_, kStopped); | |
263 | |
264 // Notify decode engine the available of new frame. | |
265 ++pending_requests_; | |
266 gpu_video_decoder_host_->FillThisBuffer(video_frame); | |
267 } | |
268 | |
269 void IpcVideoDecoder::OnFillBufferDone(scoped_refptr<VideoFrame> video_frame) { | |
270 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
271 renderer_thread_message_loop_->PostTask( | |
272 FROM_HERE, | |
273 NewRunnableMethod(this, | |
274 &IpcVideoDecoder::OnFillBufferDone, | |
275 video_frame)); | |
276 return; | |
277 } | |
278 | |
279 if (video_frame.get()) { | |
280 --pending_requests_; | |
281 fill_buffer_done_callback()->Run(video_frame); | |
282 if (state_ == kFlushing && pending_reads_ == 0 && pending_requests_ == 0) { | |
283 CHECK(flush_callback_.get()); | |
284 flush_callback_->Run(); | |
285 flush_callback_.reset(); | |
286 state_ = kPlaying; | |
287 } | |
288 | |
289 } else { | |
290 if (state_ == kFlushCodec) { | |
291 // When in kFlushCodec, any errored decode, or a 0-lengthed frame, | |
292 // is taken as a signal to stop decoding. | |
293 state_ = kEnded; | |
294 scoped_refptr<VideoFrame> video_frame; | |
295 VideoFrame::CreateEmptyFrame(&video_frame); | |
296 fill_buffer_done_callback()->Run(video_frame); | |
297 } | |
298 } | |
299 } | |
300 | |
301 void IpcVideoDecoder::OnEmptyBufferDone(scoped_refptr<Buffer> buffer) { | |
302 if (MessageLoop::current() != renderer_thread_message_loop_) { | |
303 renderer_thread_message_loop_->PostTask( | |
304 FROM_HERE, | |
305 NewRunnableMethod(this, | |
306 &IpcVideoDecoder::OnEmptyBufferDone, | |
307 buffer)); | |
308 return; | |
309 } | |
310 | |
311 // TODO(jiesun): We haven't recycle input buffer yet. | |
312 demuxer_stream_->Read(NewCallback(this, &IpcVideoDecoder::OnReadComplete)); | |
313 ++pending_reads_; | |
314 } | |
315 | |
316 void IpcVideoDecoder::OnDeviceError() { | |
317 host()->SetError(PIPELINE_ERROR_DECODE); | |
318 } | |
319 | |
320 bool IpcVideoDecoder::ProvidesBuffer() { | |
321 return true; | |
322 } | |
323 | |
324 // static | |
325 FilterFactory* IpcVideoDecoder::CreateFactory(MessageLoop* message_loop) { | |
326 return new FilterFactoryImpl1<IpcVideoDecoder, MessageLoop*>(message_loop); | |
327 } | |
328 | |
329 // static | |
330 bool IpcVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) { | |
331 std::string mime_type; | |
332 if (!format.GetAsString(MediaFormat::kMimeType, &mime_type) && | |
333 mime_type::kFFmpegVideo != mime_type) | |
334 return false; | |
335 | |
336 // TODO(jiesun): Although we current only support H264 hardware decoding, | |
337 // in the future, we should query GpuVideoService for capabilities. | |
338 int codec_id; | |
339 return format.GetAsInteger(MediaFormat::kFFmpegCodecID, &codec_id) && | |
340 codec_id == CODEC_ID_H264; | |
341 } | |
342 | |
343 } // namespace media | |
344 | |
OLD | NEW |