Bug 1341483 - MP4Metadata::GetTrackInfo() now also returns a success/error code - r=kinetik
authorGerald Squelart <gsquelart@mozilla.com>
Mon, 27 Feb 2017 17:05:41 +1100
changeset 352594 9106e5740bdde3389e5a6518987bac9a4084396e
parent 352593 8e992d6e36e447db040d1853fdc5e3d2eba27b76
child 352595 361f7a093694ab332375deddb774e8bf7bb02f00
push id89122
push userkwierso@gmail.com
push dateThu, 13 Apr 2017 01:24:36 +0000
treeherdermozilla-inbound@b31c8835bfc3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1341483
milestone55.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 1341483 - MP4Metadata::GetTrackInfo() now also returns a success/error code - r=kinetik MozReview-Commit-ID: KQ3fJzZkrGW
dom/media/MediaInfo.cpp
dom/media/MediaInfo.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/MediaInfo.cpp
+++ b/dom/media/MediaInfo.cpp
@@ -6,20 +6,19 @@
 
 #include "MediaInfo.h"
 
 namespace mozilla {
 
 const char*
 TrackTypeToStr(TrackInfo::TrackType aTrack)
 {
-  MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack
-             || aTrack == TrackInfo::kVideoTrack
-             || aTrack == TrackInfo::kTextTrack);
   switch (aTrack) {
+  case TrackInfo::kUndefinedTrack:
+    return "Undefined";
   case TrackInfo::kAudioTrack:
     return "Audio";
   case TrackInfo::kVideoTrack:
     return "Video";
   case TrackInfo::kTextTrack:
     return "Text";
   default:
     return "Unknown";
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -173,17 +173,17 @@ protected:
     mTags = aOther.mTags;
     MOZ_COUNT_CTOR(TrackInfo);
   }
 
 private:
   TrackType mType;
 };
 
-// String version of track type. Don't use with kUndefinedTrack.
+// String version of track type.
 const char* TrackTypeToStr(TrackInfo::TrackType aTrack);
 
 // Stores info relevant to presenting media frames.
 class VideoInfo : public TrackInfo
 {
 public:
   enum Rotation
   {
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -184,62 +184,82 @@ MP4Demuxer::Init()
   }
   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 =
+      mp4_demuxer::MP4Metadata::ResultAndTrackInfo info =
         metadata.GetTrackInfo(TrackInfo::kAudioTrack, i);
-      if (!info) {
+      if (!info.Ref()) {
+        if (MediaPrefs::MediaWarningsAsErrors()) {
+          return InitPromise::CreateAndReject(
+            MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                        RESULT_DETAIL("Invalid MP4 audio track (%s)",
+                                      info.Result().Description().get())),
+            __func__);
+        }
         if (result == NS_OK) {
           result = MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
-                               RESULT_DETAIL("Invalid MP4 audio track"));
+                               RESULT_DETAIL("Invalid MP4 audio track (%s)",
+                                             info.Result().Description().get()));
         }
         continue;
+      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
+        result = Move(info.Result());
       }
       UniquePtr<mp4_demuxer::IndiceWrapper> indices =
-        metadata.GetTrackIndice(info->mTrackId);
+        metadata.GetTrackIndice(info.Ref()->mTrackId);
       if (!indices) {
         if (result == NS_OK) {
           result =
             MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                         RESULT_DETAIL("Invalid MP4 audio track index list"));
         }
         continue;
       }
-      mAudioDemuxers[i] = new MP4TrackDemuxer(this, Move(info), *indices.get());
+      mAudioDemuxers[i] = new MP4TrackDemuxer(this, Move(info.Ref()), *indices.get());
     }
   }
 
   if (videoTrackCount.Ref() != 0) {
     mVideoDemuxers.SetLength(videoTrackCount.Ref());
     for (size_t i = 0; i < videoTrackCount.Ref(); i++) {
-      UniquePtr<TrackInfo> info =
+      mp4_demuxer::MP4Metadata::ResultAndTrackInfo info =
         metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
-      if (!info) {
+      if (!info.Ref()) {
+        if (MediaPrefs::MediaWarningsAsErrors()) {
+          return InitPromise::CreateAndReject(
+            MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
+                        RESULT_DETAIL("Invalid MP4 video track (%s)",
+                                      info.Result().Description().get())),
+            __func__);
+        }
         if (result == NS_OK) {
           result = MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
-                               RESULT_DETAIL("Invalid MP4 video track"));
+                               RESULT_DETAIL("Invalid MP4 video track (%s)",
+                                             info.Result().Description().get()));
         }
         continue;
+      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
+        result = Move(info.Result());
       }
       UniquePtr<mp4_demuxer::IndiceWrapper> indices =
-        metadata.GetTrackIndice(info->mTrackId);
+        metadata.GetTrackIndice(info.Ref()->mTrackId);
       if (!indices) {
         if (result == NS_OK) {
           result =
             MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
                         RESULT_DETAIL("Invalid MP4 video track index list"));
         }
         continue;
       }
-      mVideoDemuxers[i] = new MP4TrackDemuxer(this, Move(info), *indices.get());
+      mVideoDemuxers[i] = new MP4TrackDemuxer(this, Move(info.Ref()), *indices.get());
     }
   }
 
   const mp4_demuxer::CryptoFile& cryptoFile = metadata.Crypto();
   if (cryptoFile.valid) {
     const nsTArray<mp4_demuxer::PsshInfo>& psshs = cryptoFile.pssh;
     for (uint32_t i = 0; i < psshs.Length(); i++) {
       mCryptoInitData.AppendElements(psshs[i].data);
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -78,18 +78,18 @@ class MP4MetadataStagefright
 public:
   explicit MP4MetadataStagefright(Stream* aSource);
   ~MP4MetadataStagefright();
 
   static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
 
   MP4Metadata::ResultAndTrackCount
   GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
-  mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
-                                                      size_t aTrackNumber) const;
+  MP4Metadata::ResultAndTrackInfo 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);
 
 private:
   int32_t GetTrackNumber(mozilla::TrackID aTrackID);
@@ -127,18 +127,18 @@ class MP4MetadataRust
 public:
   explicit MP4MetadataRust(Stream* aSource);
   ~MP4MetadataRust();
 
   static MP4Metadata::ResultAndByteBuffer Metadata(Stream* aSource);
 
   MP4Metadata::ResultAndTrackCount
   GetNumberTracks(mozilla::TrackInfo::TrackType aType) const;
-  mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType,
-                                                      size_t aTrackNumber) const;
+  MP4Metadata::ResultAndTrackInfo 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);
 
 private:
   void UpdateCrypto();
@@ -354,102 +354,166 @@ bool MP4Metadata::ShouldPreferRust() con
   if (!mRust) {
     return false;
   }
   // See if there's an Opus track.
   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) {
+      MP4Metadata::ResultAndTrackInfo info =
+        mRust->GetTrackInfo(TrackInfo::kAudioTrack, i);
+      if (!info.Ref()) {
         return false;
       }
-      if (info->mMimeType.EqualsASCII("audio/opus") ||
-          info->mMimeType.EqualsASCII("audio/flac")) {
+      if (info.Ref()->mMimeType.EqualsASCII("audio/opus") ||
+          info.Ref()->mMimeType.EqualsASCII("audio/flac")) {
         return true;
       }
     }
   }
 
   numTracks = mRust->GetNumberTracks(TrackInfo::kVideoTrack);
   if (numTracks.Ref() != NumberTracksError()) {
     for (auto i = 0; i < numTracks.Ref(); i++) {
-      auto info = mRust->GetTrackInfo(TrackInfo::kVideoTrack, i);
-      if (!info) {
+      MP4Metadata::ResultAndTrackInfo info =
+        mRust->GetTrackInfo(TrackInfo::kVideoTrack, i);
+      if (!info.Ref()) {
         return false;
       }
-      if (info->mMimeType.EqualsASCII("video/vp9")) {
+      if (info.Ref()->mMimeType.EqualsASCII("video/vp9")) {
         return true;
       }
     }
   }
   // Otherwise, fall back.
   return false;
 }
 
-mozilla::UniquePtr<mozilla::TrackInfo>
+static const char*
+GetDifferentField(const mozilla::TrackInfo& info,
+                  const mozilla::TrackInfo& infoRust)
+{
+  if (infoRust.mId != info.mId) { return "Id"; }
+  if (infoRust.mKind == info.mKind) { return "Kind"; }
+  if (infoRust.mLabel == info.mLabel) { return "Label"; }
+  if (infoRust.mLanguage == info.mLanguage) { return "Language"; }
+  if (infoRust.mEnabled == info.mEnabled) { return "Enabled"; }
+  if (infoRust.mTrackId == info.mTrackId) { return "TrackId"; }
+  if (infoRust.mMimeType == info.mMimeType) { return "MimeType"; }
+  if (infoRust.mDuration == info.mDuration) { return "Duration"; }
+  if (infoRust.mMediaTime == info.mMediaTime) { return "MediaTime"; }
+  if (infoRust.mCrypto.mValid == info.mCrypto.mValid) { return "Crypto-Valid"; }
+  if (infoRust.mCrypto.mMode == info.mCrypto.mMode) { return "Crypto-Mode"; }
+  if (infoRust.mCrypto.mIVSize == info.mCrypto.mIVSize) { return "Crypto-IVSize"; }
+  if (infoRust.mCrypto.mKeyId == info.mCrypto.mKeyId) { return "Crypto-KeyId"; }
+  switch (info.GetType()) {
+  case mozilla::TrackInfo::kAudioTrack: {
+    const AudioInfo *audioRust = infoRust.GetAsAudioInfo();
+    const AudioInfo *audio = info.GetAsAudioInfo();
+    if (audioRust->mRate == audio->mRate) { return "Rate"; }
+    if (audioRust->mChannels == audio->mChannels) { return "Channels"; }
+    if (audioRust->mBitDepth == audio->mBitDepth) { return "BitDepth"; }
+    if (audioRust->mProfile == audio->mProfile) { return "Profile"; }
+    if (audioRust->mExtendedProfile == audio->mExtendedProfile) { return "ExtendedProfile"; }
+    break;
+  }
+  case mozilla::TrackInfo::kVideoTrack: {
+    const VideoInfo *videoRust = infoRust.GetAsVideoInfo();
+    const VideoInfo *video = info.GetAsVideoInfo();
+    if (videoRust->mDisplay == video->mDisplay) { return "Display"; }
+    if (videoRust->mImage == video->mImage) { return "Image"; }
+    if (*videoRust->mExtraData == *video->mExtraData) { return "ExtraData"; }
+    // mCodecSpecificConfig is for video/mp4-es, not video/avc. Since video/mp4-es
+    // is supported on b2g only, it could be removed from TrackInfo.
+    if (*videoRust->mCodecSpecificConfig == *video->mCodecSpecificConfig) { return "CodecSpecificConfig"; }
+    break;
+  }
+  default:
+    break;
+  }
+
+  return nullptr;
+}
+
+MP4Metadata::ResultAndTrackInfo
 MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                           size_t aTrackNumber) const
 {
-  mozilla::UniquePtr<mozilla::TrackInfo> info =
-      mStagefright->GetTrackInfo(aType, aTrackNumber);
+  MP4Metadata::ResultAndTrackInfo info =
+    mStagefright->GetTrackInfo(aType, aTrackNumber);
 
   if (!mRust) {
     return info;
   }
 
-  mozilla::UniquePtr<mozilla::TrackInfo> infoRust =
-      mRust->GetTrackInfo(aType, aTrackNumber);
+  MP4Metadata::ResultAndTrackInfo infoRust =
+    mRust->GetTrackInfo(aType, aTrackNumber);
 
 #ifndef RELEASE_OR_BETA
-  if (mRustTestMode && info) {
-    MOZ_DIAGNOSTIC_ASSERT(infoRust);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mId == info->mId);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mKind == info->mKind);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mLabel == info->mLabel);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mLanguage == info->mLanguage);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mEnabled == info->mEnabled);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mTrackId == info->mTrackId);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mMimeType == info->mMimeType);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mDuration == info->mDuration);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mMediaTime == info->mMediaTime);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mValid == info->mCrypto.mValid);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mMode == info->mCrypto.mMode);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mIVSize == info->mCrypto.mIVSize);
-    MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mKeyId == info->mCrypto.mKeyId);
+  if (mRustTestMode && info.Ref() && infoRust.Ref()) {
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mId == info.Ref()->mId);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mKind == info.Ref()->mKind);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mLabel == info.Ref()->mLabel);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mLanguage == info.Ref()->mLanguage);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mEnabled == info.Ref()->mEnabled);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mTrackId == info.Ref()->mTrackId);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mMimeType == info.Ref()->mMimeType);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mDuration == info.Ref()->mDuration);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mMediaTime == info.Ref()->mMediaTime);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mCrypto.mValid == info.Ref()->mCrypto.mValid);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mCrypto.mMode == info.Ref()->mCrypto.mMode);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mCrypto.mIVSize == info.Ref()->mCrypto.mIVSize);
+    MOZ_DIAGNOSTIC_ASSERT(infoRust.Ref()->mCrypto.mKeyId == info.Ref()->mCrypto.mKeyId);
     switch (aType) {
     case mozilla::TrackInfo::kAudioTrack: {
-      AudioInfo *audioRust = infoRust->GetAsAudioInfo(), *audio = info->GetAsAudioInfo();
+      AudioInfo *audioRust = infoRust.Ref()->GetAsAudioInfo();
+      AudioInfo *audio = info.Ref()->GetAsAudioInfo();
       MOZ_DIAGNOSTIC_ASSERT(audioRust->mRate == audio->mRate);
       MOZ_DIAGNOSTIC_ASSERT(audioRust->mChannels == audio->mChannels);
       MOZ_DIAGNOSTIC_ASSERT(audioRust->mBitDepth == audio->mBitDepth);
       MOZ_DIAGNOSTIC_ASSERT(audioRust->mProfile == audio->mProfile);
       MOZ_DIAGNOSTIC_ASSERT(audioRust->mExtendedProfile == audio->mExtendedProfile);
       MOZ_DIAGNOSTIC_ASSERT(*audioRust->mExtraData == *audio->mExtraData);
       MOZ_DIAGNOSTIC_ASSERT(*audioRust->mCodecSpecificConfig == *audio->mCodecSpecificConfig);
       break;
     }
     case mozilla::TrackInfo::kVideoTrack: {
-      VideoInfo *videoRust = infoRust->GetAsVideoInfo(), *video = info->GetAsVideoInfo();
+      VideoInfo *videoRust = infoRust.Ref()->GetAsVideoInfo();
+      VideoInfo *video = info.Ref()->GetAsVideoInfo();
       MOZ_DIAGNOSTIC_ASSERT(videoRust->mDisplay == video->mDisplay);
       MOZ_DIAGNOSTIC_ASSERT(videoRust->mImage == video->mImage);
       MOZ_DIAGNOSTIC_ASSERT(videoRust->mRotation == video->mRotation);
       MOZ_DIAGNOSTIC_ASSERT(*videoRust->mExtraData == *video->mExtraData);
       // mCodecSpecificConfig is for video/mp4-es, not video/avc. Since video/mp4-es
       // is supported on b2g only, it could be removed from TrackInfo.
       MOZ_DIAGNOSTIC_ASSERT(*videoRust->mCodecSpecificConfig == *video->mCodecSpecificConfig);
       break;
     }
     default:
       break;
     }
   }
 #endif
 
