Maybe<uint32_t> MP4Metadata::TrackTypeToGlobalTrackIndex( mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const { uint32_t tracks; auto rv = mp4parse_get_track_count(mParser.get(), &tracks); if (rv != MP4PARSE_STATUS_OK) { return Nothing(); } /* The MP4Metadata API uses a per-TrackType index of tracks, but mp4parse (and libstagefright) use a global track index. Convert the index by counting the tracks of the requested type and returning the global track index when a match is found. */ uint32_t perType = 0; for (uint32_t i = 0; i < tracks; ++i) { Mp4parseTrackInfo track_info; rv = mp4parse_get_track_info(mParser.get(), i, &track_info); if (rv != MP4PARSE_STATUS_OK) { continue; } if (TrackTypeEqual(aType, track_info.track_type)) { if (perType == aTrackNumber) { return Some(i); } perType += 1; } } return Nothing(); }
uint32_t MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const { static LazyLogModule sLog("MP4Metadata"); uint32_t tracks = mp4parse_get_track_count(mRustState.get()); 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; int32_t rv = mp4parse_get_track_info(mRustState.get(), i, &track_info); if (rv != MP4PARSE_OK) { continue; } switch (aType) { case mozilla::TrackInfo::kAudioTrack: if (track_info.track_type == MP4PARSE_TRACK_TYPE_AAC) { total += 1; } break; case mozilla::TrackInfo::kVideoTrack: if (track_info.track_type == MP4PARSE_TRACK_TYPE_H264) { total += 1; } break; default: break; } } return total; }
uint32_t MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const { #ifdef MOZ_RUST_MP4PARSE // Try in rust first. mRustState.reset(mp4parse_new()); int32_t rust_tracks = 0; bool rust_mp4parse_success = try_rust(mRustState, mSource, &rust_tracks); Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS, rust_mp4parse_success); #endif size_t tracks = mPrivate->mMetadataExtractor->countTracks(); uint32_t total = 0; for (size_t i = 0; i < tracks; i++) { sp<MetaData> metaData = mPrivate->mMetadataExtractor->getTrackMetaData(i); const char* mimeType; if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) { continue; } switch (aType) { case mozilla::TrackInfo::kAudioTrack: if (!strncmp(mimeType, "audio/", 6) && CheckTrack(mimeType, metaData.get(), i)) { total++; } break; case mozilla::TrackInfo::kVideoTrack: if (!strncmp(mimeType, "video/", 6) && CheckTrack(mimeType, metaData.get(), i)) { total++; } break; default: break; } } #ifdef MOZ_RUST_MP4PARSE uint32_t rust_total = 0; const char* rust_track_type = nullptr; if (rust_mp4parse_success && rust_tracks > 0) { for (int32_t i = 0; i < rust_tracks; ++i) { mp4parse_track_info track_info; int32_t r = mp4parse_get_track_info(mRustState.get(), i, &track_info); switch (aType) { case mozilla::TrackInfo::kAudioTrack: rust_track_type = "audio"; if (r == 0 && track_info.track_type == MP4PARSE_TRACK_TYPE_AAC) { rust_total += 1; } break; case mozilla::TrackInfo::kVideoTrack: rust_track_type = "video"; if (r == 0 && track_info.track_type == MP4PARSE_TRACK_TYPE_H264) { rust_total += 1; } break; default: break; } } } static LazyLogModule sLog("MP4Metadata"); MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=%u rust=%u", rust_track_type, total, rust_total)); #endif return total; }
MP4Metadata::ResultAndTrackInfo MP4Metadata::GetTrackInfo( mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const { Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber); if (trackIndex.isNothing()) { return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("No %s tracks", TrackTypeToStr(aType))), nullptr}; } Mp4parseTrackInfo info; auto rv = mp4parse_get_track_info(mParser.get(), trackIndex.value(), &info); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot find %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } #ifdef DEBUG bool haveSampleInfo = false; const char* codecString = "unrecognized"; Mp4parseCodec codecType = MP4PARSE_CODEC_UNKNOWN; if (info.track_type == MP4PARSE_TRACK_TYPE_AUDIO) { Mp4parseTrackAudioInfo audio; auto rv = mp4parse_get_track_audio_info(mParser.get(), trackIndex.value(), &audio); if (rv == MP4PARSE_STATUS_OK && audio.sample_info_count > 0) { codecType = audio.sample_info[0].codec_type; haveSampleInfo = true; } } else if (info.track_type == MP4PARSE_TRACK_TYPE_VIDEO) { Mp4parseTrackVideoInfo video; auto rv = mp4parse_get_track_video_info(mParser.get(), trackIndex.value(), &video); if (rv == MP4PARSE_STATUS_OK && video.sample_info_count > 0) { codecType = video.sample_info[0].codec_type; haveSampleInfo = true; } } if (haveSampleInfo) { switch (codecType) { case MP4PARSE_CODEC_UNKNOWN: codecString = "unknown"; break; case MP4PARSE_CODEC_AAC: codecString = "aac"; break; case MP4PARSE_CODEC_OPUS: codecString = "opus"; break; case MP4PARSE_CODEC_FLAC: codecString = "flac"; break; case MP4PARSE_CODEC_ALAC: codecString = "alac"; break; case MP4PARSE_CODEC_AVC: codecString = "h.264"; break; case MP4PARSE_CODEC_VP9: codecString = "vp9"; break; case MP4PARSE_CODEC_AV1: codecString = "av1"; break; case MP4PARSE_CODEC_MP3: codecString = "mp3"; break; case MP4PARSE_CODEC_MP4V: codecString = "mp4v"; break; case MP4PARSE_CODEC_JPEG: codecString = "jpeg"; break; case MP4PARSE_CODEC_AC3: codecString = "ac-3"; break; case MP4PARSE_CODEC_EC3: codecString = "ec-3"; break; } } MOZ_LOG(gMP4MetadataLog, LogLevel::Debug, ("track codec %s (%u)\n", codecString, codecType)); #endif // This specialization interface is crazy. UniquePtr<mozilla::TrackInfo> e; switch (aType) { case TrackInfo::TrackType::kAudioTrack: { Mp4parseTrackAudioInfo audio; auto rv = mp4parse_get_track_audio_info(mParser.get(), trackIndex.value(), &audio); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot parse %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } auto track = mozilla::MakeUnique<MP4AudioInfo>(); MediaResult updateStatus = track->Update(&info, &audio); if (NS_FAILED(updateStatus)) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("Updating audio track failed with %s", updateStatus.Message().get())); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL( "Failed to update %s track #%zu with error: %s", TrackTypeToStr(aType), aTrackNumber, updateStatus.Message().get())), nullptr}; } e = std::move(track); } break; case TrackInfo::TrackType::kVideoTrack: { Mp4parseTrackVideoInfo video; auto rv = mp4parse_get_track_video_info(mParser.get(), trackIndex.value(), &video); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Cannot parse %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; } auto track = mozilla::MakeUnique<MP4VideoInfo>(); MediaResult updateStatus = track->Update(&info, &video); if (NS_FAILED(updateStatus)) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("Updating video track failed with %s", updateStatus.Message().get())); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL( "Failed to update %s track #%zu with error: %s", TrackTypeToStr(aType), aTrackNumber, updateStatus.Message().get())), nullptr}; } e = std::move(track); } break; default: MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("unhandled track type %d", aType)); 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.IsPositive()) { Mp4parseFragmentInfo info; auto rv = mp4parse_get_fragment_info(mParser.get(), &info); if (rv == MP4PARSE_STATUS_OK) { e->mDuration = TimeUnit::FromMicroseconds(info.fragment_duration); } } if (e && e->IsValid()) { return {NS_OK, std::move(e)}; } MOZ_LOG(gMP4MetadataLog, LogLevel::Debug, ("TrackInfo didn't validate")); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Invalid %s track #%zu", TrackTypeToStr(aType), aTrackNumber)), nullptr}; }
MP4Metadata::ResultAndTrackCount MP4Metadata::GetNumberTracks( mozilla::TrackInfo::TrackType aType) const { uint32_t tracks; auto rv = mp4parse_get_track_count(mParser.get(), &tracks); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("rust parser error %d counting tracks", rv)); return {MediaResult(NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL("Rust parser error %d", rv)), MP4Metadata::NumberTracksError()}; } uint32_t total = 0; for (uint32_t i = 0; i < tracks; ++i) { Mp4parseTrackInfo track_info; rv = mp4parse_get_track_info(mParser.get(), i, &track_info); if (rv != MP4PARSE_STATUS_OK) { continue; } if (track_info.track_type == MP4PARSE_TRACK_TYPE_AUDIO) { Mp4parseTrackAudioInfo audio; auto rv = mp4parse_get_track_audio_info(mParser.get(), i, &audio); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv)); continue; } MOZ_DIAGNOSTIC_ASSERT(audio.sample_info_count > 0, "Must have at least one audio sample info"); if (audio.sample_info_count == 0) { return { MediaResult( NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL( "Got 0 audio sample info while checking number tracks")), MP4Metadata::NumberTracksError()}; } // We assume the codec of the first sample info is representative of the // whole track and skip it if we don't recognize the codec. if (audio.sample_info[0].codec_type == MP4PARSE_CODEC_UNKNOWN) { continue; } } else if (track_info.track_type == MP4PARSE_TRACK_TYPE_VIDEO) { Mp4parseTrackVideoInfo video; auto rv = mp4parse_get_track_video_info(mParser.get(), i, &video); if (rv != MP4PARSE_STATUS_OK) { MOZ_LOG(gMP4MetadataLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv)); continue; } MOZ_DIAGNOSTIC_ASSERT(video.sample_info_count > 0, "Must have at least one video sample info"); if (video.sample_info_count == 0) { return { MediaResult( NS_ERROR_DOM_MEDIA_METADATA_ERR, RESULT_DETAIL( "Got 0 video sample info while checking number tracks")), MP4Metadata::NumberTracksError()}; } // We assume the codec of the first sample info is representative of the // whole track and skip it if we don't recognize the codec. if (video.sample_info[0].codec_type == MP4PARSE_CODEC_UNKNOWN) { continue; } } else { // Only audio and video are supported continue; } if (TrackTypeEqual(aType, track_info.track_type)) { total += 1; } } MOZ_LOG(gMP4MetadataLog, LogLevel::Info, ("%s tracks found: %u", TrackTypeToString(aType), total)); return {NS_OK, total}; }