tor-browser

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

OggDemuxer.h (14693B)


      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 #if !defined(OggDemuxer_h_)
      7 #  define OggDemuxer_h_
      8 
      9 #  include <memory>
     10 
     11 #  include "MediaDataDemuxer.h"
     12 #  include "MediaMetadataManager.h"
     13 #  include "OggCodecState.h"
     14 #  include "OggCodecStore.h"
     15 #  include "OggRLBoxTypes.h"
     16 #  include "nsTArray.h"
     17 
     18 namespace mozilla {
     19 
     20 class OggTrackDemuxer;
     21 
     22 DDLoggedTypeDeclNameAndBase(OggDemuxer, MediaDataDemuxer);
     23 DDLoggedTypeNameAndBase(OggTrackDemuxer, MediaTrackDemuxer);
     24 
     25 class OggDemuxer : public MediaDataDemuxer,
     26                   public DecoderDoctorLifeLogger<OggDemuxer> {
     27 public:
     28  explicit OggDemuxer(MediaResource* aResource);
     29 
     30  RefPtr<InitPromise> Init() override;
     31 
     32  uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
     33 
     34  already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
     35      TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
     36 
     37  bool IsSeekable() const override;
     38 
     39  UniquePtr<EncryptionInfo> GetCrypto() override;
     40 
     41  // Set the events to notify when chaining is encountered.
     42  void SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
     43                         MediaEventProducer<void>* aOnSeekableEvent);
     44 
     45 private:
     46  // helpers for friend OggTrackDemuxer
     47  UniquePtr<TrackInfo> GetTrackInfo(TrackInfo::TrackType aType,
     48                                    size_t aTrackNumber) const;
     49 
     50  struct nsAutoOggSyncState {
     51    explicit nsAutoOggSyncState(rlbox_sandbox_ogg* aSandbox);
     52    ~nsAutoOggSyncState();
     53    rlbox_sandbox_ogg* mSandbox;
     54    tainted_opaque_ogg<ogg_sync_state*> mState;
     55  };
     56  media::TimeIntervals GetBuffered(TrackInfo::TrackType aType);
     57  void FindStartTime(media::TimeUnit& aOutStartTime);
     58  void FindStartTime(TrackInfo::TrackType, media::TimeUnit& aOutStartTime);
     59 
     60  nsresult SeekInternal(TrackInfo::TrackType aType,
     61                        const media::TimeUnit& aTarget);
     62 
     63  // Seeks to the keyframe preceding the target time using available
     64  // keyframe indexes.
     65  enum IndexedSeekResult {
     66    SEEK_OK,          // Success.
     67    SEEK_INDEX_FAIL,  // Failure due to no index, or invalid index.
     68    SEEK_FATAL_ERROR  // Error returned by a stream operation.
     69  };
     70  IndexedSeekResult SeekToKeyframeUsingIndex(TrackInfo::TrackType aType,
     71                                             const media::TimeUnit& aTarget);
     72 
     73  // Rolls back a seek-using-index attempt, returning a failure error code.
     74  IndexedSeekResult RollbackIndexedSeek(TrackInfo::TrackType aType,
     75                                        int64_t aOffset);
     76 
     77  // Represents a section of contiguous media, with a start and end offset,
     78  // and the timestamps of the start and end of that range, that is cached.
     79  // Used to denote the extremities of a range in which we can seek quickly
     80  // (because it's cached).
     81  class SeekRange {
     82   public:
     83    SeekRange()
     84        : mOffsetStart(0),
     85          mOffsetEnd(0),
     86          mTimeStart(media::TimeUnit::Zero()),
     87          mTimeEnd(media::TimeUnit::Zero()) {}
     88 
     89    SeekRange(int64_t aOffsetStart, int64_t aOffsetEnd,
     90              const media::TimeUnit& aTimeStart,
     91              const media::TimeUnit& aTimeEnd)
     92        : mOffsetStart(aOffsetStart),
     93          mOffsetEnd(aOffsetEnd),
     94          mTimeStart(aTimeStart),
     95          mTimeEnd(aTimeEnd) {}
     96 
     97    bool IsNull() const {
     98      return mOffsetStart == 0 && mOffsetEnd == 0 && mTimeStart.IsZero() &&
     99             mTimeEnd.IsZero();
    100    }
    101 
    102    int64_t mOffsetStart, mOffsetEnd;  // in bytes.
    103    media::TimeUnit mTimeStart, mTimeEnd;
    104  };
    105 
    106  nsresult GetSeekRanges(TrackInfo::TrackType aType,
    107                         nsTArray<SeekRange>& aRanges);
    108  SeekRange SelectSeekRange(TrackInfo::TrackType aType,
    109                            const nsTArray<SeekRange>& ranges,
    110                            const media::TimeUnit& aTarget,
    111                            const media::TimeUnit& aStartTime,
    112                            const media::TimeUnit& aEndTime, bool aExact);
    113 
    114  // Seeks to aTarget usecs in the buffered range aRange using bisection search.
    115  // aAdjustedTarget is an adjusted version of the target used to account for
    116  // Opus pre-roll, if necessary. aStartTime must be the presentation time at
    117  // the start of media, and aEndTime the time at end of media. aRanges must be
    118  // the time/byte ranges buffered in the media cache as per GetSeekRanges().
    119  nsresult SeekInBufferedRange(TrackInfo::TrackType aType,
    120                               const media::TimeUnit& aTarget,
    121                               media::TimeUnit& aAdjustedTarget,
    122                               const media::TimeUnit& aStartTime,
    123                               const media::TimeUnit& aEndTime,
    124                               const nsTArray<SeekRange>& aRanges,
    125                               const SeekRange& aRange);
    126 
    127  // Seeks to before aTarget usecs in media using bisection search.
    128  // This will seek to the packet required to render the
    129  // media at aTarget. Will use aRanges in order to narrow the bisection
    130  // search space. aStartTime must be the presentation time at the start of
    131  // media, and aEndTime the time at end of media. aRanges must be the time/byte
    132  // ranges buffered in the media cache as per GetSeekRanges().
    133  nsresult SeekInUnbuffered(TrackInfo::TrackType aType,
    134                            const media::TimeUnit& aTarget,
    135                            const media::TimeUnit& aStartTime,
    136                            const media::TimeUnit& aEndTime,
    137                            const nsTArray<SeekRange>& aRanges);
    138 
    139  // Performs a seek bisection to move the media stream's read cursor to the
    140  // last ogg page boundary which has end time before aTarget usecs
    141  // on the Vorbis bitstreams. Limits its search to data inside aRange;
    142  // i.e. it will only read inside of the aRange's start and end offsets.
    143  // aFuzz is the number of usecs of leniency we'll allow; we'll terminate the
    144  // seek when we land in the range (aTime - aFuzz, aTime) usecs.
    145  nsresult SeekBisection(TrackInfo::TrackType aType,
    146                         const media::TimeUnit& aTarget,
    147                         const SeekRange& aRange, const media::TimeUnit& aFuzz);
    148 
    149  // Chunk size to read when reading Ogg files. Average Ogg page length
    150  // is about 4300 bytes, so we read the file in chunks larger than that.
    151  static const int PAGE_STEP = 8192;
    152 
    153  enum PageSyncResult {
    154    PAGE_SYNC_ERROR = 1,
    155    PAGE_SYNC_END_OF_RANGE = 2,
    156    PAGE_SYNC_OK = 3
    157  };
    158  static PageSyncResult PageSync(rlbox_sandbox_ogg* aSandbox,
    159                                 MediaResourceIndex* aResource,
    160                                 tainted_opaque_ogg<ogg_sync_state*> aState,
    161                                 bool aCachedDataOnly, int64_t aOffset,
    162                                 int64_t aEndOffset,
    163                                 tainted_ogg<ogg_page*> aPage,
    164                                 int& aSkippedBytes);
    165 
    166  // Demux next Ogg packet
    167  ogg_packet* GetNextPacket(TrackInfo::TrackType aType);
    168 
    169  nsresult Reset(TrackInfo::TrackType aType);
    170 
    171  static nsString GetKind(const nsCString& aRole);
    172  static void InitTrack(MessageField* aMsgInfo, TrackInfo* aInfo, bool aEnable);
    173 
    174  // Really private!
    175  ~OggDemuxer();
    176 
    177  // Read enough of the file to identify track information and header
    178  // packets necessary for decoding to begin.
    179  nsresult ReadMetadata();
    180 
    181  // Read a page of data from the Ogg file. Returns true if a page has been
    182  // read, false if the page read failed or end of file reached.
    183  bool ReadOggPage(TrackInfo::TrackType aType,
    184                   tainted_opaque_ogg<ogg_page*> aPage);
    185 
    186  // Send a page off to the individual streams it belongs to.
    187  // Reconstructed packets, if any are ready, will be available
    188  // on the individual OggCodecStates.
    189  nsresult DemuxOggPage(TrackInfo::TrackType aType,
    190                        tainted_opaque_ogg<ogg_page*> aPage);
    191 
    192  // Read data and demux until a packet is available on the given stream state
    193  void DemuxUntilPacketAvailable(TrackInfo::TrackType aType,
    194                                 OggCodecState* aState);
    195 
    196  // Reads and decodes header packets for aState, until either header decode
    197  // fails, or is complete. Initializes the codec state before returning.
    198  // Returns true if reading headers and initializtion of the stream
    199  // succeeds.
    200  bool ReadHeaders(TrackInfo::TrackType aType, OggCodecState* aState);
    201 
    202  // Reads the next link in the chain.
    203  bool ReadOggChain(const media::TimeUnit& aLastEndTime);
    204 
    205  // Set this media as being a chain and notifies the state machine that the
    206  // media is no longer seekable.
    207  void SetChained();
    208 
    209  // Fills aTracks with the serial numbers of each active stream, for use by
    210  // various SkeletonState functions.
    211  void BuildSerialList(nsTArray<uint32_t>& aTracks);
    212 
    213  // Setup target bitstreams for decoding.
    214  void SetupTarget(OggCodecState** aSavedState, OggCodecState* aNewState);
    215  void SetupTargetSkeleton();
    216  void SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials);
    217  void FillTags(TrackInfo* aInfo, UniquePtr<MetadataTags>&& aTags);
    218 
    219  // Compute an ogg page's checksum
    220  tainted_opaque_ogg<ogg_uint32_t> GetPageChecksum(
    221      tainted_opaque_ogg<ogg_page*> aPage);
    222 
    223  // Get the end time of aEndOffset. This is the playback position we'd reach
    224  // after playback finished at aEndOffset.
    225  media::TimeUnit RangeEndTime(TrackInfo::TrackType aType, int64_t aEndOffset);
    226 
    227  // Get the end time of aEndOffset, without reading before aStartOffset.
    228  // This is the playback position we'd reach after playback finished at
    229  // aEndOffset. If bool aCachedDataOnly is true, then we'll only read
    230  // from data which is cached in the media cached, otherwise we'll do
    231  // regular blocking reads from the media stream. If bool aCachedDataOnly
    232  // is true, this can safely be called on the main thread, otherwise it
    233  // must be called on the state machine thread.
    234  media::TimeUnit RangeEndTime(TrackInfo::TrackType aType, int64_t aStartOffset,
    235                               int64_t aEndOffset, bool aCachedDataOnly);
    236 
    237  // Get the start time of the range beginning at aOffset. This is the start
    238  // time of the first aType sample we'd be able to play if we
    239  // started playback at aOffset.
    240  media::TimeUnit RangeStartTime(TrackInfo::TrackType aType, int64_t aOffset);
    241 
    242  // All invocations of libogg functionality from the demuxer is sandboxed using
    243  // wasm library sandboxes on supported platforms. These functions that create
    244  // and destroy the sandbox instance.
    245  static rlbox_sandbox_ogg* CreateSandbox();
    246  struct SandboxDestroy {
    247    void operator()(rlbox_sandbox_ogg* sandbox);
    248  };
    249 
    250  // The sandbox instance used to sandbox libogg functionality in the demuxer.
    251  // This must be declared before other members so that constructors/destructors
    252  // run in the right order.
    253  std::unique_ptr<rlbox_sandbox_ogg, SandboxDestroy> mSandbox;
    254 
    255  MediaInfo mInfo;
    256  nsTArray<RefPtr<OggTrackDemuxer>> mDemuxers;
    257 
    258  // Map of codec-specific bitstream states.
    259  OggCodecStore mCodecStore;
    260 
    261  // Decode state of the Vorbis bitstream we're decoding, if we have audio.
    262  OggCodecState* mVorbisState;
    263 
    264  // Decode state of the Opus bitstream we're decoding, if we have one.
    265  OggCodecState* mOpusState;
    266 
    267  // Get the bitstream decode state for the given track type
    268  // Decode state of the Flac bitstream we're decoding, if we have one.
    269  OggCodecState* mFlacState;
    270 
    271  OggCodecState* GetTrackCodecState(TrackInfo::TrackType aType) const;
    272  TrackInfo::TrackType GetCodecStateType(OggCodecState* aState) const;
    273 
    274  // Represents the user pref media.opus.enabled at the time our
    275  // contructor was called. We can't check it dynamically because
    276  // we're not on the main thread;
    277  bool mOpusEnabled;
    278 
    279  // Decode state of the Skeleton bitstream.
    280  SkeletonState* mSkeletonState;
    281 
    282  // Ogg decoding state.
    283  struct OggStateContext {
    284    explicit OggStateContext(MediaResource* aResource,
    285                             rlbox_sandbox_ogg* aSandbox)
    286        : mOggState(aSandbox), mResource(aResource), mNeedKeyframe(true) {}
    287    nsAutoOggSyncState mOggState;
    288    MediaResourceIndex mResource;
    289    Maybe<media::TimeUnit> mStartTime;
    290    bool mNeedKeyframe;
    291  };
    292 
    293  OggStateContext& OggState(TrackInfo::TrackType aType);
    294  tainted_opaque_ogg<ogg_sync_state*> OggSyncState(TrackInfo::TrackType aType);
    295  MediaResourceIndex* Resource(TrackInfo::TrackType aType);
    296  MediaResourceIndex* CommonResource();
    297  OggStateContext mAudioOggState;
    298 
    299  Maybe<media::TimeUnit> mStartTime;
    300 
    301  // Booleans to indicate if we have audio and/or video data
    302  bool HasVideo() const;
    303  bool HasAudio() const;
    304  bool HasSkeleton() const {
    305    return mSkeletonState != nullptr && mSkeletonState->mActive;
    306  }
    307  bool HaveStartTime() const;
    308  bool HaveStartTime(TrackInfo::TrackType aType);
    309  media::TimeUnit StartTime() const;
    310  media::TimeUnit StartTime(TrackInfo::TrackType aType);
    311 
    312  // True if we are decoding a chained ogg.
    313  bool mIsChained;
    314 
    315  // Total audio duration played so far.
    316  media::TimeUnit mDecodedAudioDuration;
    317 
    318  // Events manager
    319  TimedMetadataEventProducer* mTimedMetadataEvent;
    320  MediaEventProducer<void>* mOnSeekableEvent;
    321 
    322  // This will be populated only if a content change occurs, otherwise it
    323  // will be left as null so the original metadata is used.
    324  // It is updated once a chained ogg is encountered.
    325  // As Ogg chaining is only supported for audio, we only need an audio track
    326  // info.
    327  RefPtr<TrackInfoSharedPtr> mSharedAudioTrackInfo;
    328 
    329  friend class OggTrackDemuxer;
    330 };
    331 
    332 class OggTrackDemuxer : public MediaTrackDemuxer,
    333                        public DecoderDoctorLifeLogger<OggTrackDemuxer> {
    334 public:
    335  OggTrackDemuxer(OggDemuxer* aParent, TrackInfo::TrackType aType,
    336                  uint32_t aTrackNumber);
    337 
    338  UniquePtr<TrackInfo> GetInfo() const override;
    339 
    340  RefPtr<SeekPromise> Seek(const media::TimeUnit& aTime) override;
    341 
    342  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
    343 
    344  void Reset() override;
    345 
    346  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
    347      const media::TimeUnit& aTimeThreshold) override;
    348 
    349  media::TimeIntervals GetBuffered() override;
    350 
    351  void BreakCycles() override;
    352 
    353 private:
    354  ~OggTrackDemuxer();
    355  void SetNextKeyFrameTime();
    356  RefPtr<MediaRawData> NextSample();
    357  RefPtr<OggDemuxer> mParent;
    358  TrackInfo::TrackType mType;
    359  UniquePtr<TrackInfo> mInfo;
    360 
    361  // Queued sample extracted by the demuxer, but not yet returned.
    362  RefPtr<MediaRawData> mQueuedSample;
    363 };
    364 }  // namespace mozilla
    365 
    366 #endif