content/media/ogg/OggReader.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 #if !defined(OggReader_h_)
michael@0 7 #define OggReader_h_
michael@0 8
michael@0 9 #include <ogg/ogg.h>
michael@0 10 #include <theora/theoradec.h>
michael@0 11 #ifdef MOZ_TREMOR
michael@0 12 #include <tremor/ivorbiscodec.h>
michael@0 13 #else
michael@0 14 #include <vorbis/codec.h>
michael@0 15 #endif
michael@0 16 #include "MediaDecoderReader.h"
michael@0 17 #include "OggCodecState.h"
michael@0 18 #include "VideoUtils.h"
michael@0 19 #include "mozilla/Monitor.h"
michael@0 20
michael@0 21 namespace mozilla {
michael@0 22 namespace dom {
michael@0 23 class TimeRanges;
michael@0 24 }
michael@0 25 }
michael@0 26
michael@0 27 namespace mozilla {
michael@0 28
michael@0 29 // Thread safe container to store the codec information and the serial for each
michael@0 30 // streams.
michael@0 31 class OggCodecStore
michael@0 32 {
michael@0 33 public:
michael@0 34 OggCodecStore();
michael@0 35 void Add(uint32_t serial, OggCodecState* codecState);
michael@0 36 bool Contains(uint32_t serial);
michael@0 37 OggCodecState* Get(uint32_t serial);
michael@0 38 bool IsKnownStream(uint32_t aSerial);
michael@0 39
michael@0 40 private:
michael@0 41 // Maps Ogg serialnos to OggStreams.
michael@0 42 nsClassHashtable<nsUint32HashKey, OggCodecState> mCodecStates;
michael@0 43
michael@0 44 // Protects the |mCodecStates| and the |mKnownStreams| members.
michael@0 45 Monitor mMonitor;
michael@0 46 };
michael@0 47
michael@0 48 class OggReader : public MediaDecoderReader
michael@0 49 {
michael@0 50 public:
michael@0 51 OggReader(AbstractMediaDecoder* aDecoder);
michael@0 52 ~OggReader();
michael@0 53
michael@0 54 virtual nsresult Init(MediaDecoderReader* aCloneDonor);
michael@0 55 virtual nsresult ResetDecode();
michael@0 56 virtual bool DecodeAudioData();
michael@0 57
michael@0 58 // If the Theora granulepos has not been captured, it may read several packets
michael@0 59 // until one with a granulepos has been captured, to ensure that all packets
michael@0 60 // read have valid time info.
michael@0 61 virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
michael@0 62 int64_t aTimeThreshold);
michael@0 63
michael@0 64 virtual bool HasAudio() {
michael@0 65 return (mVorbisState != 0 && mVorbisState->mActive)
michael@0 66 #ifdef MOZ_OPUS
michael@0 67 || (mOpusState != 0 && mOpusState->mActive)
michael@0 68 #endif /* MOZ_OPUS */
michael@0 69 ;
michael@0 70 }
michael@0 71
michael@0 72 virtual bool HasVideo() {
michael@0 73 return mTheoraState != 0 && mTheoraState->mActive;
michael@0 74 }
michael@0 75
michael@0 76 virtual nsresult ReadMetadata(MediaInfo* aInfo,
michael@0 77 MetadataTags** aTags);
michael@0 78 virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
michael@0 79 virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
michael@0 80
michael@0 81 private:
michael@0 82 // This monitor should be taken when reading or writing to mIsChained.
michael@0 83 ReentrantMonitor mMonitor;
michael@0 84
michael@0 85 // Specialized Reset() method to signal if the seek is
michael@0 86 // to the start of the stream.
michael@0 87 nsresult ResetDecode(bool start);
michael@0 88
michael@0 89 bool HasSkeleton() {
michael@0 90 return mSkeletonState != 0 && mSkeletonState->mActive;
michael@0 91 }
michael@0 92
michael@0 93 // Seeks to the keyframe preceeding the target time using available
michael@0 94 // keyframe indexes.
michael@0 95 enum IndexedSeekResult {
michael@0 96 SEEK_OK, // Success.
michael@0 97 SEEK_INDEX_FAIL, // Failure due to no index, or invalid index.
michael@0 98 SEEK_FATAL_ERROR // Error returned by a stream operation.
michael@0 99 };
michael@0 100 IndexedSeekResult SeekToKeyframeUsingIndex(int64_t aTarget);
michael@0 101
michael@0 102 // Rolls back a seek-using-index attempt, returning a failure error code.
michael@0 103 IndexedSeekResult RollbackIndexedSeek(int64_t aOffset);
michael@0 104
michael@0 105 // Represents a section of contiguous media, with a start and end offset,
michael@0 106 // and the timestamps of the start and end of that range, that is cached.
michael@0 107 // Used to denote the extremities of a range in which we can seek quickly
michael@0 108 // (because it's cached).
michael@0 109 class SeekRange {
michael@0 110 public:
michael@0 111 SeekRange()
michael@0 112 : mOffsetStart(0),
michael@0 113 mOffsetEnd(0),
michael@0 114 mTimeStart(0),
michael@0 115 mTimeEnd(0)
michael@0 116 {}
michael@0 117
michael@0 118 SeekRange(int64_t aOffsetStart,
michael@0 119 int64_t aOffsetEnd,
michael@0 120 int64_t aTimeStart,
michael@0 121 int64_t aTimeEnd)
michael@0 122 : mOffsetStart(aOffsetStart),
michael@0 123 mOffsetEnd(aOffsetEnd),
michael@0 124 mTimeStart(aTimeStart),
michael@0 125 mTimeEnd(aTimeEnd)
michael@0 126 {}
michael@0 127
michael@0 128 bool IsNull() const {
michael@0 129 return mOffsetStart == 0 &&
michael@0 130 mOffsetEnd == 0 &&
michael@0 131 mTimeStart == 0 &&
michael@0 132 mTimeEnd == 0;
michael@0 133 }
michael@0 134
michael@0 135 int64_t mOffsetStart, mOffsetEnd; // in bytes.
michael@0 136 int64_t mTimeStart, mTimeEnd; // in usecs.
michael@0 137 };
michael@0 138
michael@0 139 // Seeks to aTarget usecs in the buffered range aRange using bisection search,
michael@0 140 // or to the keyframe prior to aTarget if we have video. aAdjustedTarget is
michael@0 141 // an adjusted version of the target used to account for Opus pre-roll, if
michael@0 142 // necessary. aStartTime must be the presentation time at the start of media,
michael@0 143 // and aEndTime the time at end of media. aRanges must be the time/byte ranges
michael@0 144 // buffered in the media cache as per GetSeekRanges().
michael@0 145 nsresult SeekInBufferedRange(int64_t aTarget,
michael@0 146 int64_t aAdjustedTarget,
michael@0 147 int64_t aStartTime,
michael@0 148 int64_t aEndTime,
michael@0 149 const nsTArray<SeekRange>& aRanges,
michael@0 150 const SeekRange& aRange);
michael@0 151
michael@0 152 // Seeks to before aTarget usecs in media using bisection search. If the media
michael@0 153 // has video, this will seek to before the keyframe required to render the
michael@0 154 // media at aTarget. Will use aRanges in order to narrow the bisection
michael@0 155 // search space. aStartTime must be the presentation time at the start of
michael@0 156 // media, and aEndTime the time at end of media. aRanges must be the time/byte
michael@0 157 // ranges buffered in the media cache as per GetSeekRanges().
michael@0 158 nsresult SeekInUnbuffered(int64_t aTarget,
michael@0 159 int64_t aStartTime,
michael@0 160 int64_t aEndTime,
michael@0 161 const nsTArray<SeekRange>& aRanges);
michael@0 162
michael@0 163 // Get the end time of aEndOffset. This is the playback position we'd reach
michael@0 164 // after playback finished at aEndOffset.
michael@0 165 int64_t RangeEndTime(int64_t aEndOffset);
michael@0 166
michael@0 167 // Get the end time of aEndOffset, without reading before aStartOffset.
michael@0 168 // This is the playback position we'd reach after playback finished at
michael@0 169 // aEndOffset. If bool aCachedDataOnly is true, then we'll only read
michael@0 170 // from data which is cached in the media cached, otherwise we'll do
michael@0 171 // regular blocking reads from the media stream. If bool aCachedDataOnly
michael@0 172 // is true, this can safely be called on the main thread, otherwise it
michael@0 173 // must be called on the state machine thread.
michael@0 174 int64_t RangeEndTime(int64_t aStartOffset,
michael@0 175 int64_t aEndOffset,
michael@0 176 bool aCachedDataOnly);
michael@0 177
michael@0 178 // Get the start time of the range beginning at aOffset. This is the start
michael@0 179 // time of the first frame and or audio sample we'd be able to play if we
michael@0 180 // started playback at aOffset.
michael@0 181 int64_t RangeStartTime(int64_t aOffset);
michael@0 182
michael@0 183 // Performs a seek bisection to move the media stream's read cursor to the
michael@0 184 // last ogg page boundary which has end time before aTarget usecs on both the
michael@0 185 // Theora and Vorbis bitstreams. Limits its search to data inside aRange;
michael@0 186 // i.e. it will only read inside of the aRange's start and end offsets.
michael@0 187 // aFuzz is the number of usecs of leniency we'll allow; we'll terminate the
michael@0 188 // seek when we land in the range (aTime - aFuzz, aTime) usecs.
michael@0 189 nsresult SeekBisection(int64_t aTarget,
michael@0 190 const SeekRange& aRange,
michael@0 191 uint32_t aFuzz);
michael@0 192
michael@0 193 // Returns true if the serial number is for a stream we encountered
michael@0 194 // while reading metadata. Call on the main thread only.
michael@0 195 bool IsKnownStream(uint32_t aSerial);
michael@0 196
michael@0 197 // Fills aRanges with SeekRanges denoting the sections of the media which
michael@0 198 // have been downloaded and are stored in the media cache. The reader
michael@0 199 // monitor must must be held with exactly one lock count. The MediaResource
michael@0 200 // must be pinned while calling this.
michael@0 201 nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges);
michael@0 202
michael@0 203 // Returns the range in which you should perform a seek bisection if
michael@0 204 // you wish to seek to aTarget usecs, given the known (buffered) byte ranges
michael@0 205 // in aRanges. If aExact is true, we only return an exact copy of a
michael@0 206 // range in which aTarget lies, or a null range if aTarget isn't contained
michael@0 207 // in any of the (buffered) ranges. Otherwise, when aExact is false,
michael@0 208 // we'll construct the smallest possible range we can, based on the times
michael@0 209 // and byte offsets known in aRanges. We can then use this to minimize our
michael@0 210 // bisection's search space when the target isn't in a known buffered range.
michael@0 211 SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges,
michael@0 212 int64_t aTarget,
michael@0 213 int64_t aStartTime,
michael@0 214 int64_t aEndTime,
michael@0 215 bool aExact);
michael@0 216 private:
michael@0 217
michael@0 218 // Decodes a packet of Vorbis data, and inserts its samples into the
michael@0 219 // audio queue.
michael@0 220 nsresult DecodeVorbis(ogg_packet* aPacket);
michael@0 221
michael@0 222 // Decodes a packet of Opus data, and inserts its samples into the
michael@0 223 // audio queue.
michael@0 224 nsresult DecodeOpus(ogg_packet* aPacket);
michael@0 225
michael@0 226 // Decodes a packet of Theora data, and inserts its frame into the
michael@0 227 // video queue. May return NS_ERROR_OUT_OF_MEMORY. Caller must have obtained
michael@0 228 // the reader's monitor. aTimeThreshold is the current playback position
michael@0 229 // in media time in microseconds. Frames with an end time before this will
michael@0 230 // not be enqueued.
michael@0 231 nsresult DecodeTheora(ogg_packet* aPacket, int64_t aTimeThreshold);
michael@0 232
michael@0 233 // Read a page of data from the Ogg file. Returns true if a page has been
michael@0 234 // read, false if the page read failed or end of file reached.
michael@0 235 bool ReadOggPage(ogg_page* aPage);
michael@0 236
michael@0 237 // Reads and decodes header packets for aState, until either header decode
michael@0 238 // fails, or is complete. Initializes the codec state before returning.
michael@0 239 // Returns true if reading headers and initializtion of the stream
michael@0 240 // succeeds.
michael@0 241 bool ReadHeaders(OggCodecState* aState);
michael@0 242
michael@0 243 // Reads the next link in the chain.
michael@0 244 bool ReadOggChain();
michael@0 245
michael@0 246 // Set this media as being a chain and notifies the state machine that the
michael@0 247 // media is no longer seekable.
michael@0 248 void SetChained(bool aIsChained);
michael@0 249
michael@0 250 // Returns the next Ogg packet for an bitstream/codec state. Returns a
michael@0 251 // pointer to an ogg_packet on success, or nullptr if the read failed.
michael@0 252 // The caller is responsible for deleting the packet and its |packet| field.
michael@0 253 ogg_packet* NextOggPacket(OggCodecState* aCodecState);
michael@0 254
michael@0 255 // Fills aTracks with the serial numbers of each active stream, for use by
michael@0 256 // various SkeletonState functions.
michael@0 257 void BuildSerialList(nsTArray<uint32_t>& aTracks);
michael@0 258
michael@0 259 OggCodecStore mCodecStore;
michael@0 260
michael@0 261 // Decode state of the Theora bitstream we're decoding, if we have video.
michael@0 262 TheoraState* mTheoraState;
michael@0 263
michael@0 264 // Decode state of the Vorbis bitstream we're decoding, if we have audio.
michael@0 265 VorbisState* mVorbisState;
michael@0 266
michael@0 267 #ifdef MOZ_OPUS
michael@0 268 // Decode state of the Opus bitstream we're decoding, if we have one.
michael@0 269 OpusState *mOpusState;
michael@0 270
michael@0 271 // Represents the user pref media.opus.enabled at the time our
michael@0 272 // contructor was called. We can't check it dynamically because
michael@0 273 // we're not on the main thread;
michael@0 274 bool mOpusEnabled;
michael@0 275 #endif /* MOZ_OPUS */
michael@0 276
michael@0 277 // Decode state of the Skeleton bitstream.
michael@0 278 SkeletonState* mSkeletonState;
michael@0 279
michael@0 280 // Ogg decoding state.
michael@0 281 ogg_sync_state mOggState;
michael@0 282
michael@0 283 // Vorbis/Opus/Theora data used to compute timestamps. This is written on the
michael@0 284 // decoder thread and read on the main thread. All reading on the main
michael@0 285 // thread must be done after metadataloaded. We can't use the existing
michael@0 286 // data in the codec states due to threading issues. You must check the
michael@0 287 // associated mTheoraState or mVorbisState pointer is non-null before
michael@0 288 // using this codec data.
michael@0 289 uint32_t mVorbisSerial;
michael@0 290 uint32_t mOpusSerial;
michael@0 291 uint32_t mTheoraSerial;
michael@0 292 vorbis_info mVorbisInfo;
michael@0 293 int mOpusPreSkip;
michael@0 294 th_info mTheoraInfo;
michael@0 295
michael@0 296 // The picture region inside Theora frame to be displayed, if we have
michael@0 297 // a Theora video track.
michael@0 298 nsIntRect mPicture;
michael@0 299
michael@0 300 // True if we are decoding a chained ogg. Reading or writing to this member
michael@0 301 // should be done with |mMonitor| acquired.
michael@0 302 bool mIsChained;
michael@0 303
michael@0 304 // Number of audio frames decoded so far.
michael@0 305 int64_t mDecodedAudioFrames;
michael@0 306 };
michael@0 307
michael@0 308 } // namespace mozilla
michael@0 309
michael@0 310 #endif

mercurial