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 "OmxDataDecoder.h"
8 :
9 : #include "OMX_Audio.h"
10 : #include "OMX_Component.h"
11 : #include "OMX_Types.h"
12 :
13 : #include "OmxPlatformLayer.h"
14 :
15 : #include "mozilla/IntegerPrintfMacros.h"
16 : #include "mozilla/SizePrintfMacros.h"
17 :
18 : #ifdef LOG
19 : #undef LOG
20 : #undef LOGL
21 : #endif
22 :
23 : #define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("OmxDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
24 :
25 : #define LOGL(arg, ...) \
26 : { \
27 : void* p = self; \
28 : MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
29 : ("OmxDataDecoder(%p)::%s: " arg, p, __func__, ##__VA_ARGS__)); \
30 : }
31 :
32 : #define CHECK_OMX_ERR(err) \
33 : if (err != OMX_ErrorNone) { \
34 : NotifyError(err, __func__);\
35 : return; \
36 : } \
37 :
38 : namespace mozilla {
39 :
40 : static const char*
41 0 : StateTypeToStr(OMX_STATETYPE aType)
42 : {
43 0 : MOZ_ASSERT(aType == OMX_StateLoaded ||
44 : aType == OMX_StateIdle ||
45 : aType == OMX_StateExecuting ||
46 : aType == OMX_StatePause ||
47 : aType == OMX_StateWaitForResources ||
48 : aType == OMX_StateInvalid);
49 :
50 0 : switch (aType) {
51 : case OMX_StateLoaded:
52 0 : return "OMX_StateLoaded";
53 : case OMX_StateIdle:
54 0 : return "OMX_StateIdle";
55 : case OMX_StateExecuting:
56 0 : return "OMX_StateExecuting";
57 : case OMX_StatePause:
58 0 : return "OMX_StatePause";
59 : case OMX_StateWaitForResources:
60 0 : return "OMX_StateWaitForResources";
61 : case OMX_StateInvalid:
62 0 : return "OMX_StateInvalid";
63 : default:
64 0 : return "Unknown";
65 : }
66 : }
67 :
68 : // A helper class to retrieve AudioData or VideoData.
69 : class MediaDataHelper {
70 : protected:
71 0 : virtual ~MediaDataHelper() {}
72 :
73 : public:
74 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataHelper)
75 :
76 : MediaDataHelper(const TrackInfo* aTrackInfo,
77 : layers::ImageContainer* aImageContainer,
78 : OmxPromiseLayer* aOmxLayer);
79 :
80 : already_AddRefed<MediaData> GetMediaData(BufferData* aBufferData, bool& aPlatformDepenentData);
81 :
82 : protected:
83 : already_AddRefed<AudioData> CreateAudioData(BufferData* aBufferData);
84 :
85 : already_AddRefed<VideoData> CreateYUV420VideoData(BufferData* aBufferData);
86 :
87 : const TrackInfo* mTrackInfo;
88 :
89 : OMX_PARAM_PORTDEFINITIONTYPE mOutputPortDef;
90 :
91 : // audio output
92 : MediaQueue<AudioData> mAudioQueue;
93 :
94 : AudioCompactor mAudioCompactor;
95 :
96 : // video output
97 : RefPtr<layers::ImageContainer> mImageContainer;
98 : };
99 :
100 0 : OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
101 0 : layers::ImageContainer* aImageContainer)
102 0 : : mOmxTaskQueue(CreateMediaDecodeTaskQueue("OmxDataDecoder::mOmxTaskQueue"))
103 : , mImageContainer(aImageContainer)
104 : , mWatchManager(this, mOmxTaskQueue)
105 : , mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState")
106 0 : , mTrackInfo(aTrackInfo.Clone())
107 : , mFlushing(false)
108 : , mShuttingDown(false)
109 : , mCheckingInputExhausted(false)
110 0 : , mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
111 : {
112 0 : LOG("");
113 0 : mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this, aImageContainer);
114 0 : }
115 :
116 0 : OmxDataDecoder::~OmxDataDecoder()
117 : {
118 0 : LOG("");
119 0 : }
120 :
121 : void
122 0 : OmxDataDecoder::InitializationTask()
123 : {
124 0 : mWatchManager.Watch(mOmxState, &OmxDataDecoder::OmxStateRunner);
125 0 : mWatchManager.Watch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
126 0 : }
127 :
128 : void
129 0 : OmxDataDecoder::EndOfStream()
130 : {
131 0 : LOG("");
132 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
133 :
134 0 : RefPtr<OmxDataDecoder> self = this;
135 0 : mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
136 : ->Then(mOmxTaskQueue, __func__,
137 0 : [self, this] () {
138 0 : mDrainPromise.ResolveIfExists(mDecodedData, __func__);
139 0 : mDecodedData.Clear();
140 0 : },
141 0 : [self, this] () {
142 0 : mDrainPromise.ResolveIfExists(mDecodedData, __func__);
143 0 : mDecodedData.Clear();
144 0 : });
145 0 : }
146 :
147 : RefPtr<MediaDataDecoder::InitPromise>
148 0 : OmxDataDecoder::Init()
149 : {
150 0 : LOG("");
151 :
152 0 : RefPtr<OmxDataDecoder> self = this;
153 0 : return InvokeAsync(mOmxTaskQueue, __func__, [self, this]() {
154 0 : InitializationTask();
155 :
156 0 : RefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
157 0 : mOmxLayer->Init(mTrackInfo.get())
158 : ->Then(mOmxTaskQueue, __func__,
159 0 : [self, this]() {
160 : // Omx state should be OMX_StateIdle.
161 0 : mOmxState = mOmxLayer->GetState();
162 0 : MOZ_ASSERT(mOmxState != OMX_StateIdle);
163 0 : },
164 0 : [self, this]() {
165 0 : RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
166 0 : });
167 0 : return p;
168 0 : });
169 : }
170 :
171 : RefPtr<MediaDataDecoder::DecodePromise>
172 0 : OmxDataDecoder::Decode(MediaRawData* aSample)
173 : {
174 0 : LOG("sample %p", aSample);
175 0 : MOZ_ASSERT(mInitPromise.IsEmpty());
176 :
177 0 : RefPtr<OmxDataDecoder> self = this;
178 0 : RefPtr<MediaRawData> sample = aSample;
179 0 : return InvokeAsync(mOmxTaskQueue, __func__, [self, this, sample]() {
180 0 : RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
181 0 : mMediaRawDatas.AppendElement(Move(sample));
182 :
183 : // Start to fill/empty buffers.
184 0 : if (mOmxState == OMX_StateIdle ||
185 0 : mOmxState == OMX_StateExecuting) {
186 0 : FillAndEmptyBuffers();
187 : }
188 0 : return p;
189 0 : });
190 : }
191 :
192 : RefPtr<MediaDataDecoder::FlushPromise>
193 0 : OmxDataDecoder::Flush()
194 : {
195 0 : LOG("");
196 :
197 0 : mFlushing = true;
198 :
199 0 : return InvokeAsync(mOmxTaskQueue, this, __func__, &OmxDataDecoder::DoFlush);
200 : }
201 :
202 : RefPtr<MediaDataDecoder::DecodePromise>
203 0 : OmxDataDecoder::Drain()
204 : {
205 0 : LOG("");
206 :
207 0 : RefPtr<OmxDataDecoder> self = this;
208 0 : return InvokeAsync(mOmxTaskQueue, __func__, [self, this]() {
209 0 : RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
210 0 : SendEosBuffer();
211 0 : return p;
212 0 : });
213 : }
214 :
215 : RefPtr<ShutdownPromise>
216 0 : OmxDataDecoder::Shutdown()
217 : {
218 0 : LOG("");
219 :
220 0 : mShuttingDown = true;
221 :
222 : return InvokeAsync(mOmxTaskQueue, this, __func__,
223 0 : &OmxDataDecoder::DoAsyncShutdown);
224 : }
225 :
226 : RefPtr<ShutdownPromise>
227 0 : OmxDataDecoder::DoAsyncShutdown()
228 : {
229 0 : LOG("");
230 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
231 0 : MOZ_ASSERT(!mFlushing);
232 :
233 0 : mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
234 0 : mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
235 :
236 : // Flush to all ports, so all buffers can be returned from component.
237 0 : RefPtr<OmxDataDecoder> self = this;
238 0 : mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
239 0 : ->Then(mOmxTaskQueue, __func__,
240 0 : [self] () -> RefPtr<OmxCommandPromise> {
241 0 : LOGL("DoAsyncShutdown: flush complete");
242 0 : return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
243 : },
244 0 : [self] (const OmxCommandFailureHolder& aError) {
245 0 : self->mOmxLayer->Shutdown();
246 0 : return OmxCommandPromise::CreateAndReject(aError, __func__);
247 0 : })
248 0 : ->Then(mOmxTaskQueue, __func__,
249 0 : [self] () -> RefPtr<OmxCommandPromise> {
250 : RefPtr<OmxCommandPromise> p =
251 0 : self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
252 :
253 : // According to spec 3.1.1.2.2.1:
254 : // OMX_StateLoaded needs to be sent before releasing buffers.
255 : // And state transition from OMX_StateIdle to OMX_StateLoaded
256 : // is completed when all of the buffers have been removed
257 : // from the component.
258 : // Here the buffer promises are not resolved due to displaying
259 : // in layer, it needs to wait before the layer returns the
260 : // buffers.
261 0 : LOGL("DoAsyncShutdown: releasing buffers...");
262 0 : self->ReleaseBuffers(OMX_DirInput);
263 0 : self->ReleaseBuffers(OMX_DirOutput);
264 :
265 0 : return p;
266 : },
267 0 : [self] (const OmxCommandFailureHolder& aError) {
268 0 : self->mOmxLayer->Shutdown();
269 0 : return OmxCommandPromise::CreateAndReject(aError, __func__);
270 : })
271 : ->Then(mOmxTaskQueue, __func__,
272 0 : [self] () {
273 0 : LOGL("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
274 0 : self->mOmxLayer->Shutdown();
275 0 : self->mWatchManager.Shutdown();
276 0 : self->mOmxLayer = nullptr;
277 0 : self->mMediaDataHelper = nullptr;
278 :
279 0 : self->mShuttingDown = false;
280 0 : self->mOmxTaskQueue->BeginShutdown();
281 0 : self->mOmxTaskQueue->AwaitShutdownAndIdle();
282 0 : self->mShutdownPromise.Resolve(true, __func__);
283 0 : },
284 0 : [self] () {
285 0 : self->mOmxLayer->Shutdown();
286 0 : self->mWatchManager.Shutdown();
287 0 : self->mOmxLayer = nullptr;
288 0 : self->mMediaDataHelper = nullptr;
289 :
290 0 : self->mShuttingDown = false;
291 0 : self->mOmxTaskQueue->BeginShutdown();
292 0 : self->mOmxTaskQueue->AwaitShutdownAndIdle();
293 0 : self->mShutdownPromise.Resolve(true, __func__);
294 0 : });
295 0 : return mShutdownPromise.Ensure(__func__);
296 : }
297 :
298 : void
299 0 : OmxDataDecoder::FillBufferDone(BufferData* aData)
300 : {
301 0 : MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
302 :
303 : // Don't output sample when flush or shutting down, especially for video
304 : // decoded frame. Because video decoded frame has a promise in BufferData
305 : // waiting for layer to resolve it via recycle callback on Gonk, if other
306 : // module doesn't send it to layer, it will cause a unresolved promise and
307 : // waiting for resolve infinitely.
308 0 : if (mFlushing || mShuttingDown) {
309 0 : LOG("mFlush or mShuttingDown, drop data");
310 0 : aData->mStatus = BufferData::BufferStatus::FREE;
311 0 : return;
312 : }
313 :
314 0 : if (aData->mBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
315 : // Reach eos, it's an empty data so it doesn't need to output.
316 0 : EndOfStream();
317 0 : aData->mStatus = BufferData::BufferStatus::FREE;
318 : } else {
319 0 : Output(aData);
320 0 : FillAndEmptyBuffers();
321 : }
322 : }
323 :
324 : void
325 0 : OmxDataDecoder::Output(BufferData* aData)
326 : {
327 0 : if (!mMediaDataHelper) {
328 0 : mMediaDataHelper = new MediaDataHelper(mTrackInfo.get(), mImageContainer, mOmxLayer);
329 : }
330 :
331 0 : bool isPlatformData = false;
332 0 : RefPtr<MediaData> data = mMediaDataHelper->GetMediaData(aData, isPlatformData);
333 0 : if (!data) {
334 0 : aData->mStatus = BufferData::BufferStatus::FREE;
335 0 : return;
336 : }
337 :
338 0 : if (isPlatformData) {
339 : // If the MediaData is platform dependnet data, it's mostly a kind of
340 : // limited resource, for example, GraphicBuffer on Gonk. So we use promise
341 : // to notify when the resource is free.
342 0 : aData->mStatus = BufferData::BufferStatus::OMX_CLIENT_OUTPUT;
343 :
344 0 : MOZ_RELEASE_ASSERT(aData->mPromise.IsEmpty());
345 0 : RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
346 :
347 0 : RefPtr<OmxDataDecoder> self = this;
348 0 : RefPtr<BufferData> buffer = aData;
349 : p->Then(mOmxTaskQueue, __func__,
350 0 : [self, buffer] () {
351 0 : MOZ_RELEASE_ASSERT(buffer->mStatus == BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
352 0 : buffer->mStatus = BufferData::BufferStatus::FREE;
353 0 : self->FillAndEmptyBuffers();
354 0 : },
355 0 : [buffer] () {
356 0 : MOZ_RELEASE_ASSERT(buffer->mStatus == BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
357 0 : buffer->mStatus = BufferData::BufferStatus::FREE;
358 0 : });
359 : } else {
360 0 : aData->mStatus = BufferData::BufferStatus::FREE;
361 : }
362 :
363 0 : mDecodedData.AppendElement(Move(data));
364 : }
365 :
366 : void
367 0 : OmxDataDecoder::FillBufferFailure(OmxBufferFailureHolder aFailureHolder)
368 : {
369 0 : NotifyError(aFailureHolder.mError, __func__);
370 0 : }
371 :
372 : void
373 0 : OmxDataDecoder::EmptyBufferDone(BufferData* aData)
374 : {
375 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
376 0 : MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
377 :
378 : // Nothing to do when status of input buffer is OMX_CLIENT.
379 0 : aData->mStatus = BufferData::BufferStatus::FREE;
380 0 : FillAndEmptyBuffers();
381 :
382 : // There is no way to know if component gets enough raw samples to generate
383 : // output, especially for video decoding. So here it needs to request raw
384 : // samples aggressively.
385 0 : if (!mCheckingInputExhausted && !mMediaRawDatas.Length()) {
386 0 : mCheckingInputExhausted = true;
387 :
388 0 : RefPtr<OmxDataDecoder> self = this;
389 : nsCOMPtr<nsIRunnable> r =
390 0 : NS_NewRunnableFunction("OmxDataDecoder::EmptyBufferDone", [self, this]() {
391 0 : mCheckingInputExhausted = false;
392 :
393 0 : if (mMediaRawDatas.Length()) {
394 0 : return;
395 : }
396 :
397 0 : mDecodePromise.ResolveIfExists(mDecodedData, __func__);
398 0 : mDecodedData.Clear();
399 0 : });
400 :
401 0 : mOmxTaskQueue->Dispatch(r.forget());
402 : }
403 0 : }
404 :
405 : void
406 0 : OmxDataDecoder::EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder)
407 : {
408 0 : NotifyError(aFailureHolder.mError, __func__);
409 0 : }
410 :
411 : void
412 0 : OmxDataDecoder::NotifyError(OMX_ERRORTYPE aOmxError, const char* aLine, const MediaResult& aError)
413 : {
414 0 : LOG("NotifyError %d (%" PRIu32 ") at %s", static_cast<int>(aOmxError),
415 : static_cast<uint32_t>(aError.Code()), aLine);
416 0 : mDecodedData.Clear();
417 0 : mDecodePromise.RejectIfExists(aError, __func__);
418 0 : mDrainPromise.RejectIfExists(aError, __func__);
419 0 : mFlushPromise.RejectIfExists(aError, __func__);
420 0 : }
421 :
422 : void
423 0 : OmxDataDecoder::FillAndEmptyBuffers()
424 : {
425 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
426 0 : MOZ_ASSERT(mOmxState == OMX_StateExecuting);
427 :
428 : // During the port setting changed, it is forbidden to do any buffer operation.
429 0 : if (mPortSettingsChanged != -1 || mShuttingDown || mFlushing) {
430 0 : return;
431 : }
432 :
433 : // Trigger input port.
434 0 : while (!!mMediaRawDatas.Length()) {
435 : // input buffer must be used by component if there is data available.
436 0 : RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
437 0 : if (!inbuf) {
438 0 : LOG("no input buffer!");
439 0 : break;
440 : }
441 :
442 0 : RefPtr<MediaRawData> data = mMediaRawDatas[0];
443 : // Buffer size should large enough for raw data.
444 0 : MOZ_RELEASE_ASSERT(inbuf->mBuffer->nAllocLen >= data->Size());
445 :
446 0 : memcpy(inbuf->mBuffer->pBuffer, data->Data(), data->Size());
447 0 : inbuf->mBuffer->nFilledLen = data->Size();
448 0 : inbuf->mBuffer->nOffset = 0;
449 0 : inbuf->mBuffer->nFlags = inbuf->mBuffer->nAllocLen > data->Size() ?
450 : OMX_BUFFERFLAG_ENDOFFRAME : 0;
451 0 : inbuf->mBuffer->nTimeStamp = data->mTime.ToMicroseconds();
452 0 : if (data->Size()) {
453 0 : inbuf->mRawData = mMediaRawDatas[0];
454 : } else {
455 0 : LOG("send EOS buffer");
456 0 : inbuf->mBuffer->nFlags |= OMX_BUFFERFLAG_EOS;
457 : }
458 :
459 0 : LOG("feed sample %p to omx component, len %ld, flag %lX", data.get(),
460 : inbuf->mBuffer->nFilledLen, inbuf->mBuffer->nFlags);
461 0 : mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
462 : &OmxDataDecoder::EmptyBufferDone,
463 0 : &OmxDataDecoder::EmptyBufferFailure);
464 0 : mMediaRawDatas.RemoveElementAt(0);
465 : }
466 :
467 : // Trigger output port.
468 : while (true) {
469 0 : RefPtr<BufferData> outbuf = FindAvailableBuffer(OMX_DirOutput);
470 0 : if (!outbuf) {
471 0 : break;
472 : }
473 :
474 0 : mOmxLayer->FillBuffer(outbuf)->Then(mOmxTaskQueue, __func__, this,
475 : &OmxDataDecoder::FillBufferDone,
476 0 : &OmxDataDecoder::FillBufferFailure);
477 0 : }
478 : }
479 :
480 : OmxPromiseLayer::BufferData*
481 0 : OmxDataDecoder::FindAvailableBuffer(OMX_DIRTYPE aType)
482 : {
483 0 : BUFFERLIST* buffers = GetBuffers(aType);
484 :
485 0 : for (uint32_t i = 0; i < buffers->Length(); i++) {
486 0 : BufferData* buf = buffers->ElementAt(i);
487 0 : if (buf->mStatus == BufferData::BufferStatus::FREE) {
488 0 : return buf;
489 : }
490 : }
491 :
492 0 : return nullptr;
493 : }
494 :
495 : nsresult
496 0 : OmxDataDecoder::AllocateBuffers(OMX_DIRTYPE aType)
497 : {
498 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
499 :
500 0 : return mOmxLayer->AllocateOmxBuffer(aType, GetBuffers(aType));
501 : }
502 :
503 : nsresult
504 0 : OmxDataDecoder::ReleaseBuffers(OMX_DIRTYPE aType)
505 : {
506 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
507 :
508 0 : return mOmxLayer->ReleaseOmxBuffer(aType, GetBuffers(aType));
509 : }
510 :
511 : nsTArray<RefPtr<OmxPromiseLayer::BufferData>>*
512 0 : OmxDataDecoder::GetBuffers(OMX_DIRTYPE aType)
513 : {
514 0 : MOZ_ASSERT(aType == OMX_DIRTYPE::OMX_DirInput ||
515 : aType == OMX_DIRTYPE::OMX_DirOutput);
516 :
517 0 : if (aType == OMX_DIRTYPE::OMX_DirInput) {
518 0 : return &mInPortBuffers;
519 : }
520 0 : return &mOutPortBuffers;
521 : }
522 :
523 : void
524 0 : OmxDataDecoder::ResolveInitPromise(const char* aMethodName)
525 : {
526 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
527 0 : LOG("called from %s", aMethodName);
528 0 : mInitPromise.ResolveIfExists(mTrackInfo->GetType(), aMethodName);
529 0 : }
530 :
531 : void
532 0 : OmxDataDecoder::RejectInitPromise(MediaResult aError, const char* aMethodName)
533 : {
534 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
535 0 : mInitPromise.RejectIfExists(aError, aMethodName);
536 0 : }
537 :
538 : void
539 0 : OmxDataDecoder::OmxStateRunner()
540 : {
541 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
542 0 : LOG("OMX state: %s", StateTypeToStr(mOmxState));
543 :
544 : // TODO: maybe it'd be better to use promise CompletionPromise() to replace
545 : // this state machine.
546 0 : if (mOmxState == OMX_StateLoaded) {
547 0 : ConfigCodec();
548 :
549 : // Send OpenMax state command to OMX_StateIdle.
550 0 : RefPtr<OmxDataDecoder> self = this;
551 0 : mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr)
552 : ->Then(mOmxTaskQueue, __func__,
553 0 : [self] () {
554 : // Current state should be OMX_StateIdle.
555 0 : self->mOmxState = self->mOmxLayer->GetState();
556 0 : MOZ_ASSERT(self->mOmxState == OMX_StateIdle);
557 0 : },
558 0 : [self] () {
559 0 : self->RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
560 0 : });
561 :
562 : // Allocate input and output buffers.
563 0 : OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
564 0 : for(const auto id : types) {
565 0 : if (NS_FAILED(AllocateBuffers(id))) {
566 0 : LOG("Failed to allocate buffer on port %d", id);
567 0 : RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
568 0 : break;
569 : }
570 : }
571 0 : } else if (mOmxState == OMX_StateIdle) {
572 0 : RefPtr<OmxDataDecoder> self = this;
573 0 : mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateExecuting, nullptr)
574 : ->Then(mOmxTaskQueue, __func__,
575 0 : [self] () {
576 0 : self->mOmxState = self->mOmxLayer->GetState();
577 0 : MOZ_ASSERT(self->mOmxState == OMX_StateExecuting);
578 :
579 0 : self->ResolveInitPromise(__func__);
580 0 : },
581 0 : [self] () {
582 0 : self->RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
583 0 : });
584 0 : } else if (mOmxState == OMX_StateExecuting) {
585 : // Configure codec once it gets OMX_StateExecuting state.
586 0 : FillCodecConfigDataToOmx();
587 : } else {
588 0 : MOZ_ASSERT(0);
589 : }
590 0 : }
591 :
592 : void
593 0 : OmxDataDecoder::ConfigCodec()
594 : {
595 0 : OMX_ERRORTYPE err = mOmxLayer->Config();
596 0 : CHECK_OMX_ERR(err);
597 : }
598 :
599 : void
600 0 : OmxDataDecoder::FillCodecConfigDataToOmx()
601 : {
602 : // Codec configure data should be the first sample running on Omx TaskQueue.
603 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
604 0 : MOZ_ASSERT(!mMediaRawDatas.Length());
605 0 : MOZ_ASSERT(mOmxState == OMX_StateIdle || mOmxState == OMX_StateExecuting);
606 :
607 :
608 0 : RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
609 0 : RefPtr<MediaByteBuffer> csc;
610 0 : if (mTrackInfo->IsAudio()) {
611 0 : csc = mTrackInfo->GetAsAudioInfo()->mCodecSpecificConfig;
612 0 : } else if (mTrackInfo->IsVideo()) {
613 0 : csc = mTrackInfo->GetAsVideoInfo()->mExtraData;
614 : }
615 :
616 0 : MOZ_RELEASE_ASSERT(csc);
617 :
618 : // Some codecs like h264, its codec specific data is at the first packet, not in container.
619 0 : if (csc->Length()) {
620 0 : memcpy(inbuf->mBuffer->pBuffer,
621 0 : csc->Elements(),
622 0 : csc->Length());
623 0 : inbuf->mBuffer->nFilledLen = csc->Length();
624 0 : inbuf->mBuffer->nOffset = 0;
625 0 : inbuf->mBuffer->nFlags = (OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG);
626 :
627 0 : LOG("Feed codec configure data to OMX component");
628 0 : mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
629 : &OmxDataDecoder::EmptyBufferDone,
630 0 : &OmxDataDecoder::EmptyBufferFailure);
631 : }
632 0 : }
633 :
634 : bool
635 0 : OmxDataDecoder::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
636 : {
637 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
638 :
639 0 : if (mOmxLayer->Event(aEvent, aData1, aData2)) {
640 0 : return true;
641 : }
642 :
643 0 : switch (aEvent) {
644 : case OMX_EventPortSettingsChanged:
645 : {
646 : // Don't always disable port. See bug 1235340.
647 0 : if (aData2 == 0 ||
648 : aData2 == OMX_IndexParamPortDefinition) {
649 : // According to spec: "To prevent the loss of any input data, the
650 : // component issuing the OMX_EventPortSettingsChanged event on its input
651 : // port should buffer all input port data that arrives between the
652 : // emission of the OMX_EventPortSettingsChanged event and the arrival of
653 : // the command to disable the input port."
654 : //
655 : // So client needs to disable port and reallocate buffers.
656 0 : MOZ_ASSERT(mPortSettingsChanged == -1);
657 0 : mPortSettingsChanged = aData1;
658 : }
659 0 : LOG("Got OMX_EventPortSettingsChanged event");
660 0 : break;
661 : }
662 : default:
663 : {
664 : // Got error during decoding, send msg to MFR skipping to next key frame.
665 0 : if (aEvent == OMX_EventError && mOmxState == OMX_StateExecuting) {
666 0 : NotifyError((OMX_ERRORTYPE)aData1, __func__,
667 0 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
668 0 : return true;
669 : }
670 0 : LOG("WARNING: got none handle event: %d, aData1: %ld, aData2: %ld",
671 : aEvent, aData1, aData2);
672 0 : return false;
673 : }
674 : }
675 :
676 0 : return true;
677 : }
678 :
679 : bool
680 0 : OmxDataDecoder::BuffersCanBeReleased(OMX_DIRTYPE aType)
681 : {
682 0 : BUFFERLIST* buffers = GetBuffers(aType);
683 0 : uint32_t len = buffers->Length();
684 0 : for (uint32_t i = 0; i < len; i++) {
685 0 : BufferData::BufferStatus buf_status = buffers->ElementAt(i)->mStatus;
686 0 : if (buf_status == BufferData::BufferStatus::OMX_COMPONENT ||
687 : buf_status == BufferData::BufferStatus::OMX_CLIENT_OUTPUT) {
688 0 : return false;
689 : }
690 : }
691 0 : return true;
692 : }
693 :
694 : OMX_DIRTYPE
695 0 : OmxDataDecoder::GetPortDirection(uint32_t aPortIndex)
696 : {
697 : OMX_PARAM_PORTDEFINITIONTYPE def;
698 0 : InitOmxParameter(&def);
699 0 : def.nPortIndex = mPortSettingsChanged;
700 :
701 0 : OMX_ERRORTYPE err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
702 : &def,
703 0 : sizeof(def));
704 0 : if (err != OMX_ErrorNone) {
705 0 : return OMX_DirMax;
706 : }
707 0 : return def.eDir;
708 : }
709 :
710 : RefPtr<OmxPromiseLayer::OmxBufferPromise::AllPromiseType>
711 0 : OmxDataDecoder::CollectBufferPromises(OMX_DIRTYPE aType)
712 : {
713 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
714 :
715 0 : nsTArray<RefPtr<OmxBufferPromise>> promises;
716 0 : OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
717 0 : for (const auto type : types) {
718 0 : if ((aType == type) || (aType == OMX_DirMax)) {
719 : // find the buffer which has promise.
720 0 : BUFFERLIST* buffers = GetBuffers(type);
721 :
722 0 : for (uint32_t i = 0; i < buffers->Length(); i++) {
723 0 : BufferData* buf = buffers->ElementAt(i);
724 0 : if (!buf->mPromise.IsEmpty()) {
725 : // OmxBufferPromise is not exclusive, it can be multiple "Then"s, so it
726 : // is safe to call "Ensure" here.
727 0 : promises.AppendElement(buf->mPromise.Ensure(__func__));
728 : }
729 : }
730 : }
731 : }
732 :
733 0 : LOG("CollectBufferPromises: type %d, total %" PRIuSIZE " promiese", aType, promises.Length());
734 0 : if (promises.Length()) {
735 0 : return OmxBufferPromise::All(mOmxTaskQueue, promises);
736 : }
737 :
738 0 : nsTArray<BufferData*> headers;
739 0 : return OmxBufferPromise::AllPromiseType::CreateAndResolve(headers, __func__);
740 : }
741 :
742 : void
743 0 : OmxDataDecoder::PortSettingsChanged()
744 : {
745 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
746 :
747 0 : if (mPortSettingsChanged == -1 || mOmxState == OMX_STATETYPE::OMX_StateInvalid) {
748 0 : return;
749 : }
750 :
751 : // The PortSettingsChanged algorithm:
752 : //
753 : // 1. disable port.
754 : // 2. wait for port buffers return to client and then release these buffers.
755 : // 3. enable port.
756 : // 4. allocate port buffers.
757 : //
758 :
759 : // Disable port. Get port definition if the target port is enable.
760 : OMX_PARAM_PORTDEFINITIONTYPE def;
761 0 : InitOmxParameter(&def);
762 0 : def.nPortIndex = mPortSettingsChanged;
763 :
764 0 : OMX_ERRORTYPE err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
765 : &def,
766 0 : sizeof(def));
767 0 : CHECK_OMX_ERR(err);
768 :
769 0 : RefPtr<OmxDataDecoder> self = this;
770 0 : if (def.bEnabled) {
771 : // 1. disable port.
772 0 : LOG("PortSettingsChanged: disable port %lu", def.nPortIndex);
773 0 : mOmxLayer->SendCommand(OMX_CommandPortDisable, mPortSettingsChanged, nullptr)
774 0 : ->Then(mOmxTaskQueue, __func__,
775 0 : [self, def] () -> RefPtr<OmxCommandPromise> {
776 : // 3. enable port.
777 : // Send enable port command.
778 : RefPtr<OmxCommandPromise> p =
779 0 : self->mOmxLayer->SendCommand(OMX_CommandPortEnable,
780 0 : self->mPortSettingsChanged,
781 0 : nullptr);
782 :
783 : // 4. allocate port buffers.
784 : // Allocate new port buffers.
785 0 : nsresult rv = self->AllocateBuffers(def.eDir);
786 0 : if (NS_FAILED(rv)) {
787 0 : self->NotifyError(OMX_ErrorUndefined, __func__);
788 : }
789 :
790 0 : return p;
791 : },
792 0 : [self] (const OmxCommandFailureHolder& aError) {
793 0 : self->NotifyError(OMX_ErrorUndefined, __func__);
794 0 : return OmxCommandPromise::CreateAndReject(aError, __func__);
795 0 : })
796 : ->Then(mOmxTaskQueue, __func__,
797 0 : [self] () {
798 0 : LOGL("PortSettingsChanged: port settings changed complete");
799 : // finish port setting changed.
800 0 : self->mPortSettingsChanged = -1;
801 0 : self->FillAndEmptyBuffers();
802 0 : },
803 0 : [self] () {
804 0 : self->NotifyError(OMX_ErrorUndefined, __func__);
805 0 : });
806 :
807 : // 2. wait for port buffers return to client and then release these buffers.
808 : //
809 : // Port buffers will be returned to client soon once OMX_CommandPortDisable
810 : // command is sent. Then releasing these buffers.
811 0 : CollectBufferPromises(def.eDir)
812 : ->Then(mOmxTaskQueue, __func__,
813 0 : [self, def] () {
814 0 : MOZ_ASSERT(self->BuffersCanBeReleased(def.eDir));
815 0 : nsresult rv = self->ReleaseBuffers(def.eDir);
816 0 : if (NS_FAILED(rv)) {
817 0 : MOZ_RELEASE_ASSERT(0);
818 : self->NotifyError(OMX_ErrorUndefined, __func__);
819 : }
820 0 : },
821 0 : [self] () {
822 0 : self->NotifyError(OMX_ErrorUndefined, __func__);
823 0 : });
824 : }
825 : }
826 :
827 : void
828 0 : OmxDataDecoder::SendEosBuffer()
829 : {
830 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
831 :
832 : // There is no 'Drain' API in OpenMax, so it needs to wait for output sample
833 : // with EOS flag. However, MediaRawData doesn't provide EOS information,
834 : // so here it generates an empty BufferData with eos OMX_BUFFERFLAG_EOS in queue.
835 : // This behaviour should be compliant with spec, I think...
836 0 : RefPtr<MediaRawData> eos_data = new MediaRawData();
837 0 : mMediaRawDatas.AppendElement(eos_data);
838 0 : FillAndEmptyBuffers();
839 0 : }
840 :
841 : RefPtr<MediaDataDecoder::FlushPromise>
842 0 : OmxDataDecoder::DoFlush()
843 : {
844 0 : MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
845 :
846 0 : mDecodedData.Clear();
847 0 : mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
848 0 : mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
849 :
850 0 : RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
851 :
852 : // 1. Call OMX command OMX_CommandFlush in Omx TaskQueue.
853 : // 2. Remove all elements in mMediaRawDatas when flush is completed.
854 0 : mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
855 : ->Then(mOmxTaskQueue, __func__, this,
856 : &OmxDataDecoder::FlushComplete,
857 0 : &OmxDataDecoder::FlushFailure);
858 :
859 0 : return p;
860 : }
861 :
862 : void
863 0 : OmxDataDecoder::FlushComplete(OMX_COMMANDTYPE aCommandType)
864 : {
865 0 : mMediaRawDatas.Clear();
866 0 : mFlushing = false;
867 :
868 0 : LOG("Flush complete");
869 0 : mFlushPromise.ResolveIfExists(true, __func__);
870 0 : }
871 :
872 0 : void OmxDataDecoder::FlushFailure(OmxCommandFailureHolder aFailureHolder)
873 : {
874 0 : mFlushing = false;
875 0 : mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
876 0 : }
877 :
878 0 : MediaDataHelper::MediaDataHelper(const TrackInfo* aTrackInfo,
879 : layers::ImageContainer* aImageContainer,
880 0 : OmxPromiseLayer* aOmxLayer)
881 : : mTrackInfo(aTrackInfo)
882 : , mAudioCompactor(mAudioQueue)
883 0 : , mImageContainer(aImageContainer)
884 : {
885 0 : InitOmxParameter(&mOutputPortDef);
886 0 : mOutputPortDef.nPortIndex = aOmxLayer->OutputPortIndex();
887 0 : aOmxLayer->GetParameter(OMX_IndexParamPortDefinition, &mOutputPortDef, sizeof(mOutputPortDef));
888 0 : }
889 :
890 : already_AddRefed<MediaData>
891 0 : MediaDataHelper::GetMediaData(BufferData* aBufferData, bool& aPlatformDepenentData)
892 : {
893 0 : aPlatformDepenentData = false;
894 0 : RefPtr<MediaData> data;
895 :
896 0 : if (mTrackInfo->IsAudio()) {
897 0 : if (!aBufferData->mBuffer->nFilledLen) {
898 0 : return nullptr;
899 : }
900 0 : data = CreateAudioData(aBufferData);
901 0 : } else if (mTrackInfo->IsVideo()) {
902 0 : data = aBufferData->GetPlatformMediaData();
903 0 : if (data) {
904 0 : aPlatformDepenentData = true;
905 : } else {
906 0 : if (!aBufferData->mBuffer->nFilledLen) {
907 0 : return nullptr;
908 : }
909 : // Get YUV VideoData, it uses more CPU, in most cases, on software codec.
910 0 : data = CreateYUV420VideoData(aBufferData);
911 : }
912 :
913 : // Update video time code, duration... from the raw data.
914 0 : VideoData* video(data->As<VideoData>());
915 0 : if (aBufferData->mRawData) {
916 0 : video->mTime = aBufferData->mRawData->mTime;
917 0 : video->mTimecode = aBufferData->mRawData->mTimecode;
918 0 : video->mOffset = aBufferData->mRawData->mOffset;
919 0 : video->mDuration = aBufferData->mRawData->mDuration;
920 0 : video->mKeyframe = aBufferData->mRawData->mKeyframe;
921 : }
922 : }
923 :
924 0 : return data.forget();
925 : }
926 :
927 : already_AddRefed<AudioData>
928 0 : MediaDataHelper::CreateAudioData(BufferData* aBufferData)
929 : {
930 0 : RefPtr<AudioData> audio;
931 0 : OMX_BUFFERHEADERTYPE* buf = aBufferData->mBuffer;
932 0 : const AudioInfo* info = mTrackInfo->GetAsAudioInfo();
933 0 : if (buf->nFilledLen) {
934 0 : uint64_t offset = 0;
935 0 : uint32_t frames = buf->nFilledLen / (2 * info->mChannels);
936 0 : if (aBufferData->mRawData) {
937 0 : offset = aBufferData->mRawData->mOffset;
938 : }
939 : typedef AudioCompactor::NativeCopy OmxCopy;
940 0 : mAudioCompactor.Push(offset,
941 0 : buf->nTimeStamp,
942 0 : info->mRate,
943 : frames,
944 0 : info->mChannels,
945 0 : OmxCopy(buf->pBuffer + buf->nOffset,
946 : buf->nFilledLen,
947 0 : info->mChannels));
948 0 : audio = mAudioQueue.PopFront();
949 : }
950 :
951 0 : return audio.forget();
952 : }
953 :
954 : already_AddRefed<VideoData>
955 0 : MediaDataHelper::CreateYUV420VideoData(BufferData* aBufferData)
956 : {
957 0 : uint8_t *yuv420p_buffer = (uint8_t *)aBufferData->mBuffer->pBuffer;
958 0 : int32_t stride = mOutputPortDef.format.video.nStride;
959 0 : int32_t slice_height = mOutputPortDef.format.video.nSliceHeight;
960 0 : int32_t width = mTrackInfo->GetAsVideoInfo()->mImage.width;
961 0 : int32_t height = mTrackInfo->GetAsVideoInfo()->mImage.height;
962 :
963 : // TODO: convert other formats to YUV420.
964 0 : if (mOutputPortDef.format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar) {
965 0 : return nullptr;
966 : }
967 :
968 0 : size_t yuv420p_y_size = stride * slice_height;
969 0 : size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
970 0 : uint8_t *yuv420p_y = yuv420p_buffer;
971 0 : uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
972 0 : uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
973 :
974 0 : VideoData::YCbCrBuffer b;
975 0 : b.mPlanes[0].mData = yuv420p_y;
976 0 : b.mPlanes[0].mWidth = width;
977 0 : b.mPlanes[0].mHeight = height;
978 0 : b.mPlanes[0].mStride = stride;
979 0 : b.mPlanes[0].mOffset = 0;
980 0 : b.mPlanes[0].mSkip = 0;
981 :
982 0 : b.mPlanes[1].mData = yuv420p_u;
983 0 : b.mPlanes[1].mWidth = (width + 1) / 2;
984 0 : b.mPlanes[1].mHeight = (height + 1) / 2;
985 0 : b.mPlanes[1].mStride = (stride + 1) / 2;
986 0 : b.mPlanes[1].mOffset = 0;
987 0 : b.mPlanes[1].mSkip = 0;
988 :
989 0 : b.mPlanes[2].mData = yuv420p_v;
990 0 : b.mPlanes[2].mWidth =(width + 1) / 2;
991 0 : b.mPlanes[2].mHeight = (height + 1) / 2;
992 0 : b.mPlanes[2].mStride = (stride + 1) / 2;
993 0 : b.mPlanes[2].mOffset = 0;
994 0 : b.mPlanes[2].mSkip = 0;
995 :
996 0 : VideoInfo info(*mTrackInfo->GetAsVideoInfo());
997 : RefPtr<VideoData> data =
998 0 : VideoData::CreateAndCopyData(info,
999 : mImageContainer,
1000 : 0, // Filled later by caller.
1001 0 : media::TimeUnit::Zero(), // Filled later by caller.
1002 0 : media::TimeUnit::FromMicroseconds(1), // We don't know the duration.
1003 : b,
1004 : 0, // Filled later by caller.
1005 0 : media::TimeUnit::FromMicroseconds(-1),
1006 0 : info.ImageRect());
1007 :
1008 0 : LOG("YUV420 VideoData: disp width %d, height %d, pic width %d, height %d, time %lld",
1009 : info.mDisplay.width, info.mDisplay.height, info.mImage.width,
1010 : info.mImage.height, aBufferData->mBuffer->nTimeStamp);
1011 :
1012 0 : return data.forget();
1013 : }
1014 :
1015 : }
|