Android多媒體框架(3)—— libstagefright中MediaCodec源碼分析

libstagefright中MediaCodec源碼分析

和前兩篇一樣,我們按照MediaCodec的各個狀態來分析libstagefright中MediaCodec的源代碼。

  • configure
    首先我們看一下configure在libstagefright中MediaCodec中的定義:

438status_t MediaCodec::configure(
439        const sp<AMessage> &format,
440        const sp<Surface> &surface,
441        const sp<ICrypto> &crypto,
442        uint32_t flags) {
443    sp<AMessage> msg = new AMessage(kWhatConfigure, this);
444
445    if (mIsVideo) {
446        format->findInt32("width", &mVideoWidth);
447        format->findInt32("height", &mVideoHeight);
448        if (!format->findInt32("rotation-degrees", &mRotationDegrees)) {
449            mRotationDegrees = 0;
450        }
451
452        // Prevent possible integer overflow in downstream code.
453        if (mInitIsEncoder
454                && (uint64_t)mVideoWidth * mVideoHeight > (uint64_t)INT32_MAX / 4) {
455            ALOGE("buffer size is too big, width=%d, height=%d", mVideoWidth, mVideoHeight);
456            return BAD_VALUE;
457        }
458    }
459
460    msg->setMessage("format", format);
461    msg->setInt32("flags", flags);
462    msg->setObject("surface", surface);
463
464    if (crypto != NULL) {
465        msg->setPointer("crypto", crypto.get());
466    }
467
468    // save msg for reset
469    mConfigureMsg = msg;
470
471    status_t err;
472    Vector<MediaResource> resources;
473    MediaResource::Type type = (mFlags & kFlagIsSecure) ?
474            MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
475    MediaResource::SubType subtype =
476            mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
477    resources.push_back(MediaResource(type, subtype, 1));
478    // Don't know the buffer size at this point, but it's fine to use 1 because
479    // the reclaimResource call doesn't consider the requester's buffer size for now.
480    resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
481    for (int i = 0; i <= kMaxRetry; ++i) {
482        if (i > 0) {
483            // Don't try to reclaim resource for the first time.
484            if (!mResourceManagerService->reclaimResource(resources)) {
485                break;
486            }
487        }
488
489        sp<AMessage> response;
490        err = PostAndAwaitResponse(msg, &response);
491        if (err != OK && err != INVALID_OPERATION) {
492            // MediaCodec now set state to UNINITIALIZED upon any fatal error.
493            // To maintain backward-compatibility, do a reset() to put codec
494            // back into INITIALIZED state.
495            // But don't reset if the err is INVALID_OPERATION, which means
496            // the configure failure is due to wrong state.
497
498            ALOGE("configure failed with err 0x%08x, resetting...", err);
499            reset();
500        }
501        if (!isResourceError(err)) {
502            break;
503        }
504    }
505    return err;
506}

首先,生成一個AMessage的心的強引用計數msg,AMessage的構造函數的兩個參數,一個是枚舉值kWhatConfigure(“init”),在MediaCodec.h中定義,另一個參數應該是AHandler類型,而MediaCodec就是AHandler的子類。現在來看一下是否是video(mIsVideo),如果是,要從參數format中取出視頻的width和height,分別把值賦給類屬性mVideoWidth和mVideoHeight;獲取旋轉角度,賦值給mRotationsDegrees,如果不存在該屬性,就把mRotationsDegrees設成0度。如果encoder,那麼寬高相乘要小於INT32_MAX的四分之一。一次設置msg的format,flags和surface。如果參數crypto不爲NULL,設置msg的crypto。把msg賦值給類屬性mConfigrueMsg。判斷是否安全編碼,把結果保存在type中;判斷是video還是audio,結果保存在subtype中。生成兩種MediaResource,根據type,subtype,另一種根據MediaResource::kGraphicMemory,把他們都放入之前生命的MediaResource類型的Vector resources中。進行一個循環,除了第一個循環,每次循環開始,都需要reclaim resource。調用PostAndAwaitResponse(msg, &response)(這會調用AMessage類中的同名方法,最終通過ALooper發送消息),發送msg,並且把響應存儲到response,把返回值存儲到err,如果err不等於NO_MEMORY,跳出循環。最後,返回err的值。

  • start
    下面,我們來看看start狀態。
status_t MediaCodec::start() {
568    sp<AMessage> msg = new AMessage(kWhatStart, this);
569
570    status_t err;
571    Vector<MediaResource> resources;
572    MediaResource::Type type = (mFlags & kFlagIsSecure) ?
573            MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
574    MediaResource::SubType subtype =
575            mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
576    resources.push_back(MediaResource(type, subtype, 1));
577    // Don't know the buffer size at this point, but it's fine to use 1 because
578    // the reclaimResource call doesn't consider the requester's buffer size for now.
579    resources.push_back(MediaResource(MediaResource::kGraphicMemory, 1));
580    for (int i = 0; i <= kMaxRetry; ++i) {
581        if (i > 0) {
582            // Don't try to reclaim resource for the first time.
583            if (!mResourceManagerService->reclaimResource(resources)) {
584                break;
585            }
586            // Recover codec from previous error before retry start.
587            err = reset();
588            if (err != OK) {
589                ALOGE("retrying start: failed to reset codec");
590                break;
591            }
592            sp<AMessage> response;
593            err = PostAndAwaitResponse(mConfigureMsg, &response);
594            if (err != OK) {
595                ALOGE("retrying start: failed to configure codec");
596                break;
597            }
598        }
599
600        sp<AMessage> response;
601        err = PostAndAwaitResponse(msg, &response);
602        if (!isResourceError(err)) {
603            break;
604        }
605    }
606    return err;
607}

