MatroskaDemuxer.cpp (9355B)
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 "MatroskaDemuxer.h" 8 9 #ifdef MOZ_AV1 10 # include "AOMDecoder.h" 11 #endif 12 #include "H264.h" 13 #include "H265.h" 14 #include "VPXDecoder.h" 15 #include "XiphExtradata.h" 16 #include "mozilla/glean/DomMediaMetrics.h" 17 18 namespace mozilla { 19 20 extern LazyLogModule gMediaDemuxerLog; 21 #define MKV_DEBUG(msg, ...) \ 22 MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) 23 24 static void ReportCodecUsage(int aCodec) { 25 MKV_DEBUG("ReportCodecUsage, codec: %d", aCodec); 26 switch (aCodec) { 27 case NESTEGG_CODEC_AV1: 28 mozilla::glean::media::mkv_codec_type 29 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eVideoav1) 30 .Add(); 31 break; 32 case NESTEGG_CODEC_AVC: 33 mozilla::glean::media::mkv_codec_type 34 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eVideoavc) 35 .Add(); 36 break; 37 case NESTEGG_CODEC_HEVC: 38 mozilla::glean::media::mkv_codec_type 39 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eVideohevc) 40 .Add(); 41 break; 42 case NESTEGG_CODEC_VP8: 43 mozilla::glean::media::mkv_codec_type 44 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eVideovp8) 45 .Add(); 46 break; 47 case NESTEGG_CODEC_VP9: 48 mozilla::glean::media::mkv_codec_type 49 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eVideovp9) 50 .Add(); 51 break; 52 case NESTEGG_CODEC_AAC: 53 mozilla::glean::media::mkv_codec_type 54 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eAudioaac) 55 .Add(); 56 break; 57 case NESTEGG_CODEC_MP3: 58 mozilla::glean::media::mkv_codec_type 59 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eAudiomp3) 60 .Add(); 61 break; 62 case NESTEGG_CODEC_OPUS: 63 mozilla::glean::media::mkv_codec_type 64 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eAudioopus) 65 .Add(); 66 break; 67 case NESTEGG_CODEC_VORBIS: 68 mozilla::glean::media::mkv_codec_type 69 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eAudiovorbis) 70 .Add(); 71 break; 72 case NESTEGG_CODEC_FLAC: 73 mozilla::glean::media::mkv_codec_type 74 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eAudioflac) 75 .Add(); 76 break; 77 case NESTEGG_CODEC_PCM: 78 mozilla::glean::media::mkv_codec_type 79 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eAudiopcm) 80 .Add(); 81 break; 82 default: 83 mozilla::glean::media::mkv_codec_type 84 .EnumGet(mozilla::glean::media::MkvCodecTypeLabel::eNocodecspecified) 85 .Add(); 86 break; 87 } 88 } 89 90 MatroskaDemuxer::MatroskaDemuxer(MediaResource* aResource) 91 : WebMDemuxer(aResource) {} 92 93 nsresult MatroskaDemuxer::SetVideoCodecInfo(nestegg* aContext, int aTrackId) { 94 mVideoCodec = nestegg_track_codec_id(aContext, aTrackId); 95 ReportCodecUsage(mVideoCodec); 96 // TODO : support more codecs 97 switch (mVideoCodec) { 98 case NESTEGG_CODEC_AVC: { 99 mInfo.mVideo.mMimeType = "video/avc"; 100 nsresult rv = SetCodecPrivateToVideoExtraData(aContext, aTrackId); 101 if (NS_FAILED(rv)) { 102 MKV_DEBUG("Failed to set extradata for avc"); 103 return rv; 104 } 105 break; 106 } 107 case NESTEGG_CODEC_HEVC: { 108 mInfo.mVideo.mMimeType = "video/hevc"; 109 nsresult rv = SetCodecPrivateToVideoExtraData(aContext, aTrackId); 110 if (NS_FAILED(rv)) { 111 MKV_DEBUG("Failed to set extradata for hevc"); 112 return rv; 113 } 114 break; 115 } 116 case NESTEGG_CODEC_VP8: 117 mInfo.mVideo.mMimeType = "video/vp8"; 118 break; 119 case NESTEGG_CODEC_VP9: 120 mInfo.mVideo.mMimeType = "video/vp9"; 121 break; 122 case NESTEGG_CODEC_AV1: 123 mInfo.mVideo.mMimeType = "video/av1"; 124 break; 125 default: 126 NS_WARNING("Unknown Matroska video codec"); 127 return NS_ERROR_FAILURE; 128 } 129 return NS_OK; 130 } 131 132 nsresult MatroskaDemuxer::SetCodecPrivateToVideoExtraData(nestegg* aContext, 133 int aTrackId) { 134 nsTArray<const unsigned char*> headers; 135 nsTArray<size_t> headerLens; 136 nsresult rv = GetCodecPrivateData(aContext, aTrackId, &headers, &headerLens); 137 if (NS_FAILED(rv)) { 138 MKV_DEBUG("GetCodecPrivateData error"); 139 return rv; 140 } 141 mInfo.mVideo.mExtraData->AppendElements(headers[0], headerLens[0]); 142 return NS_OK; 143 } 144 145 nsresult MatroskaDemuxer::SetAudioCodecInfo( 146 nestegg* aContext, int aTrackId, const nestegg_audio_params& aParams) { 147 mAudioCodec = nestegg_track_codec_id(aContext, aTrackId); 148 ReportCodecUsage(mAudioCodec); 149 150 static const uint64_t NSECS_PER_USEC = 1000; 151 static const uint64_t USECS_PER_S = 1e6; 152 153 // TODO : support more codecs 154 switch (mAudioCodec) { 155 case NESTEGG_CODEC_AAC: { 156 mInfo.mAudio.mMimeType = "audio/mp4a-latm"; 157 const uint32_t AAC_SAMPLES_PER_FRAME = 1024; 158 AacCodecSpecificData aacCodecSpecificData{}; 159 uint64_t codecDelayUs = aParams.codec_delay / NSECS_PER_USEC; 160 if (codecDelayUs > 0) { 161 aacCodecSpecificData.mEncoderDelayFrames = static_cast<uint32_t>( 162 std::lround(static_cast<double>(codecDelayUs) * aParams.rate / 163 (USECS_PER_S * AAC_SAMPLES_PER_FRAME))); 164 MKV_DEBUG("AAC stream in MKV container, %" PRIu32 165 " frames of encoder delay.", 166 aacCodecSpecificData.mEncoderDelayFrames); 167 } else { 168 aacCodecSpecificData.mEncoderDelayFrames = 0; 169 } 170 171 uint64_t frameCount; 172 int r = nestegg_read_total_frames_count(aContext, &frameCount); 173 if (r == -1) { 174 return NS_ERROR_FAILURE; 175 } 176 aacCodecSpecificData.mMediaFrameCount = frameCount; 177 MKV_DEBUG("AAC stream in MKV container, media frames: %" PRIu64 178 ", delay frames : %" PRIu32, 179 frameCount, aacCodecSpecificData.mEncoderDelayFrames); 180 181 // Get the codec specific data from the codec private. 182 nsTArray<const unsigned char*> headers; 183 nsTArray<size_t> headerLens; 184 nsresult rv = 185 GetCodecPrivateData(aContext, aTrackId, &headers, &headerLens); 186 if (NS_FAILED(rv)) { 187 MKV_DEBUG("GetCodecPrivateData error for AAC"); 188 return rv; 189 } 190 aacCodecSpecificData.mDecoderConfigDescriptorBinaryBlob->AppendElements( 191 headers[0], headerLens[0]); 192 mInfo.mAudio.mCodecSpecificConfig = 193 AudioCodecSpecificVariant{std::move(aacCodecSpecificData)}; 194 break; 195 } 196 case NESTEGG_CODEC_VORBIS: { 197 mInfo.mAudio.mCodecSpecificConfig = 198 AudioCodecSpecificVariant{VorbisCodecSpecificData{}}; 199 mInfo.mAudio.mMimeType = "audio/vorbis"; 200 AutoTArray<const unsigned char*, 4> headers; 201 AutoTArray<size_t, 4> headerLens; 202 nsresult rv = 203 GetCodecPrivateData(aContext, aTrackId, &headers, &headerLens); 204 if (NS_FAILED(rv)) { 205 MKV_DEBUG("GetCodecPrivateData error for vorbis"); 206 return rv; 207 } 208 // Vorbis has 3 headers, convert to Xiph extradata format to send them to 209 // the demuxer. 210 RefPtr<MediaByteBuffer> audioCodecSpecificBlob = 211 GetAudioCodecSpecificBlob(mInfo.mAudio.mCodecSpecificConfig); 212 if (!XiphHeadersToExtradata(audioCodecSpecificBlob, headers, 213 headerLens)) { 214 MKV_DEBUG("Couldn't parse Xiph headers"); 215 return NS_ERROR_FAILURE; 216 } 217 break; 218 } 219 case NESTEGG_CODEC_OPUS: { 220 uint64_t codecDelayUs = aParams.codec_delay / NSECS_PER_USEC; 221 mInfo.mAudio.mMimeType = "audio/opus"; 222 OpusCodecSpecificData opusCodecSpecificData; 223 opusCodecSpecificData.mContainerCodecDelayFrames = 224 AssertedCast<int64_t>(USECS_PER_S * codecDelayUs / 48000); 225 MKV_DEBUG("Preroll for Opus: %" PRIu64 " frames", 226 opusCodecSpecificData.mContainerCodecDelayFrames); 227 mInfo.mAudio.mCodecSpecificConfig = 228 AudioCodecSpecificVariant{std::move(opusCodecSpecificData)}; 229 break; 230 } 231 default: 232 NS_WARNING("Unknown Matroska audio codec"); 233 return NS_ERROR_FAILURE; 234 } 235 return NS_OK; 236 } 237 238 bool MatroskaDemuxer::CheckKeyFrameByExamineByteStream( 239 const MediaRawData* aSample) { 240 // TODO : support more codecs 241 switch (mVideoCodec) { 242 case NESTEGG_CODEC_AVC: { 243 auto frameType = H264::GetFrameType(aSample); 244 return frameType == H264::FrameType::I_FRAME_IDR || 245 frameType == H264::FrameType::I_FRAME_OTHER; 246 } 247 case NESTEGG_CODEC_HEVC: { 248 auto isKeyFrame = H265::IsKeyFrame(aSample); 249 return isKeyFrame.isOk() ? isKeyFrame.unwrap() : false; 250 } 251 case NESTEGG_CODEC_VP8: 252 return VPXDecoder::IsKeyframe(*aSample, VPXDecoder::Codec::VP8); 253 case NESTEGG_CODEC_VP9: 254 return VPXDecoder::IsKeyframe(*aSample, VPXDecoder::Codec::VP9); 255 #ifdef MOZ_AV1 256 case NESTEGG_CODEC_AV1: 257 return AOMDecoder::IsKeyframe(*aSample); 258 #endif 259 default: 260 MOZ_ASSERT_UNREACHABLE( 261 "Cannot detect keyframes in unknown Matroska video codec"); 262 return false; 263 } 264 } 265 266 } // namespace mozilla 267 268 #undef MKV_DEBUG