Bug 1341483 - MP4Metadata::GetNumberTracks() now also returns a success/error code - r?kinetik draft
authorGerald Squelart <gsquelart@mozilla.com>
Mon, 27 Feb 2017 13:36:59 +1100
changeset 560827 5fdb247997247e282232b05ba95c0300ebcc0582
parent 560826 0f0824d4def213e0f1e311106a0546cbf056bae7
child 560828 3b4997e74995cc88af1642d03ccc32c8d01c44ae
push id53551
push usergsquelart@mozilla.com
push dateTue, 11 Apr 2017 23:47:44 +0000
reviewerskinetik
bugs1341483
milestone55.0a1
Bug 1341483 - MP4Metadata::GetNumberTracks() now also returns a success/error code - r?kinetik In addition to the returned MediaResult, a special number-of-tracks value (not just 0) indicates an unrecoverable error. For Rust-vs-Stagefright comparison purposes, an error is considered the same as 0 (because Stagefright never returns errors, but Rust may, so complaining about that would be too noisy, and useless to us.) MozReview-Commit-ID: IwadWSOIWr4
dom/media/MediaPrefs.h
dom/media/fmp4/MP4Demuxer.cpp
media/libstagefright/binding/MP4Metadata.cpp
media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
media/libstagefright/gtest/TestParser.cpp
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -178,16 +178,18 @@ private:
 #if defined(OS_LINUX)
   DECL_MEDIA_PREF("media.rust.mp4parser",                     EnableRustMP4Parser, bool, true);
 #else
   DECL_MEDIA_PREF("media.rust.mp4parser",                     EnableRustMP4Parser, bool, false);
 #endif
 
   // Error/warning handling, Decoder Doctor
   DECL_MEDIA_PREF("media.playback.warnings-as-errors",        MediaWarningsAsErrors, bool, false);
+  DECL_MEDIA_PREF("media.playback.warnings-as-errors.stagefright-vs-rust",
+                                                              MediaWarningsAsErrorsStageFrightVsRust, bool, false);
 
 public:
   // Manage the singleton:
   static MediaPrefs& GetSingleton();
   static bool SingletonExists();
 
 private:
   template<class T> friend class StaticAutoPtr;
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <algorithm>
 #include <limits>
 #include <stdint.h>
 
 #include "MP4Demuxer.h"
 
+#include "MediaPrefs.h"
 // Used for telemetry
 #include "mozilla/Telemetry.h"
 #include "mp4_demuxer/AnnexB.h"
 #include "mp4_demuxer/H264.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "mp4_demuxer/MP4Metadata.h"
 #include "mp4_demuxer/ResourceStream.h"
 #include "mp4_demuxer/BufferStream.h"
@@ -141,27 +142,58 @@ MP4Demuxer::Init()
   }
 
   RefPtr<mp4_demuxer::BufferStream> bufferstream =
     new mp4_demuxer::BufferStream(initData.Ref());
 
   mp4_demuxer::MP4Metadata metadata{bufferstream};
 
   auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
+  if (audioTrackCount.Ref() == mp4_demuxer::MP4Metadata::NumberTracksError()) {
+    if (MediaPrefs::MediaWarningsAsErrors()) {
+      return InitPromise::CreateAndReject(
+        MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                    RESULT_DETAIL("Invalid audio track (%s)",
+                                  audioTrackCount.Result().Description().get())),
+        __func__);
+    }
+    audioTrackCount.Ref() = 0;
+  }
+
   auto videoTrackCount = metadata.GetNumberTracks(TrackInfo::kVideoTrack);
-  if (audioTrackCount == 0 && videoTrackCount == 0) {
+  if (videoTrackCount.Ref() == mp4_demuxer::MP4Metadata::NumberTracksError()) {
+    if (MediaPrefs::MediaWarningsAsErrors()) {
+      return InitPromise::CreateAndReject(
+        MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                    RESULT_DETAIL("Invalid video track (%s)",
+                                  videoTrackCount.Result().Description().get())),
+        __func__);
+    }
+    videoTrackCount.Ref() = 0;
+  }
+
+  if (audioTrackCount.Ref() == 0 && videoTrackCount.Ref() == 0) {
     return InitPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
-                  RESULT_DETAIL("No MP4 audio or video tracks")),
+                  RESULT_DETAIL("No MP4 audio (%s) or video (%s) tracks",
+                                audioTrackCount.Result().Description().get(),
+                                videoTrackCount.Result().Description().get())),
       __func__);
   }
 
