Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Side by Side Diff: media/filters/ffmpeg_audio_decoder.cc

Issue 259453003: Introduce AudioDiscardHelper. Refactor audio decoders to use it. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Simplify! Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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"
11 #include "media/base/audio_decoder_config.h" 11 #include "media/base/audio_decoder_config.h"
12 #include "media/base/audio_timestamp_helper.h" 12 #include "media/base/audio_discard_helper.h"
13 #include "media/base/bind_to_current_loop.h" 13 #include "media/base/bind_to_current_loop.h"
14 #include "media/base/decoder_buffer.h" 14 #include "media/base/decoder_buffer.h"
15 #include "media/base/limits.h" 15 #include "media/base/limits.h"
16 #include "media/base/sample_format.h" 16 #include "media/base/sample_format.h"
17 #include "media/ffmpeg/ffmpeg_common.h" 17 #include "media/ffmpeg/ffmpeg_common.h"
18 #include "media/filters/ffmpeg_glue.h" 18 #include "media/filters/ffmpeg_glue.h"
19 19
20 namespace media { 20 namespace media {
21 21
22 // Returns true if the decode result was end of stream. 22 // Returns true if the decode result was end of stream.
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 buffer.swap(reinterpret_cast<AudioBuffer**>(&opaque)); 122 buffer.swap(reinterpret_cast<AudioBuffer**>(&opaque));
123 frame->buf[0] = av_buffer_create( 123 frame->buf[0] = av_buffer_create(
124 frame->data[0], buffer_size_in_bytes, ReleaseAudioBufferImpl, opaque, 0); 124 frame->data[0], buffer_size_in_bytes, ReleaseAudioBufferImpl, opaque, 0);
125 return 0; 125 return 0;
126 } 126 }
127 127
128 FFmpegAudioDecoder::FFmpegAudioDecoder( 128 FFmpegAudioDecoder::FFmpegAudioDecoder(
129 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) 129 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
130 : task_runner_(task_runner), 130 : task_runner_(task_runner),
131 state_(kUninitialized), 131 state_(kUninitialized),
132 av_sample_format_(0), 132 av_sample_format_(0) {}
133 last_input_timestamp_(kNoTimestamp()),
134 output_frames_to_drop_(0) {}
135 133
136 FFmpegAudioDecoder::~FFmpegAudioDecoder() { 134 FFmpegAudioDecoder::~FFmpegAudioDecoder() {
137 DCHECK_EQ(state_, kUninitialized); 135 DCHECK_EQ(state_, kUninitialized);
138 DCHECK(!codec_context_); 136 DCHECK(!codec_context_);
139 DCHECK(!av_frame_); 137 DCHECK(!av_frame_);
140 } 138 }
141 139
142 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, 140 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config,
143 const PipelineStatusCB& status_cb) { 141 const PipelineStatusCB& status_cb) {
144 DCHECK(task_runner_->BelongsToCurrentThread()); 142 DCHECK(task_runner_->BelongsToCurrentThread());
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 // A decoding error occurs and decoding needs to stop. 245 // A decoding error occurs and decoding needs to stop.
248 // kFlushCodec -> kDecodeFinished: 246 // kFlushCodec -> kDecodeFinished:
249 // When avcodec_decode_audio4() returns 0 data. 247 // When avcodec_decode_audio4() returns 0 data.
250 // kFlushCodec -> kError: 248 // kFlushCodec -> kError:
251 // When avcodec_decode_audio4() errors out. 249 // When avcodec_decode_audio4() errors out.
252 // (any state) -> kNormal: 250 // (any state) -> kNormal:
253 // Any time Reset() is called. 251 // Any time Reset() is called.
254 252
255 // Make sure we are notified if http://crbug.com/49709 returns. Issue also 253 // Make sure we are notified if http://crbug.com/49709 returns. Issue also
256 // occurs with some damaged files. 254 // occurs with some damaged files.
257 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp() && 255 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp()) {
wolenetz 2014/04/28 21:52:07 Was it incorrect previously to also condition this
DaleCurtis 2014/04/28 22:03:51 I've removed this exception as the only time this
258 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) {
259 DVLOG(1) << "Received a buffer without timestamps!"; 256 DVLOG(1) << "Received a buffer without timestamps!";
260 decode_cb.Run(kDecodeError, NULL); 257 decode_cb.Run(kDecodeError, NULL);
261 return; 258 return;
262 } 259 }
263 260
264 if (!buffer->end_of_stream()) { 261 if (!buffer->end_of_stream() &&
265 DCHECK(buffer->timestamp() != kNoTimestamp()); 262 !discard_helper_->initialized() &&
266 const bool first_buffer = 263 codec_context_->codec_id == AV_CODEC_ID_VORBIS &&
267 last_input_timestamp_ == kNoTimestamp() && 264 buffer->timestamp() < base::TimeDelta()) {
268 output_timestamp_helper_->base_timestamp() == kNoTimestamp(); 265 // Dropping frames for negative timestamps as outlined in section A.2
269 if (first_buffer && codec_context_->codec_id == AV_CODEC_ID_VORBIS && 266 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html
270 buffer->timestamp() < base::TimeDelta()) { 267 const int discard_frames =
wolenetz 2014/04/28 21:52:07 If a stream has both a codec_delay and a negative
DaleCurtis 2014/04/28 22:03:51 For now this code is sufficient since it just impl
271 // Dropping frames for negative timestamps as outlined in section A.2 268 discard_helper_->TimeDeltaToFrames(-buffer->timestamp());
wolenetz 2014/04/28 21:52:07 Previously, we rounded vs currently using ceil. Wh
DaleCurtis 2014/04/28 22:03:51 ceil(). It doesn't make sense to discard 1.5 fram
wolenetz 2014/04/28 22:37:56 Suppose we have frame rate of 48000 frames/sec and
DaleCurtis 2014/04/28 23:09:03 You can see how the values are converted in ffmpeg
272 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html 269 discard_helper_->Reset(discard_frames);
wolenetz 2014/04/28 21:52:07 Is the following piece of that section A.2 Vorbis
DaleCurtis 2014/04/28 22:03:51 This is not verified. I'd fix that in a separate
wolenetz 2014/04/29 00:22:44 Seems low-pri (we might reject streams that are sl
DaleCurtis 2014/04/29 00:28:59 I don't think it's necessary to worry about.
273 DCHECK_EQ(output_frames_to_drop_, 0);
274 output_frames_to_drop_ =
275 0.5 +
276 -buffer->timestamp().InSecondsF() * config_.samples_per_second();
277
278 // If we are dropping samples for Vorbis, the timeline always starts at 0.
279 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta());
280 } else {
281 if (first_buffer) {
282 output_timestamp_helper_->SetBaseTimestamp(buffer->timestamp());
283 } else if (buffer->timestamp() < last_input_timestamp_) {
284 const base::TimeDelta diff =
285 buffer->timestamp() - last_input_timestamp_;
286 DLOG(WARNING) << "Input timestamps are not monotonically increasing! "
287 << " ts " << buffer->timestamp().InMicroseconds() << " us"
288 << " diff " << diff.InMicroseconds() << " us";
289 }
290
291 last_input_timestamp_ = buffer->timestamp();
292 }
293 } 270 }
294 271
295 // Transition to kFlushCodec on the first end of stream buffer. 272 // Transition to kFlushCodec on the first end of stream buffer.
296 if (state_ == kNormal && buffer->end_of_stream()) { 273 if (state_ == kNormal && buffer->end_of_stream()) {
297 state_ = kFlushCodec; 274 state_ = kFlushCodec;
298 } 275 }
299 276
300 if (!FFmpegDecode(buffer)) { 277 if (!FFmpegDecode(buffer)) {
301 state_ = kError; 278 state_ = kError;
302 decode_cb.Run(kDecodeError, NULL); 279 decode_cb.Run(kDecodeError, NULL);
(...skipping 11 matching lines...) Expand all
314 decode_cb.Run(kNotEnoughData, NULL); 291 decode_cb.Run(kNotEnoughData, NULL);
315 return; 292 return;
316 } 293 }
317 294
318 decode_cb.Run(kOk, queued_audio_.front()); 295 decode_cb.Run(kOk, queued_audio_.front());
319 queued_audio_.pop_front(); 296 queued_audio_.pop_front();
320 } 297 }
321 298
322 bool FFmpegAudioDecoder::FFmpegDecode( 299 bool FFmpegAudioDecoder::FFmpegDecode(
323 const scoped_refptr<DecoderBuffer>& buffer) { 300 const scoped_refptr<DecoderBuffer>& buffer) {
324
325 DCHECK(queued_audio_.empty()); 301 DCHECK(queued_audio_.empty());
326 302
327 AVPacket packet; 303 AVPacket packet;
328 av_init_packet(&packet); 304 av_init_packet(&packet);
329 if (buffer->end_of_stream()) { 305 if (buffer->end_of_stream()) {
330 packet.data = NULL; 306 packet.data = NULL;
331 packet.size = 0; 307 packet.size = 0;
332 } else { 308 } else {
333 packet.data = const_cast<uint8*>(buffer->data()); 309 packet.data = const_cast<uint8*>(buffer->data());
334 packet.size = buffer->data_size(); 310 packet.size = buffer->data_size();
335 } 311 }
336 312
337 // Each audio packet may contain several frames, so we must call the decoder 313 // Each audio packet may contain several frames, so we must call the decoder
338 // until we've exhausted the packet. Regardless of the packet size we always 314 // until we've exhausted the packet. Regardless of the packet size we always
339 // want to hand it to the decoder at least once, otherwise we would end up 315 // want to hand it to the decoder at least once, otherwise we would end up
340 // skipping end of stream packets since they have a size of zero. 316 // skipping end of stream packets since they have a size of zero.
341 do { 317 do {
342 int frame_decoded = 0; 318 int frame_decoded = 0;
343 int result = avcodec_decode_audio4( 319 const int result = avcodec_decode_audio4(
344 codec_context_.get(), av_frame_.get(), &frame_decoded, &packet); 320 codec_context_.get(), av_frame_.get(), &frame_decoded, &packet);
345 321
346 if (result < 0) { 322 if (result < 0) {
347 DCHECK(!buffer->end_of_stream()) 323 DCHECK(!buffer->end_of_stream())
348 << "End of stream buffer produced an error! " 324 << "End of stream buffer produced an error! "
349 << "This is quite possibly a bug in the audio decoder not handling " 325 << "This is quite possibly a bug in the audio decoder not handling "
350 << "end of stream AVPackets correctly."; 326 << "end of stream AVPackets correctly.";
351 327
352 DLOG(WARNING) 328 DLOG(WARNING)
353 << "Failed to decode an audio frame with timestamp: " 329 << "Failed to decode an audio frame with timestamp: "
354 << buffer->timestamp().InMicroseconds() << " us, duration: " 330 << buffer->timestamp().InMicroseconds() << " us, duration: "
355 << buffer->duration().InMicroseconds() << " us, packet size: " 331 << buffer->duration().InMicroseconds() << " us, packet size: "
356 << buffer->data_size() << " bytes"; 332 << buffer->data_size() << " bytes";
357 333
358 break; 334 break;
359 } 335 }
360 336
361 // Update packet size and data pointer in case we need to call the decoder 337 // Update packet size and data pointer in case we need to call the decoder
362 // with the remaining bytes from this packet. 338 // with the remaining bytes from this packet.
363 packet.size -= result; 339 packet.size -= result;
364 packet.data += result; 340 packet.data += result;
365 341
366 scoped_refptr<AudioBuffer> output; 342 scoped_refptr<AudioBuffer> output;
367 int decoded_frames = 0; 343 const int channels = DetermineChannels(av_frame_.get());
368 int original_frames = 0;
369 int channels = DetermineChannels(av_frame_.get());
370 if (frame_decoded) { 344 if (frame_decoded) {
371 if (av_frame_->sample_rate != config_.samples_per_second() || 345 if (av_frame_->sample_rate != config_.samples_per_second() ||
372 channels != ChannelLayoutToChannelCount(config_.channel_layout()) || 346 channels != ChannelLayoutToChannelCount(config_.channel_layout()) ||
373 av_frame_->format != av_sample_format_) { 347 av_frame_->format != av_sample_format_) {
374 DLOG(ERROR) << "Unsupported midstream configuration change!" 348 DLOG(ERROR) << "Unsupported midstream configuration change!"
375 << " Sample Rate: " << av_frame_->sample_rate << " vs " 349 << " Sample Rate: " << av_frame_->sample_rate << " vs "
376 << config_.samples_per_second() 350 << config_.samples_per_second()
377 << ", Channels: " << channels << " vs " 351 << ", Channels: " << channels << " vs "
378 << ChannelLayoutToChannelCount(config_.channel_layout()) 352 << ChannelLayoutToChannelCount(config_.channel_layout())
379 << ", Sample Format: " << av_frame_->format << " vs " 353 << ", Sample Format: " << av_frame_->format << " vs "
380 << av_sample_format_; 354 << av_sample_format_;
381 355
382 // This is an unrecoverable error, so bail out. 356 // This is an unrecoverable error, so bail out.
383 queued_audio_.clear(); 357 queued_audio_.clear();
384 av_frame_unref(av_frame_.get()); 358 av_frame_unref(av_frame_.get());
385 return false; 359 return false;
386 } 360 }
387 361
388 // Get the AudioBuffer that the data was decoded into. Adjust the number 362 // Get the AudioBuffer that the data was decoded into. Adjust the number
389 // of frames, in case fewer than requested were actually decoded. 363 // of frames, in case fewer than requested were actually decoded.
390 output = reinterpret_cast<AudioBuffer*>( 364 output = reinterpret_cast<AudioBuffer*>(
391 av_buffer_get_opaque(av_frame_->buf[0])); 365 av_buffer_get_opaque(av_frame_->buf[0]));
392 366
393 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()), 367 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()),
394 output->channel_count()); 368 output->channel_count());
395 original_frames = av_frame_->nb_samples; 369 const int unread_frames = output->frame_count() - av_frame_->nb_samples;
396 int unread_frames = output->frame_count() - original_frames;
397 DCHECK_GE(unread_frames, 0); 370 DCHECK_GE(unread_frames, 0);
398 if (unread_frames > 0) 371 if (unread_frames > 0)
399 output->TrimEnd(unread_frames); 372 output->TrimEnd(unread_frames);
400 373
401 // If there are frames to drop, get rid of as many as we can.
402 if (output_frames_to_drop_ > 0) {
403 int drop = std::min(output->frame_count(), output_frames_to_drop_);
404 output->TrimStart(drop);
405 output_frames_to_drop_ -= drop;
406 }
407
408 decoded_frames = output->frame_count();
409 av_frame_unref(av_frame_.get()); 374 av_frame_unref(av_frame_.get());
410 } 375 }
411 376
412 // WARNING: |av_frame_| no longer has valid data at this point. 377 // WARNING: |av_frame_| no longer has valid data at this point.
413 378 const int decoded_frames = frame_decoded ? output->frame_count() : 0;
414 if (decoded_frames > 0) { 379 if (IsEndOfStream(result, decoded_frames, buffer)) {
415 // Set the timestamp/duration once all the extra frames have been
416 // discarded.
417 output->set_timestamp(output_timestamp_helper_->GetTimestamp());
418 output->set_duration(
419 output_timestamp_helper_->GetFrameDuration(decoded_frames));
420 output_timestamp_helper_->AddFrames(decoded_frames);
421 } else if (IsEndOfStream(result, original_frames, buffer)) {
422 DCHECK_EQ(packet.size, 0); 380 DCHECK_EQ(packet.size, 0);
423 output = AudioBuffer::CreateEOSBuffer(); 381 queued_audio_.push_back(AudioBuffer::CreateEOSBuffer());
424 } else { 382 } else if (discard_helper_->ProcessBuffers(buffer, output)) {
wolenetz 2014/04/28 21:52:07 Should any encoded buffer originating from ffmpeg_
DaleCurtis 2014/04/28 22:03:51 It's possible for buffers originating from ffmpeg
425 // In case all the frames in the buffer were dropped. 383 queued_audio_.push_back(output);
426 output = NULL;
427 } 384 }
428
429 if (output.get())
430 queued_audio_.push_back(output);
431
432 } while (packet.size > 0); 385 } while (packet.size > 0);
433 386
434 return true; 387 return true;
435 } 388 }
436 389
437 void FFmpegAudioDecoder::ReleaseFFmpegResources() { 390 void FFmpegAudioDecoder::ReleaseFFmpegResources() {
438 codec_context_.reset(); 391 codec_context_.reset();
439 av_frame_.reset(); 392 av_frame_.reset();
440 } 393 }
441 394
(...skipping 27 matching lines...) Expand all
469 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) { 422 if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
470 DLOG(ERROR) << "Could not initialize audio decoder: " 423 DLOG(ERROR) << "Could not initialize audio decoder: "
471 << codec_context_->codec_id; 424 << codec_context_->codec_id;
472 ReleaseFFmpegResources(); 425 ReleaseFFmpegResources();
473 state_ = kUninitialized; 426 state_ = kUninitialized;
474 return false; 427 return false;
475 } 428 }
476 429
477 // Success! 430 // Success!
478 av_frame_.reset(av_frame_alloc()); 431 av_frame_.reset(av_frame_alloc());
479 output_timestamp_helper_.reset( 432 discard_helper_.reset(new AudioDiscardHelper(config_.samples_per_second()));
480 new AudioTimestampHelper(config_.samples_per_second()));
481 ResetTimestampState();
482
483 av_sample_format_ = codec_context_->sample_fmt; 433 av_sample_format_ = codec_context_->sample_fmt;
484 434
485 if (codec_context_->channels != 435 if (codec_context_->channels !=
486 ChannelLayoutToChannelCount(config_.channel_layout())) { 436 ChannelLayoutToChannelCount(config_.channel_layout())) {
487 DLOG(ERROR) << "Audio configuration specified " 437 DLOG(ERROR) << "Audio configuration specified "
488 << ChannelLayoutToChannelCount(config_.channel_layout()) 438 << ChannelLayoutToChannelCount(config_.channel_layout())
489 << " channels, but FFmpeg thinks the file contains " 439 << " channels, but FFmpeg thinks the file contains "
490 << codec_context_->channels << " channels"; 440 << codec_context_->channels << " channels";
491 ReleaseFFmpegResources(); 441 ReleaseFFmpegResources();
492 state_ = kUninitialized; 442 state_ = kUninitialized;
493 return false; 443 return false;
494 } 444 }
495 445
496 output_frames_to_drop_ = config_.codec_delay(); 446 ResetTimestampState();
497 return true; 447 return true;
498 } 448 }
499 449
500 void FFmpegAudioDecoder::ResetTimestampState() { 450 void FFmpegAudioDecoder::ResetTimestampState() {
501 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); 451 discard_helper_->Reset(config_.codec_delay());
502 last_input_timestamp_ = kNoTimestamp();
503 output_frames_to_drop_ = 0;
504 } 452 }
505 453
506 } // namespace media 454 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698