Chromium Code Reviews| Index: media/filters/audio_file_reader.cc |
| =================================================================== |
| --- media/filters/audio_file_reader.cc (revision 0) |
| +++ media/filters/audio_file_reader.cc (revision 0) |
| @@ -0,0 +1,248 @@ |
| +// Copyright (c) 2010 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 "media/base/filters.h" |
| +#include "media/ffmpeg/ffmpeg_common.h" |
| +#include "media/ffmpeg/ffmpeg_util.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(); |
| +} |
| + |
| +size_t AudioFileReader::NumberOfChannels() const { |
|
scherkus (not reviewing)
2010/12/09 17:35:40
I think it'd be best to not do type conversion her
Chris Rogers
2010/12/10 23:34:00
Done.
|
| + return codec_context_->channels; |
| +} |
| + |
| +double AudioFileReader::GetSampleRate() const { |
| + return codec_context_->sample_rate; |
| +} |
| + |
| +double AudioFileReader::GetDuration() const { |
| + return 1e-6 * format_context_->duration; |
|
scherkus (not reviewing)
2010/12/09 17:35:40
what are we converting here to/from? what are the
fbarchard
2010/12/09 21:11:02
1e-6 is obscure.
Chris Rogers
2010/12/10 23:34:00
Thanks for the tip - I've switched the code over t
Chris Rogers
2010/12/10 23:34:00
This is addressed by switching the code over to us
|
| +} |
| + |
| +size_t AudioFileReader::Length() const { |
| + return GetDuration() * GetSampleRate(); |
| +} |
| + |
| +int AudioFileReader::Open() { |
| + // Add our data reader to the protocol list and get our unique key. |
| + std::string key = FFmpegGlue::get()->AddProtocol(protocol_); |
| + |
| + // Open FFmpeg AVFormatContext. |
| + DCHECK(!format_context_); |
| + AVFormatContext* context = NULL; |
| + |
| + int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL); |
| + |
| + // Remove our data reader from protocol list since av_open_input_file() setup |
| + // the AVFormatContext with the data reader. |
| + FFmpegGlue::get()->RemoveProtocol(protocol_); |
| + |
| + if (result) { |
| + DLOG(WARNING) |
| + << "AudioFileReader::Open() : error in av_open_input_file() -" |
| + << " result: " << result; |
| + return result; |
| + } |
| + |
| + DCHECK(context); |
| + format_context_ = context; |
| + |
| + // Get the codec context. |
| + codec_context_ = NULL; |
| + for (unsigned i = 0; i < format_context_->nb_streams; ++i) { |
| + AVCodecContext* c = format_context_->streams[i]->codec; |
| + if (c->codec_type == CODEC_TYPE_AUDIO) { |
| + codec_context_ = c; |
| + break; |
| + } |
| + } |
| + |
| + // Get the codec. |
| + if (codec_context_) { |
| + av_find_stream_info(format_context_); |
| + codec_ = avcodec_find_decoder(codec_context_->codec_id); |
| + if (codec_) { |
| + if ((result = avcodec_open(codec_context_, codec_)) < 0) { |
| + DLOG(WARNING) << "AudioFileReader::Open() : could not open codec -" |
| + << " result: " << result; |
| + return result; |
| + } |
| + |
| + result = av_seek_frame(format_context_, 0, 0, 0); |
| + } |
| + } else { |
| + result = -1; |
| + } |
| + |
| + return result; |
| +} |
| + |
| +void AudioFileReader::Close() { |
| + if (codec_context_ && codec_) { |
| + avcodec_close(codec_context_); |
| + } |
| + codec_context_ = NULL; |
| + codec_ = NULL; |
| + |
| + if (format_context_) { |
| + av_close_input_file(format_context_); |
| + format_context_ = NULL; |
| + } |
| +} |
| + |
| +int AudioFileReader::Read(const std::vector<float*>& audio_data, |
| + size_t number_of_frames) { |
| + DCHECK_EQ(audio_data.size(), NumberOfChannels()); |
| + if (audio_data.size() != NumberOfChannels()) |
| + return -1; |
| + |
| + DCHECK(format_context_ && codec_context_); |
| + if (!format_context_ || !codec_context_) { |
| + DLOG(WARNING) << "AudioFileReader::Read() : reader is not opened!"; |
| + return -1; |
| + } |
| + |
| + int16* output_buffer = |
| + static_cast<int16*>(malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)); |
| + |
| + unsigned number_of_channels = NumberOfChannels(); |
| + |
| + // Read until we hit EOF or we've read the requested number of frames. |
| + AVPacket avpkt; |
| + av_init_packet(&avpkt); |
| + |
| + int result = 0; |
| + unsigned 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, |
| + &out_size, |
| + &avpkt); |
| + |
| + if (result < 0) { |
| + DLOG(WARNING) |
| + << "AudioFileReader::Read() : error in avcodec_decode_audio3() -" |
| + << result; |
| + free(output_buffer); |
| + return result; |
| + } |
| + |
| + // Determine the number of sample-frames we just decoded. |
| + size_t sample_byte_size = codec_context_->sample_fmt == AV_SAMPLE_FMT_S32 |
|
scherkus (not reviewing)
2010/12/09 17:35:40
use av_get_bits_per_sample_fmt() and divide by 8 -
fbarchard
2010/12/09 21:11:02
You missed 8 bit.
Chris Rogers
2010/12/10 23:34:00
Done.
Chris Rogers
2010/12/10 23:34:00
Done.
|
| + ? sizeof(int32) : sizeof(int16); |
| + |
| + unsigned frames_read = out_size / (number_of_channels * sample_byte_size); |
| + |
| + // Truncate, if necessary, if the destination isn't big enough. |
| + if (current_frame + frames_read > number_of_frames) { |
| + frames_read = static_cast<int>(number_of_frames) - current_frame; |
| + } |
| + |
| + // Convert to Float32 with nominal range -1.0 -> +1.0 |
| + for (unsigned channelIndex = 0; channelIndex < number_of_channels; |
|
scherkus (not reviewing)
2010/12/09 17:35:40
I can't remember if FFmpeg offers any sort of util
Chris Rogers
2010/12/10 23:34:00
I've created a utility function called Deinterleav
|
| + ++channelIndex) { |
| + float* destination = audio_data[channelIndex]; |
| + destination += current_frame; |
| + |
| + switch (codec_context_->sample_fmt) { |
| + case AV_SAMPLE_FMT_S16: |
| + { |
| + const float kScale = 1.0f / 32768.0f; |
| + for (unsigned i = 0; i < frames_read; ++i) { |
| + destination[i] = |
| + kScale * output_buffer[i * number_of_channels + channelIndex]; |
| + } |
| + break; |
| + } |
| + |
| + case AV_SAMPLE_FMT_S32: |
| + { |
| + const float kScale = 1.0f / (1L << 31); |
| + int32* output_buffer32 = reinterpret_cast<int32*>(output_buffer); |
| + for (unsigned i = 0; i < frames_read; ++i) { |
| + destination[i] = |
| + kScale * output_buffer32[i * number_of_channels + channelIndex]; |
| + } |
| + break; |
| + } |
| + |
| + default: |
| + DLOG(WARNING) |
| + << "AudioFileReader::Read() : Unsupported sample format : " |
| + << codec_context_->sample_fmt |
| + << " codec_->id : " << codec_->id; |
| + return -1; |
| + } |
| + } |
| + |
| + current_frame += frames_read; |
| + } |
| + |
| + free(output_buffer); |
| + return 0; // success |
| +} |
| + |
| +InMemoryDataReader::InMemoryDataReader(const char* data, int64 size) |
| + : data_(data), |
| + size_(size), |
| + position_(0) { |
| +} |
| + |
| +int InMemoryDataReader::Read(int size, uint8* data) { |
| + if (size < 0) |
| + return -1; |
| + |
| + int64 available_bytes = size_ - position_; |
| + if (size > available_bytes) |
| + size = available_bytes; |
| + |
| + memcpy(data, data_ + position_, size); |
| + position_ += size; |
| + return size; |
| +} |
| + |
| +bool InMemoryDataReader::GetPosition(int64* position_out) { |
| + if (position_out) |
| + *position_out = position_; |
| + return true; |
| +} |
| + |
| +bool InMemoryDataReader::SetPosition(int64 position) { |
| + if (position >= size_) |
| + return false; |
| + position_ = position; |
| + return true; |
| +} |
| + |
| +bool InMemoryDataReader::GetSize(int64* size_out) { |
| + if (size_out) |
| + *size_out = size_; |
| + return true; |
| +} |
| + |
| +bool InMemoryDataReader::IsStreaming() { |
| + return false; |
| +} |
| + |
| +} // namespace media |
| Property changes on: media/filters/audio_file_reader.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |