|
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 |
|
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" |
|
16 |
|
17 namespace mozilla { |
|
18 |
|
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: |
|
24 |
|
25 BlankMediaDataDecoder(BlankMediaDataCreator* aCreator, |
|
26 MediaTaskQueue* aTaskQueue, |
|
27 MediaDataDecoderCallback* aCallback) |
|
28 : mCreator(aCreator) |
|
29 , mTaskQueue(aTaskQueue) |
|
30 , mCallback(aCallback) |
|
31 { |
|
32 } |
|
33 |
|
34 virtual nsresult Init() MOZ_OVERRIDE { |
|
35 return NS_OK; |
|
36 } |
|
37 |
|
38 virtual nsresult Shutdown() MOZ_OVERRIDE { |
|
39 return NS_OK; |
|
40 } |
|
41 |
|
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 }; |
|
64 |
|
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 } |
|
74 |
|
75 virtual nsresult Flush() MOZ_OVERRIDE { |
|
76 return NS_OK; |
|
77 } |
|
78 |
|
79 virtual nsresult Drain() MOZ_OVERRIDE { |
|
80 return NS_OK; |
|
81 } |
|
82 |
|
83 private: |
|
84 nsAutoPtr<BlankMediaDataCreator> mCreator; |
|
85 nsAutoPtr<MediaData> mOutput; |
|
86 RefPtr<MediaTaskQueue> mTaskQueue; |
|
87 MediaDataDecoderCallback* mCallback; |
|
88 }; |
|
89 |
|
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 } |
|
102 |
|
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; |
|
114 |
|
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; |
|
122 |
|
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; |
|
130 |
|
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; |
|
138 |
|
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 }; |
|
157 |
|
158 |
|
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 } |
|
169 |
|
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 } |
|
201 |
|
202 private: |
|
203 int64_t mFrameSum; |
|
204 uint32_t mChannelCount; |
|
205 uint32_t mSampleRate; |
|
206 }; |
|
207 |
|
208 class BlankDecoderModule : public PlatformDecoderModule { |
|
209 public: |
|
210 |
|
211 // Called when the decoders have shutdown. Main thread only. |
|
212 virtual nsresult Shutdown() MOZ_OVERRIDE { |
|
213 return NS_OK; |
|
214 } |
|
215 |
|
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 } |
|
229 |
|
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 }; |
|
243 |
|
244 PlatformDecoderModule* CreateBlankDecoderModule() |
|
245 { |
|
246 return new BlankDecoderModule(); |
|
247 } |
|
248 |
|
249 } // namespace mozilla |