Android 13 - Media框架(31)- ACodec(七)

这篇具有很好参考价值的文章主要介绍了Android 13 - Media框架(31)- ACodec(七)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

之前的章节中我们解了 input buffer 是如何传递给 OMX 的,以及Output buffer 是如何分配并且注册给 OMX 的。这一节我们就来看ACodec是如何处理OMX的Callback的。

1、OMXNodeInstance Callback

这一节我们只大致记录Callback是如何传递给ACodec的。在之前的学习中我们了解到OMXNodeInstance中会有一个专门的线程来处理OMX的callback,这个线程的作用是把Callback按照时间顺序回传给ACodec。

CallbackDispatcher中维护了一个list,将消息回传给ACodec时并不是将list中的消息一条一条回传的,而是将list中所有的消息一次性回传,这也就是为什么ACodec处理OMXNodeInstance的消息时会有循环遍历。

在调用CodecObserver做消息上抛之前,会调用OMXNodeInstance::handleMessage 对消息做预处理,这里的预处里包括是否要将buffer做拷贝等等。

2、onOMXEmptyBufferDone

OMX使用完input buffer后,消息上抛到ACodec层,ACodec 会调用onOMXEmptyBufferDone再处理input buffer。

bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd) {
    ALOGV("[%s] onOMXEmptyBufferDone %u",
         mCodec->mComponentName.c_str(), bufferID);

    BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
    BufferInfo::Status status = BufferInfo::getSafeStatus(info);
    // 检查 Buffer 状态
    if (status != BufferInfo::OWNED_BY_COMPONENT) {
        ALOGE("Wrong ownership in EBD: %s(%d) buffer #%u", _asString(status), status, bufferID);
        mCodec->dumpBuffers(kPortIndexInput);
        if (fenceFd >= 0) {
            ::close(fenceFd);
        }
        return false;
    }
    // input buffer 回到 ACodec
    info->mStatus = BufferInfo::OWNED_BY_US;

    // input buffers cannot take fences, so wait for any fence now
    (void)mCodec->waitForFence(fenceFd, "onOMXEmptyBufferDone");
    fenceFd = -1;

    // still save fence for completeness
    info->setWriteFence(fenceFd, "onOMXEmptyBufferDone");

    // We're in "store-metadata-in-buffers" mode, the underlying
    // OMX component had access to data that's implicitly refcounted
    // by this "MediaBuffer" object. Now that the OMX component has
    // told us that it's done with the input buffer, we can decrement
    // the mediaBuffer's reference count.
    info->mData->meta()->setObject("mediaBufferHolder", sp<MediaBufferHolder>(nullptr));
	// 获取当前的 PortMode
    PortMode mode = getPortMode(kPortIndexInput);

    switch (mode) {
        case KEEP_BUFFERS:
            break;

        case RESUBMIT_BUFFERS:
            postFillThisBuffer(info);
            break;

        case FREE_BUFFERS:
        default:
            ALOGE("SHOULD NOT REACH HERE: cannot free empty output buffers");
            return false;
    }

    return true;
}

对input buffer的处理很简单,检查当前ACodec处在的状态并作出反应,如果处在 ExecutingState 则调用 postFillThisBuffer 将 Buffer 提交给 MediaCodec,同时清除 ACodec 存储的 mData。其他状态下则持有 input buffer 不会将其回传给 MediaCodec。

2、onOMXFillBufferDone

ACodec 处理 output buffer 的代码比较长,但是也不难,接下来就做分解学习:

首先有个 debug log,我们可以打开宏TRACK_BUFFER_TIMING来使用这部分内容,把input buffer写给 OMX 时会将pts以及调用时间做记录,在output buffer回传回来时,检查pts,打印出解码该帧消耗的时间。

#if TRACK_BUFFER_TIMING
    index = mCodec->mBufferStats.indexOfKey(timeUs);
    if (index >= 0) {
        ACodec::BufferStats *stats = &mCodec->mBufferStats.editValueAt(index);
        stats->mFillBufferDoneTimeUs = ALooper::GetNowUs();

        ALOGI("frame PTS %lld: %lld",
                timeUs,
                stats->mFillBufferDoneTimeUs - stats->mEmptyBufferTimeUs);

        mCodec->mBufferStats.removeItemsAt(index);
        stats = NULL;
    }
