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_audio_decoder.h" | 5 #include "media/filters/ffmpeg_audio_decoder.h" |
6 | 6 |
7 #include "base/callback_helpers.h" | 7 #include "base/callback_helpers.h" |
8 #include "base/single_thread_task_runner.h" | 8 #include "base/single_thread_task_runner.h" |
9 #include "media/base/audio_buffer.h" | 9 #include "media/base/audio_buffer.h" |
10 #include "media/base/audio_bus.h" | 10 #include "media/base/audio_bus.h" |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
134 log_cb_(log_cb) { | 134 log_cb_(log_cb) { |
135 } | 135 } |
136 | 136 |
137 FFmpegAudioDecoder::~FFmpegAudioDecoder() { | 137 FFmpegAudioDecoder::~FFmpegAudioDecoder() { |
138 DCHECK_EQ(state_, kUninitialized); | 138 DCHECK_EQ(state_, kUninitialized); |
139 DCHECK(!codec_context_); | 139 DCHECK(!codec_context_); |
140 DCHECK(!av_frame_); | 140 DCHECK(!av_frame_); |
141 } | 141 } |
142 | 142 |
143 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, | 143 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, |
144 const PipelineStatusCB& status_cb) { | 144 const PipelineStatusCB& status_cb, |
145 const OutputCB& output_cb) { | |
145 DCHECK(task_runner_->BelongsToCurrentThread()); | 146 DCHECK(task_runner_->BelongsToCurrentThread()); |
146 DCHECK(!config.is_encrypted()); | 147 DCHECK(!config.is_encrypted()); |
147 | 148 |
148 FFmpegGlue::InitializeFFmpeg(); | 149 FFmpegGlue::InitializeFFmpeg(); |
149 | 150 |
150 config_ = config; | 151 config_ = config; |
151 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); | 152 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
152 | 153 |
153 if (!config.IsValidConfig() || !ConfigureDecoder()) { | 154 if (!config.IsValidConfig() || !ConfigureDecoder()) { |
154 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 155 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
155 return; | 156 return; |
156 } | 157 } |
157 | 158 |
158 // Success! | 159 // Success! |
160 output_cb_ = BindToCurrentLoop(output_cb); | |
159 state_ = kNormal; | 161 state_ = kNormal; |
160 initialize_cb.Run(PIPELINE_OK); | 162 initialize_cb.Run(PIPELINE_OK); |
161 } | 163 } |
162 | 164 |
163 void FFmpegAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 165 void FFmpegAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
164 const DecodeCB& decode_cb) { | 166 const DecodeCB& decode_cb) { |
165 DCHECK(task_runner_->BelongsToCurrentThread()); | 167 DCHECK(task_runner_->BelongsToCurrentThread()); |
166 DCHECK(!decode_cb.is_null()); | 168 DCHECK(!decode_cb.is_null()); |
167 CHECK_NE(state_, kUninitialized); | 169 CHECK_NE(state_, kUninitialized); |
168 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); | 170 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); |
169 | 171 |
170 if (state_ == kError) { | 172 if (state_ == kError) { |
171 decode_cb_bound.Run(kDecodeError, NULL); | 173 decode_cb_bound.Run(kDecodeError); |
172 return; | 174 return; |
173 } | 175 } |
174 | 176 |
175 // Return empty frames if decoding has finished. | 177 // Do nothing if decoding has finished. |
176 if (state_ == kDecodeFinished) { | 178 if (state_ == kDecodeFinished) { |
177 decode_cb_bound.Run(kOk, AudioBuffer::CreateEOSBuffer()); | 179 decode_cb_bound.Run(kOk); |
178 return; | 180 return; |
179 } | 181 } |
180 | 182 |
181 if (!buffer) { | 183 if (!buffer) { |
xhwang
2014/06/05 21:53:50
hmm, should this ever happen?
Sergey Ulanov
2014/06/06 22:49:40
Done.
| |
182 decode_cb_bound.Run(kAborted, NULL); | 184 decode_cb_bound.Run(kAborted); |
183 return; | 185 return; |
184 } | 186 } |
185 | 187 |
186 DecodeBuffer(buffer, decode_cb_bound); | 188 DecodeBuffer(buffer, decode_cb_bound); |
187 } | 189 } |
188 | 190 |
189 scoped_refptr<AudioBuffer> FFmpegAudioDecoder::GetDecodeOutput() { | |
190 DCHECK(task_runner_->BelongsToCurrentThread()); | |
191 if (queued_audio_.empty()) | |
192 return NULL; | |
193 scoped_refptr<AudioBuffer> out = queued_audio_.front(); | |
194 queued_audio_.pop_front(); | |
195 return out; | |
196 } | |
197 | |
198 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { | 191 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { |
199 DCHECK(task_runner_->BelongsToCurrentThread()); | 192 DCHECK(task_runner_->BelongsToCurrentThread()); |
200 | 193 |
201 avcodec_flush_buffers(codec_context_.get()); | 194 avcodec_flush_buffers(codec_context_.get()); |
202 state_ = kNormal; | 195 state_ = kNormal; |
203 ResetTimestampState(); | 196 ResetTimestampState(); |
204 task_runner_->PostTask(FROM_HERE, closure); | 197 task_runner_->PostTask(FROM_HERE, closure); |
205 } | 198 } |
206 | 199 |
207 void FFmpegAudioDecoder::Stop() { | 200 void FFmpegAudioDecoder::Stop() { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
250 // When avcodec_decode_audio4() returns 0 data. | 243 // When avcodec_decode_audio4() returns 0 data. |
251 // kFlushCodec -> kError: | 244 // kFlushCodec -> kError: |
252 // When avcodec_decode_audio4() errors out. | 245 // When avcodec_decode_audio4() errors out. |
253 // (any state) -> kNormal: | 246 // (any state) -> kNormal: |
254 // Any time Reset() is called. | 247 // Any time Reset() is called. |
255 | 248 |
256 // Make sure we are notified if http://crbug.com/49709 returns. Issue also | 249 // Make sure we are notified if http://crbug.com/49709 returns. Issue also |
257 // occurs with some damaged files. | 250 // occurs with some damaged files. |
258 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp()) { | 251 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp()) { |
259 DVLOG(1) << "Received a buffer without timestamps!"; | 252 DVLOG(1) << "Received a buffer without timestamps!"; |
260 decode_cb.Run(kDecodeError, NULL); | 253 decode_cb.Run(kDecodeError); |
261 return; | 254 return; |
262 } | 255 } |
263 | 256 |
264 if (!buffer->end_of_stream() && !discard_helper_->initialized() && | 257 if (!buffer->end_of_stream() && !discard_helper_->initialized() && |
265 codec_context_->codec_id == AV_CODEC_ID_VORBIS && | 258 codec_context_->codec_id == AV_CODEC_ID_VORBIS && |
266 buffer->timestamp() < base::TimeDelta()) { | 259 buffer->timestamp() < base::TimeDelta()) { |
267 // Dropping frames for negative timestamps as outlined in section A.2 | 260 // Dropping frames for negative timestamps as outlined in section A.2 |
268 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html | 261 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
269 const int discard_frames = | 262 const int discard_frames = |
270 discard_helper_->TimeDeltaToFrames(-buffer->timestamp()); | 263 discard_helper_->TimeDeltaToFrames(-buffer->timestamp()); |
271 discard_helper_->Reset(discard_frames); | 264 discard_helper_->Reset(discard_frames); |
272 } | 265 } |
273 | 266 |
274 // Transition to kFlushCodec on the first end of stream buffer. | 267 // Transition to kFlushCodec on the first end of stream buffer. |
275 if (state_ == kNormal && buffer->end_of_stream()) { | 268 if (state_ == kNormal && buffer->end_of_stream()) { |
276 state_ = kFlushCodec; | 269 state_ = kFlushCodec; |
277 } | 270 } |
278 | 271 |
279 if (!FFmpegDecode(buffer)) { | 272 if (!FFmpegDecode(buffer)) { |
280 state_ = kError; | 273 state_ = kError; |
281 decode_cb.Run(kDecodeError, NULL); | 274 decode_cb.Run(kDecodeError); |
282 return; | 275 return; |
283 } | 276 } |
284 | 277 |
285 if (queued_audio_.empty()) { | 278 if (state_ == kFlushCodec) { |
286 if (state_ == kFlushCodec) { | 279 state_ = kDecodeFinished; |
287 DCHECK(buffer->end_of_stream()); | 280 output_cb_.Run(AudioBuffer::CreateEOSBuffer()); |
xhwang
2014/06/05 21:53:50
should we keep calling FFmpegDecode(EOS) until the
Sergey Ulanov
2014/06/06 22:49:40
There is a loop in FFmpegDecode() which should flu
| |
288 state_ = kDecodeFinished; | |
289 decode_cb.Run(kOk, AudioBuffer::CreateEOSBuffer()); | |
290 return; | |
291 } | |
292 | |
293 decode_cb.Run(kNotEnoughData, NULL); | |
294 return; | |
295 } | 281 } |
296 | 282 |
297 decode_cb.Run(kOk, queued_audio_.front()); | 283 decode_cb.Run(kOk); |
298 queued_audio_.pop_front(); | |
299 } | 284 } |
300 | 285 |
301 bool FFmpegAudioDecoder::FFmpegDecode( | 286 bool FFmpegAudioDecoder::FFmpegDecode( |
302 const scoped_refptr<DecoderBuffer>& buffer) { | 287 const scoped_refptr<DecoderBuffer>& buffer) { |
303 DCHECK(queued_audio_.empty()); | |
304 | |
305 AVPacket packet; | 288 AVPacket packet; |
306 av_init_packet(&packet); | 289 av_init_packet(&packet); |
307 if (buffer->end_of_stream()) { | 290 if (buffer->end_of_stream()) { |
308 packet.data = NULL; | 291 packet.data = NULL; |
309 packet.size = 0; | 292 packet.size = 0; |
310 } else { | 293 } else { |
311 packet.data = const_cast<uint8*>(buffer->data()); | 294 packet.data = const_cast<uint8*>(buffer->data()); |
312 packet.size = buffer->data_size(); | 295 packet.size = buffer->data_size(); |
313 } | 296 } |
314 | 297 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 << ", Sample Format: " << av_frame_->format << " vs " | 338 << ", Sample Format: " << av_frame_->format << " vs " |
356 << av_sample_format_; | 339 << av_sample_format_; |
357 | 340 |
358 if (config_.codec() == kCodecAAC && | 341 if (config_.codec() == kCodecAAC && |
359 av_frame_->sample_rate == 2 * config_.samples_per_second()) { | 342 av_frame_->sample_rate == 2 * config_.samples_per_second()) { |
360 MEDIA_LOG(log_cb_) << "Implicit HE-AAC signalling is being used." | 343 MEDIA_LOG(log_cb_) << "Implicit HE-AAC signalling is being used." |
361 << " Please use mp4a.40.5 instead of mp4a.40.2 in" | 344 << " Please use mp4a.40.5 instead of mp4a.40.2 in" |
362 << " the mimetype."; | 345 << " the mimetype."; |
363 } | 346 } |
364 // This is an unrecoverable error, so bail out. | 347 // This is an unrecoverable error, so bail out. |
365 queued_audio_.clear(); | |
366 av_frame_unref(av_frame_.get()); | 348 av_frame_unref(av_frame_.get()); |
367 return false; | 349 return false; |
368 } | 350 } |
369 | 351 |
370 // Get the AudioBuffer that the data was decoded into. Adjust the number | 352 // Get the AudioBuffer that the data was decoded into. Adjust the number |
371 // of frames, in case fewer than requested were actually decoded. | 353 // of frames, in case fewer than requested were actually decoded. |
372 output = reinterpret_cast<AudioBuffer*>( | 354 output = reinterpret_cast<AudioBuffer*>( |
373 av_buffer_get_opaque(av_frame_->buf[0])); | 355 av_buffer_get_opaque(av_frame_->buf[0])); |
374 | 356 |
375 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()), | 357 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()), |
376 output->channel_count()); | 358 output->channel_count()); |
377 const int unread_frames = output->frame_count() - av_frame_->nb_samples; | 359 const int unread_frames = output->frame_count() - av_frame_->nb_samples; |
378 DCHECK_GE(unread_frames, 0); | 360 DCHECK_GE(unread_frames, 0); |
379 if (unread_frames > 0) | 361 if (unread_frames > 0) |
380 output->TrimEnd(unread_frames); | 362 output->TrimEnd(unread_frames); |
381 | 363 |
382 av_frame_unref(av_frame_.get()); | 364 av_frame_unref(av_frame_.get()); |
383 } | 365 } |
384 | 366 |
385 // WARNING: |av_frame_| no longer has valid data at this point. | 367 // WARNING: |av_frame_| no longer has valid data at this point. |
386 const int decoded_frames = frame_decoded ? output->frame_count() : 0; | 368 const int decoded_frames = frame_decoded ? output->frame_count() : 0; |
387 if (IsEndOfStream(result, decoded_frames, buffer)) { | 369 if (IsEndOfStream(result, decoded_frames, buffer)) { |
388 DCHECK_EQ(packet.size, 0); | 370 DCHECK_EQ(packet.size, 0); |
389 queued_audio_.push_back(AudioBuffer::CreateEOSBuffer()); | 371 output_cb_.Run(AudioBuffer::CreateEOSBuffer()); |
390 } else if (discard_helper_->ProcessBuffers(buffer, output)) { | 372 } else if (discard_helper_->ProcessBuffers(buffer, output)) { |
391 queued_audio_.push_back(output); | 373 output_cb_.Run(output); |
392 } | 374 } |
393 } while (packet.size > 0); | 375 } while (packet.size > 0); |
394 | 376 |
395 return true; | 377 return true; |
396 } | 378 } |
397 | 379 |
398 void FFmpegAudioDecoder::ReleaseFFmpegResources() { | 380 void FFmpegAudioDecoder::ReleaseFFmpegResources() { |
399 codec_context_.reset(); | 381 codec_context_.reset(); |
400 av_frame_.reset(); | 382 av_frame_.reset(); |
401 } | 383 } |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
454 | 436 |
455 ResetTimestampState(); | 437 ResetTimestampState(); |
456 return true; | 438 return true; |
457 } | 439 } |
458 | 440 |
459 void FFmpegAudioDecoder::ResetTimestampState() { | 441 void FFmpegAudioDecoder::ResetTimestampState() { |
460 discard_helper_->Reset(config_.codec_delay()); | 442 discard_helper_->Reset(config_.codec_delay()); |
461 } | 443 } |
462 | 444 |
463 } // namespace media | 445 } // namespace media |
OLD | NEW |