-  if (audioTrackCount != 0) {
-    mAudioDemuxers.SetLength(audioTrackCount);
-    for (size_t i = 0; i < audioTrackCount; i++) {
+  if (NS_FAILED(audioTrackCount.Result()) && result == NS_OK) {
+    result = Move(audioTrackCount.Result());
+  }
+  if (NS_FAILED(videoTrackCount.Result()) && result == NS_OK) {
+    result = Move(videoTrackCount.Result());
+  }
+
+  if (audioTrackCount.Ref() != 0) {
+    mAudioDemuxers.SetLength(audioTrackCount.Ref());
+    for (size_t i = 0; i < audioTrackCount.Ref(); i++) {
       UniquePtr<TrackInfo> info =
         metadata.GetTrackInfo(TrackInfo::kAudioTrack, i);
       if (!info) {
         if (result == NS_OK) {
           result = MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                                RESULT_DETAIL("Invalid MP4 audio track"));
         }
         continue;
@@ -175,19 +207,19 @@ MP4Demuxer::Init()
                         RESULT_DETAIL("Invalid MP4 audio track index list"));
         }
         continue;
       }
       mAudioDemuxers[i] = new MP4TrackDemuxer(this, Move(info), *indices.get());
     }
   }
 
-  if (videoTrackCount != 0) {
-    mVideoDemuxers.SetLength(videoTrackCount);
-    for (size_t i = 0; i < videoTrackCount; i++) {
+  if (videoTrackCount.Ref() != 0) {
+    mVideoDemuxers.SetLength(videoTrackCount.Ref());
+    for (size_t i = 0; i < videoTrackCount.Ref(); i++) {
       UniquePtr<TrackInfo> info =
         metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
       if (!info) {
         if (result == NS_OK) {
           result = MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                                RESULT_DETAIL("Invalid MP4 video track"));
         }
         continue;
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -76,17 +76,18 @@ private:
 class MP4MetadataStagefright
 {
 public:
   explicit MP4MetadataStagefright(Stream* aSource);
   ~MP4MetadataStagefright();
 
   static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
 
-  uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
+  MP4Metadata::ResultAndTrackCount
+  GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
   mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                                                       size_t aTrackNumber) const;
   bool CanSeek() const;
 
   const CryptoFile& Crypto() const;
 
   bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
 
@@ -124,17 +125,18 @@ private:
 class MP4MetadataRust
 {
 public:
   explicit MP4MetadataRust(Stream* aSource);
   ~MP4MetadataRust();
 
   static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
 
-  uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
+  MP4Metadata::ResultAndTrackCount
+  GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
   mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                                                       size_t aTrackNumber) const;
   bool CanSeek() const;
 
   const CryptoFile& Crypto() const;
 
   bool ReadTrackIndice(mp4parse_byte_data* aIndices, mozilla::TrackID aTrackID);
 
@@ -259,75 +261,130 @@ TrackTypeToString(mozilla::TrackInfo::Tr
     return "audio";
   case mozilla::TrackInfo::kVideoTrack:
     return "video";
   default:
     return "unknown";
   }
 }
 
-uint32_t
+MP4Metadata::ResultAndTrackCount
 MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
 {
-  uint32_t numTracks = mStagefright->GetNumberTracks(aType);
+  MP4Metadata::ResultAndTrackCount numTracks =
+    mStagefright->GetNumberTracks(aType);
 
   if (!mRust) {
     return numTracks;
   }
 
-  uint32_t numTracksRust = mRust->GetNumberTracks(aType);
-  MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=%u rust=%u",
-                                 TrackTypeToString(aType), numTracks, numTracksRust));
+  MP4Metadata::ResultAndTrackCount numTracksRust =
+    mRust->GetNumberTracks(aType);
+  MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=(%s)%u rust=(%s)%u",
+                                 TrackTypeToString(aType),
+                                 numTracks.Result().Description().get(),
+                                 numTracks.Ref(),
+                                 numTracksRust.Result().Description().get(),
+                                 numTracksRust.Ref()));
 
