Always round up to the next full frame when sizing WebAudio decodes.

Also fixes a couple of the unittests and adds a debug log for when
this occurs.

BUG=176862
TEST=WebAudio reports the correct number of frames.

Review URL: https://codereview.chromium.org/187223002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255162 0039d316-1c4b-4281-b951-d872f2087c98
parent eae4d539
...@@ -41,7 +41,7 @@ bool DecodeAudioFileData( ...@@ -41,7 +41,7 @@ bool DecodeAudioFileData(
size_t number_of_channels = reader.channels(); size_t number_of_channels = reader.channels();
double file_sample_rate = reader.sample_rate(); double file_sample_rate = reader.sample_rate();
size_t number_of_frames = static_cast<size_t>(reader.number_of_frames()); size_t number_of_frames = static_cast<size_t>(reader.GetNumberOfFrames());
// Apply sanity checks to make sure crazy values aren't coming out of // Apply sanity checks to make sure crazy values aren't coming out of
// FFmpeg. // FFmpeg.
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "media/filters/audio_file_reader.h" #include "media/filters/audio_file_reader.h"
#include <cmath>
#include "base/logging.h" #include "base/logging.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "media/base/audio_bus.h" #include "media/base/audio_bus.h"
...@@ -25,21 +27,6 @@ AudioFileReader::~AudioFileReader() { ...@@ -25,21 +27,6 @@ AudioFileReader::~AudioFileReader() {
Close(); Close();
} }
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() { bool AudioFileReader::Open() {
glue_.reset(new FFmpegGlue(protocol_)); glue_.reset(new FFmpegGlue(protocol_));
AVFormatContext* format_context = glue_->format_context(); AVFormatContext* format_context = glue_->format_context();
...@@ -201,8 +188,10 @@ int AudioFileReader::Read(AudioBus* audio_bus) { ...@@ -201,8 +188,10 @@ int AudioFileReader::Read(AudioBus* audio_bus) {
} }
// Truncate, if necessary, if the destination isn't big enough. // Truncate, if necessary, if the destination isn't big enough.
if (current_frame + frames_read > audio_bus->frames()) if (current_frame + frames_read > audio_bus->frames()) {
DLOG(ERROR) << "Truncating decoded data due to output size.";
frames_read = audio_bus->frames() - current_frame; frames_read = audio_bus->frames() - current_frame;
}
// Deinterleave each channel and convert to 32bit floating-point with // Deinterleave each channel and convert to 32bit floating-point with
// nominal range -1.0 -> +1.0. If the output is already in float planar // nominal range -1.0 -> +1.0. If the output is already in float planar
...@@ -241,4 +230,19 @@ int AudioFileReader::Read(AudioBus* audio_bus) { ...@@ -241,4 +230,19 @@ int AudioFileReader::Read(AudioBus* audio_bus) {
return current_frame; return current_frame;
} }
base::TimeDelta AudioFileReader::GetDuration() 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);
}
int AudioFileReader::GetNumberOfFrames() const {
return static_cast<int>(ceil(GetDuration().InSecondsF() * sample_rate()));
}
} // namespace media } // namespace media
...@@ -28,7 +28,7 @@ class MEDIA_EXPORT AudioFileReader { ...@@ -28,7 +28,7 @@ class MEDIA_EXPORT AudioFileReader {
virtual ~AudioFileReader(); virtual ~AudioFileReader();
// Open() reads the audio data format so that the sample_rate(), // Open() reads the audio data format so that the sample_rate(),
// channels(), duration(), and number_of_frames() methods can be called. // channels(), GetDuration(), and GetNumberOfFrames() methods can be called.
// It returns |true| on success. // It returns |true| on success.
bool Open(); bool Open();
void Close(); void Close();
...@@ -46,12 +46,13 @@ class MEDIA_EXPORT AudioFileReader { ...@@ -46,12 +46,13 @@ class MEDIA_EXPORT AudioFileReader {
int channels() const { return channels_; } int channels() const { return channels_; }
int sample_rate() const { return sample_rate_; } int sample_rate() const { return sample_rate_; }
// Please note that duration() and number_of_frames() attempt to be accurate, // Please note that GetDuration() and GetNumberOfFrames() attempt to be
// but are only estimates. For some encoded formats, the actual duration // accurate, but are only estimates. For some encoded formats, the actual
// of the file can only be determined once all the file data has been read. // duration of the file can only be determined once all the file data has been
// The Read() method returns the actual number of sample-frames it has read. // read. The Read() method returns the actual number of sample-frames it has
base::TimeDelta duration() const; // read.
int64 number_of_frames() const; base::TimeDelta GetDuration() const;
int GetNumberOfFrames() const;
private: private:
scoped_ptr<FFmpegGlue> glue_; scoped_ptr<FFmpegGlue> glue_;
......
...@@ -30,7 +30,7 @@ class AudioFileReaderTest : public testing::Test { ...@@ -30,7 +30,7 @@ class AudioFileReaderTest : public testing::Test {
// Reads and the entire file provided to Initialize(). // Reads and the entire file provided to Initialize().
void ReadAndVerify(const char* expected_audio_hash, int expected_frames) { void ReadAndVerify(const char* expected_audio_hash, int expected_frames) {
scoped_ptr<AudioBus> decoded_audio_data = AudioBus::Create( scoped_ptr<AudioBus> decoded_audio_data = AudioBus::Create(
reader_->channels(), reader_->number_of_frames()); reader_->channels(), reader_->GetNumberOfFrames());
int actual_frames = reader_->Read(decoded_audio_data.get()); int actual_frames = reader_->Read(decoded_audio_data.get());
ASSERT_LE(actual_frames, decoded_audio_data->frames()); ASSERT_LE(actual_frames, decoded_audio_data->frames());
ASSERT_EQ(expected_frames, actual_frames); ASSERT_EQ(expected_frames, actual_frames);
...@@ -46,8 +46,9 @@ class AudioFileReaderTest : public testing::Test { ...@@ -46,8 +46,9 @@ class AudioFileReaderTest : public testing::Test {
ASSERT_TRUE(reader_->Open()); ASSERT_TRUE(reader_->Open());
EXPECT_EQ(channels, reader_->channels()); EXPECT_EQ(channels, reader_->channels());
EXPECT_EQ(sample_rate, reader_->sample_rate()); EXPECT_EQ(sample_rate, reader_->sample_rate());
EXPECT_EQ(duration.InMicroseconds(), reader_->duration().InMicroseconds()); EXPECT_EQ(duration.InMicroseconds(),
EXPECT_EQ(frames, reader_->number_of_frames()); reader_->GetDuration().InMicroseconds());
EXPECT_EQ(frames, reader_->GetNumberOfFrames());
ReadAndVerify(hash, trimmed_frames); ReadAndVerify(hash, trimmed_frames);
} }
...@@ -60,7 +61,7 @@ class AudioFileReaderTest : public testing::Test { ...@@ -60,7 +61,7 @@ class AudioFileReaderTest : public testing::Test {
Initialize(fn); Initialize(fn);
EXPECT_TRUE(reader_->Open()); EXPECT_TRUE(reader_->Open());
scoped_ptr<AudioBus> decoded_audio_data = AudioBus::Create( scoped_ptr<AudioBus> decoded_audio_data = AudioBus::Create(
reader_->channels(), reader_->number_of_frames()); reader_->channels(), reader_->GetNumberOfFrames());
EXPECT_EQ(reader_->Read(decoded_audio_data.get()), 0); EXPECT_EQ(reader_->Read(decoded_audio_data.get()), 0);
} }
...@@ -81,49 +82,49 @@ TEST_F(AudioFileReaderTest, InvalidFile) { ...@@ -81,49 +82,49 @@ TEST_F(AudioFileReaderTest, InvalidFile) {
} }
TEST_F(AudioFileReaderTest, WithVideo) { TEST_F(AudioFileReaderTest, WithVideo) {
RunTest("bear.ogv", "-2.49,-0.75,0.38,1.60,-0.15,-1.22,", 2, 44100, RunTest("bear.ogv", "-2.49,-0.75,0.38,1.60,0.70,-1.22,", 2, 44100,
base::TimeDelta::FromMicroseconds(1011520), 44608, 44608); base::TimeDelta::FromMicroseconds(1011520), 44609, 44609);
} }
TEST_F(AudioFileReaderTest, Vorbis) { TEST_F(AudioFileReaderTest, Vorbis) {
RunTest("sfx.ogg", "4.36,4.81,4.84,4.34,4.61,4.63,", 1, 44100, RunTest("sfx.ogg", "4.36,4.81,4.84,4.45,4.61,4.63,", 1, 44100,
base::TimeDelta::FromMicroseconds(350001), 15435, 15435); base::TimeDelta::FromMicroseconds(350001), 15436, 15436);
} }
TEST_F(AudioFileReaderTest, WaveU8) { TEST_F(AudioFileReaderTest, WaveU8) {
RunTest("sfx_u8.wav", "-1.23,-1.57,-1.14,-0.91,-0.87,-0.07,", 1, 44100, RunTest("sfx_u8.wav", "-1.23,-1.57,-1.14,-0.91,-0.87,-0.07,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12719, 12719); base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
} }
TEST_F(AudioFileReaderTest, WaveS16LE) { TEST_F(AudioFileReaderTest, WaveS16LE) {
RunTest("sfx_s16le.wav", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100, RunTest("sfx_s16le.wav", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12719, 12719); base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
} }
TEST_F(AudioFileReaderTest, WaveS24LE) { TEST_F(AudioFileReaderTest, WaveS24LE) {
RunTest("sfx_s24le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100, RunTest("sfx_s24le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12719, 12719); base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
} }
TEST_F(AudioFileReaderTest, WaveF32LE) { TEST_F(AudioFileReaderTest, WaveF32LE) {
RunTest("sfx_f32le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100, RunTest("sfx_f32le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12719, 12719); base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
} }
#if defined(USE_PROPRIETARY_CODECS) #if defined(USE_PROPRIETARY_CODECS)
TEST_F(AudioFileReaderTest, MP3) { TEST_F(AudioFileReaderTest, MP3) {
RunTest("sfx.mp3", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100, RunTest("sfx.mp3", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100,
base::TimeDelta::FromMicroseconds(313470), 13824, 12719); base::TimeDelta::FromMicroseconds(313470), 13825, 12719);
} }
TEST_F(AudioFileReaderTest, CorruptMP3) { TEST_F(AudioFileReaderTest, CorruptMP3) {
RunTest("corrupt.mp3", "-4.95,-2.95,-0.44,1.16,0.31,-2.21,", 1, 44100, RunTest("corrupt.mp3", "-4.95,-2.95,-0.44,1.16,0.31,-2.21,", 1, 44100,
base::TimeDelta::FromMicroseconds(1018826), 44930, 44928); base::TimeDelta::FromMicroseconds(1018826), 44931, 44928);
} }
TEST_F(AudioFileReaderTest, AAC) { TEST_F(AudioFileReaderTest, AAC) {
RunTest("sfx.m4a", "1.81,1.66,2.32,3.27,4.46,3.36,", 1, 44100, RunTest("sfx.m4a", "1.81,1.66,2.32,3.27,4.46,3.36,", 1, 44100,
base::TimeDelta::FromMicroseconds(312001), 13759, 13312); base::TimeDelta::FromMicroseconds(312001), 13760, 13312);
} }
TEST_F(AudioFileReaderTest, MidStreamConfigChangesFail) { TEST_F(AudioFileReaderTest, MidStreamConfigChangesFail) {
...@@ -137,7 +138,7 @@ TEST_F(AudioFileReaderTest, VorbisInvalidChannelLayout) { ...@@ -137,7 +138,7 @@ TEST_F(AudioFileReaderTest, VorbisInvalidChannelLayout) {
TEST_F(AudioFileReaderTest, WaveValidFourChannelLayout) { TEST_F(AudioFileReaderTest, WaveValidFourChannelLayout) {
RunTest("4ch.wav", "131.71,38.02,130.31,44.89,135.98,42.52,", 4, 44100, RunTest("4ch.wav", "131.71,38.02,130.31,44.89,135.98,42.52,", 4, 44100,
base::TimeDelta::FromMicroseconds(100001), 4410, 4410); base::TimeDelta::FromMicroseconds(100001), 4411, 4410);
} }
} // namespace media } // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment