Bug 1341483 - MP4Metadata::GetTrackInfo() now also returns a success/error code - r?kinetik draft
authorGerald Squelart <gsquelart@mozilla.com>
Mon, 27 Feb 2017 17:05:41 +1100
changeset 560828 3b4997e74995cc88af1642d03ccc32c8d01c44ae
parent 560827 5fdb247997247e282232b05ba95c0300ebcc0582
child 560829 53766f81f74ef80a491427523039c2c8d9703147
push id53551
push usergsquelart@mozilla.com
push dateTue, 11 Apr 2017 23:47:44 +0000
reviewerskinetik
bugs1341483
milestone55.0a1
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);
 }