-  bool numTracksMatch = numTracks == numTracksRust;
+  // Consider '0' and 'error' the same for comparison purposes.
+  // (Mostly because Stagefright never returns errors, but Rust may.)
+  bool numTracksMatch =
+    (numTracks.Ref() != NumberTracksError() ? numTracks.Ref() : 0) ==
+    (numTracksRust.Ref() != NumberTracksError() ? numTracksRust.Ref() : 0);
 
   if (aType == mozilla::TrackInfo::kAudioTrack && !mReportedAudioTrackTelemetry) {
     Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO,
                           numTracksMatch);
     mReportedAudioTrackTelemetry = true;
   } else if (aType == mozilla::TrackInfo::kVideoTrack && !mReportedVideoTrackTelemetry) {
     Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO,
                             numTracksMatch);
     mReportedVideoTrackTelemetry = true;
   }
 
+  if (!numTracksMatch &&
+      MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()) {
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("Different numbers of tracks: "
+                                      "Stagefright=%u (%s) Rust=%u (%s)",
+                                      numTracks.Ref(),
+                                      numTracks.Result().Description().get(),
+                                      numTracksRust.Ref(),
+                                      numTracksRust.Result().Description().get())),
+            NumberTracksError()};
+  }
+
+  // If we prefer Rust, just return it.
   if (mPreferRust || ShouldPreferRust()) {
     MOZ_LOG(sLog, LogLevel::Info, ("Preferring rust demuxer"));
     mPreferRust = true;
     return numTracksRust;
   }
 
-  return numTracks;
+  // If numbers are different, return the stagefright number with a warning.
+  if (!numTracksMatch) {
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("Different numbers of tracks: "
+                                      "Stagefright=%u (%s) Rust=%u (%s)",
+                                      numTracks.Ref(),
+                                      numTracks.Result().Description().get(),
+                                      numTracksRust.Ref(),
+                                      numTracksRust.Result().Description().get())),
+            numTracks.Ref()};
+  }
+
+  // Numbers are effectively the same.
+
+  // Error(s) -> Combine both messages to get more details out.
+  if (numTracks.Ref() == NumberTracksError() ||
+      numTracksRust.Ref() == NumberTracksError()) {
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("Errors: "
+                                      "Stagefright=(%s) Rust=(%s)",
+                                      numTracks.Result().Description().get(),
+                                      numTracksRust.Result().Description().get())),
+            numTracks.Ref()};
+  }
+
+  // Same non-error numbers, just return any.
+  // (Choosing Rust here, in case it carries a warning, we'd want to know that.)
+  return numTracksRust;
 }
 
 bool MP4Metadata::ShouldPreferRust() const {
   if (!mRust) {
     return false;
   }
   // See if there's an Opus track.
-  uint32_t numTracks = mRust->GetNumberTracks(TrackInfo::kAudioTrack);
-  for (auto i = 0; i < numTracks; i++) {
-    auto info = mRust->GetTrackInfo(TrackInfo::kAudioTrack, i);
-    if (!info) {
-      return false;
-    }
-    if (info->mMimeType.EqualsASCII("audio/opus") ||
-        info->mMimeType.EqualsASCII("audio/flac")) {
-      return true;
+  MP4Metadata::ResultAndTrackCount numTracks =
+    mRust->GetNumberTracks(TrackInfo::kAudioTrack);
+  if (numTracks.Ref() != NumberTracksError()) {
+    for (auto i = 0; i < numTracks.Ref(); i++) {
+      auto info = mRust->GetTrackInfo(TrackInfo::kAudioTrack, i);
+      if (!info) {
+        return false;
+      }
+      if (info->mMimeType.EqualsASCII("audio/opus") ||
+          info->mMimeType.EqualsASCII("audio/flac")) {
+        return true;
+      }
     }
   }
 
   numTracks = mRust->GetNumberTracks(TrackInfo::kVideoTrack);
-  for (auto i = 0; i < numTracks; i++) {
-    auto info = mRust->GetTrackInfo(TrackInfo::kVideoTrack, i);
-    if (!info) {
-      return false;
-    }
-    if (info->mMimeType.EqualsASCII("video/vp9")) {
-      return true;
+  if (numTracks.Ref() != NumberTracksError()) {
+    for (auto i = 0; i < numTracks.Ref(); i++) {
+      auto info = mRust->GetTrackInfo(TrackInfo::kVideoTrack, i);
+      if (!info) {
+        return false;
+      }
+      if (info->mMimeType.EqualsASCII("video/vp9")) {
+        return true;
+      }
     }
   }
   // Otherwise, fall back.
   return false;
 }
 
 mozilla::UniquePtr<mozilla::TrackInfo>
 MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
@@ -496,17 +553,17 @@ MP4MetadataStagefright::MP4MetadataStage
     UpdateCrypto(metaData.get());
   }
 }
 
 MP4MetadataStagefright::~MP4MetadataStagefright()
 {
 }
 
-uint32_t
+MP4Metadata::ResultAndTrackCount
 MP4MetadataStagefright::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
 {
   size_t tracks = mMetadataExtractor->countTracks();
   uint32_t total = 0;
   for (size_t i = 0; i < tracks; i++) {
     sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i);
 
     const char* mimeType;
@@ -525,17 +582,17 @@ MP4MetadataStagefright::GetNumberTracks(
             CheckTrack(mimeType, metaData.get(), i)) {
           total++;
         }
         break;
       default:
         break;
     }
   }
-  return total;
+  return {NS_OK, total};
 }
 
 mozilla::UniquePtr<mozilla::TrackInfo>
 MP4MetadataStagefright::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                                      size_t aTrackNumber) const
 {
   size_t tracks = mMetadataExtractor->countTracks();
   if (!tracks) {
@@ -782,41 +839,43 @@ TrackTypeEqual(TrackInfo::TrackType aLHS
     return aRHS == mp4parse_track_type_AUDIO;
   case TrackInfo::kVideoTrack:
     return aRHS == mp4parse_track_type_VIDEO;
   default:
     return false;
   }
 }
 
-uint32_t
+MP4Metadata::ResultAndTrackCount
 MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
 {
   uint32_t tracks;
   auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
   if (rv != mp4parse_status_OK) {
     MOZ_LOG(sLog, LogLevel::Warning,
         ("rust parser error %d counting tracks", rv));
-    return 0;
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("Rust parser error %d", rv)),
+            MP4Metadata::NumberTracksError()};
   }
   MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks));
 
   uint32_t total = 0;
   for (uint32_t i = 0; i < tracks; ++i) {
     mp4parse_track_info track_info;
     rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info);
     if (rv != mp4parse_status_OK) {
       continue;
     }
     if (TrackTypeEqual(aType, track_info.track_type)) {
         total += 1;
     }
   }
 