#endif

记录output BufferInfo是在第几帧被使用,mDequeueCounter可以看作是当前解码的帧数。

    info->mDequeuedAt = ++mCodec->mDequeueCounter;
    info->mStatus = BufferInfo::OWNED_BY_US;

2.1 Executing

在 Executing 状态下,会检查output buffer flag 和 size:

  • output buffer size为0,flag 不是 OMX_BUFFERFLAG_EOS,说明没有解出有效数据,重新回传给 OMX 使用;
  • output buffer size为0,flag 是 OMX_BUFFERFLAG_EOS,ACodec 已经收到 EOS,重新把 buffer 交给 OMX;
  • 其他情况说明数据有效,或者是flag是 OMX_BUFFERFLAG_EOS,需要把output buffer回传给上层。
        case RESUBMIT_BUFFERS:
        {
        	// 如果output buffer长度为0,flag 不是 OMX_BUFFERFLAG_EOS
        	// 如果output buffer长度为0,已将收到 EOS
        	// 重新把 output buffer 提交给 OMX
            if (rangeLength == 0 && (!(flags & OMX_BUFFERFLAG_EOS)
                    || mCodec->mPortEOS[kPortIndexOutput])) {
                ALOGV("[%s] calling fillBuffer %u",
                     mCodec->mComponentName.c_str(), info->mBufferID);

                err = mCodec->fillBuffer(info);
                if (err != OK) {
                    mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
                    return true;
                }
                break;
            }

            sp<MediaCodecBuffer> buffer = info->mData;
            // ......
            // 设定 pts
            buffer->meta()->setInt64("timeUs", timeUs);
			// 解除 ACodec 引用
            info->mData.clear();
			// 调用 drainThisBuffer
            mCodec->mBufferChannel->drainThisBuffer(info->mBufferID, flags);

            info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
			// 如果 flag 为 OMX_BUFFERFLAG_EOS,将PortEOS置为true
            if (flags & OMX_BUFFERFLAG_EOS) {
                ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str());

                mCodec->mCallback->onEos(mCodec->mInputEOSResult);
                mCodec->mPortEOS[kPortIndexOutput] = true;
            }
            break;
        }

2.2 OutputPortSettingsChangedState

onOMXFillBufferDone 中有个比较特殊的case:FREE_BUFFERS,表示所有回传上来的 output buffer 都需要被释放。不难发现 FREE_BUFFERS 是属于 OutputPortSettingsChangedState 的,所以我们要先了解 OutputPortSettingsChangedState 这个状态是什么。

ACodec::BaseState::PortMode ACodec::OutputPortSettingsChangedState::getPortMode(
        OMX_U32 portIndex) {
    if (portIndex == kPortIndexOutput) {
        return FREE_BUFFERS;
    }

    CHECK_EQ(portIndex, (OMX_U32)kPortIndexInput);

    return RESUBMIT_BUFFERS;
}

有以下两种情况:

  1. 在播放之前,我们并不知道输出output buffer格式;
  2. 播放过程中,码流格式发生变化,原先的output buffer并不适用新的输出;

出现这两种情况时需要重新分配output buffer,这个分配的过程ACodec的OutputPortSettingsChangedState中处理。

bool ACodec::ExecutingState::onOMXEvent(
        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
    switch (event) {
        case OMX_EventPortSettingsChanged:
        {
        	// 检查是不是output端口发生变化
            CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
			// 调用 ACodec onOutputFormatChanged
            mCodec->onOutputFormatChanged();

            if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
                mCodec->mMetadataBuffersToSubmit = 0;
                // 关闭 OMX output port的使用
                CHECK_EQ(mCodec->mOMXNode->sendCommand(
                            OMX_CommandPortDisable, kPortIndexOutput),
                         (status_t)OK);
				// 释放没有被omx持有的output buffer
                mCodec->freeOutputBuffersNotOwnedByComponent();
				// 切换到 OutputPortSettingsChangedState
                mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
            } else if (data2 != OMX_IndexConfigCommonOutputCrop
                    && data2 != OMX_IndexConfigAndroidIntraRefresh) {
                ALOGV("[%s] OMX_EventPortSettingsChanged 0x%08x",
                     mCodec->mComponentName.c_str(), data2);
            }

            return true;
        }
    }
}

