tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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