diff options
-rw-r--r-- | media/audio/audio_util.cc | 46 | ||||
-rw-r--r-- | media/audio/audio_util.h | 15 | ||||
-rw-r--r-- | media/filters/audio_file_reader.cc | 226 | ||||
-rw-r--r-- | media/filters/audio_file_reader.h | 79 | ||||
-rw-r--r-- | media/media.gyp | 2 | ||||
-rw-r--r-- | webkit/glue/media/audio_decoder.cc | 76 | ||||
-rw-r--r-- | webkit/glue/media/audio_decoder.h | 20 | ||||
-rw-r--r-- | webkit/glue/webkit_glue.gypi | 2 | ||||
-rw-r--r-- | webkit/glue/webkitclient_impl.cc | 11 | ||||
-rw-r--r-- | webkit/glue/webkitclient_impl.h | 3 |
10 files changed, 479 insertions, 1 deletions
diff --git a/media/audio/audio_util.cc b/media/audio/audio_util.cc index e3e94cd..4c8be79 100644 --- a/media/audio/audio_util.cc +++ b/media/audio/audio_util.cc @@ -158,4 +158,50 @@ bool FoldChannels(void* buf, return false; } +bool DeinterleaveAudioChannel(void* source, + float* destination, + int channels, + int channel_index, + int bytes_per_sample, + size_t number_of_frames) { + switch (bytes_per_sample) { + case 1: + { + uint8* source8 = static_cast<uint8*>(source) + channel_index; + const float kScale = 1.0f / 128.0f; + for (unsigned i = 0; i < number_of_frames; ++i) { + destination[i] = kScale * static_cast<int>(*source8 + 128); + source8 += channels; + } + return true; + } + + case 2: + { + int16* source16 = static_cast<int16*>(source) + channel_index; + const float kScale = 1.0f / 32768.0f; + for (unsigned i = 0; i < number_of_frames; ++i) { + destination[i] = kScale * *source16; + source16 += channels; + } + return true; + } + + case 4: + { + int32* source32 = static_cast<int32*>(source) + channel_index; + const float kScale = 1.0f / (1L << 31); + for (unsigned i = 0; i < number_of_frames; ++i) { + destination[i] = kScale * *source32; + source32 += channels; + } + return true; + } + + default: + break; + } + return false; +} + } // namespace media diff --git a/media/audio/audio_util.h b/media/audio/audio_util.h index edfe7ab..669b4fa 100644 --- a/media/audio/audio_util.h +++ b/media/audio/audio_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -49,6 +49,19 @@ bool FoldChannels(void* buf, int bytes_per_sample, float volume); +// DeinterleaveAudioChannel() takes interleaved audio buffer |source| +// of the given |sample_fmt| and |number_of_channels| and extracts +// |number_of_frames| data for the given |channel_index| and +// puts it in the floating point |destination|. +// It returns |true| on success, or |false| if the |sample_fmt| is +// not recognized. +bool DeinterleaveAudioChannel(void* source, + float* destination, + int channels, + int channel_index, + int bytes_per_sample, + size_t number_of_frames); + } // namespace media #endif // MEDIA_AUDIO_AUDIO_UTIL_H_ diff --git a/media/filters/audio_file_reader.cc b/media/filters/audio_file_reader.cc new file mode 100644 index 0000000..ceeb3a1 --- /dev/null +++ b/media/filters/audio_file_reader.cc @@ -0,0 +1,226 @@ +// 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 "base/time.h" +#include "media/audio/audio_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(); +} + +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 ConvertTimestamp(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 = 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::GetInstance()->RemoveProtocol(protocol_); + + if (result) { + DLOG(WARNING) + << "AudioFileReader::Open() : error in av_open_input_file() -" + << " 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 == CODEC_TYPE_AUDIO) { + codec_context_ = c; + break; + } + } + + // Get the codec. + if (!codec_context_) + return false; + + 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 false; + } + + result = av_seek_frame(format_context_, 0, 0, 0); + } + + return true; +} + +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; + } +} + +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_); + if (!format_context_ || !codec_context_) { + 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; + av_init_packet(&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); + + if (result < 0) { + DLOG(WARNING) + << "AudioFileReader::Read() : error in avcodec_decode_audio3() -" + << result; + return false; + } + + // Determine the number of sample-frames we just decoded. + size_t bytes_per_sample = + av_get_bits_per_sample_fmt(codec_context_->sample_fmt) >> 3; + 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; +} + +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; + + int available_bytes = static_cast<int>(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 diff --git a/media/filters/audio_file_reader.h b/media/filters/audio_file_reader.h new file mode 100644 index 0000000..ae929b6 --- /dev/null +++ b/media/filters/audio_file_reader.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef MEDIA_FILTERS_AUDIO_FILE_READER_H_ +#define MEDIA_FILTERS_AUDIO_FILE_READER_H_ + +#include <vector> +#include "media/filters/ffmpeg_glue.h" + +struct AVCodec; +struct AVCodecContext; +struct AVFormatContext; + +namespace base { class TimeDelta; } + +namespace media { + +class FFmpegURLProtocol; + +class AudioFileReader { + public: + // Audio file data will be read using the given protocol. + // The AudioFileReader does not take ownership of |protocol| and + // simply maintains a weak reference to it. + explicit AudioFileReader(FFmpegURLProtocol* protocol); + virtual ~AudioFileReader(); + + // Open() reads the audio data format so that the sample_rate(), + // channels(), duration(), and number_of_frames() methods can be called. + // It returns |true| on success. + bool Open(); + void Close(); + + // After a call to Open(), reads |number_of_frames| into |audio_data|. + // |audio_data| must be of the same size as channels(). + // The audio data will be decoded as floating-point linear PCM with + // a nominal range of -1.0 -> +1.0. + // Returns |true| on success. + bool Read(const std::vector<float*>& audio_data, size_t number_of_frames); + + // These methods can be called once Open() has been called. + int channels() const; + int sample_rate() const; + base::TimeDelta duration() const; + int64 number_of_frames() const; + + private: + FFmpegURLProtocol* protocol_; + AVFormatContext* format_context_; + AVCodecContext* codec_context_; + AVCodec* codec_; + + DISALLOW_COPY_AND_ASSIGN(AudioFileReader); +}; + +class InMemoryDataReader : public FFmpegURLProtocol { + public: + // Ownership of |data| is not taken, instead it simply maintains + // a weak reference. + InMemoryDataReader(const char* data, int64 size); + + virtual int Read(int size, uint8* data); + virtual bool GetPosition(int64* position_out); + virtual bool SetPosition(int64 position); + virtual bool GetSize(int64* size_out); + virtual bool IsStreaming(); + + private: + const char* data_; + int64 size_; + int64 position_; + + DISALLOW_COPY_AND_ASSIGN(InMemoryDataReader); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_AUDIO_FILE_READER_H_ diff --git a/media/media.gyp b/media/media.gyp index da1ff6f..6fe8e83 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -115,6 +115,8 @@ 'ffmpeg/ffmpeg_util.h', 'ffmpeg/file_protocol.cc', 'ffmpeg/file_protocol.h', + 'filters/audio_file_reader.cc', + 'filters/audio_file_reader.h', 'filters/audio_renderer_algorithm_base.cc', 'filters/audio_renderer_algorithm_base.h', 'filters/audio_renderer_algorithm_default.cc', diff --git a/webkit/glue/media/audio_decoder.cc b/webkit/glue/media/audio_decoder.cc new file mode 100644 index 0000000..3fc05c9 --- /dev/null +++ b/webkit/glue/media/audio_decoder.cc @@ -0,0 +1,76 @@ +// 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 "webkit/glue/media/audio_decoder.h" + +#include <vector> +#include "base/basictypes.h" +#include "base/string_util.h" +#include "base/time.h" +#include "media/filters/audio_file_reader.h" +#include "third_party/WebKit/WebKit/chromium/public/WebAudioBus.h" + +using media::AudioFileReader; +using media::InMemoryDataReader; +using std::vector; +using WebKit::WebAudioBus; + +namespace webkit_glue { + +// Decode in-memory audio file data. +bool DecodeAudioFileData( + WebKit::WebAudioBus* destination_bus, + const char* data, size_t data_size, double sample_rate) { + DCHECK(destination_bus); + if (!destination_bus) + return false; + + // Uses the FFmpeg library for audio file reading. + InMemoryDataReader data_reader(data, data_size); + AudioFileReader reader(&data_reader); + + if (!reader.Open()) + return false; + + size_t number_of_channels = reader.channels(); + double file_sample_rate = reader.sample_rate(); + double duration = reader.duration().InSecondsF(); + size_t number_of_frames = static_cast<size_t>(reader.number_of_frames()); + + // TODO(crogers) : do sample-rate conversion with FFmpeg. + // For now, we're ignoring the requested 'sample_rate' and returning + // the WebAudioBus at the file's sample-rate. + // double destination_sample_rate = + // (sample_rate != 0.0) ? sample_rate : file_sample_rate; + double destination_sample_rate = file_sample_rate; + + DLOG(INFO) << "Decoding file data -" + << " data: " << data + << " data size: " << data_size + << " duration: " << duration + << " number of frames: " << number_of_frames + << " sample rate: " << file_sample_rate + << " number of channels: " << number_of_channels; + + // Change to destination sample-rate. + number_of_frames = static_cast<size_t>(number_of_frames * + (destination_sample_rate / file_sample_rate)); + + // Allocate and configure the output audio channel data. + destination_bus->initialize(number_of_channels, + number_of_frames, + destination_sample_rate); + + // Wrap the channel pointers which will receive the decoded PCM audio. + vector<float*> audio_data; + audio_data.reserve(number_of_channels); + for (size_t i = 0; i < number_of_channels; ++i) { + audio_data.push_back(destination_bus->channelData(i)); + } + + // Decode the audio file data. + return reader.Read(audio_data, number_of_frames); +} + +} // namespace webkit_glue diff --git a/webkit/glue/media/audio_decoder.h b/webkit/glue/media/audio_decoder.h new file mode 100644 index 0000000..57cc90b --- /dev/null +++ b/webkit/glue/media/audio_decoder.h @@ -0,0 +1,20 @@ +// 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. + +#ifndef WEBKIT_GLUE_MEDIA_AUDIO_DECODER_H_ +#define WEBKIT_GLUE_MEDIA_AUDIO_DECODER_H_ + +#include "base/basictypes.h" + +namespace WebKit { class WebAudioBus; } + +namespace webkit_glue { + +// Decode in-memory audio file data. +bool DecodeAudioFileData(WebKit::WebAudioBus* destination_bus, const char* data, + size_t data_size, double sample_rate); + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_MEDIA_AUDIO_DECODER_H_ diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi index eb5b31b..d986324 100644 --- a/webkit/glue/webkit_glue.gypi +++ b/webkit/glue/webkit_glue.gypi @@ -250,6 +250,8 @@ '../plugins/ppapi/var.h', '../plugins/ppapi/var_object_class.cc', '../plugins/ppapi/var_object_class.h', + 'media/audio_decoder.cc', + 'media/audio_decoder.h', 'media/buffered_data_source.cc', 'media/buffered_data_source.h', 'media/buffered_resource_loader.cc', diff --git a/webkit/glue/webkitclient_impl.cc b/webkit/glue/webkitclient_impl.cc index 42e1d1d8..5346181 100644 --- a/webkit/glue/webkitclient_impl.cc +++ b/webkit/glue/webkitclient_impl.cc @@ -34,6 +34,7 @@ #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/WebKit/chromium/public/WebVector.h" #include "third_party/WebKit/WebKit/chromium/public/WebURL.h" +#include "webkit/glue/media/audio_decoder.h" #include "webkit/glue/plugins/plugin_instance.h" #include "webkit/glue/plugins/webplugininfo.h" #include "webkit/glue/webkit_glue.h" @@ -44,6 +45,7 @@ #include "v8/include/v8.h" #endif +using WebKit::WebAudioBus; using WebKit::WebCookie; using WebKit::WebData; using WebKit::WebLocalizedString; @@ -352,6 +354,15 @@ WebData WebKitClientImpl::loadResource(const char* name) { return WebData(); } +bool WebKitClientImpl::loadAudioResource( + WebKit::WebAudioBus* destination_bus, const char* audio_file_data, + size_t data_size, double sample_rate) { + return DecodeAudioFileData(destination_bus, + audio_file_data, + data_size, + sample_rate); +} + WebString WebKitClientImpl::queryLocalizedString( WebLocalizedString::Name name) { int message_id = ToMessageID(name); diff --git a/webkit/glue/webkitclient_impl.h b/webkit/glue/webkitclient_impl.h index 2836e8e..cc361d3 100644 --- a/webkit/glue/webkitclient_impl.h +++ b/webkit/glue/webkitclient_impl.h @@ -52,6 +52,9 @@ class WebKitClientImpl : public WebKit::WebKitClient { virtual void traceEventBegin(const char* name, void* id, const char* extra); virtual void traceEventEnd(const char* name, void* id, const char* extra); virtual WebKit::WebData loadResource(const char* name); + virtual bool loadAudioResource( + WebKit::WebAudioBus* destination_bus, const char* audio_file_data, + size_t data_size, double sample_rate); virtual WebKit::WebString queryLocalizedString( WebKit::WebLocalizedString::Name name); virtual WebKit::WebString queryLocalizedString( |