Executing 状态下,如果ACodec收到OMX_EventPortSettingsChanged事件,则会进入端口设置改变的处理流程中:

  1. 首先检查设置改变的端口是否是 output 端口;
  2. 调用 onOutputFormatChanged 方法;
  3. OMX_CommandPortDisable 关闭 ouput port的使用;
  4. 释放没有被omx持有的output buffer;
  5. 切换到 OutputPortSettingsChangedState;

从这个流程中我们大致可以猜测,处理PortSettingsChanged事件,需要把所有的output buffer销毁。buffer可能有两种状态,一种是被OMX持有,另一种是被上层持有(ACodec、MediaCodec),这两种状态的销毁流程是不太一样的,被OMX持有的output buffer需要回到上层才能被销毁。

status_t ACodec::freeOutputBuffersNotOwnedByComponent() {
    status_t err = OK;
    for (size_t i = mBuffers[kPortIndexOutput].size(); i > 0;) {
        i--;
        BufferInfo *info =
            &mBuffers[kPortIndexOutput].editItemAt(i);

        // At this time some buffers may still be with the component
        // or being drained.
        if (info->mStatus != BufferInfo::OWNED_BY_COMPONENT &&
            info->mStatus != BufferInfo::OWNED_BY_DOWNSTREAM) {
            status_t err2 = freeBuffer(kPortIndexOutput, i);
            if (err == OK) {
                err = err2;
            }
        }
    }

    return err;
}

销毁buffer在freeBuffer中完成:文章来源地址https://www.toymoban.com/news/detail-802881.html

status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
    BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
    status_t err = OK;

    // there should not be any fences in the metadata
    if (mPortMode[portIndex] == IOMX::kPortModeDynamicANWBuffer && info->mCodecData != NULL
            && info->mCodecData->size() >= sizeof(VideoNativeMetadata)) {
        int fenceFd = ((VideoNativeMetadata *)info->mCodecData->base())->nFenceFd;
        if (fenceFd >= 0) {
            ALOGW("unreleased fence (%d) in %s metadata buffer %zu",
                    fenceFd, portIndex == kPortIndexInput ? "input" : "output", i);
        }
    }

    switch (info->mStatus) {
        case BufferInfo::OWNED_BY_US:
            if (portIndex == kPortIndexOutput && mNativeWindow != NULL) {
                (void)cancelBufferToNativeWindow(info);
            }
            FALLTHROUGH_INTENDED;

        case BufferInfo::OWNED_BY_NATIVE_WINDOW:
            err = mOMXNode->freeBuffer(portIndex, info->mBufferID);
            break;

        default:
            ALOGE("trying to free buffer not owned by us or ANW (%d)", info->mStatus);
            err = FAILED_TRANSACTION;
            break;
    }

    if (info->mFenceFd >= 0) {
        ::close(info->mFenceFd);
    }

    if (portIndex == kPortIndexOutput) {
        mRenderTracker.untrackFrame(info->mRenderInfo, i);
        info->mRenderInfo = NULL;
    }

    // remove buffer even if mOMXNode->freeBuffer fails
    mBuffers[portIndex].removeAt(i);
    return err;
}

