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