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))發送消息並等待返回。