Android 13 - Media框架(20)- ACodec(二)

這一節開始我們就來學習 ACodec 的實現

1、創建 ACodec

ACodec 是在 MediaCodec 中創建的,這里先貼出創建部分的代碼:

    mCodec = mGetCodecBase(name, owner);if (mCodec == NULL) {ALOGE("Getting codec base with name '%s' (owner='%s') failed", name.c_str(), owner);return NAME_NOT_FOUND;}if (mDomain == DOMAIN_VIDEO) {// video codec needs dedicated looperif (mCodecLooper == NULL) {status_t err = OK;mCodecLooper = new ALooper;mCodecLooper->setName("CodecLooper");err = mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);if (OK != err) {ALOGE("Codec Looper failed to start");return err;}}mCodecLooper->registerHandler(mCodec);} else {mLooper->registerHandler(mCodec);}

從前面的學習我們可以知道,MediaCodec 使用的是異步消息處理的機制,創建MediaCodec 時需要傳入一個 ALooper 對象用于處理發送給 MediaCodec 的消息。同樣的 ACodec 也是用的異步消息處理機制,它也需要一個 ALooper,這個 ALooper 應該由上一級 MediaCodec 傳遞,從上面的代碼我們可以知道,如果創建的是音頻解碼器,那么 ACodec 將會復用 MediaCodec 的 ALooper,也就是它們的消息處理會在相同線程當中;如果是視頻解碼器,那么 MediaCodec 會創建一個專門的 ALooper 給 ACodec 使用,ACodec 和 MediaCodec 的消息處理在不同線程中。

ACodec::ACodec(): mSampleRate(0),mNodeGeneration(0),mUsingNativeWindow(false),mNativeWindowUsageBits(0),mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),mIsVideo(false),mIsImage(false),mIsEncoder(false),mFatalError(false),mShutdownInProgress(false),mExplicitShutdown(false),mIsLegacyVP9Decoder(false),mIsStreamCorruptFree(false),mIsLowLatency(false),mEncoderDelay(0),mEncoderPadding(0),mRotationDegrees(0),mChannelMaskPresent(false),mChannelMask(0),mDequeueCounter(0),mMetadataBuffersToSubmit(0),mNumUndequeuedBuffers(0),mRepeatFrameDelayUs(-1LL),mMaxPtsGapUs(0LL),mMaxFps(-1),mFps(-1.0),mCaptureFps(-1.0),mCreateInputBuffersSuspended(false),mTunneled(false),mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),mDescribeHDR10PlusInfoIndex((OMX_INDEXTYPE)0),mStateGeneration(0),mVendorExtensionsStatus(kExtensionsUnchecked) {memset(&mLastHDRStaticInfo, 0, sizeof(mLastHDRStaticInfo));mUninitializedState = new UninitializedState(this);mLoadedState = new LoadedState(this);mLoadedToIdleState = new LoadedToIdleState(this);mIdleToExecutingState = new IdleToExecutingState(this);mExecutingState = new ExecutingState(this);mOutputPortSettingsChangedState =new OutputPortSettingsChangedState(this);mExecutingToIdleState = new ExecutingToIdleState(this);mIdleToLoadedState = new IdleToLoadedState(this);mFlushingState = new FlushingState(this);mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;mInputEOSResult = OK;mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));changeState(mUninitializedState);
}

ACodec 的構造函數主要是初始化了成員對象,實例化了各個狀態對象,并且將狀態切換到了 UninitializedState,這時候我們就要去查看它的 stateEntered 方法:

