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