--- 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/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);
}