到了这里,关于Android 13 - Media框架(31)- ACodec(七)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Media3:Android下一代媒体框架

    无论您是在构建音乐播放器、视频流应用程序还是其他需要播放媒体内容的 Android 应用程序,拥有可靠的媒体播放库都是必不可少的。 这就是 Media3 发挥作用的地方。 Media3 是由 Google 作为 AndroidX 的一部分推出的强大媒体播放库。它提供了一个易于使用的 API,简化了 Android 应

    2024年02月16日
    浏览(63)
  • Android Framework | AOSP源码下载及编译指南(基于Android13)

    AOSP(Android Open Source Project)是Android操作系统的开源项目,通过下载和编译AOSP源码,您可以获得原始的Android系统,并进行定制和开发。本教程将向您介绍如何下载AOSP源码并进行编译的步骤。 如何只是浏览查看AOSP源码,则没必要下载源码,可以使用google提供的Code Search工具进

    2024年01月16日
    浏览(59)
  • 【Android Framework系列】第13章 SVG矢量图形自定义组件(绘制中国地图)

    本章节我们来了解下什么是 SVG 矢量图形,怎么通过 SVG 实现图形的绘制,通过 SVG 实现不规则的自定义控件,项目实现一个中国地图,实现每个省都能够点击,项目地址在文末请自取。 SVG 指可伸缩矢量图形 (Scalable Vector Graphics) SVG 用来定义用于网络的基于矢量的图形 SVG 使用

    2024年02月10日
    浏览(64)
  • Android 13.0 framework中实现默认长按电源键弹出关机对话框功能

    在13.0的系统定制化开发中,在12.0的系统之前默认的都是长按电源键弹出关机对话框,而在13以后 就改成音量+电源键弹出对话框,由于使用不方便,所以就改成默认长按弹出关机对话框功能 PhoneWindowManager是供系统进程使用,是WindowManagerService 的一部分,WindowManagerService 会利用

    2024年02月11日
    浏览(63)
  • Android框架mqtt库无法兼容高版本android13的问题

    最近使用mqtt库,测试的时候发现在Android12及以下正常,但在13上闪退,闪退日志如下 提示很明显是version 31版本在创建PendingIntent的时候需要做适配,于是全局搜索把适配代码加上,代码如下 加上之后还是报同样的错误,几经周折才发现mqtt库里也用到了PendingIntent且没有做适配

    2024年02月07日
    浏览(38)
  • Android13 Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE

    最近把以前的11的代码移植到13上碰到的问题,记录一下: 错误提示在使用PendingIntent时,无论是创建或使用的时候flags参数要求必须添加 FLAG_IMMUTABLE或者FLAG_MUTABLE的两个之中其中一个,而且官方还强烈推荐使用FLAG_IMMUTABLE(代码不同,依情况而定官方的推荐)。 1、在清单文件

    2024年02月11日
    浏览(42)
  • Android13音频子系统分析(三)---音效算法集成框架

    目录 一、Android音效C/S架构 二、EffectHAL音效框架的初始化与使用 2.1音效框架初始化 2.2创建并加载音效算法 2.3执行音效算法 三、AudioFlinger对音效框架的二次封装 四、Device音效的绑定过程 4.1 DeviceHAL处理音效数据的方式 4.2 AudioFlinger处理音效数据的方式 五、Stream音效的绑定过程

    2024年02月03日
    浏览(43)
  • android 12 /mnt/media

    storage下可读取,但/mnt/media_rw不可读取 1 base/core/res/AndroidManifest.xml –安卓12 -----已替代android.permission.WRITE_MEDIA_STORAGE 安卓新版本适配是真谛烦 =。= 2 framework/base/data/etc/platform.xml 看情况是否需要添加此处,否则media_rw权限不可读  

    2024年02月07日
    浏览(35)
  • 【学习】从零开发的Android音视频开发(13)——MediaCodec到OMX框架过程及其硬解码

    在讲NuPlayer时,NuPlayer解码部分会创建MediaCodec,并且最终到达OMX框架,先看MediaCodec的 init 函数 从init函数中可以看到,首先创建了 ACodec ,并且初始化了 ALooper 、 AMessage ,由于ACodec继承自 AHandler ,那么一套消息机制就有了。最后发送 kWhatInit 消息,收到消息的逻辑位于ACodec.

    2023年04月08日
    浏览(50)
  • Android Media3 ExoPlayer 开启缓存功能

        ExoPlayer 开启播放缓存功能,在下次加载已经播放过的网络资源的时候,可以直接从本地缓存加载,实现为用户节省流量和提升加载效率的作用。 第 1 步:实现 Exoplayer 参考 Exoplayer 官网 Release notes : 对应关系: 2.19.0 (2023-07-05)  -- AndroidX Media3 1.1.0 release. 2.19.1 (2023-08-14) 

    2024年02月04日
    浏览(73)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包