Line data Source code
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 "GMPVideoDecoder.h"
8 : #include "GMPDecoderModule.h"
9 : #include "GMPVideoHost.h"
10 : #include "MediaData.h"
11 : #include "VPXDecoder.h"
12 : #include "mozilla/EndianUtils.h"
13 : #include "prsystem.h"
14 : #include "mp4_demuxer/AnnexB.h"
15 :
16 : namespace mozilla {
17 :
18 : #if defined(DEBUG)
19 0 : static bool IsOnGMPThread()
20 : {
21 : nsCOMPtr<mozIGeckoMediaPluginService> mps =
22 0 : do_GetService("@mozilla.org/gecko-media-plugin-service;1");
23 0 : MOZ_ASSERT(mps);
24 :
25 0 : nsCOMPtr<nsIThread> gmpThread;
26 0 : nsresult rv = mps->GetThread(getter_AddRefs(gmpThread));
27 0 : MOZ_ASSERT(NS_SUCCEEDED(rv) && gmpThread);
28 0 : return gmpThread->EventTarget()->IsOnCurrentThread();
29 : }
30 : #endif
31 :
32 0 : GMPVideoDecoderParams::GMPVideoDecoderParams(const CreateDecoderParams& aParams)
33 0 : : mConfig(aParams.VideoConfig())
34 0 : , mTaskQueue(aParams.mTaskQueue)
35 0 : , mImageContainer(aParams.mImageContainer)
36 0 : , mLayersBackend(aParams.GetLayersBackend())
37 0 : , mCrashHelper(aParams.mCrashHelper)
38 : {
39 0 : }
40 :
41 : void
42 0 : GMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
43 : {
44 0 : GMPUniquePtr<GMPVideoi420Frame> decodedFrame(aDecodedFrame);
45 :
46 0 : MOZ_ASSERT(IsOnGMPThread());
47 :
48 0 : VideoData::YCbCrBuffer b;
49 0 : for (int i = 0; i < kGMPNumOfPlanes; ++i) {
50 0 : b.mPlanes[i].mData = decodedFrame->Buffer(GMPPlaneType(i));
51 0 : b.mPlanes[i].mStride = decodedFrame->Stride(GMPPlaneType(i));
52 0 : if (i == kGMPYPlane) {
53 0 : b.mPlanes[i].mWidth = decodedFrame->Width();
54 0 : b.mPlanes[i].mHeight = decodedFrame->Height();
55 : } else {
56 0 : b.mPlanes[i].mWidth = (decodedFrame->Width() + 1) / 2;
57 0 : b.mPlanes[i].mHeight = (decodedFrame->Height() + 1) / 2;
58 : }
59 0 : b.mPlanes[i].mOffset = 0;
60 0 : b.mPlanes[i].mSkip = 0;
61 : }
62 :
63 : gfx::IntRect pictureRegion(
64 0 : 0, 0, decodedFrame->Width(), decodedFrame->Height());
65 0 : RefPtr<VideoData> v = VideoData::CreateAndCopyData(
66 : mConfig,
67 : mImageContainer,
68 : mLastStreamOffset,
69 0 : media::TimeUnit::FromMicroseconds(decodedFrame->Timestamp()),
70 0 : media::TimeUnit::FromMicroseconds(decodedFrame->Duration()),
71 : b,
72 : false,
73 0 : media::TimeUnit::FromMicroseconds(-1),
74 0 : pictureRegion);
75 0 : RefPtr<GMPVideoDecoder> self = this;
76 0 : if (v) {
77 0 : mDecodedData.AppendElement(Move(v));
78 : } else {
79 0 : mDecodedData.Clear();
80 0 : mDecodePromise.RejectIfExists(
81 0 : MediaResult(NS_ERROR_OUT_OF_MEMORY,
82 0 : RESULT_DETAIL("CallBack::CreateAndCopyData")),
83 0 : __func__);
84 : }
85 0 : }
86 :
87 : void
88 0 : GMPVideoDecoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
89 : {
90 0 : MOZ_ASSERT(IsOnGMPThread());
91 0 : }
92 :
93 : void
94 0 : GMPVideoDecoder::ReceivedDecodedFrame(const uint64_t aPictureId)
95 : {
96 0 : MOZ_ASSERT(IsOnGMPThread());
97 0 : }
98 :
99 : void
100 0 : GMPVideoDecoder::InputDataExhausted()
101 : {
102 0 : MOZ_ASSERT(IsOnGMPThread());
103 0 : mDecodePromise.ResolveIfExists(mDecodedData, __func__);
104 0 : mDecodedData.Clear();
105 0 : }
106 :
107 : void
108 0 : GMPVideoDecoder::DrainComplete()
109 : {
110 0 : MOZ_ASSERT(IsOnGMPThread());
111 0 : mDrainPromise.ResolveIfExists(mDecodedData, __func__);
112 0 : mDecodedData.Clear();
113 0 : }
114 :
115 : void
116 0 : GMPVideoDecoder::ResetComplete()
117 : {
118 0 : MOZ_ASSERT(IsOnGMPThread());
119 0 : mFlushPromise.ResolveIfExists(true, __func__);
120 0 : }
121 :
122 : void
123 0 : GMPVideoDecoder::Error(GMPErr aErr)
124 : {
125 0 : MOZ_ASSERT(IsOnGMPThread());
126 : auto error = MediaResult(aErr == GMPDecodeErr ? NS_ERROR_DOM_MEDIA_DECODE_ERR
127 : : NS_ERROR_DOM_MEDIA_FATAL_ERR,
128 0 : RESULT_DETAIL("GMPErr:%x", aErr));
129 0 : mDecodePromise.RejectIfExists(error, __func__);
130 0 : mDrainPromise.RejectIfExists(error, __func__);
131 0 : mFlushPromise.RejectIfExists(error, __func__);
132 0 : }
133 :
134 : void
135 0 : GMPVideoDecoder::Terminated()
136 : {
137 0 : MOZ_ASSERT(IsOnGMPThread());
138 0 : Error(GMPErr::GMPAbortedErr);
139 0 : }
140 :
141 0 : GMPVideoDecoder::GMPVideoDecoder(const GMPVideoDecoderParams& aParams)
142 : : mConfig(aParams.mConfig)
143 : , mGMP(nullptr)
144 : , mHost(nullptr)
145 : , mConvertNALUnitLengths(false)
146 : , mCrashHelper(aParams.mCrashHelper)
147 0 : , mImageContainer(aParams.mImageContainer)
148 : {
149 0 : }
150 :
151 : void
152 0 : GMPVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
153 : {
154 0 : if (MP4Decoder::IsH264(mConfig.mMimeType)) {
155 0 : aTags.AppendElement(NS_LITERAL_CSTRING("h264"));
156 0 : } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
157 0 : aTags.AppendElement(NS_LITERAL_CSTRING("vp8"));
158 0 : } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
159 0 : aTags.AppendElement(NS_LITERAL_CSTRING("vp9"));
160 : }
161 0 : }
162 :
163 : nsCString
164 0 : GMPVideoDecoder::GetNodeId()
165 : {
166 0 : return SHARED_GMP_DECODING_NODE_ID;
167 : }
168 :
169 : GMPUniquePtr<GMPVideoEncodedFrame>
170 0 : GMPVideoDecoder::CreateFrame(MediaRawData* aSample)
171 : {
172 0 : GMPVideoFrame* ftmp = nullptr;
173 0 : GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
174 0 : if (GMP_FAILED(err)) {
175 0 : return nullptr;
176 : }
177 :
178 : GMPUniquePtr<GMPVideoEncodedFrame> frame(
179 0 : static_cast<GMPVideoEncodedFrame*>(ftmp));
180 0 : err = frame->CreateEmptyFrame(aSample->Size());
181 0 : if (GMP_FAILED(err)) {
182 0 : return nullptr;
183 : }
184 :
185 0 : memcpy(frame->Buffer(), aSample->Data(), frame->Size());
186 :
187 : // Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
188 : // suit the GMP API.
189 0 : if (mConvertNALUnitLengths) {
190 0 : const int kNALLengthSize = 4;
191 0 : uint8_t* buf = frame->Buffer();
192 0 : while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
193 0 : uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
194 0 : *reinterpret_cast<uint32_t *>(buf) = length;
195 0 : buf += length;
196 : }
197 : }
198 :
199 0 : frame->SetBufferType(GMP_BufferLength32);
200 :
201 0 : frame->SetEncodedWidth(mConfig.mDisplay.width);
202 0 : frame->SetEncodedHeight(mConfig.mDisplay.height);
203 0 : frame->SetTimeStamp(aSample->mTime.ToMicroseconds());
204 0 : frame->SetCompleteFrame(true);
205 0 : frame->SetDuration(aSample->mDuration.ToMicroseconds());
206 0 : frame->SetFrameType(aSample->mKeyframe ? kGMPKeyFrame : kGMPDeltaFrame);
207 :
208 0 : return frame;
209 : }
210 :
211 : const VideoInfo&
212 0 : GMPVideoDecoder::GetConfig() const
213 : {
214 0 : return mConfig;
215 : }
216 :
217 : void
218 0 : GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
219 : {
220 0 : MOZ_ASSERT(IsOnGMPThread());
221 :
222 0 : if (!aGMP) {
223 0 : mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
224 0 : return;
225 : }
226 0 : MOZ_ASSERT(aHost);
227 :
228 0 : if (mInitPromise.IsEmpty()) {
229 : // GMP must have been shutdown while we were waiting for Init operation
230 : // to complete.
231 0 : aGMP->Close();
232 0 : return;
233 : }
234 :
235 0 : bool isOpenH264 = aGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
236 :
237 : GMPVideoCodec codec;
238 0 : memset(&codec, 0, sizeof(codec));
239 :
240 0 : codec.mGMPApiVersion = kGMPVersion33;
241 0 : nsTArray<uint8_t> codecSpecific;
242 0 : if (MP4Decoder::IsH264(mConfig.mMimeType)) {
243 0 : codec.mCodecType = kGMPVideoCodecH264;
244 0 : codecSpecific.AppendElement(0); // mPacketizationMode.
245 0 : codecSpecific.AppendElements(mConfig.mExtraData->Elements(),
246 0 : mConfig.mExtraData->Length());
247 : // OpenH264 expects pseudo-AVCC, but others must be passed
248 : // AnnexB for H264.
249 0 : mConvertToAnnexB = !isOpenH264;
250 0 : } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
251 0 : codec.mCodecType = kGMPVideoCodecVP8;
252 0 : } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
253 0 : codec.mCodecType = kGMPVideoCodecVP9;
254 : } else {
255 : // Unrecognized mime type
256 0 : aGMP->Close();
257 0 : mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
258 0 : return;
259 : }
260 0 : codec.mWidth = mConfig.mImage.width;
261 0 : codec.mHeight = mConfig.mImage.height;
262 :
263 0 : nsresult rv = aGMP->InitDecode(codec,
264 : codecSpecific,
265 : this,
266 0 : PR_GetNumberOfProcessors());
267 0 : if (NS_FAILED(rv)) {
268 0 : aGMP->Close();
269 0 : mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
270 0 : return;
271 : }
272 :
273 0 : mGMP = aGMP;
274 0 : mHost = aHost;
275 :
276 : // GMP implementations have interpreted the meaning of GMP_BufferLength32
277 : // differently. The OpenH264 GMP expects GMP_BufferLength32 to behave as
278 : // specified in the GMP API, where each buffer is prefixed by a 32-bit
279 : // host-endian buffer length that includes the size of the buffer length
280 : // field. Other existing GMPs currently expect GMP_BufferLength32 (when
281 : // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
282 : // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
283 : // and do not include the length of the buffer length field.
284 0 : mConvertNALUnitLengths = isOpenH264;
285 :
286 0 : mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__);
287 : }
288 :
289 : RefPtr<MediaDataDecoder::InitPromise>
290 0 : GMPVideoDecoder::Init()
291 : {
292 0 : MOZ_ASSERT(IsOnGMPThread());
293 :
294 0 : mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
295 0 : MOZ_ASSERT(mMPS);
296 :
297 0 : RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
298 :
299 0 : nsTArray<nsCString> tags;
300 0 : InitTags(tags);
301 0 : UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
302 0 : if (NS_FAILED(mMPS->GetDecryptingGMPVideoDecoder(mCrashHelper,
303 : &tags,
304 : GetNodeId(),
305 : Move(callback),
306 : DecryptorId()))) {
307 0 : mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
308 : }
309 :
310 0 : return promise;
311 : }
312 :
313 : RefPtr<MediaDataDecoder::DecodePromise>
314 0 : GMPVideoDecoder::Decode(MediaRawData* aSample)
315 : {
316 0 : MOZ_ASSERT(IsOnGMPThread());
317 :
318 0 : RefPtr<MediaRawData> sample(aSample);
319 0 : if (!mGMP) {
320 : return DecodePromise::CreateAndReject(
321 0 : MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
322 0 : RESULT_DETAIL("mGMP not initialized")),
323 0 : __func__);
324 : }
325 :
326 0 : mLastStreamOffset = sample->mOffset;
327 :
328 0 : GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(sample);
329 0 : if (!frame) {
330 : return DecodePromise::CreateAndReject(
331 0 : MediaResult(NS_ERROR_OUT_OF_MEMORY,
332 0 : RESULT_DETAIL("CreateFrame returned null")),
333 0 : __func__);
334 : }
335 0 : RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
336 0 : nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
337 0 : nsresult rv = mGMP->Decode(Move(frame), false, info, 0);
338 0 : if (NS_FAILED(rv)) {
339 0 : mDecodePromise.Reject(MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
340 0 : RESULT_DETAIL("mGMP->Decode:%" PRIx32,
341 : static_cast<uint32_t>(rv))),
342 0 : __func__);
343 : }
344 0 : return p;
345 : }
346 :
347 : RefPtr<MediaDataDecoder::FlushPromise>
348 0 : GMPVideoDecoder::Flush()
349 : {
350 0 : MOZ_ASSERT(IsOnGMPThread());
351 :
352 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
353 0 : mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
354 :
355 0 : RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
356 0 : if (!mGMP || NS_FAILED(mGMP->Reset())) {
357 : // Abort the flush.
358 0 : mFlushPromise.Resolve(true, __func__);
359 : }
360 0 : return p;
361 : }
362 :
363 : RefPtr<MediaDataDecoder::DecodePromise>
364 0 : GMPVideoDecoder::Drain()
365 : {
366 0 : MOZ_ASSERT(IsOnGMPThread());
367 :
368 0 : MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
369 :
370 0 : RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
371 0 : if (!mGMP || NS_FAILED(mGMP->Drain())) {
372 0 : mDrainPromise.Resolve(DecodedData(), __func__);
373 : }
374 :
375 0 : return p;
376 : }
377 :
378 : RefPtr<ShutdownPromise>
379 0 : GMPVideoDecoder::Shutdown()
380 : {
381 0 : mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
382 0 : mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
383 :
384 : // Note that this *may* be called from the proxy thread also.
385 : // TODO: If that's the case, then this code is racy.
386 0 : if (!mGMP) {
387 0 : return ShutdownPromise::CreateAndResolve(true, __func__);
388 : }
389 : // Note this unblocks flush and drain operations waiting for callbacks.
390 0 : mGMP->Close();
391 0 : mGMP = nullptr;
392 0 : return ShutdownPromise::CreateAndResolve(true, __func__);
393 : }
394 :
395 : } // namespace mozilla
|