+  if (mRustTestMode && info.Ref() && infoRust.Ref()) {
+    const char* diff = GetDifferentField(*info.Ref(), *infoRust.Ref());
+    if (diff) {
+      return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                          RESULT_DETAIL("Different field '%s' between "
+                                        "Stagefright (%s) and Rust (%s)",
+                                        diff,
+                                        info.Result().Description().get(),
+                                        infoRust.Result().Description().get())),
+              MediaPrefs::MediaWarningsAsErrorsStageFrightVsRust()
+              ? mozilla::UniquePtr<mozilla::TrackInfo>(nullptr)
+              : mPreferRust ? Move(infoRust.Ref()) : Move(info.Ref())};
+    }
+  }
+
   if (mPreferRust) {
     return infoRust;
   }
 
   return info;
 }
 
 bool
@@ -585,23 +649,26 @@ MP4MetadataStagefright::GetNumberTracks(
         break;
       default:
         break;
     }
   }
   return {NS_OK, total};
 }
 
-mozilla::UniquePtr<mozilla::TrackInfo>
+MP4Metadata::ResultAndTrackInfo
 MP4MetadataStagefright::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                                      size_t aTrackNumber) const
 {
   size_t tracks = mMetadataExtractor->countTracks();
   if (!tracks) {
-    return nullptr;
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("No %s tracks",
+                                      TrackTypeToStr(aType))),
+            nullptr};
   }
   int32_t index = -1;
   const char* mimeType;
   sp<MetaData> metaData;
 
   size_t i = 0;
   while (i < tracks) {
     metaData = mMetadataExtractor->getTrackMetaData(i);
@@ -626,32 +693,36 @@ MP4MetadataStagefright::GetTrackInfo(moz
         break;
     }
     if (index == aTrackNumber) {
       break;
     }
     i++;
   }
   if (index < 0) {
-    return nullptr;
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("Cannot access %s track #%zu",
+                                      TrackTypeToStr(aType),
+                                      aTrackNumber)),
+            nullptr};
   }
 
   UniquePtr<mozilla::TrackInfo> e = CheckTrack(mimeType, metaData.get(), index);
 
   if (e) {
     metaData = mMetadataExtractor->getMetaData();
     int64_t movieDuration;
     if (!e->mDuration &&
         metaData->findInt64(kKeyMovieDuration, &movieDuration)) {
       // No duration in track, use movie extend header box one.
       e->mDuration = movieDuration;
     }
   }
 