void ACodec::UninitializedState::stateEntered() {ALOGV("Now uninitialized");if (mDeathNotifier != NULL) {if (mCodec->mOMXNode != NULL) {auto tOmxNode = mCodec->mOMXNode->getHalInterface<IOmxNode>();if (tOmxNode) {tOmxNode->unlinkToDeath(mDeathNotifier);}}mDeathNotifier.clear();}mCodec->mUsingNativeWindow = false;mCodec->mNativeWindow.clear();mCodec->mNativeWindowUsageBits = 0;mCodec->mOMX.clear();mCodec->mOMXNode.clear();mCodec->mFlags = 0;mCodec->mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;mCodec->mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;mCodec->mConverter[0].clear();mCodec->mConverter[1].clear();mCodec->mComponentName.clear();
}

UninitializedState::stateEntered 主要是將與 OMX 組件相關的成員對象重置初始化

2、initiateAllocateComponent

創建 MediaCodec 時,ACodec 也就被創建了,隨后就會調用 initiateAllocateComponent 方法創建 OMX 組件,ACodec 創建之后處在 UninitializedState,所以消息最終在該狀態中被處理:

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {ALOGV("onAllocateComponent");CHECK(mCodec->mOMXNode == NULL);mCodec->mFatalError = false;// 創建 Callback 消息,并且設置好 notifysp<AMessage> notify = new AMessage(kWhatOMXMessageList, mCodec);// notify 的 generation 為 nodegeneration + 1,這是因為進入 loaded 狀態后,mNodeGeneration 會 + 1notify->setInt32("generation", mCodec->mNodeGeneration + 1);// 需要檢查 codecInfo 才能創建 OMXNodesp<RefBase> obj;CHECK(msg->findObject("codecInfo", &obj));sp<MediaCodecInfo> info = (MediaCodecInfo *)obj.get();if (info == nullptr) {ALOGE("Unexpected nullptr for codec information");mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR);return false;}AString owner = (info->getOwnerName() == nullptr) ? "default" : info->getOwnerName();AString componentName;CHECK(msg->findString("componentName", &componentName));// 創建 callback 對象sp<CodecObserver> observer = new CodecObserver(notify);sp<IOMX> omx;sp<IOMXNode> omxNode;status_t err = NAME_NOT_FOUND;// 創建 OMXClientOMXClient client;// 獲取 IOmx 服務if (client.connect(owner.c_str()) != OK) {mCodec->signalError(OMX_ErrorUndefined, NO_INIT);return false;}// 將獲取到的 IOmx 服務代理封裝為 Legacy 模式omx = client.interface();pid_t tid = gettid();int prevPriority = androidGetThreadPriority(tid);androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);// 創建 IOmxNode 服務代理,并且封裝為 IOMXNodeerr = omx->allocateNode(componentName.c_str(), observer, &omxNode);androidSetThreadPriority(tid, prevPriority);mDeathNotifier = new DeathNotifier(new AMessage(kWhatOMXDied, mCodec));auto tOmxNode = omxNode->getHalInterface<IOmxNode>();if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) {mDeathNotifier.clear();}// 記錄新的狀態下的 ACodec 狀態++mCodec->mNodeGeneration;mCodec->mComponentName = componentName;mCodec->mRenderTracker.setComponentName(componentName);mCodec->mFlags = 0;// 記錄是否創建的是 secure 組件if (componentName.endsWith(".secure")) {mCodec->mFlags |= kFlagIsSecure;mCodec->mFlags |= kFlagIsGrallocUsageProtected;mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;}mCodec->mOMX = omx;mCodec->mOMXNode = omxNode;// 調用 callback 通知 MediaCodec 完成阻塞調用mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());// 切換狀態到 LoadedStatemCodec->changeState(mCodec->mLoadedState);return true;
}

