From 30b51506c49b727e2a12cf3a1d66e5fd38c5c834 Mon Sep 17 00:00:00 2001 From: Thomas Inskip Date: Mon, 7 Apr 2014 10:27:07 -0700 Subject: [PATCH] Check-in of unmodified audio elementary stream parsing code. Source of media/base/*: http://src.chromium.org/chrome/trunk/src/media/base@262934 Source of media/formats/mpeg/*: http://src.chromium.org/chrome/trunk/src/media/formats/mpeg@257932 Change-Id: Icd56b6f496befcd0c9f4eec2460fb2e998ae8bd2 --- media/base/audio_timestamp_helper.cc | 75 +++++++++++ media/base/audio_timestamp_helper.h | 72 +++++++++++ media/base/audio_timestamp_helper_unittest.cc | 122 ++++++++++++++++++ media/formats/mpeg/adts_constants.cc | 27 ++++ media/formats/mpeg/adts_constants.h | 28 ++++ 5 files changed, 324 insertions(+) create mode 100644 media/base/audio_timestamp_helper.cc create mode 100644 media/base/audio_timestamp_helper.h create mode 100644 media/base/audio_timestamp_helper_unittest.cc create mode 100644 media/formats/mpeg/adts_constants.cc create mode 100644 media/formats/mpeg/adts_constants.h diff --git a/media/base/audio_timestamp_helper.cc b/media/base/audio_timestamp_helper.cc new file mode 100644 index 0000000000..38fde1f0bd --- /dev/null +++ b/media/base/audio_timestamp_helper.cc @@ -0,0 +1,75 @@ +// 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 "media/base/audio_timestamp_helper.h" + +#include "base/logging.h" +#include "media/base/buffers.h" + +namespace media { + +AudioTimestampHelper::AudioTimestampHelper(int samples_per_second) + : base_timestamp_(kNoTimestamp()), + frame_count_(0) { + DCHECK_GT(samples_per_second, 0); + double fps = samples_per_second; + microseconds_per_frame_ = base::Time::kMicrosecondsPerSecond / fps; +} + +void AudioTimestampHelper::SetBaseTimestamp(base::TimeDelta base_timestamp) { + base_timestamp_ = base_timestamp; + frame_count_ = 0; +} + +base::TimeDelta AudioTimestampHelper::base_timestamp() const { + return base_timestamp_; +} + +void AudioTimestampHelper::AddFrames(int frame_count) { + DCHECK_GE(frame_count, 0); + DCHECK(base_timestamp_ != kNoTimestamp()); + frame_count_ += frame_count; +} + +base::TimeDelta AudioTimestampHelper::GetTimestamp() const { + return ComputeTimestamp(frame_count_); +} + +base::TimeDelta AudioTimestampHelper::GetFrameDuration(int frame_count) const { + DCHECK_GE(frame_count, 0); + base::TimeDelta end_timestamp = ComputeTimestamp(frame_count_ + frame_count); + return end_timestamp - GetTimestamp(); +} + +int64 AudioTimestampHelper::GetFramesToTarget(base::TimeDelta target) const { + DCHECK(base_timestamp_ != kNoTimestamp()); + DCHECK(target >= base_timestamp_); + + int64 delta_in_us = (target - GetTimestamp()).InMicroseconds(); + if (delta_in_us == 0) + return 0; + + // Compute a timestamp relative to |base_timestamp_| since timestamps + // created from |frame_count_| are computed relative to this base. + // This ensures that the time to frame computation here is the proper inverse + // of the frame to time computation in ComputeTimestamp(). + base::TimeDelta delta_from_base = target - base_timestamp_; + + // Compute frame count for the time delta. This computation rounds to + // the nearest whole number of frames. + double threshold = microseconds_per_frame_ / 2; + int64 target_frame_count = + (delta_from_base.InMicroseconds() + threshold) / microseconds_per_frame_; + return target_frame_count - frame_count_; +} + +base::TimeDelta AudioTimestampHelper::ComputeTimestamp( + int64 frame_count) const { + DCHECK_GE(frame_count, 0); + DCHECK(base_timestamp_ != kNoTimestamp()); + double frames_us = microseconds_per_frame_ * frame_count; + return base_timestamp_ + base::TimeDelta::FromMicroseconds(frames_us); +} + +} // namespace media diff --git a/media/base/audio_timestamp_helper.h b/media/base/audio_timestamp_helper.h new file mode 100644 index 0000000000..1da8b4a7cd --- /dev/null +++ b/media/base/audio_timestamp_helper.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef MEDIA_BASE_AUDIO_TIMESTAMP_HELPER_H_ +#define MEDIA_BASE_AUDIO_TIMESTAMP_HELPER_H_ + +#include "base/time/time.h" +#include "media/base/media_export.h" + +namespace media { + +// Generates timestamps for a sequence of audio sample frames. This class should +// be used any place timestamps need to be calculated for a sequence of audio +// samples. It helps avoid timestamps inaccuracies caused by rounding/truncation +// in repeated sample count to timestamp conversions. +// +// The class is constructed with samples_per_second information so that it can +// convert audio sample frame counts into timestamps. After the object is +// constructed, SetBaseTimestamp() must be called to specify the starting +// timestamp of the audio sequence. As audio samples are received, their frame +// counts are added using AddFrames(). These frame counts are accumulated by +// this class so GetTimestamp() can be used to determine the timestamp for the +// samples that have been added. GetDuration() calculates the proper duration +// values for samples added to the current timestamp. GetFramesToTarget() +// determines the number of frames that need to be added/removed from the +// accumulated frames to reach a target timestamp. +class MEDIA_EXPORT AudioTimestampHelper { + public: + explicit AudioTimestampHelper(int samples_per_second); + + // Sets the base timestamp to |base_timestamp| and the sets count to 0. + void SetBaseTimestamp(base::TimeDelta base_timestamp); + + base::TimeDelta base_timestamp() const; + int64 frame_count() const { return frame_count_; } + + // Adds |frame_count| to the frame counter. + // Note: SetBaseTimestamp() must be called with a value other than + // kNoTimestamp() before this method can be called. + void AddFrames(int frame_count); + + // Get the current timestamp. This value is computed from the base_timestamp() + // and the number of sample frames that have been added so far. + base::TimeDelta GetTimestamp() const; + + // Gets the duration if |frame_count| frames were added to the current + // timestamp reported by GetTimestamp(). This method ensures that + // (GetTimestamp() + GetFrameDuration(n)) will equal the timestamp that + // GetTimestamp() will return if AddFrames(n) is called. + base::TimeDelta GetFrameDuration(int frame_count) const; + + // Returns the number of frames needed to reach the target timestamp. + // Note: |target| must be >= |base_timestamp_|. + int64 GetFramesToTarget(base::TimeDelta target) const; + + private: + base::TimeDelta ComputeTimestamp(int64 frame_count) const; + + double microseconds_per_frame_; + + base::TimeDelta base_timestamp_; + + // Number of frames accumulated by AddFrames() calls. + int64 frame_count_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AudioTimestampHelper); +}; + +} // namespace media + +#endif diff --git a/media/base/audio_timestamp_helper_unittest.cc b/media/base/audio_timestamp_helper_unittest.cc new file mode 100644 index 0000000000..a0cfa3bbfa --- /dev/null +++ b/media/base/audio_timestamp_helper_unittest.cc @@ -0,0 +1,122 @@ +// 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 "media/base/audio_timestamp_helper.h" +#include "media/base/buffers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +static const int kDefaultSampleRate = 44100; + +class AudioTimestampHelperTest : public ::testing::Test { + public: + AudioTimestampHelperTest() : helper_(kDefaultSampleRate) { + helper_.SetBaseTimestamp(base::TimeDelta()); + } + + // Adds frames to the helper and returns the current timestamp in + // microseconds. + int64 AddFrames(int frames) { + helper_.AddFrames(frames); + return helper_.GetTimestamp().InMicroseconds(); + } + + int64 FramesToTarget(int target_in_microseconds) { + return helper_.GetFramesToTarget( + base::TimeDelta::FromMicroseconds(target_in_microseconds)); + } + + void TestGetFramesToTargetRange(int frame_count, int start, int end) { + for (int i = start; i <= end; ++i) { + EXPECT_EQ(frame_count, FramesToTarget(i)) << " Failure for timestamp " + << i << " us."; + } + } + + protected: + AudioTimestampHelper helper_; + + DISALLOW_COPY_AND_ASSIGN(AudioTimestampHelperTest); +}; + +TEST_F(AudioTimestampHelperTest, Basic) { + EXPECT_EQ(0, helper_.GetTimestamp().InMicroseconds()); + + // Verify that the output timestamp is always rounded down to the + // nearest microsecond. 1 frame @ 44100 is ~22.67573 microseconds, + // which is why the timestamp sometimes increments by 23 microseconds + // and other times it increments by 22 microseconds. + EXPECT_EQ(0, AddFrames(0)); + EXPECT_EQ(22, AddFrames(1)); + EXPECT_EQ(45, AddFrames(1)); + EXPECT_EQ(68, AddFrames(1)); + EXPECT_EQ(90, AddFrames(1)); + EXPECT_EQ(113, AddFrames(1)); + + // Verify that adding frames one frame at a time matches the timestamp + // returned if the same number of frames are added all at once. + base::TimeDelta timestamp_1 = helper_.GetTimestamp(); + helper_.SetBaseTimestamp(kNoTimestamp()); + EXPECT_TRUE(kNoTimestamp() == helper_.base_timestamp()); + helper_.SetBaseTimestamp(base::TimeDelta()); + EXPECT_EQ(0, helper_.GetTimestamp().InMicroseconds()); + + helper_.AddFrames(5); + EXPECT_EQ(113, helper_.GetTimestamp().InMicroseconds()); + EXPECT_TRUE(timestamp_1 == helper_.GetTimestamp()); +} + + +TEST_F(AudioTimestampHelperTest, GetDuration) { + helper_.SetBaseTimestamp(base::TimeDelta::FromMicroseconds(100)); + + int frame_count = 5; + int64 expected_durations[] = { 113, 113, 114, 113, 113, 114 }; + for (size_t i = 0; i < arraysize(expected_durations); ++i) { + base::TimeDelta duration = helper_.GetFrameDuration(frame_count); + EXPECT_EQ(expected_durations[i], duration.InMicroseconds()); + + base::TimeDelta timestamp_1 = helper_.GetTimestamp() + duration; + helper_.AddFrames(frame_count); + base::TimeDelta timestamp_2 = helper_.GetTimestamp(); + EXPECT_TRUE(timestamp_1 == timestamp_2); + } +} + +TEST_F(AudioTimestampHelperTest, GetFramesToTarget) { + // Verify GetFramesToTarget() rounding behavior. + // 1 frame @ 44100 is ~22.67573 microseconds, + + // Test values less than half of the frame duration. + TestGetFramesToTargetRange(0, 0, 11); + + // Test values between half the frame duration & the + // full frame duration. + TestGetFramesToTargetRange(1, 12, 22); + + // Verify that the same number of frames is returned up + // to the next half a frame. + TestGetFramesToTargetRange(1, 23, 34); + + // Verify the next 3 ranges. + TestGetFramesToTargetRange(2, 35, 56); + TestGetFramesToTargetRange(3, 57, 79); + TestGetFramesToTargetRange(4, 80, 102); + TestGetFramesToTargetRange(5, 103, 124); + + // Add frames to the helper so negative frame counts can be tested. + helper_.AddFrames(5); + + // Note: The timestamp ranges must match the positive values + // tested above to verify that the code is rounding properly. + TestGetFramesToTargetRange(0, 103, 124); + TestGetFramesToTargetRange(-1, 80, 102); + TestGetFramesToTargetRange(-2, 57, 79); + TestGetFramesToTargetRange(-3, 35, 56); + TestGetFramesToTargetRange(-4, 12, 34); + TestGetFramesToTargetRange(-5, 0, 11); +} + +} // namespace media diff --git a/media/formats/mpeg/adts_constants.cc b/media/formats/mpeg/adts_constants.cc new file mode 100644 index 0000000000..bc898908fa --- /dev/null +++ b/media/formats/mpeg/adts_constants.cc @@ -0,0 +1,27 @@ +// Copyright 2014 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/formats/mpeg/adts_constants.h" + +#include "base/macros.h" + +namespace media { + +// The following conversion table is extracted from ISO 14496 Part 3 - +// Table 1.16 - Sampling Frequency Index. +const int kADTSFrequencyTable[] = {96000, 88200, 64000, 48000, 44100, + 32000, 24000, 22050, 16000, 12000, + 11025, 8000, 7350}; +const size_t kADTSFrequencyTableSize = arraysize(kADTSFrequencyTable); + +// The following conversion table is extracted from ISO 14496 Part 3 - +// Table 1.17 - Channel Configuration. +const media::ChannelLayout kADTSChannelLayoutTable[] = { + media::CHANNEL_LAYOUT_NONE, media::CHANNEL_LAYOUT_MONO, + media::CHANNEL_LAYOUT_STEREO, media::CHANNEL_LAYOUT_SURROUND, + media::CHANNEL_LAYOUT_4_0, media::CHANNEL_LAYOUT_5_0_BACK, + media::CHANNEL_LAYOUT_5_1_BACK, media::CHANNEL_LAYOUT_7_1}; +const size_t kADTSChannelLayoutTableSize = arraysize(kADTSChannelLayoutTable); + +} // namespace media diff --git a/media/formats/mpeg/adts_constants.h b/media/formats/mpeg/adts_constants.h new file mode 100644 index 0000000000..aa8ea2015c --- /dev/null +++ b/media/formats/mpeg/adts_constants.h @@ -0,0 +1,28 @@ +// Copyright 2014 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_FORMATS_MPEG_ADTS_CONSTANTS_H_ +#define MEDIA_FORMATS_MPEG_ADTS_CONSTANTS_H_ + +#include + +#include "media/base/channel_layout.h" +#include "media/base/media_export.h" + +namespace media { + +enum { + kADTSHeaderMinSize = 7, + kSamplesPerAACFrame = 1024, +}; + +MEDIA_EXPORT extern const int kADTSFrequencyTable[]; +MEDIA_EXPORT extern const size_t kADTSFrequencyTableSize; + +MEDIA_EXPORT extern const media::ChannelLayout kADTSChannelLayoutTable[]; +MEDIA_EXPORT extern const size_t kADTSChannelLayoutTableSize; + +} // namespace media + +#endif // MEDIA_FORMATS_MPEG_ADTS_CONSTANTS_H_