-  return e;
+  return {NS_OK, Move(e)};
 }
 
 mozilla::UniquePtr<mozilla::TrackInfo>
 MP4MetadataStagefright::CheckTrack(const char* aMimeType,
                                    stagefright::MetaData* aMetaData,
                                    int32_t aIndex) const
 {
   sp<MediaSource> track = mMetadataExtractor->getTrack(aIndex);
@@ -899,30 +970,37 @@ MP4MetadataRust::TrackTypeToGlobalTrackI
       }
       perType += 1;
     }
   }
 
   return Nothing();
 }
 
-mozilla::UniquePtr<mozilla::TrackInfo>
+MP4Metadata::ResultAndTrackInfo
 MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
                               size_t aTrackNumber) const
 {
   Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber);
   if (trackIndex.isNothing()) {
-    return nullptr;
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("No %s tracks",
+                                      TrackTypeToStr(aType))),
+            nullptr};
   }
 
   mp4parse_track_info info;
   auto rv = mp4parse_get_track_info(mRustParser.get(), trackIndex.value(), &info);
   if (rv != mp4parse_status_OK) {
     MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv));
-    return nullptr;
+    return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                        RESULT_DETAIL("Cannot find %s track #%zu",
+                                      TrackTypeToStr(aType),
+                                      aTrackNumber)),
+            nullptr};
   }
 #ifdef DEBUG
   const char* codec_string = "unrecognized";
   switch (info.codec) {
     case mp4parse_codec_UNKNOWN: codec_string = "unknown"; break;
     case mp4parse_codec_AAC: codec_string = "aac"; break;
     case mp4parse_codec_OPUS: codec_string = "opus"; break;
     case mp4parse_codec_FLAC: codec_string = "flac"; break;
@@ -937,56 +1015,71 @@ MP4MetadataRust::GetTrackInfo(mozilla::T
   // This specialization interface is crazy.
   UniquePtr<mozilla::TrackInfo> e;
   switch (aType) {
     case TrackInfo::TrackType::kAudioTrack: {
       mp4parse_track_audio_info audio;
       auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio);
       if (rv != mp4parse_status_OK) {
         MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv));
-        return nullptr;
+        return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                            RESULT_DETAIL("Cannot parse %s track #%zu",
+                                          TrackTypeToStr(aType),
+                                          aTrackNumber)),
+                nullptr};
       }
       auto track = mozilla::MakeUnique<MP4AudioInfo>();
       track->Update(&info, &audio);
       e = Move(track);
     }
     break;
     case TrackInfo::TrackType::kVideoTrack: {
       mp4parse_track_video_info video;
       auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video);
       if (rv != mp4parse_status_OK) {
         MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv));
