blob: db93e6f3b0ef66e5ce1ee33572792117ecade773 [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 "cobalt/media/filters/audio_file_reader.h"
#include <memory>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/md5.h"
#include "build/build_config.h"
#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media/base/audio_hash.h"
#include "cobalt/media/base/decoder_buffer.h"
#include "cobalt/media/base/test_data_util.h"
#include "cobalt/media/ffmpeg/ffmpeg_common.h"
#include "cobalt/media/filters/in_memory_url_protocol.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace media {
class AudioFileReaderTest : public testing::Test {
public:
AudioFileReaderTest() : packet_verification_disabled_(false) {}
~AudioFileReaderTest() override {}
void Initialize(const char* filename) {
data_ = ReadTestDataFile(filename);
protocol_.reset(
new InMemoryUrlProtocol(data_->data(), data_->data_size(), false));
reader_.reset(new AudioFileReader(protocol_.get()));
}
// Reads and the entire file provided to Initialize().
void ReadAndVerify(const char* expected_audio_hash, int expected_frames) {
std::unique_ptr<AudioBus> decoded_audio_data =
AudioBus::Create(reader_->channels(), reader_->GetNumberOfFrames());
int actual_frames = reader_->Read(decoded_audio_data.get());
ASSERT_LE(actual_frames, decoded_audio_data->frames());
ASSERT_EQ(expected_frames, actual_frames);
AudioHash audio_hash;
audio_hash.Update(decoded_audio_data.get(), actual_frames);
EXPECT_EQ(expected_audio_hash, audio_hash.ToString());
}
// Verify packets are consistent across demuxer runs. Reads the first few
// packets and then seeks back to the start timestamp and verifies that the
// hashes match on the packets just read.
void VerifyPackets() {
const int kReads = 3;
const int kTestPasses = 2;
AVPacket packet;
base::TimeDelta start_timestamp;
std::vector<std::string> packet_md5_hashes_;
for (int i = 0; i < kTestPasses; ++i) {
for (int j = 0; j < kReads; ++j) {
ASSERT_TRUE(reader_->ReadPacketForTesting(&packet));
// Remove metadata from the packet data section before hashing.
av_packet_split_side_data(&packet);
// On the first pass save the MD5 hash of each packet, on subsequent
// passes ensure it matches.
const std::string md5_hash = base::MD5String(base::StringPiece(
reinterpret_cast<char*>(packet.data), packet.size));
if (i == 0) {
packet_md5_hashes_.push_back(md5_hash);
if (j == 0) {
start_timestamp = ConvertFromTimeBase(
reader_->codec_context_for_testing()->time_base, packet.pts);
}
} else {
EXPECT_EQ(packet_md5_hashes_[j], md5_hash) << "j = " << j;
}
av_packet_unref(&packet);
}
ASSERT_TRUE(reader_->SeekForTesting(start_timestamp));
}
}
void RunTest(const char* fn, const char* hash, int channels, int sample_rate,
base::TimeDelta duration, int frames, int trimmed_frames) {
Initialize(fn);
ASSERT_TRUE(reader_->Open());
EXPECT_EQ(channels, reader_->channels());
EXPECT_EQ(sample_rate, reader_->sample_rate());
EXPECT_EQ(duration.InMicroseconds(),
reader_->GetDuration().InMicroseconds());
EXPECT_EQ(frames, reader_->GetNumberOfFrames());
if (!packet_verification_disabled_)
ASSERT_NO_FATAL_FAILURE(VerifyPackets());
ReadAndVerify(hash, trimmed_frames);
}
void RunTestFailingDemux(const char* fn) {
Initialize(fn);
EXPECT_FALSE(reader_->Open());
}
void RunTestFailingDecode(const char* fn) {
Initialize(fn);
EXPECT_TRUE(reader_->Open());
std::unique_ptr<AudioBus> decoded_audio_data =
AudioBus::Create(reader_->channels(), reader_->GetNumberOfFrames());
EXPECT_EQ(reader_->Read(decoded_audio_data.get()), 0);
}
void disable_packet_verification() { packet_verification_disabled_ = true; }
protected:
scoped_refptr<DecoderBuffer> data_;
std::unique_ptr<InMemoryUrlProtocol> protocol_;
std::unique_ptr<AudioFileReader> reader_;
bool packet_verification_disabled_;
private:
DISALLOW_COPY_AND_ASSIGN(AudioFileReaderTest);
};
TEST_F(AudioFileReaderTest, WithoutOpen) { Initialize("bear.ogv"); }
TEST_F(AudioFileReaderTest, InvalidFile) {
RunTestFailingDemux("ten_byte_file");
}
TEST_F(AudioFileReaderTest, WithVideo) {
RunTest("bear.ogv", "-2.49,-0.75,0.38,1.60,0.70,-1.22,", 2, 44100,
base::TimeDelta::FromMicroseconds(1011520), 44609, 44609);
}
TEST_F(AudioFileReaderTest, Vorbis) {
RunTest("sfx.ogg", "4.36,4.81,4.84,4.45,4.61,4.63,", 1, 44100,
base::TimeDelta::FromMicroseconds(350001), 15436, 15436);
}
TEST_F(AudioFileReaderTest, WaveU8) {
RunTest("sfx_u8.wav", "-1.23,-1.57,-1.14,-0.91,-0.87,-0.07,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
}
TEST_F(AudioFileReaderTest, WaveS16LE) {
RunTest("sfx_s16le.wav", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
}
TEST_F(AudioFileReaderTest, WaveS24LE) {
RunTest("sfx_s24le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
}
TEST_F(AudioFileReaderTest, WaveF32LE) {
RunTest("sfx_f32le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100,
base::TimeDelta::FromMicroseconds(288414), 12720, 12719);
}
#if defined(USE_PROPRIETARY_CODECS)
TEST_F(AudioFileReaderTest, MP3) {
RunTest("sfx.mp3", "1.30,2.72,4.56,5.08,3.74,2.03,", 1, 44100,
base::TimeDelta::FromMicroseconds(313470), 13825, 11025);
}
TEST_F(AudioFileReaderTest, CorruptMP3) {
// Disable packet verification since the file is corrupt and FFmpeg does not
// make any guarantees on packet consistency in this case.
disable_packet_verification();
RunTest("corrupt.mp3", "-4.95,-2.95,-0.44,1.16,0.31,-2.21,", 1, 44100,
base::TimeDelta::FromMicroseconds(1018801), 44930, 44928);
}
TEST_F(AudioFileReaderTest, AAC) {
RunTest("sfx.m4a", "1.81,1.66,2.32,3.27,4.46,3.36,", 1, 44100,
base::TimeDelta::FromMicroseconds(371660), 16391, 13312);
}
TEST_F(AudioFileReaderTest, MidStreamConfigChangesFail) {
RunTestFailingDecode("midstream_config_change.mp3");
}
#endif
TEST_F(AudioFileReaderTest, VorbisInvalidChannelLayout) {
RunTestFailingDemux("9ch.ogg");
}
TEST_F(AudioFileReaderTest, WaveValidFourChannelLayout) {
RunTest("4ch.wav", "131.71,38.02,130.31,44.89,135.98,42.52,", 4, 44100,
base::TimeDelta::FromMicroseconds(100001), 4411, 4410);
}
} // namespace media
} // namespace cobalt