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