|
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 file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 #include <unistd.h> |
|
7 #include <fcntl.h> |
|
8 |
|
9 #include "base/basictypes.h" |
|
10 #include <cutils/properties.h> |
|
11 #include <stagefright/foundation/ADebug.h> |
|
12 #include <stagefright/foundation/AMessage.h> |
|
13 #include <stagefright/MediaExtractor.h> |
|
14 #include <stagefright/MetaData.h> |
|
15 #include <stagefright/OMXClient.h> |
|
16 #include <stagefright/OMXCodec.h> |
|
17 #include <OMX.h> |
|
18 #if MOZ_WIDGET_GONK && ANDROID_VERSION >= 17 |
|
19 #include <ui/Fence.h> |
|
20 #endif |
|
21 |
|
22 #include "mozilla/layers/GrallocTextureClient.h" |
|
23 #include "mozilla/layers/TextureClient.h" |
|
24 #include "mozilla/Preferences.h" |
|
25 #include "mozilla/Types.h" |
|
26 #include "mozilla/Monitor.h" |
|
27 #include "nsMimeTypes.h" |
|
28 #include "MPAPI.h" |
|
29 #include "prlog.h" |
|
30 |
|
31 #include "GonkNativeWindow.h" |
|
32 #include "GonkNativeWindowClient.h" |
|
33 #include "OMXCodecProxy.h" |
|
34 #include "OmxDecoder.h" |
|
35 #include "nsISeekableStream.h" |
|
36 |
|
37 #ifdef PR_LOGGING |
|
38 PRLogModuleInfo *gOmxDecoderLog; |
|
39 #define LOG(type, msg...) PR_LOG(gOmxDecoderLog, type, (msg)) |
|
40 #else |
|
41 #define LOG(x...) |
|
42 #endif |
|
43 |
|
44 using namespace MPAPI; |
|
45 using namespace mozilla; |
|
46 using namespace mozilla::gfx; |
|
47 using namespace mozilla::layers; |
|
48 |
|
49 namespace mozilla { |
|
50 |
|
51 class ReleaseOmxDecoderRunnable : public nsRunnable |
|
52 { |
|
53 public: |
|
54 ReleaseOmxDecoderRunnable(const android::sp<android::OmxDecoder>& aOmxDecoder) |
|
55 : mOmxDecoder(aOmxDecoder) |
|
56 { |
|
57 } |
|
58 |
|
59 NS_METHOD Run() MOZ_OVERRIDE |
|
60 { |
|
61 MOZ_ASSERT(NS_IsMainThread()); |
|
62 mOmxDecoder = nullptr; // release OmxDecoder |
|
63 return NS_OK; |
|
64 } |
|
65 |
|
66 private: |
|
67 android::sp<android::OmxDecoder> mOmxDecoder; |
|
68 }; |
|
69 |
|
70 class OmxDecoderProcessCachedDataTask : public Task |
|
71 { |
|
72 public: |
|
73 OmxDecoderProcessCachedDataTask(android::OmxDecoder* aOmxDecoder, int64_t aOffset) |
|
74 : mOmxDecoder(aOmxDecoder), |
|
75 mOffset(aOffset) |
|
76 { } |
|
77 |
|
78 void Run() |
|
79 { |
|
80 MOZ_ASSERT(!NS_IsMainThread()); |
|
81 MOZ_ASSERT(mOmxDecoder.get()); |
|
82 int64_t rem = mOmxDecoder->ProcessCachedData(mOffset, false); |
|
83 |
|
84 if (rem <= 0) { |
|
85 ReleaseOmxDecoderRunnable* r = new ReleaseOmxDecoderRunnable(mOmxDecoder); |
|
86 mOmxDecoder.clear(); |
|
87 NS_DispatchToMainThread(r); |
|
88 } |
|
89 } |
|
90 |
|
91 private: |
|
92 android::sp<android::OmxDecoder> mOmxDecoder; |
|
93 int64_t mOffset; |
|
94 }; |
|
95 |
|
96 // When loading an MP3 stream from a file, we need to parse the file's |
|
97 // content to find its duration. Reading files of 100 MiB or more can |
|
98 // delay the player app noticably, so the file is read and decoded in |
|
99 // smaller chunks. |
|
100 // |
|
101 // We first read on the decode thread, but parsing must be done on the |
|
102 // main thread. After we read the file's initial MiBs in the decode |
|
103 // thread, an instance of this class is scheduled to the main thread for |
|
104 // parsing the MP3 stream. The decode thread waits until it has finished. |
|
105 // |
|
106 // If there is more data available from the file, the runnable dispatches |
|
107 // a task to the IO thread for retrieving the next chunk of data, and |
|
108 // the IO task dispatches a runnable to the main thread for parsing the |
|
109 // data. This goes on until all of the MP3 file has been parsed. |
|
110 |
|
111 class OmxDecoderNotifyDataArrivedRunnable : public nsRunnable |
|
112 { |
|
113 public: |
|
114 OmxDecoderNotifyDataArrivedRunnable(android::OmxDecoder* aOmxDecoder, |
|
115 const char* aBuffer, uint64_t aLength, |
|
116 int64_t aOffset, uint64_t aFullLength) |
|
117 : mOmxDecoder(aOmxDecoder), |
|
118 mBuffer(aBuffer), |
|
119 mLength(aLength), |
|
120 mOffset(aOffset), |
|
121 mFullLength(aFullLength), |
|
122 mCompletedMonitor("OmxDecoderNotifyDataArrived.mCompleted"), |
|
123 mCompleted(false) |
|
124 { |
|
125 MOZ_ASSERT(mOmxDecoder.get()); |
|
126 MOZ_ASSERT(mBuffer.get() || !mLength); |
|
127 } |
|
128 |
|
129 NS_IMETHOD Run() |
|
130 { |
|
131 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
132 |
|
133 NotifyDataArrived(); |
|
134 Completed(); |
|
135 |
|
136 return NS_OK; |
|
137 } |
|
138 |
|
139 void WaitForCompletion() |
|
140 { |
|
141 MOZ_ASSERT(!NS_IsMainThread()); |
|
142 |
|
143 MonitorAutoLock mon(mCompletedMonitor); |
|
144 if (!mCompleted) { |
|
145 mCompletedMonitor.Wait(); |
|
146 } |
|
147 } |
|
148 |
|
149 private: |
|
150 void NotifyDataArrived() |
|
151 { |
|
152 const char* buffer = mBuffer.get(); |
|
153 |
|
154 while (mLength) { |
|
155 uint32_t length = std::min<uint64_t>(mLength, UINT32_MAX); |
|
156 bool success = mOmxDecoder->NotifyDataArrived(buffer, mLength, |
|
157 mOffset); |
|
158 if (!success) { |
|
159 return; |
|
160 } |
|
161 |
|
162 buffer += length; |
|
163 mLength -= length; |
|
164 mOffset += length; |
|
165 } |
|
166 |
|
167 if (mOffset < mFullLength) { |
|
168 // We cannot read data in the main thread because it |
|
169 // might block for too long. Instead we post an IO task |
|
170 // to the IO thread if there is more data available. |
|
171 XRE_GetIOMessageLoop()->PostTask(FROM_HERE, |
|
172 new OmxDecoderProcessCachedDataTask(mOmxDecoder.get(), mOffset)); |
|
173 } |
|
174 } |
|
175 |
|
176 // Call this function at the end of Run() to notify waiting |
|
177 // threads. |
|
178 void Completed() |
|
179 { |
|
180 MonitorAutoLock mon(mCompletedMonitor); |
|
181 MOZ_ASSERT(!mCompleted); |
|
182 mCompleted = true; |
|
183 mCompletedMonitor.Notify(); |
|
184 } |
|
185 |
|
186 android::sp<android::OmxDecoder> mOmxDecoder; |
|
187 nsAutoArrayPtr<const char> mBuffer; |
|
188 uint64_t mLength; |
|
189 int64_t mOffset; |
|
190 uint64_t mFullLength; |
|
191 |
|
192 Monitor mCompletedMonitor; |
|
193 bool mCompleted; |
|
194 }; |
|
195 |
|
196 } |
|
197 |
|
198 namespace android { |
|
199 |
|
200 MediaStreamSource::MediaStreamSource(MediaResource *aResource, |
|
201 AbstractMediaDecoder *aDecoder) : |
|
202 mResource(aResource), mDecoder(aDecoder) |
|
203 { |
|
204 } |
|
205 |
|
206 MediaStreamSource::~MediaStreamSource() |
|
207 { |
|
208 } |
|
209 |
|
210 status_t MediaStreamSource::initCheck() const |
|
211 { |
|
212 return OK; |
|
213 } |
|
214 |
|
215 ssize_t MediaStreamSource::readAt(off64_t offset, void *data, size_t size) |
|
216 { |
|
217 char *ptr = static_cast<char *>(data); |
|
218 size_t todo = size; |
|
219 while (todo > 0) { |
|
220 Mutex::Autolock autoLock(mLock); |
|
221 uint32_t bytesRead; |
|
222 if ((offset != mResource->Tell() && |
|
223 NS_FAILED(mResource->Seek(nsISeekableStream::NS_SEEK_SET, offset))) || |
|
224 NS_FAILED(mResource->Read(ptr, todo, &bytesRead))) { |
|
225 return ERROR_IO; |
|
226 } |
|
227 |
|
228 if (bytesRead == 0) { |
|
229 return size - todo; |
|
230 } |
|
231 |
|
232 offset += bytesRead; |
|
233 todo -= bytesRead; |
|
234 ptr += bytesRead; |
|
235 } |
|
236 return size; |
|
237 } |
|
238 |
|
239 status_t MediaStreamSource::getSize(off64_t *size) |
|
240 { |
|
241 uint64_t length = mResource->GetLength(); |
|
242 if (length == static_cast<uint64_t>(-1)) |
|
243 return ERROR_UNSUPPORTED; |
|
244 |
|
245 *size = length; |
|
246 |
|
247 return OK; |
|
248 } |
|
249 |
|
250 } // namespace android |
|
251 |
|
252 using namespace android; |
|
253 |
|
254 OmxDecoder::OmxDecoder(MediaResource *aResource, |
|
255 AbstractMediaDecoder *aDecoder) : |
|
256 mDecoder(aDecoder), |
|
257 mResource(aResource), |
|
258 mDisplayWidth(0), |
|
259 mDisplayHeight(0), |
|
260 mVideoWidth(0), |
|
261 mVideoHeight(0), |
|
262 mVideoColorFormat(0), |
|
263 mVideoStride(0), |
|
264 mVideoSliceHeight(0), |
|
265 mVideoRotation(0), |
|
266 mAudioChannels(-1), |
|
267 mAudioSampleRate(-1), |
|
268 mDurationUs(-1), |
|
269 mMP3FrameParser(aResource->GetLength()), |
|
270 mIsMp3(false), |
|
271 mVideoBuffer(nullptr), |
|
272 mAudioBuffer(nullptr), |
|
273 mIsVideoSeeking(false), |
|
274 mAudioMetadataRead(false), |
|
275 mAudioPaused(false), |
|
276 mVideoPaused(false) |
|
277 { |
|
278 mLooper = new ALooper; |
|
279 mLooper->setName("OmxDecoder"); |
|
280 |
|
281 mReflector = new AHandlerReflector<OmxDecoder>(this); |
|
282 // Register AMessage handler to ALooper. |
|
283 mLooper->registerHandler(mReflector); |
|
284 // Start ALooper thread. |
|
285 mLooper->start(); |
|
286 } |
|
287 |
|
288 OmxDecoder::~OmxDecoder() |
|
289 { |
|
290 MOZ_ASSERT(NS_IsMainThread()); |
|
291 |
|
292 ReleaseMediaResources(); |
|
293 |
|
294 // unregister AMessage handler from ALooper. |
|
295 mLooper->unregisterHandler(mReflector->id()); |
|
296 // Stop ALooper thread. |
|
297 mLooper->stop(); |
|
298 } |
|
299 |
|
300 void OmxDecoder::statusChanged() |
|
301 { |
|
302 sp<AMessage> notify = |
|
303 new AMessage(kNotifyStatusChanged, mReflector->id()); |
|
304 // post AMessage to OmxDecoder via ALooper. |
|
305 notify->post(); |
|
306 } |
|
307 |
|
308 static sp<IOMX> sOMX = nullptr; |
|
309 static sp<IOMX> GetOMX() |
|
310 { |
|
311 if(sOMX.get() == nullptr) { |
|
312 sOMX = new OMX; |
|
313 } |
|
314 return sOMX; |
|
315 } |
|
316 |
|
317 bool OmxDecoder::Init(sp<MediaExtractor>& extractor) { |
|
318 #ifdef PR_LOGGING |
|
319 if (!gOmxDecoderLog) { |
|
320 gOmxDecoderLog = PR_NewLogModule("OmxDecoder"); |
|
321 } |
|
322 #endif |
|
323 |
|
324 const char* extractorMime; |
|
325 sp<MetaData> meta = extractor->getMetaData(); |
|
326 if (meta->findCString(kKeyMIMEType, &extractorMime) && !strcasecmp(extractorMime, AUDIO_MP3)) { |
|
327 mIsMp3 = true; |
|
328 } |
|
329 |
|
330 ssize_t audioTrackIndex = -1; |
|
331 ssize_t videoTrackIndex = -1; |
|
332 |
|
333 for (size_t i = 0; i < extractor->countTracks(); ++i) { |
|
334 sp<MetaData> meta = extractor->getTrackMetaData(i); |
|
335 |
|
336 int32_t bitRate; |
|
337 if (!meta->findInt32(kKeyBitRate, &bitRate)) |
|
338 bitRate = 0; |
|
339 |
|
340 const char *mime; |
|
341 if (!meta->findCString(kKeyMIMEType, &mime)) { |
|
342 continue; |
|
343 } |
|
344 |
|
345 if (videoTrackIndex == -1 && !strncasecmp(mime, "video/", 6)) { |
|
346 videoTrackIndex = i; |
|
347 } else if (audioTrackIndex == -1 && !strncasecmp(mime, "audio/", 6)) { |
|
348 audioTrackIndex = i; |
|
349 } |
|
350 } |
|
351 |
|
352 if (videoTrackIndex == -1 && audioTrackIndex == -1) { |
|
353 NS_WARNING("OMX decoder could not find video or audio tracks"); |
|
354 return false; |
|
355 } |
|
356 |
|
357 mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK); |
|
358 |
|
359 if (videoTrackIndex != -1) { |
|
360 mVideoTrack = extractor->getTrack(videoTrackIndex); |
|
361 } |
|
362 |
|
363 if (audioTrackIndex != -1) { |
|
364 mAudioTrack = extractor->getTrack(audioTrackIndex); |
|
365 |
|
366 #ifdef MOZ_AUDIO_OFFLOAD |
|
367 // mAudioTrack is be used by OMXCodec. For offloaded audio track, using same |
|
368 // object gives undetermined behavior. So get a new track |
|
369 mAudioOffloadTrack = extractor->getTrack(audioTrackIndex); |
|
370 #endif |
|
371 } |
|
372 return true; |
|
373 } |
|
374 |
|
375 bool OmxDecoder::TryLoad() { |
|
376 |
|
377 if (!AllocateMediaResources()) { |
|
378 return false; |
|
379 } |
|
380 |
|
381 //check if video is waiting resources |
|
382 if (mVideoSource.get()) { |
|
383 if (mVideoSource->IsWaitingResources()) { |
|
384 return true; |
|
385 } |
|
386 } |
|
387 |
|
388 // calculate duration |
|
389 int64_t totalDurationUs = 0; |
|
390 int64_t durationUs = 0; |
|
391 if (mVideoTrack.get() && mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) { |
|
392 if (durationUs > totalDurationUs) |
|
393 totalDurationUs = durationUs; |
|
394 } |
|
395 if (mAudioTrack.get()) { |
|
396 durationUs = -1; |
|
397 const char* audioMime; |
|
398 sp<MetaData> meta = mAudioTrack->getFormat(); |
|
399 |
|
400 if (mIsMp3) { |
|
401 // Feed MP3 parser with cached data. Local files will be fully |
|
402 // cached already, network streams will update with sucessive |
|
403 // calls to NotifyDataArrived. |
|
404 if (ProcessCachedData(0, true) >= 0) { |
|
405 durationUs = mMP3FrameParser.GetDuration(); |
|
406 if (durationUs > totalDurationUs) { |
|
407 totalDurationUs = durationUs; |
|
408 } |
|
409 } |
|
410 } |
|
411 if ((durationUs == -1) && meta->findInt64(kKeyDuration, &durationUs)) { |
|
412 if (durationUs > totalDurationUs) { |
|
413 totalDurationUs = durationUs; |
|
414 } |
|
415 } |
|
416 } |
|
417 mDurationUs = totalDurationUs; |
|
418 |
|
419 // read video metadata |
|
420 if (mVideoSource.get() && !SetVideoFormat()) { |
|
421 NS_WARNING("Couldn't set OMX video format"); |
|
422 return false; |
|
423 } |
|
424 |
|
425 // read audio metadata |
|
426 if (mAudioSource.get()) { |
|
427 // To reliably get the channel and sample rate data we need to read from the |
|
428 // audio source until we get a INFO_FORMAT_CHANGE status |
|
429 status_t err = mAudioSource->read(&mAudioBuffer); |
|
430 if (err != INFO_FORMAT_CHANGED) { |
|
431 if (err != OK) { |
|
432 NS_WARNING("Couldn't read audio buffer from OMX decoder"); |
|
433 return false; |
|
434 } |
|
435 sp<MetaData> meta = mAudioSource->getFormat(); |
|
436 if (!meta->findInt32(kKeyChannelCount, &mAudioChannels) || |
|
437 !meta->findInt32(kKeySampleRate, &mAudioSampleRate)) { |
|
438 NS_WARNING("Couldn't get audio metadata from OMX decoder"); |
|
439 return false; |
|
440 } |
|
441 mAudioMetadataRead = true; |
|
442 } |
|
443 else if (!SetAudioFormat()) { |
|
444 NS_WARNING("Couldn't set audio format"); |
|
445 return false; |
|
446 } |
|
447 } |
|
448 |
|
449 return true; |
|
450 } |
|
451 |
|
452 bool OmxDecoder::IsDormantNeeded() |
|
453 { |
|
454 if (mVideoTrack.get()) { |
|
455 return true; |
|
456 } |
|
457 return false; |
|
458 } |
|
459 |
|
460 bool OmxDecoder::IsWaitingMediaResources() |
|
461 { |
|
462 if (mVideoSource.get()) { |
|
463 return mVideoSource->IsWaitingResources(); |
|
464 } |
|
465 return false; |
|
466 } |
|
467 |
|
468 static bool isInEmulator() |
|
469 { |
|
470 char propQemu[PROPERTY_VALUE_MAX]; |
|
471 property_get("ro.kernel.qemu", propQemu, ""); |
|
472 return !strncmp(propQemu, "1", 1); |
|
473 } |
|
474 |
|
475 bool OmxDecoder::AllocateMediaResources() |
|
476 { |
|
477 // OMXClient::connect() always returns OK and abort's fatally if |
|
478 // it can't connect. |
|
479 OMXClient client; |
|
480 DebugOnly<status_t> err = client.connect(); |
|
481 NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver."); |
|
482 sp<IOMX> omx = client.interface(); |
|
483 |
|
484 if ((mVideoTrack != nullptr) && (mVideoSource == nullptr)) { |
|
485 mNativeWindow = new GonkNativeWindow(); |
|
486 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 |
|
487 mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow->getBufferQueue()); |
|
488 #else |
|
489 mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow); |
|
490 #endif |
|
491 |
|
492 // Experience with OMX codecs is that only the HW decoders are |
|
493 // worth bothering with, at least on the platforms where this code |
|
494 // is currently used, and for formats this code is currently used |
|
495 // for (h.264). So if we don't get a hardware decoder, just give |
|
496 // up. |
|
497 int flags = kHardwareCodecsOnly; |
|
498 |
|
499 if (isInEmulator()) { |
|
500 // If we are in emulator, allow to fall back to software. |
|
501 flags = 0; |
|
502 } |
|
503 mVideoSource = |
|
504 OMXCodecProxy::Create(omx, |
|
505 mVideoTrack->getFormat(), |
|
506 false, // decoder |
|
507 mVideoTrack, |
|
508 nullptr, |
|
509 flags, |
|
510 mNativeWindowClient); |
|
511 if (mVideoSource == nullptr) { |
|
512 NS_WARNING("Couldn't create OMX video source"); |
|
513 return false; |
|
514 } else { |
|
515 sp<OMXCodecProxy::EventListener> listener = this; |
|
516 mVideoSource->setEventListener(listener); |
|
517 mVideoSource->requestResource(); |
|
518 } |
|
519 } |
|
520 |
|
521 if ((mAudioTrack != nullptr) && (mAudioSource == nullptr)) { |
|
522 const char *audioMime = nullptr; |
|
523 sp<MetaData> meta = mAudioTrack->getFormat(); |
|
524 if (!meta->findCString(kKeyMIMEType, &audioMime)) { |
|
525 return false; |
|
526 } |
|
527 if (!strcasecmp(audioMime, "audio/raw")) { |
|
528 mAudioSource = mAudioTrack; |
|
529 } else { |
|
530 // try to load hardware codec in mediaserver process. |
|
531 int flags = kHardwareCodecsOnly; |
|
532 mAudioSource = OMXCodec::Create(omx, |
|
533 mAudioTrack->getFormat(), |
|
534 false, // decoder |
|
535 mAudioTrack, |
|
536 nullptr, |
|
537 flags); |
|
538 } |
|
539 |
|
540 if (mAudioSource == nullptr) { |
|
541 // try to load software codec in this process. |
|
542 int flags = kSoftwareCodecsOnly; |
|
543 mAudioSource = OMXCodec::Create(GetOMX(), |
|
544 mAudioTrack->getFormat(), |
|
545 false, // decoder |
|
546 mAudioTrack, |
|
547 nullptr, |
|
548 flags); |
|
549 if (mAudioSource == nullptr) { |
|
550 NS_WARNING("Couldn't create OMX audio source"); |
|
551 return false; |
|
552 } |
|
553 } |
|
554 if (mAudioSource->start() != OK) { |
|
555 NS_WARNING("Couldn't start OMX audio source"); |
|
556 mAudioSource.clear(); |
|
557 return false; |
|
558 } |
|
559 } |
|
560 return true; |
|
561 } |
|
562 |
|
563 |
|
564 void OmxDecoder::ReleaseMediaResources() { |
|
565 { |
|
566 // Free all pending video buffers. |
|
567 Mutex::Autolock autoLock(mSeekLock); |
|
568 ReleaseAllPendingVideoBuffersLocked(); |
|
569 } |
|
570 |
|
571 ReleaseVideoBuffer(); |
|
572 ReleaseAudioBuffer(); |
|
573 |
|
574 if (mVideoSource.get()) { |
|
575 mVideoSource->stop(); |
|
576 mVideoSource.clear(); |
|
577 } |
|
578 |
|
579 if (mAudioSource.get()) { |
|
580 mAudioSource->stop(); |
|
581 mAudioSource.clear(); |
|
582 } |
|
583 |
|
584 mNativeWindowClient.clear(); |
|
585 mNativeWindow.clear(); |
|
586 } |
|
587 |
|
588 bool OmxDecoder::SetVideoFormat() { |
|
589 const char *componentName; |
|
590 |
|
591 if (!mVideoSource->getFormat()->findInt32(kKeyWidth, &mVideoWidth) || |
|
592 !mVideoSource->getFormat()->findInt32(kKeyHeight, &mVideoHeight) || |
|
593 !mVideoSource->getFormat()->findCString(kKeyDecoderComponent, &componentName) || |
|
594 !mVideoSource->getFormat()->findInt32(kKeyColorFormat, &mVideoColorFormat) ) { |
|
595 return false; |
|
596 } |
|
597 |
|
598 if (!mVideoTrack.get() || !mVideoTrack->getFormat()->findInt32(kKeyDisplayWidth, &mDisplayWidth)) { |
|
599 mDisplayWidth = mVideoWidth; |
|
600 NS_WARNING("display width not available, assuming width"); |
|
601 } |
|
602 |
|
603 if (!mVideoTrack.get() || !mVideoTrack->getFormat()->findInt32(kKeyDisplayHeight, &mDisplayHeight)) { |
|
604 mDisplayHeight = mVideoHeight; |
|
605 NS_WARNING("display height not available, assuming height"); |
|
606 } |
|
607 |
|
608 if (!mVideoSource->getFormat()->findInt32(kKeyStride, &mVideoStride)) { |
|
609 mVideoStride = mVideoWidth; |
|
610 NS_WARNING("stride not available, assuming width"); |
|
611 } |
|
612 |
|
613 if (!mVideoSource->getFormat()->findInt32(kKeySliceHeight, &mVideoSliceHeight)) { |
|
614 mVideoSliceHeight = mVideoHeight; |
|
615 NS_WARNING("slice height not available, assuming height"); |
|
616 } |
|
617 |
|
618 // Since ICS, valid video side is caluculated from kKeyCropRect. |
|
619 // kKeyWidth means decoded video buffer width. |
|
620 // kKeyHeight means decoded video buffer height. |
|
621 // On some hardwares, decoded video buffer and valid video size are different. |
|
622 int32_t crop_left, crop_top, crop_right, crop_bottom; |
|
623 if (mVideoSource->getFormat()->findRect(kKeyCropRect, |
|
624 &crop_left, |
|
625 &crop_top, |
|
626 &crop_right, |
|
627 &crop_bottom)) { |
|
628 mVideoWidth = crop_right - crop_left + 1; |
|
629 mVideoHeight = crop_bottom - crop_top + 1; |
|
630 } |
|
631 |
|
632 if (!mVideoSource->getFormat()->findInt32(kKeyRotation, &mVideoRotation)) { |
|
633 mVideoRotation = 0; |
|
634 NS_WARNING("rotation not available, assuming 0"); |
|
635 } |
|
636 |
|
637 LOG(PR_LOG_DEBUG, "display width: %d display height %d width: %d height: %d component: %s format: %d stride: %d sliceHeight: %d rotation: %d", |
|
638 mDisplayWidth, mDisplayHeight, mVideoWidth, mVideoHeight, componentName, |
|
639 mVideoColorFormat, mVideoStride, mVideoSliceHeight, mVideoRotation); |
|
640 |
|
641 return true; |
|
642 } |
|
643 |
|
644 bool OmxDecoder::SetAudioFormat() { |
|
645 // If the format changed, update our cached info. |
|
646 if (!mAudioSource->getFormat()->findInt32(kKeyChannelCount, &mAudioChannels) || |
|
647 !mAudioSource->getFormat()->findInt32(kKeySampleRate, &mAudioSampleRate)) { |
|
648 return false; |
|
649 } |
|
650 |
|
651 LOG(PR_LOG_DEBUG, "channelCount: %d sampleRate: %d", |
|
652 mAudioChannels, mAudioSampleRate); |
|
653 |
|
654 return true; |
|
655 } |
|
656 |
|
657 void OmxDecoder::ReleaseDecoder() |
|
658 { |
|
659 mDecoder = nullptr; |
|
660 } |
|
661 |
|
662 bool OmxDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) |
|
663 { |
|
664 if (!mAudioTrack.get() || !mIsMp3 || !mMP3FrameParser.IsMP3() || !mDecoder) { |
|
665 return false; |
|
666 } |
|
667 |
|
668 mMP3FrameParser.Parse(aBuffer, aLength, aOffset); |
|
669 |
|
670 int64_t durationUs = mMP3FrameParser.GetDuration(); |
|
671 |
|
672 if (durationUs != mDurationUs) { |
|
673 mDurationUs = durationUs; |
|
674 |
|
675 MOZ_ASSERT(mDecoder); |
|
676 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
677 mDecoder->UpdateEstimatedMediaDuration(mDurationUs); |
|
678 } |
|
679 |
|
680 return true; |
|
681 } |
|
682 |
|
683 void OmxDecoder::ReleaseVideoBuffer() { |
|
684 if (mVideoBuffer) { |
|
685 mVideoBuffer->release(); |
|
686 mVideoBuffer = nullptr; |
|
687 } |
|
688 } |
|
689 |
|
690 void OmxDecoder::ReleaseAudioBuffer() { |
|
691 if (mAudioBuffer) { |
|
692 mAudioBuffer->release(); |
|
693 mAudioBuffer = nullptr; |
|
694 } |
|
695 } |
|
696 |
|
697 void OmxDecoder::PlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { |
|
698 void *y = aData; |
|
699 void *u = static_cast<uint8_t *>(y) + mVideoStride * mVideoSliceHeight; |
|
700 void *v = static_cast<uint8_t *>(u) + mVideoStride/2 * mVideoSliceHeight/2; |
|
701 |
|
702 aFrame->Set(aTimeUs, aKeyFrame, |
|
703 aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, |
|
704 y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0, |
|
705 u, mVideoStride/2, mVideoWidth/2, mVideoHeight/2, 0, 0, |
|
706 v, mVideoStride/2, mVideoWidth/2, mVideoHeight/2, 0, 0); |
|
707 } |
|
708 |
|
709 void OmxDecoder::CbYCrYFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { |
|
710 aFrame->Set(aTimeUs, aKeyFrame, |
|
711 aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, |
|
712 aData, mVideoStride, mVideoWidth, mVideoHeight, 1, 1, |
|
713 aData, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 3, |
|
714 aData, mVideoStride, mVideoWidth/2, mVideoHeight/2, 2, 3); |
|
715 } |
|
716 |
|
717 void OmxDecoder::SemiPlanarYUV420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { |
|
718 void *y = aData; |
|
719 void *uv = static_cast<uint8_t *>(y) + (mVideoStride * mVideoSliceHeight); |
|
720 |
|
721 aFrame->Set(aTimeUs, aKeyFrame, |
|
722 aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, |
|
723 y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0, |
|
724 uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 1, |
|
725 uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 1, 1); |
|
726 } |
|
727 |
|
728 void OmxDecoder::SemiPlanarYVU420Frame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { |
|
729 SemiPlanarYUV420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame); |
|
730 aFrame->Cb.mOffset = 1; |
|
731 aFrame->Cr.mOffset = 0; |
|
732 } |
|
733 |
|
734 bool OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { |
|
735 const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; |
|
736 |
|
737 aFrame->mGraphicBuffer = nullptr; |
|
738 |
|
739 switch (mVideoColorFormat) { |
|
740 case OMX_COLOR_FormatYUV420Planar: |
|
741 PlanarYUV420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame); |
|
742 break; |
|
743 case OMX_COLOR_FormatCbYCrY: |
|
744 CbYCrYFrame(aFrame, aTimeUs, aData, aSize, aKeyFrame); |
|
745 break; |
|
746 case OMX_COLOR_FormatYUV420SemiPlanar: |
|
747 SemiPlanarYUV420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame); |
|
748 break; |
|
749 case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: |
|
750 SemiPlanarYVU420Frame(aFrame, aTimeUs, aData, aSize, aKeyFrame); |
|
751 break; |
|
752 default: |
|
753 LOG(PR_LOG_DEBUG, "Unknown video color format %08x", mVideoColorFormat); |
|
754 return false; |
|
755 } |
|
756 return true; |
|
757 } |
|
758 |
|
759 bool OmxDecoder::ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize, int32_t aAudioChannels, int32_t aAudioSampleRate) |
|
760 { |
|
761 aFrame->Set(aTimeUs, static_cast<char *>(aData) + aDataOffset, aSize, aAudioChannels, aAudioSampleRate); |
|
762 return true; |
|
763 } |
|
764 |
|
765 bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs, |
|
766 bool aKeyframeSkip, bool aDoSeek) |
|
767 { |
|
768 if (!mVideoSource.get()) |
|
769 return false; |
|
770 |
|
771 ReleaseVideoBuffer(); |
|
772 |
|
773 status_t err; |
|
774 |
|
775 if (aDoSeek) { |
|
776 { |
|
777 Mutex::Autolock autoLock(mSeekLock); |
|
778 mIsVideoSeeking = true; |
|
779 } |
|
780 MediaSource::ReadOptions options; |
|
781 options.setSeekTo(aTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); |
|
782 err = mVideoSource->read(&mVideoBuffer, &options); |
|
783 { |
|
784 Mutex::Autolock autoLock(mSeekLock); |
|
785 mIsVideoSeeking = false; |
|
786 PostReleaseVideoBuffer(nullptr, FenceHandle()); |
|
787 } |
|
788 |
|
789 aDoSeek = false; |
|
790 } else { |
|
791 err = mVideoSource->read(&mVideoBuffer); |
|
792 } |
|
793 |
|
794 aFrame->mSize = 0; |
|
795 |
|
796 if (err == OK) { |
|
797 int64_t timeUs; |
|
798 int32_t unreadable; |
|
799 int32_t keyFrame; |
|
800 |
|
801 if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs) ) { |
|
802 NS_WARNING("OMX decoder did not return frame time"); |
|
803 return false; |
|
804 } |
|
805 |
|
806 if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) { |
|
807 keyFrame = 0; |
|
808 } |
|
809 |
|
810 if (!mVideoBuffer->meta_data()->findInt32(kKeyIsUnreadable, &unreadable)) { |
|
811 unreadable = 0; |
|
812 } |
|
813 |
|
814 RefPtr<mozilla::layers::TextureClient> textureClient; |
|
815 if ((mVideoBuffer->graphicBuffer().get())) { |
|
816 textureClient = mNativeWindow->getTextureClientFromBuffer(mVideoBuffer->graphicBuffer().get()); |
|
817 } |
|
818 |
|
819 if (textureClient) { |
|
820 // Manually increment reference count to keep MediaBuffer alive |
|
821 // during TextureClient is in use. |
|
822 mVideoBuffer->add_ref(); |
|
823 GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get()); |
|
824 grallocClient->SetMediaBuffer(mVideoBuffer); |
|
825 // Set recycle callback for TextureClient |
|
826 textureClient->SetRecycleCallback(OmxDecoder::RecycleCallback, this); |
|
827 |
|
828 aFrame->mGraphicBuffer = textureClient; |
|
829 aFrame->mRotation = mVideoRotation; |
|
830 aFrame->mTimeUs = timeUs; |
|
831 aFrame->mKeyFrame = keyFrame; |
|
832 aFrame->Y.mWidth = mVideoWidth; |
|
833 aFrame->Y.mHeight = mVideoHeight; |
|
834 // Release to hold video buffer in OmxDecoder more. |
|
835 // MediaBuffer's ref count is changed from 2 to 1. |
|
836 ReleaseVideoBuffer(); |
|
837 } else if (mVideoBuffer->range_length() > 0) { |
|
838 char *data = static_cast<char *>(mVideoBuffer->data()) + mVideoBuffer->range_offset(); |
|
839 size_t length = mVideoBuffer->range_length(); |
|
840 |
|
841 if (unreadable) { |
|
842 LOG(PR_LOG_DEBUG, "video frame is unreadable"); |
|
843 } |
|
844 |
|
845 if (!ToVideoFrame(aFrame, timeUs, data, length, keyFrame)) { |
|
846 return false; |
|
847 } |
|
848 } |
|
849 |
|
850 if (aKeyframeSkip && timeUs < aTimeUs) { |
|
851 aFrame->mShouldSkip = true; |
|
852 } |
|
853 } |
|
854 else if (err == INFO_FORMAT_CHANGED) { |
|
855 // If the format changed, update our cached info. |
|
856 if (!SetVideoFormat()) { |
|
857 return false; |
|
858 } else { |
|
859 return ReadVideo(aFrame, aTimeUs, aKeyframeSkip, aDoSeek); |
|
860 } |
|
861 } |
|
862 else if (err == ERROR_END_OF_STREAM) { |
|
863 return false; |
|
864 } |
|
865 else if (err == -ETIMEDOUT) { |
|
866 LOG(PR_LOG_DEBUG, "OmxDecoder::ReadVideo timed out, will retry"); |
|
867 return true; |
|
868 } |
|
869 else { |
|
870 // UNKNOWN_ERROR is sometimes is used to mean "out of memory", but |
|
871 // regardless, don't keep trying to decode if the decoder doesn't want to. |
|
872 LOG(PR_LOG_DEBUG, "OmxDecoder::ReadVideo failed, err=%d", err); |
|
873 return false; |
|
874 } |
|
875 |
|
876 return true; |
|
877 } |
|
878 |
|
879 bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs) |
|
880 { |
|
881 status_t err; |
|
882 |
|
883 if (mAudioMetadataRead && aSeekTimeUs == -1) { |
|
884 // Use the data read into the buffer during metadata time |
|
885 err = OK; |
|
886 } |
|
887 else { |
|
888 ReleaseAudioBuffer(); |
|
889 if (aSeekTimeUs != -1) { |
|
890 MediaSource::ReadOptions options; |
|
891 options.setSeekTo(aSeekTimeUs); |
|
892 err = mAudioSource->read(&mAudioBuffer, &options); |
|
893 } else { |
|
894 err = mAudioSource->read(&mAudioBuffer); |
|
895 } |
|
896 } |
|
897 mAudioMetadataRead = false; |
|
898 |
|
899 aSeekTimeUs = -1; |
|
900 aFrame->mSize = 0; |
|
901 |
|
902 if (err == OK && mAudioBuffer && mAudioBuffer->range_length() != 0) { |
|
903 int64_t timeUs; |
|
904 if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) |
|
905 return false; |
|
906 |
|
907 return ToAudioFrame(aFrame, timeUs, |
|
908 mAudioBuffer->data(), |
|
909 mAudioBuffer->range_offset(), |
|
910 mAudioBuffer->range_length(), |
|
911 mAudioChannels, mAudioSampleRate); |
|
912 } |
|
913 else if (err == INFO_FORMAT_CHANGED) { |
|
914 // If the format changed, update our cached info. |
|
915 if (!SetAudioFormat()) { |
|
916 return false; |
|
917 } else { |
|
918 return ReadAudio(aFrame, aSeekTimeUs); |
|
919 } |
|
920 } |
|
921 else if (err == ERROR_END_OF_STREAM) { |
|
922 if (aFrame->mSize == 0) { |
|
923 return false; |
|
924 } |
|
925 } |
|
926 else if (err == -ETIMEDOUT) { |
|
927 LOG(PR_LOG_DEBUG, "OmxDecoder::ReadAudio timed out, will retry"); |
|
928 return true; |
|
929 } |
|
930 else if (err != OK) { |
|
931 LOG(PR_LOG_DEBUG, "OmxDecoder::ReadAudio failed, err=%d", err); |
|
932 return false; |
|
933 } |
|
934 |
|
935 return true; |
|
936 } |
|
937 |
|
938 nsresult OmxDecoder::Play() |
|
939 { |
|
940 if (!mVideoPaused && !mAudioPaused) { |
|
941 return NS_OK; |
|
942 } |
|
943 |
|
944 if (mVideoPaused && mVideoSource.get() && mVideoSource->start() != OK) { |
|
945 return NS_ERROR_UNEXPECTED; |
|
946 } |
|
947 mVideoPaused = false; |
|
948 |
|
949 if (mAudioPaused && mAudioSource.get() && mAudioSource->start() != OK) { |
|
950 return NS_ERROR_UNEXPECTED; |
|
951 } |
|
952 mAudioPaused = false; |
|
953 |
|
954 return NS_OK; |
|
955 } |
|
956 |
|
957 // AOSP didn't give implementation on OMXCodec::Pause() and not define |
|
958 // OMXCodec::Start() should be called for resuming the decoding. Currently |
|
959 // it is customized by a specific open source repository only. |
|
960 // ToDo The one not supported OMXCodec::Pause() should return error code here, |
|
961 // so OMXCodec::Start() doesn't be called again for resuming. But if someone |
|
962 // implement the OMXCodec::Pause() and need a following OMXCodec::Read() with |
|
963 // seek option (define in MediaSource.h) then it is still not supported here. |
|
964 // We need to fix it until it is really happened. |
|
965 void OmxDecoder::Pause() |
|
966 { |
|
967 /* The implementation of OMXCodec::pause is flawed. |
|
968 * OMXCodec::start will not restore from the paused state and result in |
|
969 * buffer timeout which causes timeouts in mochitests. |
|
970 * Since there is not power consumption problem in emulator, we will just |
|
971 * return when running in emulator to fix timeouts in mochitests. |
|
972 */ |
|
973 if (isInEmulator()) { |
|
974 return; |
|
975 } |
|
976 |
|
977 if (mVideoPaused || mAudioPaused) { |
|
978 return; |
|
979 } |
|
980 |
|
981 if (mVideoSource.get() && mVideoSource->pause() == OK) { |
|
982 mVideoPaused = true; |
|
983 } |
|
984 |
|
985 if (mAudioSource.get() && mAudioSource->pause() == OK) { |
|
986 mAudioPaused = true; |
|
987 } |
|
988 } |
|
989 |
|
990 // Called on ALooper thread. |
|
991 void OmxDecoder::onMessageReceived(const sp<AMessage> &msg) |
|
992 { |
|
993 switch (msg->what()) { |
|
994 case kNotifyPostReleaseVideoBuffer: |
|
995 { |
|
996 Mutex::Autolock autoLock(mSeekLock); |
|
997 // Free pending video buffers when OmxDecoder is not seeking video. |
|
998 // If OmxDecoder is seeking video, the buffers are freed on seek exit. |
|
999 if (!mIsVideoSeeking) { |
|
1000 ReleaseAllPendingVideoBuffersLocked(); |
|
1001 } |
|
1002 break; |
|
1003 } |
|
1004 |
|
1005 case kNotifyStatusChanged: |
|
1006 { |
|
1007 // Our decode may have acquired the hardware resource that it needs |
|
1008 // to start. Notify the state machine to resume loading metadata. |
|
1009 mDecoder->NotifyWaitingForResourcesStatusChanged(); |
|
1010 break; |
|
1011 } |
|
1012 |
|
1013 default: |
|
1014 TRESPASS(); |
|
1015 break; |
|
1016 } |
|
1017 } |
|
1018 |
|
1019 void OmxDecoder::PostReleaseVideoBuffer(MediaBuffer *aBuffer, const FenceHandle& aReleaseFenceHandle) |
|
1020 { |
|
1021 { |
|
1022 Mutex::Autolock autoLock(mPendingVideoBuffersLock); |
|
1023 if (aBuffer) { |
|
1024 mPendingVideoBuffers.push(BufferItem(aBuffer, aReleaseFenceHandle)); |
|
1025 } |
|
1026 } |
|
1027 |
|
1028 sp<AMessage> notify = |
|
1029 new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id()); |
|
1030 // post AMessage to OmxDecoder via ALooper. |
|
1031 notify->post(); |
|
1032 } |
|
1033 |
|
1034 void OmxDecoder::ReleaseAllPendingVideoBuffersLocked() |
|
1035 { |
|
1036 Vector<BufferItem> releasingVideoBuffers; |
|
1037 { |
|
1038 Mutex::Autolock autoLock(mPendingVideoBuffersLock); |
|
1039 |
|
1040 int size = mPendingVideoBuffers.size(); |
|
1041 for (int i = 0; i < size; i++) { |
|
1042 releasingVideoBuffers.push(mPendingVideoBuffers[i]); |
|
1043 } |
|
1044 mPendingVideoBuffers.clear(); |
|
1045 } |
|
1046 // Free all pending video buffers without holding mPendingVideoBuffersLock. |
|
1047 int size = releasingVideoBuffers.size(); |
|
1048 for (int i = 0; i < size; i++) { |
|
1049 MediaBuffer *buffer; |
|
1050 buffer = releasingVideoBuffers[i].mMediaBuffer; |
|
1051 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 |
|
1052 android::sp<Fence> fence; |
|
1053 int fenceFd = -1; |
|
1054 fence = releasingVideoBuffers[i].mReleaseFenceHandle.mFence; |
|
1055 if (fence.get() && fence->isValid()) { |
|
1056 fenceFd = fence->dup(); |
|
1057 } |
|
1058 MOZ_ASSERT(buffer->refcount() == 1); |
|
1059 // This code expect MediaBuffer's ref count is 1. |
|
1060 // Return gralloc buffer to ANativeWindow |
|
1061 ANativeWindow* window = static_cast<ANativeWindow*>(mNativeWindowClient.get()); |
|
1062 window->cancelBuffer(window, |
|
1063 buffer->graphicBuffer().get(), |
|
1064 fenceFd); |
|
1065 // Mark MediaBuffer as rendered. |
|
1066 // When gralloc buffer is directly returned to ANativeWindow, |
|
1067 // this mark is necesary. |
|
1068 sp<MetaData> metaData = buffer->meta_data(); |
|
1069 metaData->setInt32(kKeyRendered, 1); |
|
1070 #endif |
|
1071 // Return MediaBuffer to OMXCodec. |
|
1072 buffer->release(); |
|
1073 } |
|
1074 releasingVideoBuffers.clear(); |
|
1075 } |
|
1076 |
|
1077 /* static */ void |
|
1078 OmxDecoder::RecycleCallback(TextureClient* aClient, void* aClosure) |
|
1079 { |
|
1080 OmxDecoder* decoder = static_cast<OmxDecoder*>(aClosure); |
|
1081 GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(aClient); |
|
1082 |
|
1083 aClient->ClearRecycleCallback(); |
|
1084 decoder->PostReleaseVideoBuffer(client->GetMediaBuffer(), client->GetReleaseFenceHandle()); |
|
1085 } |
|
1086 |
|
1087 int64_t OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion) |
|
1088 { |
|
1089 // We read data in chunks of 32 KiB. We can reduce this |
|
1090 // value if media, such as sdcards, is too slow. |
|
1091 // Because of SD card's slowness, need to keep sReadSize to small size. |
|
1092 // See Bug 914870. |
|
1093 static const int64_t sReadSize = 32 * 1024; |
|
1094 |
|
1095 NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread."); |
|
1096 |
|
1097 MOZ_ASSERT(mResource); |
|
1098 |
|
1099 int64_t resourceLength = mResource->GetCachedDataEnd(0); |
|
1100 NS_ENSURE_TRUE(resourceLength >= 0, -1); |
|
1101 |
|
1102 if (aOffset >= resourceLength) { |
|
1103 return 0; // Cache is empty, nothing to do |
|
1104 } |
|
1105 |
|
1106 int64_t bufferLength = std::min<int64_t>(resourceLength-aOffset, sReadSize); |
|
1107 |
|
1108 nsAutoArrayPtr<char> buffer(new char[bufferLength]); |
|
1109 |
|
1110 nsresult rv = mResource->ReadFromCache(buffer.get(), aOffset, bufferLength); |
|
1111 NS_ENSURE_SUCCESS(rv, -1); |
|
1112 |
|
1113 nsRefPtr<OmxDecoderNotifyDataArrivedRunnable> runnable( |
|
1114 new OmxDecoderNotifyDataArrivedRunnable(this, |
|
1115 buffer.forget(), |
|
1116 bufferLength, |
|
1117 aOffset, |
|
1118 resourceLength)); |
|
1119 |
|
1120 rv = NS_DispatchToMainThread(runnable.get()); |
|
1121 NS_ENSURE_SUCCESS(rv, -1); |
|
1122 |
|
1123 if (aWaitForCompletion) { |
|
1124 runnable->WaitForCompletion(); |
|
1125 } |
|
1126 |
|
1127 return resourceLength - aOffset - bufferLength; |
|
1128 } |