summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authordalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-20 01:59:39 +0000
committerdalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-20 01:59:39 +0000
commita9721dd681919183d4a9c9062475e6c21a7a339a (patch)
tree8deb998359ca3133b5da2c9259b36d5f37f9e8d0 /media
parent185fd4761b276f12d7dc0e6d90f547a9c6570055 (diff)
downloadchromium_src-a9721dd681919183d4a9c9062475e6c21a7a339a.zip
chromium_src-a9721dd681919183d4a9c9062475e6c21a7a339a.tar.gz
chromium_src-a9721dd681919183d4a9c9062475e6c21a7a339a.tar.bz2
Introduce AudioHash for fuzzy audio matching.
The old MD5 approach is too strict due to differences in floating point implementations on each platform. AudioHash provides a simple algorithm for ensuring signals are within roughly -40 dBFS. BUG=168204 TEST=media_unittests on Linux && Win. Review URL: https://chromiumcodereview.appspot.com/12925002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@189197 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/audio/null_audio_sink.cc50
-rw-r--r--media/audio/null_audio_sink.h15
-rw-r--r--media/base/audio_hash.cc51
-rw-r--r--media/base/audio_hash.h59
-rw-r--r--media/base/audio_hash_unittest.cc146
-rw-r--r--media/filters/audio_file_reader_unittest.cc52
-rw-r--r--media/filters/pipeline_integration_test.cc7
-rw-r--r--media/filters/pipeline_integration_test_base.h2
-rw-r--r--media/media.gyp3
9 files changed, 289 insertions, 96 deletions
diff --git a/media/audio/null_audio_sink.cc b/media/audio/null_audio_sink.cc
index 8fee966..5c4f67a 100644
--- a/media/audio/null_audio_sink.cc
+++ b/media/audio/null_audio_sink.cc
@@ -6,9 +6,8 @@
#include "base/bind.h"
#include "base/message_loop_proxy.h"
-#include "base/stringprintf.h"
-#include "base/sys_byteorder.h"
#include "media/audio/fake_audio_consumer.h"
+#include "media/base/audio_hash.h"
namespace media {
@@ -17,8 +16,6 @@ NullAudioSink::NullAudioSink(
: initialized_(false),
playing_(false),
callback_(NULL),
- hash_audio_for_testing_(false),
- channels_(0),
message_loop_(message_loop) {
}
@@ -27,16 +24,7 @@ NullAudioSink::~NullAudioSink() {}
void NullAudioSink::Initialize(const AudioParameters& params,
RenderCallback* callback) {
DCHECK(!initialized_);
-
fake_consumer_.reset(new FakeAudioConsumer(message_loop_, params));
-
- if (hash_audio_for_testing_) {
- channels_ = params.channels();
- md5_channel_contexts_.reset(new base::MD5Context[params.channels()]);
- for (int i = 0; i < params.channels(); i++)
- base::MD5Init(&md5_channel_contexts_[i]);
- }
-
callback_ = callback;
initialized_ = true;
}
@@ -85,47 +73,19 @@ void NullAudioSink::CallRender(AudioBus* audio_bus) {
DCHECK(message_loop_->BelongsToCurrentThread());
int frames_received = callback_->Render(audio_bus, 0);
- if (!hash_audio_for_testing_ || frames_received <= 0)
+ if (!audio_hash_ || frames_received <= 0)
return;
- DCHECK_EQ(sizeof(float), sizeof(uint32));
- int channels = audio_bus->channels();
- for (int channel_idx = 0; channel_idx < channels; ++channel_idx) {
- float* channel = audio_bus->channel(channel_idx);
- for (int frame_idx = 0; frame_idx < frames_received; frame_idx++) {
- // Convert float to uint32 w/o conversion loss.
- uint32 frame = base::ByteSwapToLE32(bit_cast<uint32>(channel[frame_idx]));
- base::MD5Update(&md5_channel_contexts_[channel_idx], base::StringPiece(
- reinterpret_cast<char*>(&frame), sizeof(frame)));
- }
- }
+ audio_hash_->Update(audio_bus, frames_received);
}
void NullAudioSink::StartAudioHashForTesting() {
DCHECK(!initialized_);
- hash_audio_for_testing_ = true;
+ audio_hash_.reset(new AudioHash());
}
std::string NullAudioSink::GetAudioHashForTesting() {
- DCHECK(hash_audio_for_testing_);
-
- base::MD5Digest digest;
- if (channels_ == 0) {
- // If initialize failed or was never called, ensure we return an empty hash.
- base::MD5Context context;
- base::MD5Init(&context);
- base::MD5Final(&digest, &context);
- } else {
- // Hash all channels into the first channel.
- for (int i = 1; i < channels_; i++) {
- base::MD5Final(&digest, &md5_channel_contexts_[i]);
- base::MD5Update(&md5_channel_contexts_[0], base::StringPiece(
- reinterpret_cast<char*>(&digest), sizeof(base::MD5Digest)));
- }
- base::MD5Final(&digest, &md5_channel_contexts_[0]);
- }
-
- return base::MD5DigestToBase16(digest);
+ return audio_hash_ ? audio_hash_->ToString() : "";
}
} // namespace media
diff --git a/media/audio/null_audio_sink.h b/media/audio/null_audio_sink.h
index 9fb7237..37022b7 100644
--- a/media/audio/null_audio_sink.h
+++ b/media/audio/null_audio_sink.h
@@ -5,7 +5,8 @@
#ifndef MEDIA_AUDIO_NULL_AUDIO_SINK_H_
#define MEDIA_AUDIO_NULL_AUDIO_SINK_H_
-#include "base/md5.h"
+#include <string>
+
#include "base/memory/scoped_ptr.h"
#include "media/base/audio_renderer_sink.h"
@@ -15,6 +16,7 @@ class MessageLoopProxy;
namespace media {
class AudioBus;
+class AudioHash;
class FakeAudioConsumer;
class MEDIA_EXPORT NullAudioSink
@@ -31,11 +33,10 @@ class MEDIA_EXPORT NullAudioSink
virtual void Play() OVERRIDE;
virtual bool SetVolume(double volume) OVERRIDE;
- // Enables audio frame hashing and reinitializes the MD5 context. Must be
- // called prior to Initialize().
+ // Enables audio frame hashing. Must be called prior to Initialize().
void StartAudioHashForTesting();
- // Returns the MD5 hash of all audio frames seen since the last reset.
+ // Returns the hash of all audio frames seen since construction.
std::string GetAudioHashForTesting();
protected:
@@ -49,10 +50,8 @@ class MEDIA_EXPORT NullAudioSink
bool playing_;
RenderCallback* callback_;
- // Controls whether or not a running MD5 hash is computed for audio frames.
- bool hash_audio_for_testing_;
- int channels_;
- scoped_array<base::MD5Context> md5_channel_contexts_;
+ // Controls whether or not a running hash is computed for audio frames.
+ scoped_ptr<AudioHash> audio_hash_;
scoped_refptr<base::MessageLoopProxy> message_loop_;
scoped_ptr<FakeAudioConsumer> fake_consumer_;
diff --git a/media/base/audio_hash.cc b/media/base/audio_hash.cc
new file mode 100644
index 0000000..5cea0c3
--- /dev/null
+++ b/media/base/audio_hash.cc
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+#include <cmath>
+
+#include "media/base/audio_hash.h"
+
+#include "base/stringprintf.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+
+AudioHash::AudioHash()
+ : audio_hash_(),
+ hash_count_(0) {
+ COMPILE_ASSERT(arraysize(audio_hash_) == kHashBuckets, audio_hash_size_error);
+}
+
+AudioHash::~AudioHash() {}
+
+void AudioHash::Update(const AudioBus* audio_bus, int frames) {
+ for (int ch = 0; ch < audio_bus->channels(); ++ch) {
+ const float* channel = audio_bus->channel(ch);
+ for (int i = 0; i < frames; ++i) {
+ const int kHashIndex =
+ (i * (ch + 1) + hash_count_) % arraysize(audio_hash_);
+
+ // Mix in a sine wave with the result so we ensure that sequences of empty
+ // buffers don't result in an empty hash.
+ if (ch == 0) {
+ audio_hash_[kHashIndex] += channel[i] + sin(2.0 * M_PI * M_PI * i);
+ } else {
+ audio_hash_[kHashIndex] += channel[i];
+ }
+ }
+ }
+
+ ++hash_count_;
+}
+
+std::string AudioHash::ToString() const {
+ std::string result;
+ for (size_t i = 0; i < arraysize(audio_hash_); ++i)
+ result += base::StringPrintf("%.2f,", audio_hash_[i]);
+ return result;
+}
+
+} // namespace media \ No newline at end of file
diff --git a/media/base/audio_hash.h b/media/base/audio_hash.h
new file mode 100644
index 0000000..66a2cf9
--- /dev/null
+++ b/media/base/audio_hash.h
@@ -0,0 +1,59 @@
+// Copyright 2013 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_BASE_AUDIO_HASH_H_
+#define MEDIA_BASE_AUDIO_HASH_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioBus;
+
+// Computes a running hash for a series of AudioBus objects. The hash is the
+// sum of each sample bucketed based on the frame index, channel number, and
+// current hash count. The hash was designed with two properties in mind:
+//
+// 1. Uniform error distribution across the input sample.
+// 2. Resilience to error below a certain threshold.
+//
+// The first is achieved by using a simple summing approach and moving position
+// weighting into the bucket choice. The second is handled during conversion to
+// string by rounding out values to only two decimal places.
+//
+// Using only two decimal places allows for roughly -40 dBFS of error. For
+// reference, SincResampler produces an RMS error of around -15 dBFS. See
+// http://en.wikipedia.org/wiki/DBFS and http://crbug.com/168204 for more info.
+class MEDIA_EXPORT AudioHash {
+ public:
+ AudioHash();
+ ~AudioHash();
+
+ // Update current hash with the contents of the provided AudioBus.
+ void Update(const AudioBus* audio_bus, int frames);
+
+ // Return a string representation of the current hash.
+ std::string ToString() const;
+
+ private:
+ // Storage for the audio hash. The number of buckets controls the importance
+ // of position in the hash. A higher number reduces the chance of false
+ // positives related to incorrect sample position. Value chosen by dice roll.
+ enum { kHashBuckets = 6 };
+ float audio_hash_[kHashBuckets];
+
+ // Essentially the number of times Update() has been called. Adds weight to
+ // the hash for the order of calls.
+ size_t hash_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioHash);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_AUDIO_HASH_H_ \ No newline at end of file
diff --git a/media/base/audio_hash_unittest.cc b/media/base/audio_hash_unittest.cc
new file mode 100644
index 0000000..02b8310
--- /dev/null
+++ b/media/base/audio_hash_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright 2013 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 "base/logging.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_hash.h"
+#include "media/base/fake_audio_render_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+static const int kChannelCount = 2;
+static const int kFrameCount = 1024;
+
+class AudioHashTest : public testing::Test {
+ public:
+ AudioHashTest()
+ : bus_one_(AudioBus::Create(kChannelCount, kFrameCount)),
+ bus_two_(AudioBus::Create(kChannelCount, kFrameCount)),
+ fake_callback_(0.01) {
+
+ // Fill each channel in each bus with unique data.
+ GenerateUniqueChannels(bus_one_.get());
+ GenerateUniqueChannels(bus_two_.get());
+ }
+
+ void GenerateUniqueChannels(AudioBus* audio_bus) {
+ // Use an AudioBus wrapper to avoid an extra memcpy when filling channels.
+ scoped_ptr<AudioBus> wrapped_bus = AudioBus::CreateWrapper(1);
+ wrapped_bus->set_frames(audio_bus->frames());
+
+ // Since FakeAudioRenderCallback generates only a single channel of unique
+ // audio data, we need to fill each channel manually.
+ for (int ch = 0; ch < audio_bus->channels(); ++ch) {
+ wrapped_bus->SetChannelData(0, audio_bus->channel(ch));
+ fake_callback_.Render(wrapped_bus.get(), 0);
+ }
+ }
+
+ virtual ~AudioHashTest() {}
+
+ protected:
+ scoped_ptr<AudioBus> bus_one_;
+ scoped_ptr<AudioBus> bus_two_;
+ FakeAudioRenderCallback fake_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioHashTest);
+};
+
+// Ensure the same data hashes the same.
+TEST_F(AudioHashTest, Equivalence) {
+ AudioHash hash_one;
+ hash_one.Update(bus_one_.get(), bus_one_->frames());
+
+ AudioHash hash_two;
+ hash_two.Update(bus_one_.get(), bus_one_->frames());
+
+ EXPECT_EQ(hash_one.ToString(), hash_two.ToString());
+}
+
+// Ensure sample order matters to the hash.
+TEST_F(AudioHashTest, SampleOrder) {
+ AudioHash original_hash;
+ original_hash.Update(bus_one_.get(), bus_one_->frames());
+
+ // Swap a sample in the bus.
+ std::swap(bus_one_->channel(0)[0], bus_one_->channel(0)[1]);
+
+ AudioHash swapped_hash;
+ swapped_hash.Update(bus_one_.get(), bus_one_->frames());
+
+ EXPECT_NE(original_hash.ToString(), swapped_hash.ToString());
+}
+
+// Ensure channel order matters to the hash.
+TEST_F(AudioHashTest, ChannelOrder) {
+ AudioHash original_hash;
+ original_hash.Update(bus_one_.get(), bus_one_->frames());
+
+ // Reverse channel order for the same sample data.
+ const int channels = bus_one_->channels();
+ scoped_ptr<AudioBus> swapped_ch_bus = AudioBus::CreateWrapper(channels);
+ swapped_ch_bus->set_frames(bus_one_->frames());
+ for (int i = channels - 1; i >= 0; --i)
+ swapped_ch_bus->SetChannelData(channels - (i + 1), bus_one_->channel(i));
+
+ AudioHash swapped_hash;
+ swapped_hash.Update(swapped_ch_bus.get(), swapped_ch_bus->frames());
+
+ EXPECT_NE(original_hash.ToString(), swapped_hash.ToString());
+}
+
+// Ensure bus order matters to the hash.
+TEST_F(AudioHashTest, BusOrder) {
+ AudioHash original_hash;
+ original_hash.Update(bus_one_.get(), bus_one_->frames());
+ original_hash.Update(bus_two_.get(), bus_two_->frames());
+
+ AudioHash reordered_hash;
+ reordered_hash.Update(bus_two_.get(), bus_two_->frames());
+ reordered_hash.Update(bus_one_.get(), bus_one_->frames());
+
+ EXPECT_NE(original_hash.ToString(), reordered_hash.ToString());
+}
+
+// Ensure bus order matters to the hash even with empty buses.
+TEST_F(AudioHashTest, EmptyBusOrder) {
+ bus_one_->Zero();
+ bus_two_->Zero();
+
+ AudioHash one_bus_hash;
+ one_bus_hash.Update(bus_one_.get(), bus_one_->frames());
+
+ AudioHash two_bus_hash;
+ two_bus_hash.Update(bus_one_.get(), bus_one_->frames());
+ two_bus_hash.Update(bus_two_.get(), bus_two_->frames());
+
+ EXPECT_NE(one_bus_hash.ToString(), two_bus_hash.ToString());
+}
+
+// Ensure approximate hashes pass verification.
+TEST_F(AudioHashTest, VerifySimilarHash) {
+ AudioHash hash_one;
+ hash_one.Update(bus_one_.get(), bus_one_->frames());
+
+ // Twiddle the values inside the first bus.
+ float* channel = bus_one_->channel(0);
+ for (int i = 0; i < bus_one_->frames(); i += bus_one_->frames() / 64)
+ channel[i] += 0.0001f;
+
+ AudioHash hash_two;
+ hash_two.Update(bus_one_.get(), bus_one_->frames());
+
+ EXPECT_EQ(hash_one.ToString(), hash_two.ToString());
+
+ // Twiddle the values too much...
+ for (int i = 0; i < bus_one_->frames(); ++i)
+ channel[i] += 0.0001f;
+
+ AudioHash hash_three;
+ hash_three.Update(bus_one_.get(), bus_one_->frames());
+ EXPECT_NE(hash_one.ToString(), hash_three.ToString());
+}
+
+} // namespace media \ No newline at end of file
diff --git a/media/filters/audio_file_reader_unittest.cc b/media/filters/audio_file_reader_unittest.cc
index 64c58e0..df0298a 100644
--- a/media/filters/audio_file_reader_unittest.cc
+++ b/media/filters/audio_file_reader_unittest.cc
@@ -3,11 +3,10 @@
// found in the LICENSE file.
#include "base/logging.h"
-#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
-#include "base/sys_byteorder.h"
#include "build/build_config.h"
#include "media/base/audio_bus.h"
+#include "media/base/audio_hash.h"
#include "media/base/decoder_buffer.h"
#include "media/base/test_data_util.h"
#include "media/filters/audio_file_reader.h"
@@ -28,38 +27,17 @@ class AudioFileReaderTest : public testing::Test {
reader_.reset(new AudioFileReader(protocol_.get()));
}
- // Reads and the entire file provided to Initialize(). If NULL is specified
- // for |audio_hash| MD5 checks are skipped.
- void ReadAndVerify(const char* audio_hash, int expected_frames) {
+ // Reads and the entire file provided to Initialize().
+ void ReadAndVerify(const char* expected_audio_hash, int expected_frames) {
scoped_ptr<AudioBus> decoded_audio_data = AudioBus::Create(
reader_->channels(), reader_->number_of_frames());
int actual_frames = reader_->Read(decoded_audio_data.get());
ASSERT_LE(actual_frames, decoded_audio_data->frames());
ASSERT_EQ(expected_frames, actual_frames);
- // TODO(dalecurtis): Audio decoded in float does not have a consistent hash
- // across platforms. Fix this: http://crbug.com/168204
- if (!audio_hash)
- return;
-
- base::MD5Context md5_context;
- base::MD5Init(&md5_context);
-
- DCHECK_EQ(sizeof(float), sizeof(uint32));
- int channels = decoded_audio_data->channels();
- for (int ch = 0; ch < channels; ++ch) {
- float* channel = decoded_audio_data->channel(ch);
- for (int i = 0; i < actual_frames; ++i) {
- // Convert float to uint32 w/o conversion loss.
- uint32 frame = base::ByteSwapToLE32(bit_cast<uint32>(channel[i]));
- base::MD5Update(&md5_context, base::StringPiece(
- reinterpret_cast<char*>(&frame), sizeof(frame)));
- }
- }
-
- base::MD5Digest digest;
- base::MD5Final(&digest, &md5_context);
- EXPECT_EQ(audio_hash, base::MD5DigestToBase16(digest));
+ AudioHash audio_hash;
+ audio_hash.Update(decoded_audio_data.get(), actual_frames);
+ EXPECT_EQ(expected_audio_hash, audio_hash.ToString());
}
void RunTest(const char* fn, const char* hash, int channels, int sample_rate,
@@ -103,43 +81,43 @@ TEST_F(AudioFileReaderTest, InvalidFile) {
}
TEST_F(AudioFileReaderTest, WithVideo) {
- RunTest("bear.ogv", NULL, 2, 44100,
+ RunTest("bear.ogv", "-2.49,-0.75,0.38,1.60,-0.15,-1.22,", 2, 44100,
base::TimeDelta::FromMicroseconds(1011520), 44608, 44608);
}
TEST_F(AudioFileReaderTest, Vorbis) {
- RunTest("sfx.ogg", NULL, 1, 44100,
+ RunTest("sfx.ogg", "4.36,4.81,4.84,4.34,4.61,4.63,", 1, 44100,
base::TimeDelta::FromMicroseconds(350001), 15435, 15435);
}
TEST_F(AudioFileReaderTest, WaveU8) {
- RunTest("sfx_u8.wav", "d7e255a8e634fffdf9f744c5803632f8", 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);
}
TEST_F(AudioFileReaderTest, WaveS16LE) {
- RunTest("sfx_s16le.wav", "2a5847207fdcba1c05e52f65ad010f66", 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);
}
TEST_F(AudioFileReaderTest, WaveS24LE) {
- RunTest("sfx_s24le.wav", "66296b4ec633290581f9abf3c21cd5e7", 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);
}
TEST_F(AudioFileReaderTest, WaveF32LE) {
- RunTest("sfx_f32le.wav", "66296b4ec633290581f9abf3c21cd5e7", 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);
}
#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS)
TEST_F(AudioFileReaderTest, MP3) {
- RunTest("sfx.mp3", NULL, 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);
}
TEST_F(AudioFileReaderTest, AAC) {
- RunTest("sfx.m4a", NULL, 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);
}
@@ -153,7 +131,7 @@ TEST_F(AudioFileReaderTest, VorbisInvalidChannelLayout) {
}
TEST_F(AudioFileReaderTest, WaveValidFourChannelLayout) {
- RunTest("4ch.wav", "d40bb7dbe532b2f1cf2e3558e780caa2", 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);
}
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
index d1720c1..5aec7c1 100644
--- a/media/filters/pipeline_integration_test.cc
+++ b/media/filters/pipeline_integration_test.cc
@@ -418,11 +418,8 @@ TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
ASSERT_TRUE(WaitUntilOnEnded());
- EXPECT_EQ(GetVideoHash(), "f0be120a90a811506777c99a2cdf7cc1");
-
- // TODO(dalecurtis): Audio decoded in float does not have a consistent hash
- // across platforms. Fix this: http://crbug.com/168204
- // EXPECT_EQ(GetAudioHash(), "");
+ EXPECT_EQ("f0be120a90a811506777c99a2cdf7cc1", GetVideoHash());
+ EXPECT_EQ("-5.15,2.31,-4.78,5.24,-2.23,4.54,", GetAudioHash());
}
// TODO(fgalligan): Enable test when code to parse encrypted WebM files lands
diff --git a/media/filters/pipeline_integration_test_base.h b/media/filters/pipeline_integration_test_base.h
index 05e2e7e..75f67ba 100644
--- a/media/filters/pipeline_integration_test_base.h
+++ b/media/filters/pipeline_integration_test_base.h
@@ -65,7 +65,7 @@ class PipelineIntegrationTestBase {
// with hashing enabled.
std::string GetVideoHash();
- // Returns the MD5 hash of all audio frames seen. Should only be called once
+ // Returns the hash of all audio frames seen. Should only be called once
// after playback completes. Pipeline must have been started with hashing
// enabled.
std::string GetAudioHash();
diff --git a/media/media.gyp b/media/media.gyp
index 63cd33a..29113ed 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -198,6 +198,8 @@
'base/audio_fifo.h',
'base/audio_hardware_config.cc',
'base/audio_hardware_config.h',
+ 'base/audio_hash.cc',
+ 'base/audio_hash.h',
'base/audio_pull_fifo.cc',
'base/audio_pull_fifo.h',
'base/audio_renderer.cc',
@@ -875,6 +877,7 @@
'base/audio_converter_unittest.cc',
'base/audio_fifo_unittest.cc',
'base/audio_hardware_config_unittest.cc',
+ 'base/audio_hash_unittest.cc',
'base/audio_pull_fifo_unittest.cc',
'base/audio_renderer_mixer_input_unittest.cc',
'base/audio_renderer_mixer_unittest.cc',