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/. */
7 #include "MediaDecoderReader.h"
8 #include "PlatformDecoderModule.h"
9 #include "nsRect.h"
10 #include "mozilla/RefPtr.h"
11 #include "mozilla/CheckedInt.h"
12 #include "VideoUtils.h"
13 #include "ImageContainer.h"
14 #include "mp4_demuxer/mp4_demuxer.h"
15 #include "MediaTaskQueue.h"
17 namespace mozilla {
19 // Decoder that uses a passed in object's Create function to create blank
20 // MediaData objects.
21 template<class BlankMediaDataCreator>
22 class BlankMediaDataDecoder : public MediaDataDecoder {
23 public:
25 BlankMediaDataDecoder(BlankMediaDataCreator* aCreator,
26 MediaTaskQueue* aTaskQueue,
27 MediaDataDecoderCallback* aCallback)
28 : mCreator(aCreator)
29 , mTaskQueue(aTaskQueue)
30 , mCallback(aCallback)
31 {
32 }
34 virtual nsresult Init() MOZ_OVERRIDE {
35 return NS_OK;
36 }
38 virtual nsresult Shutdown() MOZ_OVERRIDE {
39 return NS_OK;
40 }
42 class OutputEvent : public nsRunnable {
43 public:
44 OutputEvent(mp4_demuxer::MP4Sample* aSample,
45 MediaDataDecoderCallback* aCallback,
46 BlankMediaDataCreator* aCreator)
47 : mSample(aSample)
48 , mCreator(aCreator)
49 , mCallback(aCallback)
50 {
51 }
52 NS_IMETHOD Run() MOZ_OVERRIDE
53 {
54 mCallback->Output(mCreator->Create(mSample->composition_timestamp,
55 mSample->duration,
56 mSample->byte_offset));
57 return NS_OK;
58 }
59 private:
60 nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
61 BlankMediaDataCreator* mCreator;
62 MediaDataDecoderCallback* mCallback;
63 };
65 virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE
66 {
67 // The MediaDataDecoder must delete the sample when we're finished
68 // with it, so the OutputEvent stores it in an nsAutoPtr and deletes
69 // it once it's run.
70 RefPtr<nsIRunnable> r(new OutputEvent(aSample, mCallback, mCreator));
71 mTaskQueue->Dispatch(r);
72 return NS_OK;
73 }
75 virtual nsresult Flush() MOZ_OVERRIDE {
76 return NS_OK;
77 }
79 virtual nsresult Drain() MOZ_OVERRIDE {
80 return NS_OK;
81 }
83 private:
84 nsAutoPtr<BlankMediaDataCreator> mCreator;
85 nsAutoPtr<MediaData> mOutput;
86 RefPtr<MediaTaskQueue> mTaskQueue;
87 MediaDataDecoderCallback* mCallback;
88 };
90 class BlankVideoDataCreator {
91 public:
92 BlankVideoDataCreator(uint32_t aFrameWidth,
93 uint32_t aFrameHeight,
94 layers::ImageContainer* aImageContainer)
95 : mFrameWidth(aFrameWidth)
96 , mFrameHeight(aFrameHeight)
97 , mImageContainer(aImageContainer)
98 {
99 mInfo.mDisplay = nsIntSize(mFrameWidth, mFrameHeight);
100 mPicture = gfx::IntRect(0, 0, mFrameWidth, mFrameHeight);
101 }
103 MediaData* Create(Microseconds aDTS,
104 Microseconds aDuration,
105 int64_t aOffsetInStream)
106 {
107 // Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane,
108 // with a U and V plane that are half the size of the Y plane, i.e 8 bit,
109 // 2x2 subsampled. Have the data pointers of each frame point to the
110 // first plane, they'll always be zero'd memory anyway.
111 uint8_t* frame = new uint8_t[mFrameWidth * mFrameHeight];
112 memset(frame, 0, mFrameWidth * mFrameHeight);
113 VideoData::YCbCrBuffer buffer;
115 // Y plane.
116 buffer.mPlanes[0].mData = frame;
117 buffer.mPlanes[0].mStride = mFrameWidth;
118 buffer.mPlanes[0].mHeight = mFrameHeight;
119 buffer.mPlanes[0].mWidth = mFrameWidth;
120 buffer.mPlanes[0].mOffset = 0;
121 buffer.mPlanes[0].mSkip = 0;
123 // Cb plane.
124 buffer.mPlanes[1].mData = frame;
125 buffer.mPlanes[1].mStride = mFrameWidth / 2;
126 buffer.mPlanes[1].mHeight = mFrameHeight / 2;
127 buffer.mPlanes[1].mWidth = mFrameWidth / 2;
128 buffer.mPlanes[1].mOffset = 0;
129 buffer.mPlanes[1].mSkip = 0;
131 // Cr plane.
132 buffer.mPlanes[2].mData = frame;
133 buffer.mPlanes[2].mStride = mFrameWidth / 2;
134 buffer.mPlanes[2].mHeight = mFrameHeight / 2;
135 buffer.mPlanes[2].mWidth = mFrameWidth / 2;
136 buffer.mPlanes[2].mOffset = 0;
137 buffer.mPlanes[2].mSkip = 0;
139 return VideoData::Create(mInfo,
140 mImageContainer,
141 nullptr,
142 aOffsetInStream,
143 aDTS,
144 aDuration,
145 buffer,
146 true,
147 aDTS,
148 mPicture);
149 }
150 private:
151 VideoInfo mInfo;
152 gfx::IntRect mPicture;
153 uint32_t mFrameWidth;
154 uint32_t mFrameHeight;
155 RefPtr<layers::ImageContainer> mImageContainer;
156 };
159 class BlankAudioDataCreator {
160 public:
161 BlankAudioDataCreator(uint32_t aChannelCount,
162 uint32_t aSampleRate,
163 uint16_t aBitsPerSample)
164 : mFrameSum(0)
165 , mChannelCount(aChannelCount)
166 , mSampleRate(aSampleRate)
167 {
168 }
170 MediaData* Create(Microseconds aDTS,
171 Microseconds aDuration,
172 int64_t aOffsetInStream)
173 {
174 // Convert duration to frames. We add 1 to duration to account for
175 // rounding errors, so we get a consistent tone.
176 CheckedInt64 frames = UsecsToFrames(aDuration+1, mSampleRate);
177 if (!frames.isValid() ||
178 !mChannelCount ||
179 !mSampleRate ||
180 frames.value() > (UINT32_MAX / mChannelCount)) {
181 return nullptr;
182 }
183 AudioDataValue* samples = new AudioDataValue[frames.value() * mChannelCount];
184 // Fill the sound buffer with an A4 tone.
185 static const float pi = 3.14159265f;
186 static const float noteHz = 440.0f;
187 for (int i = 0; i < frames.value(); i++) {
188 float f = sin(2 * pi * noteHz * mFrameSum / mSampleRate);
189 for (unsigned c = 0; c < mChannelCount; c++) {
190 samples[i * mChannelCount + c] = AudioDataValue(f);
191 }
192 mFrameSum++;
193 }
194 return new AudioData(aOffsetInStream,
195 aDTS,
196 aDuration,
197 uint32_t(frames.value()),
198 samples,
199 mChannelCount);
200 }
202 private:
203 int64_t mFrameSum;
204 uint32_t mChannelCount;
205 uint32_t mSampleRate;
206 };
208 class BlankDecoderModule : public PlatformDecoderModule {
209 public:
211 // Called when the decoders have shutdown. Main thread only.
212 virtual nsresult Shutdown() MOZ_OVERRIDE {
213 return NS_OK;
214 }
216 // Decode thread.
217 virtual MediaDataDecoder* CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
218 layers::LayersBackend aLayersBackend,
219 layers::ImageContainer* aImageContainer,
220 MediaTaskQueue* aVideoTaskQueue,
221 MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE {
222 BlankVideoDataCreator* decoder = new BlankVideoDataCreator(aConfig.visible_rect().width(),
223 aConfig.visible_rect().height(),
224 aImageContainer);
225 return new BlankMediaDataDecoder<BlankVideoDataCreator>(decoder,
226 aVideoTaskQueue,
227 aCallback);
228 }
230 // Decode thread.
231 virtual MediaDataDecoder* CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
232 MediaTaskQueue* aAudioTaskQueue,
233 MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE {
234 BlankAudioDataCreator* decoder =
235 new BlankAudioDataCreator(ChannelLayoutToChannelCount(aConfig.channel_layout()),
236 aConfig.samples_per_second(),
237 aConfig.bits_per_channel());
238 return new BlankMediaDataDecoder<BlankAudioDataCreator>(decoder,
239 aAudioTaskQueue,
240 aCallback);
241 }
242 };
244 PlatformDecoderModule* CreateBlankDecoderModule()
245 {
246 return new BlankDecoderModule();
247 }
249 } // namespace mozilla