這里涉及的內容比較多:

  1. 創建 notify 對象,并且傳入到 CodecObserver 對象中,CodecObserver 會把 OMX 發送回來的消息重新封裝,再通過 notify message 轉發給 ACodec,最后在不同狀態中處理。這里有個是 mNodeGeneration 用于檢查 OMX 消息及時性的,但是實際并未啟用。這里有一點需要注意,這些 State 狀態類都是 ACodec 的內部類,C++11之后內部類可以訪問外部類的私有成員以及私有方法,所以雖然這些類并不是 ACodec 的友元,但是同樣是可以調用 ACodec 所有方法的。
  2. 創建 OMXNode 之前,會先檢查從 MediaCodec 層獲取到的 MediaCodecInfo,如果沒有這個信息將會報錯,這里算是一個雙重檢查,防止強行越過 MediaCodecList 的檢查;
  3. 調用 OMXClient 的方法獲取 IOmx 的代理,并用該代理創建 IOmxNode 代理,傳入參數為組件名稱;
  4. 如果組件名稱以 secure 結尾,那么說明需要創建安全組件,并且記錄到 ACodec mFlags 成員中;
  5. 調用 onComponentAllocated 通知 MediaCodec 完成阻塞調用;
  6. 切換狀態到 LoadedState

在看 LoadedState 的 stateEntered 方法之前,我們要先看下 BaseState 給出的 stateExited 方法,這里用到了 mStateGeneration,用來記錄 ACodec 當前的狀態變化,在處理消息時,如果傳來的消息 generation 不等于當前的generation,說明狀態機發生錯誤,這和之前看到的部分是不一樣的,具體什么情況會出現不一樣我們后續再做了解。

void ACodec::BaseState::stateExited() {++mCodec->mStateGeneration;
}

從上面的代碼我們可以知道,每次狀態切換,mStateGeneration數都會加 1 。

接下來看 LoadedState 的 stateEntered

void ACodec::LoadedState::stateEntered() {ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());mCodec->mPortEOS[kPortIndexInput] =mCodec->mPortEOS[kPortIndexOutput] = false;mCodec->mInputEOSResult = OK;mCodec->mDequeueCounter = 0;mCodec->mMetadataBuffersToSubmit = 0;mCodec->mRepeatFrameDelayUs = -1LL;mCodec->mInputFormat.clear();mCodec->mOutputFormat.clear();mCodec->mBaseOutputFormat.clear();mCodec->mGraphicBufferSource.clear();if (mCodec->mShutdownInProgress) {bool keepComponentAllocated = mCodec->mKeepComponentAllocated;mCodec->mShutdownInProgress = false;mCodec->mKeepComponentAllocated = false;onShutdown(keepComponentAllocated);}mCodec->mExplicitShutdown = false;mCodec->processDeferredMessages();
}

LoadedState::stateEntered 會對編解碼過程中記錄信息的成員變量進行重置,后面是關于 shutdown 的處理流程,這里暫時不看。

到這里我們先做一個小結:MediaCodec 創建完成后,ACodec 最終會進入到 LoadedState,這個狀態代表了內部的 OMX 組件已經創建完成,UninitializedState 則表示OMX組件還未創建或者是已經銷毀的狀態。

3、initiateConfigureComponent

組件創建完成后,就要開始配置組件了,這時候狀態在 LoadedState,所以我們去這個狀態下找對應的處理:

bool ACodec::LoadedState::onConfigureComponent(const sp<AMessage> &msg) {ALOGV("onConfigureComponent");CHECK(mCodec->mOMXNode != NULL);status_t err = OK;// 檢查 mime type,調用 configureCodec 方法AString mime;if (!msg->findString("mime", &mime)) {err = BAD_VALUE;} else {err = mCodec->configureCodec(mime.c_str(), msg);}if (err != OK) {ALOGE("[%s] configureCodec returning error %d",mCodec->mComponentName.c_str(), err);mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));return false;}// 調用 CallbackmCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat);return true;
}

onConfigureComponent 會先去檢查 mime type,如果沒有將會返回error,如果有則會再調用 ACodec 的 configureCodec 方法做組件的配置,這個方法會比較復雜,我們留到下一節來講,最后會調用 callback 完成 MediaCodec 阻塞調用,同時把 input format 和 output format 回傳給 MediaCodec,需要注意的是這里的 output format 并不一定是準確的,可能是 omx 設定的默認值,在decoder解出相關序列信息之后會把真正的 output format 再回傳回來。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/210879.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/210879.shtml
英文地址,請注明出處:http://en.pswp.cn/news/210879.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ES 如何將國際標準時間格式進行格式化與調整時區

