blob: cf295b6f4467f5c0b93da91f4103636f90f6d14b [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/filters/audio_file_reader.h"
#include "base/logging.h"
#include "base/time.h"
#include "media/base/audio_bus.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_glue.h"
namespace media {
AudioFileReader::AudioFileReader(FFmpegURLProtocol* protocol)
: codec_context_(NULL),
stream_index_(0),
protocol_(protocol) {
}
AudioFileReader::~AudioFileReader() {
Close();
}
int AudioFileReader::channels() const {
return codec_context_->channels;
}
int AudioFileReader::sample_rate() const {
return codec_context_->sample_rate;
}
base::TimeDelta AudioFileReader::duration() const {
const AVRational av_time_base = {1, AV_TIME_BASE};
// Add one microsecond to avoid rounding-down errors which can occur when
// |duration| has been calculated from an exact number of sample-frames.
// One microsecond is much less than the time of a single sample-frame
// at any real-world sample-rate.
return ConvertFromTimeBase(
av_time_base, glue_->format_context()->duration + 1);
}
int64 AudioFileReader::number_of_frames() const {
return static_cast<int64>(duration().InSecondsF() * sample_rate());
}
bool AudioFileReader::Open() {
glue_.reset(new FFmpegGlue(protocol_));
AVFormatContext* format_context = glue_->format_context();
// Open FFmpeg AVFormatContext.
if (!glue_->OpenContext()) {
DLOG(WARNING) << "AudioFileReader::Open() : error in avformat_open_input()";
return false;
}
// Get the codec context.
codec_context_ = NULL;
for (size_t i = 0; i < format_context->nb_streams; ++i) {
AVCodecContext* c = format_context->streams[i]->codec;
if (c->codec_type == AVMEDIA_TYPE_AUDIO) {
codec_context_ = c;
stream_index_ = i;
break;
}
}
// Get the codec.
if (!codec_context_)
return false;
int result = avformat_find_stream_info(format_context, NULL);
if (result < 0) {
DLOG(WARNING)
<< "AudioFileReader::Open() : error in avformat_find_stream_info()";
return false;
}
AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
if (codec) {
if ((result = avcodec_open2(codec_context_, codec, NULL)) < 0) {
DLOG(WARNING) << "AudioFileReader::Open() : could not open codec -"
<< " result: " << result;
return false;
}
} else {
DLOG(WARNING) << "AudioFileReader::Open() : could not find codec -"
<< " result: " << result;
return false;
}
return true;
}
void AudioFileReader::Close() {
if (codec_context_) {
avcodec_close(codec_context_);
codec_context_ = NULL;
}
}
int AudioFileReader::Read(AudioBus* audio_bus) {
DCHECK(glue_.get() && codec_context_) <<
"AudioFileReader::Read() : reader is not opened!";
DCHECK_EQ(audio_bus->channels(), channels());
if (audio_bus->channels() != channels())
return 0;
size_t bytes_per_sample = av_get_bytes_per_sample(codec_context_->sample_fmt);
// Holds decoded audio.
scoped_ptr_malloc<AVFrame, ScopedPtrAVFree> av_frame(avcodec_alloc_frame());
// Read until we hit EOF or we've read the requested number of frames.
AVPacket packet;
int current_frame = 0;
bool continue_decoding = true;
while (current_frame < audio_bus->frames() && continue_decoding &&
av_read_frame(glue_->format_context(), &packet) >= 0 &&
av_dup_packet(&packet) >= 0) {
// Skip packets from other streams.
if (packet.stream_index != stream_index_) {
av_free_packet(&packet);
continue;
}
// Make a shallow copy of packet so we can slide packet.data as frames are
// decoded from the packet; otherwise av_free_packet() will corrupt memory.
AVPacket packet_temp = packet;
do {
avcodec_get_frame_defaults(av_frame.get());
int frame_decoded = 0;
int result = avcodec_decode_audio4(
codec_context_, av_frame.get(), &frame_decoded, &packet_temp);
if (result < 0) {
DLOG(WARNING)
<< "AudioFileReader::Read() : error in avcodec_decode_audio4() -"
<< result;
continue_decoding = false;
break;
}
// Update packet size and data pointer in case we need to call the decoder
// with the remaining bytes from this packet.
packet_temp.size -= result;
packet_temp.data += result;
if (!frame_decoded)
continue;
// Determine the number of sample-frames we just decoded. Check overflow.
int frames_read = av_frame->nb_samples;
if (frames_read < 0) {
continue_decoding = false;
break;
}
// Truncate, if necessary, if the destination isn't big enough.
if (current_frame + frames_read > audio_bus->frames())
frames_read = audio_bus->frames() - current_frame;
// Deinterleave each channel and convert to 32bit floating-point
// with nominal range -1.0 -> +1.0.
audio_bus->FromInterleavedPartial(
av_frame->data[0], current_frame, frames_read, bytes_per_sample);
current_frame += frames_read;
} while (packet_temp.size > 0);
av_free_packet(&packet);
}
// Zero any remaining frames.
audio_bus->ZeroFramesPartial(
current_frame, audio_bus->frames() - current_frame);
// Returns the actual number of sample-frames decoded.
// Ideally this represents the "true" exact length of the file.
return current_frame;
}
} // namespace media