/* static */ bool MP4Decoder::IsSupportedType(const MediaContainerType& aType, DecoderDoctorDiagnostics* aDiagnostics) { if (!IsEnabled()) { return false; } // Whitelist MP4 types, so they explicitly match what we encounter on // the web, as opposed to what we use internally (i.e. what our demuxers // etc output). const bool isAudio = aType.Type() == MEDIAMIMETYPE("audio/mp4") || aType.Type() == MEDIAMIMETYPE("audio/x-m4a"); const bool isVideo = aType.Type() == MEDIAMIMETYPE("video/mp4") || aType.Type() == MEDIAMIMETYPE("video/quicktime") || aType.Type() == MEDIAMIMETYPE("video/x-m4v"); if (!isAudio && !isVideo) { return false; } nsTArray<UniquePtr<TrackInfo>> trackInfos; if (aType.ExtendedType().Codecs().IsEmpty()) { // No codecs specified. Assume H.264 if (isAudio) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); } else { MOZ_ASSERT(isVideo); trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType)); } } else { // Verify that all the codecs specified are ones that we expect that // we can play. for (const auto& codec : aType.ExtendedType().Codecs().Range()) { if (IsAACCodecString(codec)) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); continue; } if (codec.EqualsLiteral("mp3")) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mpeg"), aType)); continue; } if (codec.EqualsLiteral("opus")) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/opus"), aType)); continue; } if (codec.EqualsLiteral("flac")) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/flac"), aType)); continue; } if (IsVP9CodecString(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/vp9"), aType); uint8_t profile = 0; uint8_t level = 0; uint8_t bitDepth = 0; if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) { trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth; } trackInfos.AppendElement(Move(trackInfo)); continue; } // Note: Only accept H.264 in a video content type, not in an audio // content type. if (IsWhitelistedH264Codec(codec) && isVideo) { trackInfos.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType)); continue; } // Some unsupported codec. return false; } } // Verify that we have a PDM that supports the whitelisted types. RefPtr<PDMFactory> platform = new PDMFactory(); for (const auto& trackInfo : trackInfos) { if (!trackInfo || !platform->Supports(*trackInfo, aDiagnostics)) { return false; } } return true; }
/* static */ bool MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, const nsAString& aCodecs) { if (!IsEnabled()) { return false; } // Whitelist MP4 types, so they explicitly match what we encounter on // the web, as opposed to what we use internally (i.e. what our demuxers // etc output). const bool isMP4Audio = aMIMETypeExcludingCodecs.EqualsASCII("audio/mp4") || aMIMETypeExcludingCodecs.EqualsASCII("audio/x-m4a"); const bool isMP4Video = // On B2G, treat 3GPP as MP4 when Gonk PDM is available. #ifdef MOZ_GONK_MEDIACODEC aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP) || #endif aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") || aMIMETypeExcludingCodecs.EqualsASCII("video/quicktime") || aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v"); if (!isMP4Audio && !isMP4Video) { return false; } nsTArray<nsCString> codecMimes; if (aCodecs.IsEmpty()) { // No codecs specified. Assume AAC/H.264 if (isMP4Audio) { codecMimes.AppendElement(NS_LITERAL_CSTRING("audio/mp4a-latm")); } else { MOZ_ASSERT(isMP4Video); codecMimes.AppendElement(NS_LITERAL_CSTRING("video/avc")); } } else { // Verify that all the codecs specified are ones that we expect that // we can play. nsTArray<nsString> codecs; if (!ParseCodecsString(aCodecs, codecs)) { return false; } for (const nsString& codec : codecs) { if (IsAACCodecString(codec)) { codecMimes.AppendElement(NS_LITERAL_CSTRING("audio/mp4a-latm")); continue; } if (codec.EqualsLiteral("mp3")) { codecMimes.AppendElement(NS_LITERAL_CSTRING("audio/mpeg")); continue; } // Note: Only accept H.264 in a video content type, not in an audio // content type. if (IsWhitelistedH264Codec(codec) && isMP4Video) { codecMimes.AppendElement(NS_LITERAL_CSTRING("video/avc")); continue; } // Some unsupported codec. return false; } } // Verify that we have a PDM that supports the whitelisted types. PDMFactory::Init(); RefPtr<PDMFactory> platform = new PDMFactory(); for (const nsCString& codecMime : codecMimes) { if (!platform->SupportsMimeType(codecMime)) { return false; } } return true; }
/* statis */ nsTArray<UniquePtr<TrackInfo>> MP4Decoder::GetTracksInfo(const MediaContainerType& aType, MediaResult& aError) { nsTArray<UniquePtr<TrackInfo>> tracks; if (!IsTypeValid(aType)) { aError = MediaResult( NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Invalid type:%s", aType.Type().AsString().get())); return tracks; } aError = NS_OK; const MediaCodecs& codecs = aType.ExtendedType().Codecs(); if (codecs.IsEmpty()) { return tracks; } const bool isVideo = aType.Type() == MEDIAMIMETYPE("video/mp4") || aType.Type() == MEDIAMIMETYPE("video/quicktime") || aType.Type() == MEDIAMIMETYPE("video/x-m4v"); for (const auto& codec : codecs.Range()) { if (IsAACCodecString(codec)) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mp4a-latm"), aType)); continue; } if (codec.EqualsLiteral("mp3")) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/mpeg"), aType)); continue; } if (codec.EqualsLiteral("opus") || codec.EqualsLiteral("flac")) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("audio/") + NS_ConvertUTF16toUTF8(codec), aType)); continue; } if (IsVP9CodecString(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/vp9"), aType); uint8_t profile = 0; uint8_t level = 0; uint8_t bitDepth = 0; if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) { trackInfo->GetAsVideoInfo()->mColorDepth = gfx::ColorDepthForBitDepth(bitDepth); } tracks.AppendElement(std::move(trackInfo)); continue; } #ifdef MOZ_AV1 if (IsAV1CodecString(codec)) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/av1"), aType)); continue; } #endif if (isVideo && IsWhitelistedH264Codec(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/avc"), aType); uint8_t profile = 0, constraint = 0, level = 0; MOZ_ALWAYS_TRUE( ExtractH264CodecDetails(codec, profile, constraint, level)); uint32_t width = aType.ExtendedType().GetWidth().refOr(1280); uint32_t height = aType.ExtendedType().GetHeight().refOr(720); trackInfo->GetAsVideoInfo()->mExtraData = H264::CreateExtraData(profile, constraint, level, { width, height }); tracks.AppendElement(std::move(trackInfo)); continue; } // Unknown codec aError = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("Unknown codec:%s", NS_ConvertUTF16toUTF8(codec).get())); } return tracks; }