-        return nullptr;
+        return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                            RESULT_DETAIL("Cannot parse %s track #%zu",
+                                          TrackTypeToStr(aType),
+                                          aTrackNumber)),
+                nullptr};
       }
       auto track = mozilla::MakeUnique<MP4VideoInfo>();
       track->Update(&info, &video);
       e = Move(track);
     }
     break;
     default:
       MOZ_LOG(sLog, LogLevel::Warning, ("unhandled track type %d", aType));
-      return nullptr;
-      break;
+      return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                          RESULT_DETAIL("Cannot handle %s track #%zu",
+                                        TrackTypeToStr(aType),
+                                        aTrackNumber)),
+              nullptr};
   }
 
   // No duration in track, use fragment_duration.
   if (e && !e->mDuration) {
     mp4parse_fragment_info info;
     auto rv = mp4parse_get_fragment_info(mRustParser.get(), &info);
     if (rv == mp4parse_status_OK) {
       e->mDuration = info.fragment_duration;
     }
   }
 
   if (e && e->IsValid()) {
-    return e;
+    return {NS_OK, Move(e)};
   }
   MOZ_LOG(sLog, LogLevel::Debug, ("TrackInfo didn't validate"));
 
-  return nullptr;
+  return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR,
+                      RESULT_DETAIL("Invalid %s track #%zu",
+                                    TrackTypeToStr(aType),
+                                    aTrackNumber)),
+          nullptr};
 }
 
 bool
 MP4MetadataRust::CanSeek() const
 {
   MOZ_ASSERT(false, "Not yet implemented");
   return false;
 }
--- a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h
@@ -61,18 +61,22 @@ public:
   };
 
   using ResultAndByteBuffer = ResultAndType<RefPtr<mozilla::MediaByteBuffer>>;
   static ResultAndByteBuffer Metadata(Stream* aSource);
 
   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;
+
+  using ResultAndTrackInfo =
+    ResultAndType<mozilla::UniquePtr<mozilla::TrackInfo>>;
+  ResultAndTrackInfo GetTrackInfo(mozilla::TrackInfo::TrackType aType,
+                                  size_t aTrackNumber) const;
+
   bool CanSeek() const;
 
   const CryptoFile& Crypto() const;
 
   mozilla::UniquePtr<IndiceWrapper> GetTrackIndice(mozilla::TrackID aTrackID);
 
 private:
   UniquePtr<MP4MetadataStagefright> mStagefright;
--- a/media/libstagefright/gtest/TestParser.cpp
+++ b/media/libstagefright/gtest/TestParser.cpp
@@ -87,21 +87,19 @@ TEST(stagefright_MP4Metadata, EmptyStrea
   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));
