OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/filters/ffmpeg_video_decoder.h" | 5 #include "media/filters/ffmpeg_video_decoder.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/message_loop_proxy.h" | 14 #include "base/message_loop_proxy.h" |
15 #include "base/string_number_conversions.h" | 15 #include "base/string_number_conversions.h" |
| 16 #include "media/base/bind_to_loop.h" |
16 #include "media/base/decoder_buffer.h" | 17 #include "media/base/decoder_buffer.h" |
17 #include "media/base/demuxer_stream.h" | 18 #include "media/base/demuxer_stream.h" |
18 #include "media/base/limits.h" | 19 #include "media/base/limits.h" |
19 #include "media/base/media_switches.h" | 20 #include "media/base/media_switches.h" |
20 #include "media/base/pipeline.h" | 21 #include "media/base/pipeline.h" |
21 #include "media/base/video_decoder_config.h" | 22 #include "media/base/video_decoder_config.h" |
22 #include "media/base/video_frame.h" | 23 #include "media/base/video_frame.h" |
23 #include "media/base/video_util.h" | 24 #include "media/base/video_util.h" |
24 #include "media/ffmpeg/ffmpeg_common.h" | 25 #include "media/ffmpeg/ffmpeg_common.h" |
25 #include "media/filters/ffmpeg_glue.h" | 26 #include "media/filters/ffmpeg_glue.h" |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 | 127 |
127 // The FFmpeg API expects us to zero the data pointers in | 128 // The FFmpeg API expects us to zero the data pointers in |
128 // this callback | 129 // this callback |
129 memset(frame->data, 0, sizeof(frame->data)); | 130 memset(frame->data, 0, sizeof(frame->data)); |
130 frame->opaque = NULL; | 131 frame->opaque = NULL; |
131 } | 132 } |
132 | 133 |
133 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, | 134 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
134 const PipelineStatusCB& status_cb, | 135 const PipelineStatusCB& status_cb, |
135 const StatisticsCB& statistics_cb) { | 136 const StatisticsCB& statistics_cb) { |
136 if (!message_loop_->BelongsToCurrentThread()) { | 137 DCHECK(message_loop_->BelongsToCurrentThread()); |
137 message_loop_->PostTask(FROM_HERE, base::Bind( | 138 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
138 &FFmpegVideoDecoder::Initialize, this, | |
139 stream, status_cb, statistics_cb)); | |
140 return; | |
141 } | |
142 | 139 |
143 FFmpegGlue::InitializeFFmpeg(); | 140 FFmpegGlue::InitializeFFmpeg(); |
144 DCHECK(!demuxer_stream_) << "Already initialized."; | 141 DCHECK(!demuxer_stream_) << "Already initialized."; |
145 | 142 |
146 if (!stream) { | 143 if (!stream) { |
147 status_cb.Run(PIPELINE_ERROR_DECODE); | 144 initialize_cb.Run(PIPELINE_ERROR_DECODE); |
148 return; | 145 return; |
149 } | 146 } |
150 | 147 |
151 demuxer_stream_ = stream; | 148 demuxer_stream_ = stream; |
152 statistics_cb_ = statistics_cb; | 149 statistics_cb_ = statistics_cb; |
153 | 150 |
154 if (!ConfigureDecoder()) { | 151 if (!ConfigureDecoder()) { |
155 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 152 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
156 return; | 153 return; |
157 } | 154 } |
158 | 155 |
159 // Success! | 156 // Success! |
160 state_ = kNormal; | 157 state_ = kNormal; |
161 status_cb.Run(PIPELINE_OK); | 158 initialize_cb.Run(PIPELINE_OK); |
162 } | 159 } |
163 | 160 |
164 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { | 161 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { |
165 // Complete operation asynchronously on different stack of execution as per | 162 DCHECK(message_loop_->BelongsToCurrentThread()); |
166 // the API contract of VideoDecoder::Read() | 163 DCHECK(!read_cb.is_null()); |
167 message_loop_->PostTask(FROM_HERE, base::Bind( | 164 CHECK_NE(state_, kUninitialized); |
168 &FFmpegVideoDecoder::DoRead, this, read_cb)); | 165 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
| 166 read_cb_ = BindToCurrentLoop(read_cb); |
| 167 |
| 168 // Return empty frames if decoding has finished. |
| 169 if (state_ == kDecodeFinished) { |
| 170 base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame()); |
| 171 return; |
| 172 } |
| 173 |
| 174 ReadFromDemuxerStream(); |
169 } | 175 } |
170 | 176 |
171 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { | 177 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
172 if (!message_loop_->BelongsToCurrentThread()) { | 178 DCHECK(message_loop_->BelongsToCurrentThread()); |
173 message_loop_->PostTask(FROM_HERE, base::Bind( | |
174 &FFmpegVideoDecoder::Reset, this, closure)); | |
175 return; | |
176 } | |
177 | |
178 DCHECK(reset_cb_.is_null()); | 179 DCHECK(reset_cb_.is_null()); |
179 reset_cb_ = closure; | 180 reset_cb_ = BindToCurrentLoop(closure); |
180 | 181 |
181 if (decryptor_) | 182 if (decryptor_) |
182 decryptor_->CancelDecrypt(Decryptor::kVideo); | 183 decryptor_->CancelDecrypt(Decryptor::kVideo); |
183 | 184 |
184 // Defer the reset if a read is pending. | 185 // Defer the reset if a read is pending. |
185 if (!read_cb_.is_null()) | 186 if (!read_cb_.is_null()) |
186 return; | 187 return; |
187 | 188 |
188 DoReset(); | 189 DoReset(); |
189 } | 190 } |
190 | 191 |
191 void FFmpegVideoDecoder::DoReset() { | 192 void FFmpegVideoDecoder::DoReset() { |
192 DCHECK(read_cb_.is_null()); | 193 DCHECK(read_cb_.is_null()); |
193 | 194 |
194 avcodec_flush_buffers(codec_context_); | 195 avcodec_flush_buffers(codec_context_); |
195 state_ = kNormal; | 196 state_ = kNormal; |
196 reset_cb_.Run(); | 197 base::ResetAndReturn(&reset_cb_).Run(); |
197 reset_cb_.Reset(); | |
198 } | 198 } |
199 | 199 |
200 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { | 200 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { |
201 if (!message_loop_->BelongsToCurrentThread()) { | 201 DCHECK(message_loop_->BelongsToCurrentThread()); |
202 message_loop_->PostTask(FROM_HERE, base::Bind( | 202 base::ScopedClosureRunner runner(BindToCurrentLoop(closure)); |
203 &FFmpegVideoDecoder::Stop, this, closure)); | 203 |
| 204 if (state_ == kUninitialized) |
204 return; | 205 return; |
205 } | |
206 | |
207 if (state_ == kUninitialized) { | |
208 closure.Run(); | |
209 return; | |
210 } | |
211 | 206 |
212 if (decryptor_) | 207 if (decryptor_) |
213 decryptor_->CancelDecrypt(Decryptor::kVideo); | 208 decryptor_->CancelDecrypt(Decryptor::kVideo); |
214 | 209 |
215 if (!read_cb_.is_null()) | 210 if (!read_cb_.is_null()) |
216 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 211 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
217 | 212 |
218 ReleaseFFmpegResources(); | 213 ReleaseFFmpegResources(); |
219 state_ = kUninitialized; | 214 state_ = kUninitialized; |
220 closure.Run(); | |
221 } | 215 } |
222 | 216 |
223 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | 217 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
224 DCHECK_EQ(kUninitialized, state_); | 218 DCHECK_EQ(kUninitialized, state_); |
225 DCHECK(!codec_context_); | 219 DCHECK(!codec_context_); |
226 DCHECK(!av_frame_); | 220 DCHECK(!av_frame_); |
227 } | 221 } |
228 | 222 |
229 void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { | |
230 DCHECK(message_loop_->BelongsToCurrentThread()); | |
231 DCHECK(!read_cb.is_null()); | |
232 CHECK_NE(state_, kUninitialized); | |
233 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
234 | |
235 // Return empty frames if decoding has finished. | |
236 if (state_ == kDecodeFinished) { | |
237 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
238 return; | |
239 } | |
240 | |
241 read_cb_ = read_cb; | |
242 ReadFromDemuxerStream(); | |
243 } | |
244 | |
245 void FFmpegVideoDecoder::ReadFromDemuxerStream() { | 223 void FFmpegVideoDecoder::ReadFromDemuxerStream() { |
246 DCHECK_NE(state_, kUninitialized); | 224 DCHECK_NE(state_, kUninitialized); |
247 DCHECK_NE(state_, kDecodeFinished); | 225 DCHECK_NE(state_, kDecodeFinished); |
248 DCHECK(!read_cb_.is_null()); | 226 DCHECK(!read_cb_.is_null()); |
249 | 227 |
250 demuxer_stream_->Read(base::Bind( | 228 demuxer_stream_->Read(base::Bind( |
251 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this)); | 229 &FFmpegVideoDecoder::DecryptOrDecodeBuffer, this)); |
252 } | 230 } |
253 | 231 |
254 void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( | 232 void FFmpegVideoDecoder::DecryptOrDecodeBuffer( |
255 DemuxerStream::Status status, | 233 DemuxerStream::Status status, |
256 const scoped_refptr<DecoderBuffer>& buffer) { | 234 const scoped_refptr<DecoderBuffer>& buffer) { |
257 if (!message_loop_->BelongsToCurrentThread()) { | 235 DCHECK(message_loop_->BelongsToCurrentThread()); |
258 message_loop_->PostTask(FROM_HERE, base::Bind( | |
259 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer)); | |
260 return; | |
261 } | |
262 | |
263 DCHECK_NE(state_, kDecodeFinished); | 236 DCHECK_NE(state_, kDecodeFinished); |
264 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | 237 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
265 | 238 |
266 if (state_ == kUninitialized) | 239 if (state_ == kUninitialized) |
267 return; | 240 return; |
268 | 241 |
269 DCHECK(!read_cb_.is_null()); | 242 DCHECK(!read_cb_.is_null()); |
270 | 243 |
271 if (!reset_cb_.is_null()) { | 244 if (!reset_cb_.is_null()) { |
272 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 245 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
(...skipping 11 matching lines...) Expand all Loading... |
284 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 257 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
285 return; | 258 return; |
286 } | 259 } |
287 | 260 |
288 ReadFromDemuxerStream(); | 261 ReadFromDemuxerStream(); |
289 return; | 262 return; |
290 } | 263 } |
291 | 264 |
292 DCHECK_EQ(status, DemuxerStream::kOk); | 265 DCHECK_EQ(status, DemuxerStream::kOk); |
293 | 266 |
| 267 // TODO(xhwang): Remove decryptor after DecryptingDemuxerStream is ready. |
294 if (buffer->GetDecryptConfig() && buffer->GetDataSize()) { | 268 if (buffer->GetDecryptConfig() && buffer->GetDataSize()) { |
295 decryptor_->Decrypt(Decryptor::kVideo, | 269 decryptor_->Decrypt(Decryptor::kVideo, buffer, BindToCurrentLoop( |
296 buffer, | 270 base::Bind(&FFmpegVideoDecoder::BufferDecrypted, this))); |
297 base::Bind(&FFmpegVideoDecoder::BufferDecrypted, this)); | |
298 return; | 271 return; |
299 } | 272 } |
300 | 273 |
301 DecodeBuffer(buffer); | 274 DecodeBuffer(buffer); |
302 } | 275 } |
303 | 276 |
304 void FFmpegVideoDecoder::BufferDecrypted( | 277 void FFmpegVideoDecoder::BufferDecrypted( |
305 Decryptor::Status decrypt_status, | 278 Decryptor::Status decrypt_status, |
306 const scoped_refptr<DecoderBuffer>& buffer) { | 279 const scoped_refptr<DecoderBuffer>& buffer) { |
307 message_loop_->PostTask(FROM_HERE, base::Bind( | |
308 &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); | |
309 } | |
310 | |
311 void FFmpegVideoDecoder::DoBufferDecrypted( | |
312 Decryptor::Status decrypt_status, | |
313 const scoped_refptr<DecoderBuffer>& buffer) { | |
314 DCHECK(message_loop_->BelongsToCurrentThread()); | 280 DCHECK(message_loop_->BelongsToCurrentThread()); |
315 DCHECK_NE(state_, kDecodeFinished); | 281 DCHECK_NE(state_, kDecodeFinished); |
316 | 282 |
317 if (state_ == kUninitialized) | 283 if (state_ == kUninitialized) |
318 return; | 284 return; |
319 | 285 |
320 DCHECK(!read_cb_.is_null()); | 286 DCHECK(!read_cb_.is_null()); |
321 | 287 |
322 if (!reset_cb_.is_null()) { | 288 if (!reset_cb_.is_null()) { |
323 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 289 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
524 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { | 490 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { |
525 ReleaseFFmpegResources(); | 491 ReleaseFFmpegResources(); |
526 return false; | 492 return false; |
527 } | 493 } |
528 | 494 |
529 av_frame_ = avcodec_alloc_frame(); | 495 av_frame_ = avcodec_alloc_frame(); |
530 return true; | 496 return true; |
531 } | 497 } |
532 | 498 |
533 } // namespace media | 499 } // namespace media |
OLD | NEW |