首先以kWhatStart狀態創建一個AMessage的強引用計數msg。同configure一樣,type和subtype一樣,分別表示是否安全編碼和音頻或視頻編碼。分別以type和subtype創建MediaResource,存入向量resources中。在循環之中,如果不是首次循環,需要對資源進行回收再利用。每次循環都要進行reset。如果reset失敗,終止循環。調用PostAndAwaitResponse(mConfigureMsg, &response)發送消息並等待返回,mConfigureMsg在configure階段已經賦值。如果返回值不等於OK,終止循環。調用PostAndAwaitResponse(msg, &response)發送消息並等待返回。如果返回值不是NO_MEMORY,跳出循環。最後,函數返回最後設置的err的值。

  • dequeueInputBuffe
    現在來看看輸入緩衝區的出列處理。
status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
748    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
749    msg->setInt64("timeoutUs", timeoutUs);
750
751    sp<AMessage> response;
752    status_t err;
753    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
754        return err;
755    }
756
757    CHECK(response->findSize("index", index));
758
759    return OK;
760}

同樣是,生成AMessage的強引用計數msg和response。調用PostAndAwaitResponse(msg, &response))發送消息並等待返回。如果返回不是OK,就把返回的error code作爲函數的返回值。否則,返回OK。

  • queueInputBuffer
    將數據壓入輸入緩衝區隊列。
status_t MediaCodec::queueInputBuffer(
689        size_t index,
690        size_t offset,
691        size_t size,
692        int64_t presentationTimeUs,
693        uint32_t flags,
694        AString *errorDetailMsg) {
695    if (errorDetailMsg != NULL) {
696        errorDetailMsg->clear();
697    }
698
699    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
700    msg->setSize("index", index);
701    msg->setSize("offset", offset);
702    msg->setSize("size", size);
703    msg->setInt64("timeUs", presentationTimeUs);
704    msg->setInt32("flags", flags);
705    msg->setPointer("errorDetailMsg", errorDetailMsg);
706
707    sp<AMessage> response;
708    return PostAndAwaitResponse(msg, &response);
709}

首先,創建AMessage的強引用計數msg,用參數index,offset,size,presentationTimeUs,flags,errorDetailMsg來初始化msg的index,offset,size,timeUs,flags,errorDetailMsg。PostAndAwaitResponse(msg, &response)發送並等待消息返回,並且,用這個調用的返回值作爲函數的返回值。

  • stop
    stop的代碼比較簡單。
status_t MediaCodec::stop() {
610    sp<AMessage> msg = new AMessage(kWhatStop, this);
611
612    sp<AMessage> response;
613    return PostAndAwaitResponse(msg, &response);
614}

同樣是,生成AMessage的強引用計數msg和response。調用PostAndAwaitResponse(msg, &response))發送消息並等待返回。

  • reset
    reset的代碼如下:
status_t MediaCodec::reset() {
654    /* When external-facing MediaCodec object is created,
655       it is already initialized.  Thus, reset is essentially
656       release() followed by init(), plus clearing the state */
657
658    status_t err = release();
659
660    // unregister handlers
661    if (mCodec != NULL) {
662        if (mCodecLooper != NULL) {
663            mCodecLooper->unregisterHandler(mCodec->id());
664        } else {
665            mLooper->unregisterHandler(mCodec->id());
666        }
667        mCodec = NULL;
668    }
669    mLooper->unregisterHandler(id());
670
671    mFlags = 0;    // clear all flags
672    mStickyError = OK;
673
674    // reset state not reset by setState(UNINITIALIZED)
675    mReplyID = 0;
676    mDequeueInputReplyID = 0;
677    mDequeueOutputReplyID = 0;
678    mDequeueInputTimeoutGeneration = 0;
679    mDequeueOutputTimeoutGeneration = 0;
680    mHaveInputSurface = false;
681
682    if (err == OK) {
683        err = init(mInitName, mInitNameIsType, mInitIsEncoder);
684    }
685    return err;
686}

reset首先調用release釋放codec。如果mCodec(CodecBase類型)不爲NULL,如果mCodecLooper也不爲NULL,首先對mCodecLooper反註冊Handler,否則,對mLooper反註冊Handler,並且把mCodec置爲NULL。接下來進行一系列重新賦值操作,都賦值爲初始化值。如果err等於OK,調用init進行初始化。

  • release
    我們看看reset中調用,並且可以做爲一個獨立狀態的release。
status_t MediaCodec::release() {
647    sp<AMessage> msg = new AMessage(kWhatRelease, this);
648
649    sp<AMessage> response;
650    return PostAndAwaitResponse(msg, &response);
651}

同樣是,生成AMessage的強引用計數msg和response。調用PostAndAwaitResponse(msg, &response))發送消息並等待返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章