/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 | | } |