-  return total;
+  return {NS_OK, total};
 }
 
 Maybe<uint32_t>
 MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const
 {
   uint32_t tracks;
   auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks);
   if (rv != mp4parse_status_OK) {
--- a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
@@ -58,17 +58,19 @@ public:
   private:
     mozilla::MediaResult mResult;
     typename mozilla::Decay<T>::Type mT;
   };
 
   using ResultAndByteBuffer = ResultAndType<RefPtr<mozilla::MediaByteBuffer>>;
   static ResultAndByteBuffer Metadata(Stream* aSource);
 
-  uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
+  static constexpr uint32_t NumberTracksError() { return UINT32_MAX; }
+  using ResultAndTrackCount = ResultAndType<uint32_t>;
+  ResultAndTrackCount GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
   mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                                                       size_t aTrackNumber) const;
   bool CanSeek() const;
 
   const CryptoFile& Crypto() const;
 
   mozilla::UniquePtr<IndiceWrapper> GetTrackIndice(mozilla::TrackID aTrackID);
 
--- a/media/libstagefright/gtest/TestParser.cpp
+++ b/media/libstagefright/gtest/TestParser.cpp
@@ -10,16 +10,18 @@
 #include "mozilla/Preferences.h"
 #include "mp4_demuxer/BufferStream.h"
 #include "mp4_demuxer/MP4Metadata.h"
 #include "mp4_demuxer/MoofParser.h"
 
 using namespace mozilla;
 using namespace mp4_demuxer;
 
