Coverage Report

Created: 2018-09-25 14:53

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