+  EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0).Ref());
+  EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0).Ref());
+  EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0).Ref());
   // We can seek anywhere in any MPEG4.
   EXPECT_TRUE(metadata.CanSeek());
   EXPECT_FALSE(metadata.Crypto().valid);
 }
 
 TEST(stagefright_MoofParser, EmptyStream)
 {
   RefPtr<Stream> stream = new TestStream(nullptr, 0);
@@ -300,24 +298,25 @@ TEST(stagefright_MPEG4Metadata, test_cas
       // 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);
+      EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0).Ref());
+      MP4Metadata::ResultAndTrackInfo trackInfo =
+        metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
       if (tests[test].mNumberVideoTracks == 0 ||
           tests[test].mNumberVideoTracks == E) {
-        EXPECT_TRUE(!trackInfo);
+        EXPECT_TRUE(!trackInfo.Ref());
       } else {
-        ASSERT_TRUE(!!trackInfo);
-        const VideoInfo* videoInfo = trackInfo->GetAsVideoInfo();
+        ASSERT_TRUE(!!trackInfo.Ref());
+        const VideoInfo* videoInfo = trackInfo.Ref()->GetAsVideoInfo();
         ASSERT_TRUE(!!videoInfo);
         EXPECT_TRUE(videoInfo->IsValid());
         EXPECT_TRUE(videoInfo->IsVideo());
         EXPECT_EQ(tests[test].mVideoDuration, videoInfo->mDuration);
         EXPECT_EQ(tests[test].mWidth, videoInfo->mDisplay.width);
         EXPECT_EQ(tests[test].mHeight, videoInfo->mDisplay.height);
 
         UniquePtr<IndiceWrapper> indices = metadata.GetTrackIndice(videoInfo->mTrackId);
@@ -327,20 +326,20 @@ TEST(stagefright_MPEG4Metadata, test_cas
           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 ||
           tests[test].mNumberAudioTracks == E) {
-        EXPECT_TRUE(!trackInfo);
+        EXPECT_TRUE(!trackInfo.Ref());
       } else {
-        ASSERT_TRUE(!!trackInfo);
-        const AudioInfo* audioInfo = trackInfo->GetAsAudioInfo();
+        ASSERT_TRUE(!!trackInfo.Ref());
+        const AudioInfo* audioInfo = trackInfo.Ref()->GetAsAudioInfo();
         ASSERT_TRUE(!!audioInfo);
         EXPECT_TRUE(audioInfo->IsValid());
         EXPECT_TRUE(audioInfo->IsAudio());
         EXPECT_EQ(tests[test].mAudioDuration, audioInfo->mDuration);
         EXPECT_EQ(tests[test].mAudioProfile, audioInfo->mProfile);
         if (tests[test].mAudioDuration != audioInfo->mDuration) {
           MOZ_RELEASE_ASSERT(false);
         }
@@ -349,18 +348,18 @@ TEST(stagefright_MPEG4Metadata, test_cas
         EXPECT_TRUE(!!indices);
         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(int64_t(data.start_composition) <= int64_t(data.end_composition));
         }
       }
-      EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0));
-      EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0));
+      EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0).Ref());
+      EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0).Ref());
       // We can see anywhere in any MPEG4.
       EXPECT_TRUE(metadata.CanSeek());
       EXPECT_EQ(tests[test].mHasCrypto, metadata.Crypto().valid);
     }
   }
 }
 
 // Bug 1224019: This test produces way to much output, disabling for now.
@@ -577,16 +576,15 @@ 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).Ref());
-  mozilla::UniquePtr<mozilla::TrackInfo> track =
-    metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
-  EXPECT_TRUE(track != nullptr);
+  MP4Metadata::ResultAndTrackInfo track = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
+  EXPECT_TRUE(track.Ref() != nullptr);
   // We can seek anywhere in any MPEG4.
   EXPECT_TRUE(metadata.CanSeek());
   EXPECT_FALSE(metadata.Crypto().valid);
 }