+static const uint32_t E = mp4_demuxer::MP4Metadata::NumberTracksError();
+
 class TestStream : public Stream
 {
 public:
   TestStream(const uint8_t* aBuffer, size_t aSize)
     : mHighestSuccessfulEndOffset(0)
     , mBuffer(aBuffer)
     , mSize(aSize)
   {
@@ -75,21 +77,26 @@ TEST(stagefright_MP4Metadata, EmptyStrea
   RefPtr<Stream> stream = new TestStream(nullptr, 0);
 
   MP4Metadata::ResultAndByteBuffer metadataBuffer =
     MP4Metadata::Metadata(stream);
   EXPECT_TRUE(NS_OK != metadataBuffer.Result());
   EXPECT_FALSE(static_cast<bool>(metadataBuffer.Ref()));
 
   MP4Metadata metadata(stream);
-  EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack));
-  EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack));
-  EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
-  EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack));
-  EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)));
+  EXPECT_TRUE(0u == metadata.GetNumberTracks(TrackInfo::kUndefinedTrack).Ref() ||
+              E == metadata.GetNumberTracks(TrackInfo::kUndefinedTrack).Ref());
+  EXPECT_TRUE(0u == metadata.GetNumberTracks(TrackInfo::kAudioTrack).Ref() ||
+              E == metadata.GetNumberTracks(TrackInfo::kAudioTrack).Ref());
+  EXPECT_TRUE(0u == metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref() ||
+              E == metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref());
+  EXPECT_TRUE(0u == metadata.GetNumberTracks(TrackInfo::kTextTrack).Ref() ||
+              E == metadata.GetNumberTracks(TrackInfo::kTextTrack).Ref());
+  EXPECT_TRUE(0u == metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)).Ref() ||
+              E == metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)).Ref());
   EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0));
   EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0));
   EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0));
   EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0));
   EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0));
   // We can seek anywhere in any MPEG4.
   EXPECT_TRUE(metadata.CanSeek());
   EXPECT_FALSE(metadata.Crypto().valid);
@@ -207,33 +214,33 @@ static const TestFileData testFiles[] = 
   { "test_case_1301065-u64max.mp4", 0, -1,   0,   0, 1,  0, false,   0, false, false, 2 },
   { "test_case_1329061.mov",        0, -1,   0,   0, 1,  234567981,
                                                             false,   0, false, false, 2 },
   { "test_case_1351094.mp4",        0, -1,   0,   0, 0, -1, false,   0, true,  true,  0 },
 };
 
 static const TestFileData rustTestFiles[] = {
   // filename                      #V dur   w    h  #A dur  crypt        off   moof  headr  audio_profile
-  { "test_case_1156505.mp4",        0, -1,   0,   0, 0, -1, false, 152, false, false, 0 },
+  { "test_case_1156505.mp4",        E, -1,   0,   0, E, -1, false, 152, false, false, 0 },
   { "test_case_1181213.mp4",        1, 416666,
                                            320, 240, 1, 477460,
                                                              true,   0, false, false, 2 },
-  { "test_case_1181215.mp4",        0, -1,   0,   0, 0, -1, false,   0, false, false, 0 },
-  { "test_case_1181220.mp4",        0, -1,   0,   0, 0, -1, false,   0, false, false, 0 },
+  { "test_case_1181215.mp4",        E, -1,   0,   0, E, -1, false,   0, false, false, 0 },
+  { "test_case_1181220.mp4",        E, -1,   0,   0, E, -1, false,   0, false, false, 0 },
   { "test_case_1181223.mp4",        1, 416666,
                                            320, 240, 0, -1, false,   0, false, false, 0 },
-  { "test_case_1181719.mp4",        0, -1,   0,   0, 0, -1, false,   0, false, false, 0 },
+  { "test_case_1181719.mp4",        E, -1,   0,   0, E, -1, false,   0, false, false, 0 },
   { "test_case_1185230.mp4",        2, 416666,
                                            320, 240, 2,  5, false,   0, false, false, 2 },
   { "test_case_1187067.mp4",        1, 80000,
                                            160,  90, 0, -1, false,   0, false, false, 0 },
   { "test_case_1200326.mp4",        0, -1,   0,   0, 0, -1, false,   0, false, false, 0 },
   { "test_case_1204580.mp4",        1, 502500,
                                            320, 180, 0, -1, false,   0, false, false, 0 },
-  { "test_case_1216748.mp4",        0, -1,   0,   0, 0, -1, false, 152, false, false, 0 },
+  { "test_case_1216748.mp4",        E, -1,   0,   0, E, -1, false, 152, false, false, 0 },
   { "test_case_1296473.mp4",        0, -1,   0,   0, 0, -1, false,   0, false, false, 0 },
   { "test_case_1296532.mp4",        1, 5589333,
                                            560, 320, 1, 5589333,
                                                             true,    0, true,  true,  2  },
   { "test_case_1301065.mp4",        0, -1,   0,   0, 1, 100079991719000000,
                                                             false,   0, false, false, 2 },
   { "test_case_1301065-u32max.mp4", 0, -1,   0,   0, 1, 97391548639,
                                                             false,   0, false, false, 2 },
@@ -278,26 +285,35 @@ TEST(stagefright_MPEG4Metadata, test_cas
       RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
 
       MP4Metadata::ResultAndByteBuffer metadataBuffer =
         MP4Metadata::Metadata(stream);
       EXPECT_EQ(NS_OK, metadataBuffer.Result());
       EXPECT_TRUE(metadataBuffer.Ref());
 
       MP4Metadata metadata(stream);
-      EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack));
       EXPECT_EQ(tests[test].mNumberAudioTracks,
-                metadata.GetNumberTracks(TrackInfo::kAudioTrack));
+                metadata.GetNumberTracks(TrackInfo::kAudioTrack).Ref())
+        << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
       EXPECT_EQ(tests[test].mNumberVideoTracks,
-                metadata.GetNumberTracks(TrackInfo::kVideoTrack));
-      EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack));
-      EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)));
+                metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref())
+        << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
+      // If there is an error, we should expect an error code instead of zero
+      // for non-Audio/Video tracks.
+      const uint32_t None = (tests[test].mNumberVideoTracks == E) ? E : 0;
+      EXPECT_EQ(None, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack).Ref())
+        << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
+      EXPECT_EQ(None, metadata.GetNumberTracks(TrackInfo::kTextTrack).Ref())
+        << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
+      EXPECT_EQ(None, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1)).Ref())
+        << (rust ? "rust/" : "stagefright/") << tests[test].mFilename;
       EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0));
       UniquePtr<TrackInfo> trackInfo = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
