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

Unified Diff: media/filters/ffmpeg_audio_decoder.cc

Issue 141243003: Add AudioBufferStream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@decoderstream_rebased
Patch Set: fix a couple comments Created 6 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: media/filters/ffmpeg_audio_decoder.cc
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
index 3ea70ff02407ac1df9d9b9b9ef5a39ae0cf85c07..58e1680940be63e89536cffe8ae36737e4106bb7 100644
--- a/media/filters/ffmpeg_audio_decoder.cc
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -23,12 +23,6 @@
namespace media {
-// Helper structure for managing multiple decoded audio frames per packet.
-struct QueuedAudioBuffer {
- AudioDecoder::Status status;
- scoped_refptr<AudioBuffer> buffer;
-};
-
// Returns true if the decode result was end of stream.
static inline bool IsEndOfStream(int result,
int decoded_size,
@@ -73,63 +67,75 @@ FFmpegAudioDecoder::FFmpegAudioDecoder(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
: task_runner_(task_runner),
weak_factory_(this),
- demuxer_stream_(NULL),
+ state_(kUninitialized),
bytes_per_channel_(0),
channel_layout_(CHANNEL_LAYOUT_NONE),
channels_(0),
samples_per_second_(0),
av_sample_format_(0),
last_input_timestamp_(kNoTimestamp()),
- output_frames_to_drop_(0) {
+ output_frames_to_drop_(0) {}
+
+FFmpegAudioDecoder::~FFmpegAudioDecoder() {
+ DCHECK_EQ(state_, kUninitialized);
+ DCHECK(!codec_context_);
+ DCHECK(!av_frame_);
}
-void FFmpegAudioDecoder::Initialize(
- DemuxerStream* stream,
- const PipelineStatusCB& status_cb,
- const StatisticsCB& statistics_cb) {
+void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config,
+ const PipelineStatusCB& status_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
- PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb);
+ DCHECK(!config.is_encrypted());
FFmpegGlue::InitializeFFmpeg();
-
- if (demuxer_stream_) {
- // TODO(scherkus): initialization currently happens more than once in
- // PipelineIntegrationTest.BasicPlayback.
- LOG(ERROR) << "Initialize has already been called.";
- CHECK(false);
- }
-
weak_this_ = weak_factory_.GetWeakPtr();
- demuxer_stream_ = stream;
- if (!ConfigureDecoder()) {
- status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
+ config_ = config;
+ PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb);
+
+ if (!config.IsValidConfig() || !ConfigureDecoder()) {
+ initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
return;
}
- statistics_cb_ = statistics_cb;
+ // Success!
+ state_ = kNormal;
initialize_cb.Run(PIPELINE_OK);
}
-void FFmpegAudioDecoder::Read(const ReadCB& read_cb) {
+void FFmpegAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const DecodeCB& decode_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(!read_cb.is_null());
- CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
- DCHECK(reset_cb_.is_null());
- DCHECK(stop_cb_.is_null());
+ DCHECK(!decode_cb.is_null());
+ CHECK_NE(state_, kUninitialized);
+ DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb);
- read_cb_ = BindToCurrentLoop(read_cb);
+ if (state_ == kError) {
+ decode_cb_bound.Run(kDecodeError, NULL);
+ return;
+ }
- // If we don't have any queued audio from the last packet we decoded, ask for
- // more data from the demuxer to satisfy this read.
- if (queued_audio_.empty()) {
- ReadFromDemuxerStream();
+ // Return empty frames if decoding has finished.
+ if (state_ == kDecodeFinished) {
+ decode_cb_bound.Run(kOk, AudioBuffer::CreateEOSBuffer());
return;
}
- base::ResetAndReturn(&read_cb_).Run(
- queued_audio_.front().status, queued_audio_.front().buffer);
+ if (!buffer) {
+ decode_cb_bound.Run(kAborted, NULL);
+ return;
+ }
+
+ DecodeBuffer(buffer, decode_cb_bound);
+}
+
+scoped_refptr<AudioBuffer> FFmpegAudioDecoder::GetDecodeOutput() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (queued_audio_.empty())
+ return NULL;
+ scoped_refptr<AudioBuffer> out = queued_audio_.front();
queued_audio_.pop_front();
+ return out;
}
int FFmpegAudioDecoder::bits_per_channel() {
@@ -149,39 +155,34 @@ int FFmpegAudioDecoder::samples_per_second() {
void FFmpegAudioDecoder::Reset(const base::Closure& closure) {
DCHECK(task_runner_->BelongsToCurrentThread());
- reset_cb_ = BindToCurrentLoop(closure);
-
- // A demuxer read is pending, we'll wait until it finishes.
- if (!read_cb_.is_null())
- return;
- DoReset();
+ avcodec_flush_buffers(codec_context_.get());
+ state_ = kNormal;
+ ResetTimestampState();
+ task_runner_->PostTask(FROM_HERE, closure);
}
void FFmpegAudioDecoder::Stop(const base::Closure& closure) {
DCHECK(task_runner_->BelongsToCurrentThread());
- stop_cb_ = BindToCurrentLoop(closure);
-
- // A demuxer read is pending, we'll wait until it finishes.
- if (!read_cb_.is_null())
- return;
+ base::ScopedClosureRunner runner(BindToCurrentLoop(closure));
- if (!reset_cb_.is_null()) {
- DoReset();
+ if (state_ == kUninitialized)
return;
- }
- DoStop();
+ ReleaseFFmpegResources();
+ ResetTimestampState();
+ state_ = kUninitialized;
}
-FFmpegAudioDecoder::~FFmpegAudioDecoder() {}
-
+// Callback called from within FFmpeg to allocate a buffer based on
DaleCurtis 2014/03/04 01:01:33 We put method comments in the .h file generally.
rileya (GONE FROM CHROMIUM) 2014/03/04 02:03:01 Oh wow, I saw that the original version did this a
+// the dimensions of |codec_context|. See AVCodecContext.get_buffer2
+// documentation inside FFmpeg.
int FFmpegAudioDecoder::GetAudioBuffer(AVCodecContext* codec,
AVFrame* frame,
int flags) {
// Since this routine is called by FFmpeg when a buffer is required for audio
// data, use the values supplied by FFmpeg (ignoring the current settings).
- // RunDecodeLoop() gets to determine if the buffer is useable or not.
+ // FFmpegDecode() gets to determine if the buffer is useable or not.
AVSampleFormat format = static_cast<AVSampleFormat>(frame->format);
SampleFormat sample_format = AVSampleFormatToSampleFormat(format);
int channels = DetermineChannels(frame);
@@ -241,241 +242,117 @@ int FFmpegAudioDecoder::GetAudioBuffer(AVCodecContext* codec,
return 0;
}
-void FFmpegAudioDecoder::DoStop() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(!stop_cb_.is_null());
- DCHECK(read_cb_.is_null());
- DCHECK(reset_cb_.is_null());
-
- ResetTimestampState();
- queued_audio_.clear();
- ReleaseFFmpegResources();
- base::ResetAndReturn(&stop_cb_).Run();
-}
-
-void FFmpegAudioDecoder::DoReset() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(!reset_cb_.is_null());
- DCHECK(read_cb_.is_null());
-
- avcodec_flush_buffers(codec_context_.get());
- ResetTimestampState();
- queued_audio_.clear();
- base::ResetAndReturn(&reset_cb_).Run();
-
- if (!stop_cb_.is_null())
- DoStop();
-}
-
-void FFmpegAudioDecoder::ReadFromDemuxerStream() {
- DCHECK(!read_cb_.is_null());
- demuxer_stream_->Read(base::Bind(
- &FFmpegAudioDecoder::BufferReady, weak_this_));
-}
-
-void FFmpegAudioDecoder::BufferReady(
- DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& input) {
+void FFmpegAudioDecoder::DecodeBuffer(
+ const scoped_refptr<DecoderBuffer>& buffer,
+ const DecodeCB& decode_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(!read_cb_.is_null());
- DCHECK(queued_audio_.empty());
- DCHECK_EQ(status != DemuxerStream::kOk, !input.get()) << status;
-
- // Pending Reset: ignore the buffer we just got, send kAborted to |read_cb_|
- // and carry out the Reset().
- // If there happens to also be a pending Stop(), that will be handled at
- // the end of DoReset().
- if (!reset_cb_.is_null()) {
- base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
- DoReset();
- return;
- }
-
- // Pending Stop: ignore the buffer we just got, send kAborted to |read_cb_|
- // and carry out the Stop().
- if (!stop_cb_.is_null()) {
- base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
- DoStop();
- return;
- }
-
- if (status == DemuxerStream::kAborted) {
- DCHECK(!input.get());
- base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
- return;
- }
-
- if (status == DemuxerStream::kConfigChanged) {
- DCHECK(!input.get());
-
- // Send a "end of stream" buffer to the decode loop
- // to output any remaining data still in the decoder.
- RunDecodeLoop(DecoderBuffer::CreateEOSBuffer(), true);
-
- DVLOG(1) << "Config changed.";
-
- if (!ConfigureDecoder()) {
- base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
- return;
- }
-
- ResetTimestampState();
-
- if (queued_audio_.empty()) {
- ReadFromDemuxerStream();
- return;
- }
-
- base::ResetAndReturn(&read_cb_).Run(
- queued_audio_.front().status, queued_audio_.front().buffer);
- queued_audio_.pop_front();
- return;
- }
-
- DCHECK_EQ(status, DemuxerStream::kOk);
- DCHECK(input.get());
+ DCHECK_NE(state_, kUninitialized);
+ DCHECK_NE(state_, kDecodeFinished);
+ DCHECK_NE(state_, kError);
+
+ DCHECK(buffer);
+
+ // During decode, because reads are issued asynchronously, it is possible to
DaleCurtis 2014/03/04 01:01:33 Instead of having this similar / same comment in b
rileya (GONE FROM CHROMIUM) 2014/03/04 02:03:01 The problem is that this logic is only shared by t
+ // receive multiple end of stream buffers since each decode is acked. When the
+ // first end of stream buffer is read, FFmpeg may still have frames queued
+ // up in the decoder so we need to go through the decode loop until it stops
+ // giving sensible data. After that, the decoder should output empty
+ // frames. There are three states the decoder can be in:
+ //
+ // kNormal: This is the starting state. Buffers are decoded. Decode errors
+ // are discarded.
+ // kFlushCodec: There isn't any more input data. Call avcodec_decode_audio4
+ // until no more data is returned to flush out remaining
+ // frames. The input buffer is ignored at this point.
+ // kDecodeFinished: All calls return empty frames.
+ // kError: Unexpected error happened.
+ //
+ // These are the possible state transitions.
+ //
+ // kNormal -> kFlushCodec:
+ // When buffer->end_of_stream() is first true.
+ // kNormal -> kError:
+ // A decoding error occurs and decoding needs to stop.
+ // kFlushCodec -> kDecodeFinished:
+ // When avcodec_decode_audio4() returns 0 data.
+ // kFlushCodec -> kError:
+ // When avcodec_decode_audio4() errors out.
+ // (any state) -> kNormal:
+ // Any time Reset() is called.
// Make sure we are notified if http://crbug.com/49709 returns. Issue also
// occurs with some damaged files.
- if (!input->end_of_stream() && input->timestamp() == kNoTimestamp() &&
+ if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp() &&
output_timestamp_helper_->base_timestamp() == kNoTimestamp()) {
DVLOG(1) << "Received a buffer without timestamps!";
- base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
+ decode_cb.Run(kDecodeError, NULL);
return;
}
- if (!input->end_of_stream()) {
+ if (!buffer->end_of_stream()) {
if (last_input_timestamp_ == kNoTimestamp() &&
codec_context_->codec_id == AV_CODEC_ID_VORBIS &&
- input->timestamp() < base::TimeDelta()) {
+ buffer->timestamp() < base::TimeDelta()) {
// Dropping frames for negative timestamps as outlined in section A.2
// in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html
output_frames_to_drop_ = floor(
- 0.5 + -input->timestamp().InSecondsF() * samples_per_second_);
+ 0.5 + -buffer->timestamp().InSecondsF() * samples_per_second_);
} else {
if (last_input_timestamp_ != kNoTimestamp() &&
- input->timestamp() < last_input_timestamp_) {
- const base::TimeDelta diff = input->timestamp() - last_input_timestamp_;
+ buffer->timestamp() < last_input_timestamp_) {
+ const base::TimeDelta diff =
+ buffer->timestamp() - last_input_timestamp_;
DLOG(WARNING)
<< "Input timestamps are not monotonically increasing! "
- << " ts " << input->timestamp().InMicroseconds() << " us"
+ << " ts " << buffer->timestamp().InMicroseconds() << " us"
<< " diff " << diff.InMicroseconds() << " us";
}
- last_input_timestamp_ = input->timestamp();
+ last_input_timestamp_ = buffer->timestamp();
}
}
- RunDecodeLoop(input, false);
-
- // We exhausted the provided packet, but it wasn't enough for a frame. Ask
- // for more data in order to fulfill this read.
- if (queued_audio_.empty()) {
- ReadFromDemuxerStream();
- return;
- }
-
- // Execute callback to return the first frame we decoded.
- base::ResetAndReturn(&read_cb_).Run(
- queued_audio_.front().status, queued_audio_.front().buffer);
- queued_audio_.pop_front();
-}
-
-bool FFmpegAudioDecoder::ConfigureDecoder() {
- const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config();
-
- if (!config.IsValidConfig()) {
- DLOG(ERROR) << "Invalid audio stream -"
- << " codec: " << config.codec()
- << " channel layout: " << config.channel_layout()
- << " bits per channel: " << config.bits_per_channel()
- << " samples per second: " << config.samples_per_second();
- return false;
- }
-
- if (config.is_encrypted()) {
- DLOG(ERROR) << "Encrypted audio stream not supported";
- return false;
+ // Transition to kFlushCodec on the first end of stream buffer.
+ if (state_ == kNormal && buffer->end_of_stream()) {
+ state_ = kFlushCodec;
}
- if (codec_context_.get() &&
- (bytes_per_channel_ != config.bytes_per_channel() ||
- channel_layout_ != config.channel_layout() ||
- samples_per_second_ != config.samples_per_second())) {
- DVLOG(1) << "Unsupported config change :";
- DVLOG(1) << "\tbytes_per_channel : " << bytes_per_channel_
- << " -> " << config.bytes_per_channel();
- DVLOG(1) << "\tchannel_layout : " << channel_layout_
- << " -> " << config.channel_layout();
- DVLOG(1) << "\tsample_rate : " << samples_per_second_
- << " -> " << config.samples_per_second();
- return false;
- }
-
- // Release existing decoder resources if necessary.
- ReleaseFFmpegResources();
-
- // Initialize AVCodecContext structure.
- codec_context_.reset(avcodec_alloc_context3(NULL));
- AudioDecoderConfigToAVCodecContext(config, codec_context_.get());
-
- codec_context_->opaque = this;
- codec_context_->get_buffer2 = GetAudioBufferImpl;
- codec_context_->refcounted_frames = 1;
-
- AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
- if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
- DLOG(ERROR) << "Could not initialize audio decoder: "
- << codec_context_->codec_id;
- return false;
+ scoped_refptr<AudioBuffer> audio_buffer;
DaleCurtis 2014/03/04 01:01:33 Unused? You could just set result/audio_buffer= ou
rileya (GONE FROM CHROMIUM) 2014/03/04 02:03:01 Whoops, yup. I ended up just removing it, moving t
+ if (!FFmpegDecode(buffer)) {
+ state_ = kError;
+ decode_cb.Run(kDecodeError, NULL);
+ return;
}
- // Success!
- av_frame_.reset(av_frame_alloc());
- channel_layout_ = config.channel_layout();
- samples_per_second_ = config.samples_per_second();
- output_timestamp_helper_.reset(
- new AudioTimestampHelper(config.samples_per_second()));
+ if (queued_audio_.empty()) {
+ if (state_ == kFlushCodec) {
+ DCHECK(buffer->end_of_stream());
+ state_ = kDecodeFinished;
+ decode_cb.Run(kOk, AudioBuffer::CreateEOSBuffer());
+ return;
+ }
- // Store initial values to guard against midstream configuration changes.
- channels_ = codec_context_->channels;
- if (channels_ != ChannelLayoutToChannelCount(channel_layout_)) {
- DLOG(ERROR) << "Audio configuration specified "
- << ChannelLayoutToChannelCount(channel_layout_)
- << " channels, but FFmpeg thinks the file contains "
- << channels_ << " channels";
- return false;
+ decode_cb.Run(kNotEnoughData, NULL);
+ return;
}
- av_sample_format_ = codec_context_->sample_fmt;
- sample_format_ = AVSampleFormatToSampleFormat(
- static_cast<AVSampleFormat>(av_sample_format_));
- bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format_);
- return true;
+ decode_cb.Run(kOk, queued_audio_.front());
+ queued_audio_.pop_front();
}
-void FFmpegAudioDecoder::ReleaseFFmpegResources() {
- codec_context_.reset();
- av_frame_.reset();
-}
+bool FFmpegAudioDecoder::FFmpegDecode(
+ const scoped_refptr<DecoderBuffer>& buffer) {
-void FFmpegAudioDecoder::ResetTimestampState() {
- output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp());
- last_input_timestamp_ = kNoTimestamp();
- output_frames_to_drop_ = 0;
-}
+ DCHECK(queued_audio_.empty());
-void FFmpegAudioDecoder::RunDecodeLoop(
- const scoped_refptr<DecoderBuffer>& input,
- bool skip_eos_append) {
AVPacket packet;
av_init_packet(&packet);
- if (input->end_of_stream()) {
+ if (buffer->end_of_stream()) {
packet.data = NULL;
packet.size = 0;
} else {
- packet.data = const_cast<uint8*>(input->data());
- packet.size = input->data_size();
+ packet.data = const_cast<uint8*>(buffer->data());
+ packet.size = buffer->data_size();
}
// Each audio packet may contain several frames, so we must call the decoder
@@ -488,16 +365,16 @@ void FFmpegAudioDecoder::RunDecodeLoop(
codec_context_.get(), av_frame_.get(), &frame_decoded, &packet);
if (result < 0) {
- DCHECK(!input->end_of_stream())
+ DCHECK(!buffer->end_of_stream())
<< "End of stream buffer produced an error! "
<< "This is quite possibly a bug in the audio decoder not handling "
<< "end of stream AVPackets correctly.";
DLOG(WARNING)
<< "Failed to decode an audio frame with timestamp: "
- << input->timestamp().InMicroseconds() << " us, duration: "
- << input->duration().InMicroseconds() << " us, packet size: "
- << input->data_size() << " bytes";
+ << buffer->timestamp().InMicroseconds() << " us, duration: "
+ << buffer->duration().InMicroseconds() << " us, packet size: "
+ << buffer->data_size() << " bytes";
break;
}
@@ -508,15 +385,15 @@ void FFmpegAudioDecoder::RunDecodeLoop(
packet.data += result;
if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() &&
- !input->end_of_stream()) {
- DCHECK(input->timestamp() != kNoTimestamp());
+ !buffer->end_of_stream()) {
+ DCHECK(buffer->timestamp() != kNoTimestamp());
if (output_frames_to_drop_ > 0) {
// Currently Vorbis is the only codec that causes us to drop samples.
// If we have to drop samples it always means the timeline starts at 0.
DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS);
output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta());
} else {
- output_timestamp_helper_->SetBaseTimestamp(input->timestamp());
+ output_timestamp_helper_->SetBaseTimestamp(buffer->timestamp());
}
}
@@ -525,7 +402,10 @@ void FFmpegAudioDecoder::RunDecodeLoop(
int original_frames = 0;
int channels = DetermineChannels(av_frame_.get());
if (frame_decoded) {
- if (av_frame_->sample_rate != samples_per_second_ ||
+
+ // TODO(rileya) Remove this check once we properly support midstream audio
+ // config changes.
+ if (av_frame_->sample_rate != config_.samples_per_second() ||
channels != channels_ ||
av_frame_->format != av_sample_format_) {
DLOG(ERROR) << "Unsupported midstream configuration change!"
@@ -537,16 +417,16 @@ void FFmpegAudioDecoder::RunDecodeLoop(
<< av_sample_format_;
// This is an unrecoverable error, so bail out.
- QueuedAudioBuffer queue_entry = { kDecodeError, NULL };
- queued_audio_.push_back(queue_entry);
+ queued_audio_.clear();
rileya (GONE FROM CHROMIUM) 2014/03/04 00:14:27 Previously we could queue up buffers which were no
av_frame_unref(av_frame_.get());
- break;
+ return false;
}
// Get the AudioBuffer that the data was decoded into. Adjust the number
// of frames, in case fewer than requested were actually decoded.
output = reinterpret_cast<AudioBuffer*>(
av_buffer_get_opaque(av_frame_->buf[0]));
+
DCHECK_EQ(channels_, output->channel_count());
original_frames = av_frame_->nb_samples;
int unread_frames = output->frame_count() - original_frames;
@@ -574,8 +454,7 @@ void FFmpegAudioDecoder::RunDecodeLoop(
output->set_duration(
output_timestamp_helper_->GetFrameDuration(decoded_frames));
output_timestamp_helper_->AddFrames(decoded_frames);
- } else if (IsEndOfStream(result, original_frames, input) &&
- !skip_eos_append) {
+ } else if (IsEndOfStream(result, original_frames, buffer)) {
DCHECK_EQ(packet.size, 0);
output = AudioBuffer::CreateEOSBuffer();
} else {
@@ -583,18 +462,98 @@ void FFmpegAudioDecoder::RunDecodeLoop(
output = NULL;
}
- if (output.get()) {
- QueuedAudioBuffer queue_entry = { kOk, output };
- queued_audio_.push_back(queue_entry);
- }
+ if (output.get())
+ queued_audio_.push_back(output);
- // Decoding finished successfully, update statistics.
- if (result > 0) {
- PipelineStatistics statistics;
- statistics.audio_bytes_decoded = result;
- statistics_cb_.Run(statistics);
- }
} while (packet.size > 0);
+
+ return true;
+}
+
+void FFmpegAudioDecoder::ReleaseFFmpegResources() {
+ codec_context_.reset();
+ av_frame_.reset();
+}
+
+bool FFmpegAudioDecoder::ConfigureDecoder() {
+ if (!config_.IsValidConfig()) {
+ DLOG(ERROR) << "Invalid audio stream -"
+ << " codec: " << config_.codec()
+ << " channel layout: " << config_.channel_layout()
+ << " bits per channel: " << config_.bits_per_channel()
+ << " samples per second: " << config_.samples_per_second();
+ return false;
+ }
+
+ if (config_.is_encrypted()) {
+ DLOG(ERROR) << "Encrypted audio stream not supported";
+ return false;
+ }
+
+ // TODO(rileya) Remove this check once we properly support midstream audio
DaleCurtis 2014/03/04 01:01:33 Note: Some of these were added to avoid ffmpeg sec
+ // config changes.
+ if (codec_context_.get() &&
+ (bytes_per_channel_ != config_.bytes_per_channel() ||
+ channel_layout_ != config_.channel_layout() ||
+ samples_per_second_ != config_.samples_per_second())) {
+ DVLOG(1) << "Unsupported config change :";
+ DVLOG(1) << "\tbytes_per_channel : " << bytes_per_channel_
+ << " -> " << config_.bytes_per_channel();
+ DVLOG(1) << "\tchannel_layout : " << channel_layout_
+ << " -> " << config_.channel_layout();
+ DVLOG(1) << "\tsample_rate : " << samples_per_second_
+ << " -> " << config_.samples_per_second();
+ return false;
+ }
+
+ // Release existing decoder resources if necessary.
+ ReleaseFFmpegResources();
+
+ // Initialize AVCodecContext structure.
+ codec_context_.reset(avcodec_alloc_context3(NULL));
+ AudioDecoderConfigToAVCodecContext(config_, codec_context_.get());
+
+ codec_context_->opaque = this;
+ codec_context_->get_buffer2 = GetAudioBufferImpl;
+ codec_context_->refcounted_frames = 1;
+
+ AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
+ if (!codec || avcodec_open2(codec_context_.get(), codec, NULL) < 0) {
+ DLOG(ERROR) << "Could not initialize audio decoder: "
+ << codec_context_->codec_id;
+ ReleaseFFmpegResources();
+ state_ = kUninitialized;
+ return false;
+ }
+
+ // Success!
+ av_frame_.reset(av_frame_alloc());
+ channel_layout_ = config_.channel_layout();
+ samples_per_second_ = config_.samples_per_second();
+ output_timestamp_helper_.reset(
+ new AudioTimestampHelper(config_.samples_per_second()));
+
+ // Store initial values to guard against midstream configuration changes.
+ channels_ = codec_context_->channels;
+ if (channels_ != ChannelLayoutToChannelCount(channel_layout_)) {
+ DLOG(ERROR) << "Audio configuration specified "
+ << ChannelLayoutToChannelCount(channel_layout_)
+ << " channels, but FFmpeg thinks the file contains "
+ << channels_ << " channels";
+ return false;
+ }
+ av_sample_format_ = codec_context_->sample_fmt;
+ sample_format_ = AVSampleFormatToSampleFormat(
+ static_cast<AVSampleFormat>(av_sample_format_));
+ bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format_);
+
+ return true;
+}
+
+void FFmpegAudioDecoder::ResetTimestampState() {
+ output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp());
+ last_input_timestamp_ = kNoTimestamp();
+ output_frames_to_drop_ = 0;
}
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698