MP4Decoder.cpp (7310B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "MP4Decoder.h" 8 9 #include "H264.h" 10 #include "VPXDecoder.h" 11 #ifdef MOZ_AV1 12 # include "AOMDecoder.h" 13 #endif 14 #include "MP4Demuxer.h" 15 #include "MediaContainerType.h" 16 #include "PDMFactory.h" 17 #include "PlatformDecoderModule.h" 18 #include "VideoUtils.h" 19 #include "mozilla/StaticPrefs_media.h" 20 #include "mozilla/gfx/Tools.h" 21 #include "nsMimeTypes.h" 22 #include "nsReadableUtils.h" 23 24 namespace mozilla { 25 26 static bool IsTypeValid(const MediaContainerType& aType) { 27 // Whitelist MP4 types, so they explicitly match what we encounter on 28 // the web, as opposed to what we use internally (i.e. what our demuxers 29 // etc output). 30 return aType.Type() == MEDIAMIMETYPE("audio/mp4") || 31 aType.Type() == MEDIAMIMETYPE("audio/x-m4a") || 32 aType.Type() == MEDIAMIMETYPE("video/mp4") || 33 aType.Type() == MEDIAMIMETYPE("video/quicktime") || 34 aType.Type() == MEDIAMIMETYPE("video/x-m4v"); 35 } 36 37 /* statis */ 38 nsTArray<UniquePtr<TrackInfo>> MP4Decoder::GetTracksInfo( 39 const MediaContainerType& aType, MediaResult& aError) { 40 nsTArray<UniquePtr<TrackInfo>> tracks; 41 42 if (!IsTypeValid(aType)) { 43 aError = MediaResult( 44 NS_ERROR_DOM_MEDIA_FATAL_ERR, 45 RESULT_DETAIL("Invalid type:%s", aType.Type().AsString().get())); 46 return tracks; 47 } 48 49 aError = NS_OK; 50 51 const MediaCodecs& codecs = aType.ExtendedType().Codecs(); 52 if (codecs.IsEmpty()) { 53 return tracks; 54 } 55 56 const bool isVideo = aType.Type() == MEDIAMIMETYPE("video/mp4") || 57 aType.Type() == MEDIAMIMETYPE("video/quicktime") || 58 aType.Type() == MEDIAMIMETYPE("video/x-m4v"); 59 60 for (const auto& codec : codecs.Range()) { 61 if (IsAACCodecString(codec)) { 62 tracks.AppendElement( 63 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 64 "audio/mp4a-latm"_ns, aType)); 65 continue; 66 } 67 if (codec.EqualsLiteral("mp3")) { 68 tracks.AppendElement( 69 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 70 "audio/mpeg"_ns, aType)); 71 continue; 72 } 73 // The valid codecs parameter value with mp4 MIME types should be "Opus" and 74 // "fLaC", but "opus" and "flac" are acceptable due to historical reasons. 75 if (codec.EqualsLiteral("opus") || codec.EqualsLiteral("Opus") || 76 codec.EqualsLiteral("flac") || codec.EqualsLiteral("fLaC")) { 77 NS_ConvertUTF16toUTF8 c(codec); 78 ToLowerCase(c); 79 tracks.AppendElement( 80 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 81 "audio/"_ns + c, aType)); 82 continue; 83 } 84 if (IsVP9CodecString(codec)) { 85 auto trackInfo = 86 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 87 "video/vp9"_ns, aType); 88 VPXDecoder::SetVideoInfo(trackInfo->GetAsVideoInfo(), codec); 89 tracks.AppendElement(std::move(trackInfo)); 90 continue; 91 } 92 #ifdef MOZ_AV1 93 if (StaticPrefs::media_av1_enabled() && IsAV1CodecString(codec)) { 94 auto trackInfo = 95 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 96 "video/av1"_ns, aType); 97 AOMDecoder::SetVideoInfo(trackInfo->GetAsVideoInfo(), codec); 98 tracks.AppendElement(std::move(trackInfo)); 99 continue; 100 } 101 #endif 102 if (StaticPrefs::media_hevc_enabled() && IsH265CodecString(codec)) { 103 auto trackInfo = 104 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 105 "video/hevc"_ns, aType); 106 tracks.AppendElement(std::move(trackInfo)); 107 continue; 108 } 109 if (isVideo && IsAllowedH264Codec(codec)) { 110 auto trackInfo = 111 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 112 "video/avc"_ns, aType); 113 uint8_t profile = 0, constraint = 0; 114 H264_LEVEL level; 115 MOZ_ALWAYS_TRUE( 116 ExtractH264CodecDetails(codec, profile, constraint, level, 117 H264CodecStringStrictness::Lenient)); 118 uint32_t width = aType.ExtendedType().GetWidth().refOr(1280); 119 uint32_t height = aType.ExtendedType().GetHeight().refOr(720); 120 trackInfo->GetAsVideoInfo()->mExtraData = 121 H264::CreateExtraData(profile, constraint, level, {width, height}); 122 tracks.AppendElement(std::move(trackInfo)); 123 continue; 124 } 125 // Unknown codec 126 aError = MediaResult( 127 NS_ERROR_DOM_MEDIA_FATAL_ERR, 128 RESULT_DETAIL("Unknown codec:%s", NS_ConvertUTF16toUTF8(codec).get())); 129 } 130 return tracks; 131 } 132 133 /* static */ 134 bool MP4Decoder::IsSupportedType(const MediaContainerType& aType, 135 DecoderDoctorDiagnostics* aDiagnostics) { 136 if (!IsEnabled()) { 137 return false; 138 } 139 140 MediaResult rv = NS_OK; 141 auto tracks = GetTracksInfo(aType, rv); 142 if (NS_FAILED(rv)) { 143 return false; 144 } 145 146 if (!tracks.IsEmpty()) { 147 // Look for exact match as we know used codecs. 148 RefPtr<PDMFactory> platform = new PDMFactory(); 149 for (const auto& track : tracks) { 150 if (!track || 151 platform->Supports(SupportDecoderParams(*track), aDiagnostics) 152 .isEmpty()) { 153 return false; 154 } 155 } 156 return true; 157 } 158 159 // We have only container info so try to guess the content type. 160 // Assume H.264/AV1 or AAC 161 if (aType.Type() == MEDIAMIMETYPE("audio/mp4") || 162 aType.Type() == MEDIAMIMETYPE("audio/x-m4a")) { 163 tracks.AppendElement( 164 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 165 "audio/mp4a-latm"_ns, aType)); 166 } else { 167 tracks.AppendElement( 168 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 169 "video/avc"_ns, aType)); 170 if (StaticPrefs::media_av1_enabled()) { 171 tracks.AppendElement( 172 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 173 "video/av1"_ns, aType)); 174 } 175 if (StaticPrefs::media_hevc_enabled()) { 176 tracks.AppendElement( 177 CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( 178 "video/hevc"_ns, aType)); 179 } 180 } 181 182 // Check that something is supported at least. 183 RefPtr<PDMFactory> platform = new PDMFactory(); 184 for (const auto& track : tracks) { 185 if (track && !platform->Supports(SupportDecoderParams(*track), aDiagnostics) 186 .isEmpty()) { 187 return true; 188 } 189 } 190 return false; 191 } 192 193 /* static */ 194 bool MP4Decoder::IsH264(const nsACString& aMimeType) { 195 return aMimeType.EqualsLiteral("video/mp4") || 196 aMimeType.EqualsLiteral("video/avc"); 197 } 198 199 /* static */ 200 bool MP4Decoder::IsAAC(const nsACString& aMimeType) { 201 return aMimeType.EqualsLiteral("audio/mp4a-latm"); 202 } 203 204 /* static */ 205 bool MP4Decoder::IsHEVC(const nsACString& aMimeType) { 206 return aMimeType.EqualsLiteral("video/hevc"); 207 } 208 209 /* static */ 210 bool MP4Decoder::IsEnabled() { return StaticPrefs::media_mp4_enabled(); } 211 212 /* static */ 213 nsTArray<UniquePtr<TrackInfo>> MP4Decoder::GetTracksInfo( 214 const MediaContainerType& aType) { 215 MediaResult rv = NS_OK; 216 return GetTracksInfo(aType, rv); 217 } 218 219 } // namespace mozilla