Bug 1500713: P1 - MP3 Test for the files with VBRI headers. r=kinetik
☠☠ backed out by 5edbe9b1b822 ☠ ☠
authorChun-Min Chang <chun.m.chang@gmail.com>
Thu, 14 Mar 2019 17:18:26 +0000
changeset 521952 49c4cfbf2beee350e31e4e43e2314735ab6fbc6b
parent 521951 a5beba8cc1907dd96ad78dca466b656620768af2
child 521953 2633cea7d11903a6d29f260ed133fd5ebfb3597a
push id10870
push usernbeleuzu@mozilla.com
push dateFri, 15 Mar 2019 20:00:07 +0000
treeherdermozilla-beta@c594aee5b7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1500713
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1500713: P1 - MP3 Test for the files with VBRI headers. r=kinetik Differential Revision: https://phabricator.services.mozilla.com/D19096
dom/media/gtest/TestMP3Demuxer.cpp
dom/media/gtest/moz.build
dom/media/gtest/test_vbri.mp3
--- a/dom/media/gtest/TestMP3Demuxer.cpp
+++ b/dom/media/gtest/TestMP3Demuxer.cpp
@@ -42,18 +42,21 @@ class MockMP3StreamMediaResource
 
   int64_t GetLength() override { return -1; }
 
  protected:
   virtual ~MockMP3StreamMediaResource() {}
 };
 
 struct MP3Resource {
+  enum HeaderType { NONE, XING, VBRI };
+
   const char* mFilePath;
   bool mIsVBR;
+  HeaderType mHeaderType;
   int64_t mFileSize;
   int32_t mMPEGLayer;
   int32_t mMPEGVersion;
   uint8_t mID3MajorVersion;
   uint8_t mID3MinorVersion;
   uint8_t mID3Flags;
   uint32_t mID3Size;
 
@@ -78,16 +81,17 @@ struct MP3Resource {
 
 class MP3DemuxerTest : public ::testing::Test {
  protected:
   void SetUp() override {
     {
       MP3Resource res;
       res.mFilePath = "noise.mp3";
       res.mIsVBR = false;
+      res.mHeaderType = MP3Resource::NONE;
       res.mFileSize = 965257;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 3;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 2141;
       res.mDuration = 30067000;
@@ -122,16 +126,17 @@ class MP3DemuxerTest : public ::testing:
       MP3Resource res;
       // This file trips up the MP3 demuxer if ID3v2 tags aren't properly
       // skipped. If skipping is not properly implemented, depending on the
       // strictness of the MPEG frame parser a false sync will be detected
       // somewhere within the metadata at or after 112087, or failing that, at
       // the artificially added extraneous header at 114532.
       res.mFilePath = "id3v2header.mp3";
       res.mIsVBR = false;
+      res.mHeaderType = MP3Resource::NONE;
       res.mFileSize = 191302;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 3;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 115304;
       res.mDuration = 3166167;
@@ -161,16 +166,17 @@ class MP3DemuxerTest : public ::testing:
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
     {
       MP3Resource res;
       res.mFilePath = "noise_vbr.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::XING;
       res.mFileSize = 583679;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 3;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 2221;
       res.mDuration = 30081000;
@@ -199,16 +205,17 @@ class MP3DemuxerTest : public ::testing:
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
     {
       MP3Resource res;
       res.mFilePath = "small-shot.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::XING;
       res.mFileSize = 6825;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 4;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 24;
       res.mDuration = 336686;
@@ -239,16 +246,17 @@ class MP3DemuxerTest : public ::testing:
     }
 
     {
       MP3Resource res;
       // This file contains a false frame sync at 34, just after the ID3 tag,
       // which should be identified as a false positive and skipped.
       res.mFilePath = "small-shot-false-positive.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::XING;
       res.mFileSize = 6845;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 4;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 24;
       res.mDuration = 336686;
@@ -277,16 +285,17 @@ class MP3DemuxerTest : public ::testing:
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
     {
       MP3Resource res;
       res.mFilePath = "small-shot-partial-xing.mp3";
       res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::XING;
       res.mFileSize = 6825;
       res.mMPEGLayer = 3;
       res.mMPEGVersion = 1;
       res.mID3MajorVersion = 4;
       res.mID3MinorVersion = 0;
       res.mID3Flags = 0;
       res.mID3Size = 24;
       res.mDuration = 336686;
@@ -311,16 +320,55 @@ class MP3DemuxerTest : public ::testing:
       res.mDemuxer = new MP3TrackDemuxer(res.mResource);
       mTargets.push_back(res);
 
       streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
+    {
+      MP3Resource res;
+      res.mFilePath = "test_vbri.mp3";
+      res.mIsVBR = true;
+      res.mHeaderType = MP3Resource::VBRI;
+      res.mFileSize = 16519;
+      res.mMPEGLayer = 3;
+      res.mMPEGVersion = 1;
+      res.mID3MajorVersion = 3;
+      res.mID3MinorVersion = 0;
+      res.mID3Flags = 0;
+      res.mID3Size = 4202;
+      res.mDuration = 783660;
+      res.mDurationError = 0.01f;
+      res.mSeekError = 0.02f;
+      res.mSampleRate = 44100;
+      res.mSamplesPerFrame = 1152;
+      res.mNumSamples = 29;
+      res.mNumTrailingFrames = 0;
+      res.mBitrate = 0;
+      res.mSlotSize = 1;
+      res.mPrivate = 0;
+      const int syncs[] = {4212, 4734, 5047, 5464, 5986, 6403};
+      res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 6);
+
+      // VBR stream resources contain header info on total frames numbers, which
+      // is used to estimate the total duration.
+      MP3Resource streamRes = res;
+      streamRes.mFileSize = -1;
+
+      res.mResource = new MockMP3MediaResource(res.mFilePath);
+      res.mDemuxer = new MP3TrackDemuxer(res.mResource);
+      mTargets.push_back(res);
+
+      streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
+      streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
+      mTargets.push_back(streamRes);
+    }
+
     for (auto& target : mTargets) {
       ASSERT_EQ(NS_OK, target.mResource->Open());
       ASSERT_TRUE(target.mDemuxer->Init());
     }
   }
 
   std::vector<MP3Resource> mTargets;
 };
@@ -342,22 +390,25 @@ TEST_F(MP3DemuxerTest, ID3Tags) {
 
 TEST_F(MP3DemuxerTest, VBRHeader) {
   for (const auto& target : mTargets) {
     RefPtr<MediaRawData> frame(target.mDemuxer->DemuxSample());
     ASSERT_TRUE(frame);
 
     const auto& vbr = target.mDemuxer->VBRInfo();
 
-    if (target.mIsVBR) {
+    if (target.mHeaderType == MP3Resource::XING) {
       EXPECT_EQ(FrameParser::VBRHeader::XING, vbr.Type());
       // TODO: find reference number which accounts for trailing headers.
       // EXPECT_EQ(target.mNumSamples / target.mSamplesPerFrame,
       // vbr.NumAudioFrames().value());
-    } else {
+    } else if (target.mHeaderType == MP3Resource::VBRI) {
+      EXPECT_TRUE(target.mIsVBR);
+      EXPECT_EQ(FrameParser::VBRHeader::VBRI, vbr.Type());
+    } else {  // MP3Resource::NONE
       EXPECT_EQ(FrameParser::VBRHeader::NONE, vbr.Type());
       EXPECT_FALSE(vbr.NumAudioFrames());
     }
   }
 }
 
 TEST_F(MP3DemuxerTest, FrameParsing) {
   for (const auto& target : mTargets) {
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -68,16 +68,17 @@ TEST_HARNESS_FILES.gtest += [
     'short-zero-inband.mov',
     'small-shot-false-positive.mp3',
     'small-shot-partial-xing.mp3',
     'small-shot.mp3',
     'test.webm',
     'test_case_1224361.vp8.ivf',
     'test_case_1224363.vp8.ivf',
     'test_case_1224369.vp8.ivf',
+    'test_vbri.mp3',
 ]
 
 TEST_DIRS += [
     'mp4_demuxer',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..efd74503385c9d8c6afaf59a8923cf67ebd71805
GIT binary patch
literal 16519
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
RO7CGzPyM{||8^tz{{j-3?G*q3