content/media/omx/OmxDecoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial