blob: 974304c1fa9f2c3a59af498dea605a6f5f9b9db9 [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 <string>
#include "base/basictypes.h"
#include "base/string_util.h"
#include "base/time.h"
#include "media/audio/audio_util.h"
#include "media/base/filters.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_glue.h"
namespace media {
AudioFileReader::AudioFileReader(FFmpegURLProtocol* protocol)
: protocol_(protocol),
format_context_(NULL),
codec_context_(NULL),
codec_(NULL) {
}
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};
return ConvertFromTimeBase(av_time_base, format_context_->duration);
}
int64 AudioFileReader::number_of_frames() const {
return static_cast<int64>(duration().InSecondsF() * sample_rate());
}
bool AudioFileReader::Open() {
// Add our data reader to the protocol list and get our unique key.
std::string key = FFmpegGlue::GetInstance()->AddProtocol(protocol_);
// Open FFmpeg AVFormatContext.
DCHECK(!format_context_);
AVFormatContext* context = NULL;
int result = avformat_open_input(&context, key.c_str(), NULL, NULL);
// Remove our data reader from protocol list since avformat_open_input() setup
// the AVFormatContext with the data reader.
FFmpegGlue::GetInstance()->RemoveProtocol(protocol_);
if (result) {
DLOG(WARNING)
<< "AudioFileReader::Open() : error in avformat_open_input() -"
<< " result: " << result;
return false;
}
DCHECK(context);
format_context_ = context;
// 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;
break;
}
}
// Get the codec.
if (!codec_context_)
return false;
avformat_find_stream_info(format_context_, NULL);
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_ && codec_)
avcodec_close(codec_context_);
codec_context_ = NULL;
codec_ = NULL;
if (format_context_) {
avformat_close_input(&format_context_);
format_context_ = NULL;
}
}
bool AudioFileReader::Read(const std::vector<float*>& audio_data,
size_t number_of_frames) {
size_t channels = this->channels();
DCHECK_EQ(audio_data.size(), channels);
if (audio_data.size() != channels)
return false;
DCHECK(format_context_ && codec_context_ && codec_);
if (!format_context_ || !codec_context_ || !codec_) {
DLOG(WARNING) << "AudioFileReader::Read() : reader is not opened!";
return false;
}
scoped_ptr_malloc<int16, ScopedPtrAVFree> output_buffer(
static_cast<int16*>(av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)));
// Read until we hit EOF or we've read the requested number of frames.
AVPacket avpkt;
int result = 0;
size_t current_frame = 0;
while (current_frame < number_of_frames &&
(result = av_read_frame(format_context_, &avpkt)) >= 0) {
int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
result = avcodec_decode_audio3(codec_context_,
output_buffer.get(),
&out_size,
&avpkt);
av_free_packet(&avpkt);
if (result < 0) {
DLOG(WARNING)
<< "AudioFileReader::Read() : error in avcodec_decode_audio3() -"
<< result;
// Fail if nothing has been decoded, otherwise return partial data.
return current_frame > 0;
}
// Determine the number of sample-frames we just decoded.
size_t bytes_per_sample =
av_get_bytes_per_sample(codec_context_->sample_fmt);
size_t frames_read = out_size / (channels * bytes_per_sample);
// Truncate, if necessary, if the destination isn't big enough.
if (current_frame + frames_read > number_of_frames)
frames_read = number_of_frames - current_frame;
// Deinterleave each channel and convert to 32bit floating-point
// with nominal range -1.0 -> +1.0.
for (size_t channel_index = 0; channel_index < channels;
++channel_index) {
if (!DeinterleaveAudioChannel(output_buffer.get(),
audio_data[channel_index] + current_frame,
channels,
channel_index,
bytes_per_sample,
frames_read)) {
DLOG(WARNING)
<< "AudioFileReader::Read() : Unsupported sample format : "
<< codec_context_->sample_fmt
<< " codec_->id : " << codec_->id;
return false;
}
}
current_frame += frames_read;
}
return true;
}
} // namespace media