-      if (tests[test].mNumberVideoTracks == 0) {
+      if (tests[test].mNumberVideoTracks == 0 ||
+          tests[test].mNumberVideoTracks == E) {
         EXPECT_TRUE(!trackInfo);
       } else {
         ASSERT_TRUE(!!trackInfo);
         const VideoInfo* videoInfo = trackInfo->GetAsVideoInfo();
         ASSERT_TRUE(!!videoInfo);
         EXPECT_TRUE(videoInfo->IsValid());
         EXPECT_TRUE(videoInfo->IsVideo());
         EXPECT_EQ(tests[test].mVideoDuration, videoInfo->mDuration);
@@ -309,17 +325,18 @@ TEST(stagefright_MPEG4Metadata, test_cas
         for (size_t i = 0; i < indices->Length(); i++) {
           Index::Indice data;
           EXPECT_TRUE(indices->GetIndice(i, data));
           EXPECT_TRUE(data.start_offset <= data.end_offset);
           EXPECT_TRUE(data.start_composition <= data.end_composition);
         }
       }
       trackInfo = metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0);
-      if (tests[test].mNumberAudioTracks == 0) {
+      if (tests[test].mNumberAudioTracks == 0 ||
+          tests[test].mNumberAudioTracks == E) {
         EXPECT_TRUE(!trackInfo);
       } else {
         ASSERT_TRUE(!!trackInfo);
         const AudioInfo* audioInfo = trackInfo->GetAsAudioInfo();
         ASSERT_TRUE(!!audioInfo);
         EXPECT_TRUE(audioInfo->IsValid());
         EXPECT_TRUE(audioInfo->IsAudio());
         EXPECT_EQ(tests[test].mAudioDuration, audioInfo->mDuration);
@@ -559,17 +576,17 @@ TEST(stagefright_MP4Metadata, EmptyCTTS)
 
   MP4Metadata::ResultAndByteBuffer metadataBuffer =
     MP4Metadata::Metadata(stream);
   EXPECT_EQ(NS_OK, metadataBuffer.Result());
   EXPECT_TRUE(metadataBuffer.Ref());
 
   MP4Metadata metadata(stream);
 
-  EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
+  EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack).Ref());
   mozilla::UniquePtr<mozilla::TrackInfo> track =
     metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
   EXPECT_TRUE(track != nullptr);
   // We can seek anywhere in any MPEG4.
   EXPECT_TRUE(metadata.CanSeek());
   EXPECT_FALSE(metadata.Crypto().valid);
 }