需求&#xff0c;日志收集的時候&#xff0c;時間格式是國際標準時間格式。形如yyyy-MM-ddTHH:mm:ss.SSS。 &#xff08;2023-12-05T02:45:50.282Z&#xff09;這個時區也不對&#xff0c;那如何將此類型的時間&#xff0c;進行格式化呢&#xff1f; 本篇文章體統一個案例&…

Other -- ChatGPT 原理

本文為個人理解&#xff0c;幫助小白&#xff08;本人就是&#xff09;了解正在創建新時代的 AI 產品&#xff0c;如文中理解有誤歡迎留言。 [參考鏈接--](https://baijiahao.baidu.com/s?id1765556782543603120&wfrspider&forpc) 1. 了解一些基本概念 大語言模型&a…

修改 Ganglia 監控 Grid Report timezone 時區 為 東八區 +8 PRC

Ganglia 監控 Grid Report timezone 默認時區 為 零時區 0 現在要修改為 東八區 8 具體操作如下 modify ganglia-web report timezone 0 --> 8 vim /apps/svr/httpd-2.4.48/htdocs/ganglia/header.php // add timezone GMT8 ini_set(date.timezone, PRC);詳細記錄&#x…

【面試】測試/測開(ING)

63. APP端特有的測試 參考&#xff1a;APP專項測試、APP應用測試 crash和anr的區別 1&#xff09;網絡測試 2&#xff09;中斷測試 3&#xff09;安裝、卸載測試 4&#xff09;兼容測試 5&#xff09;性能測試&#xff08;耗電量、流量、內存、服務器端&#xff09; 6&#xf…

畫對比折線圖【Python】

出這一期想必是我做某個課程作業遇到了。 由于去各個官網下載對比圖要錢&#xff0c;我還是不想花錢的&#xff01;真討厭&#xff01;淺淺水一期。 以下是要做的對比圖的數據&#xff1a; 代碼&#xff1a; from matplotlib import pyplot as plt#設置中文顯示plt.rcParams[…

CSS新手入門筆記整理:CSS浮動布局

文檔流概述 正常文檔流 “文檔流”指元素在頁面中出現的先后順序。正常文檔流&#xff0c;又稱為“普通文檔流”或“普通流”&#xff0c;也就是W3C標準所說的“normal flow”。正常文檔流&#xff0c;將一個頁面從上到下分為一行一行&#xff0c;其中塊元素獨占一行&#xf…

ChatGPT OpenAI API請求限制 嘗試解決

1. OpenAI API請求限制 Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for gpt-3.5-turbo-16k in organization org-U7I2eKpAo6xA7RUa2Nq307ae on reques…

讓內存無處可逃:智能指針[C++11]

智能指針 文章目錄 智能指針前言RAII什么是智能指針智能指針的應用示例 C98的auto_ptr共享型智能指針&#xff1a;shared_ptrshared_ptr的使用初始化獲取原生指針指定刪除器默認刪除器default_delete指定刪除器指定刪除器管理動態數組 shared_ptr的偽實現shared_ptr的注意事項避…

【Docker】進階之路:(五)Docker引擎

【Docker】進階之路&#xff1a;&#xff08;五&#xff09;Docker引擎 Docker引擎簡介Docker引擎的組件構成runccontainerd Docker引擎簡介 Docker引擎是用來運行和管理容器的核心部分。Docker首次發布時&#xff0c;Docker 引擎由LXC 和 Docker daemon 兩個核心組件構成。 …

linux驅動開發——內核調試技術

目錄 一、前言 二、內核調試方法 2.1 內核調試概述 2.2 學會分析內核源程序 2.3調試方法介紹 三、內核打印函數 3.1內核鏡像解壓前的串口輸出函數 3.2 內核鏡像解壓后的串口輸出函數 3.3 內核打印函數 四、獲取內核信息 4.1系統請求鍵 4.2 通過/proc 接口 4.3 通過…

算法:有效的括號(入棧出棧)

時間復雜度 O(n) 空間復雜度 O(n∣Σ∣)&#xff0c;其中 Σ 表示字符集&#xff0c;本題中字符串只包含 6 種括號 /*** param {string} s* return {boolean}*/ var isValid function(s) {const map {"(":")","{":"}","["…

List截取指定長度(java截取拼接URL)

場景&#xff1a; N多個參數&#xff0c;截取指定個數&#xff0c;拼接URL public static void main(final String[] args) {int count 0;//每頁數量final int pageSize 5;final List<Integer> memberNos ListUtil.toList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13…

python格式化內容

1.字符串格式化: 定義列表 [{"姓名": "張三", "年齡": 18, "性別": "男"}, {"姓名": "里斯李四李斯", "年齡": 18, "性別": "男"}, {"姓名": "斯托夫斯基…

C++知識 抽象基類

抽象基類通常包含至少一個純虛函數&#xff0c;即一個沒有具體實現的虛函數&#xff0c;通過在基類中聲明純虛函數&#xff0c;它強制派生類提供這個函數的具體實現。 通過在類的聲明中使用 virtual 關鍵字和 0 初始化來創建純虛函數&#xff0c;這樣的類就成為抽象基類。以下…

上位機與PLC:ModbusTCP通訊之數據類型轉換

前請提要: 從PLC讀取的數值,不管是讀正負整數還是正負浮點數,讀取過來后都會變成UInt16,也就是Ushort類型 一、ushort(UInt16)轉成 Int32 源代碼方法: //ushort類型轉Int32類型的方法private int ushortToInt32(ushort[] date, int start){//先進行判斷,長度是否正確…

MySQL_6.MySQL常用創建語句

1.數據庫創建,查詢,刪除 (1)創建一個test數據庫 CREATE DATABASE test ; CREATE DATABASE IF NOT EXISTS test; # default character set :默認字符集 CREATE DATABASE IF NOT EXISTS test default character set UTF8; # default collate&#xff1a;默認排序規格 # utf8_g…

前端知識(七)———HTTPS:保護網絡通信安全的關鍵

當談到網絡通信和數據傳輸時&#xff0c;安全性是一個至關重要的問題。在互聯網上&#xff0c;有許多敏感信息需要通過網絡進行傳輸&#xff0c;例如個人身份信息、銀行賬戶信息和商業機密等。為了保護這些信息不被未經授權的人訪問和篡改&#xff0c;HTTPS&#xff08;超文本傳…

AI:大語言模型LLM

LLM 大語言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;是一種利用大量文本數據進行訓練的自然語言處理模型&#xff0c;其評價可以從多個方面進行。 以下是一些主要的評價方面&#xff1a; 語言理解和生成能力&#xff1a;評價大語言模型在自然語言理…

模型評價指標

用訓練好的模型結果進行預測&#xff0c;需要采用一些評價指標來進行評價&#xff0c;才可以得到最優的模型 常用的指標&#xff1a; 1.分類任務 ConfusionMatrix 混淆矩陣Accuracy 準確率Precision 精確率Recall 召回率F1 score H-mean值ROC Curve ROC曲線PR …

PostgreSQL pgvector:如何利用向量數據庫提升搜索效率和精度

LLMs模型實戰教程 文章來源&#xff1a;https://zhuanlan.zhihu.com/p/641516393 Kevin 一、介紹 隨著基礎模型的興起&#xff0c;向量數據庫的受歡迎程度也飆升。事實上&#xff0c;在大型語言模型環境中&#xff0c;向量數據庫也很有用。 在機器學習領域&#xff0c;我們經…