Android 13 - Media框架(23)- ACodecBufferChannel

全新系列文章已更新:


这一节我们将了解 ACodecBufferChannel

上一节我们了解到input buffer 和 output buffer 是如何分配的了,allocateBuffersOnPort 方法的最后会将ACodec::BufferInfo 中的 mData 成员组织成为数组,最后提交给 ACodecBufferChannel 管理。这一节我们将尝试了解ACodecBufferChannel 的作用,以及 ACodec::BufferInfo 中部分成员的作用。

1、ACodecBufferChannel

ACodecBufferChannel 介于 MediaCodec 和 ACodec之间,我们之前了解过 BufferChannelBase 的接口,基类中的接口主要是给 MediaCodec 调用的,我们后面要了解的CCodec中的BufferChannel同样也是继承于该基类。ACodecBufferChannel起着中介的作用,降低了 MediaCodec 和 ACodec 的耦合度,不过我还是会想为什么要有 BufferChannel 呢?除了降低耦合还干了什么呢?其实 BufferChannel 还有另外的功能,那就是解扰(descrambler)和解密(decrypto)的作用,只不过是我们平时不常用,所以就忽略它们了。解扰和解密的流程放在 BufferChannel 中可以简化 ACodec 的内容,让分工更加明确。

回到 ACodecBufferChannel 中来,头文件中声明的函数分为两部分,一部分是给MediaCodec使用的(BufferChannelBase中的接口),另一部分是给ACodec调用的,我们这里主要来看给ACodec调用的接口。

    struct BufferInfo {
        BufferInfo(
                const sp<MediaCodecBuffer> &buffer,
                IOMX::buffer_id bufferId,
                const sp<IMemory> &sharedEncryptedBuffer);

        BufferInfo() = delete;

        // Buffer facing MediaCodec and its clients.
        const sp<MediaCodecBuffer> mClientBuffer;
        // Buffer facing CodecBase.
        const sp<MediaCodecBuffer> mCodecBuffer;
        // OMX buffer ID.
        const IOMX::buffer_id mBufferId;
        // Encrypted buffer in case of secure input.
        const sp<IMemory> mSharedEncryptedBuffer;
    };

ACodeBufferChannel 中也声明了一个 BufferInfomCodecBuffer 指向的是ACodec中传递的mData,mClientBuffer指向的是回传给 MediaCodec 的 buffer,未什么要分成两个部分我们后面再说。

先来看 ACodec 是如何调用 setInputBufferArray 把 buffer 交给ACodecBufferChannel管理的,setInputBufferArray传入参数为一个BufferAndId数组,将MediaCodecBuffer和buffer-id组成成为BufferAndId,然后再传入ACodecBufferChannel。

void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
	...
    std::vector<const BufferInfo> inputBuffers;
    for (const BufferAndId &elem : array) {
        sp<IMemory> sharedEncryptedBuffer;
        if (hasCryptoOrDescrambler()) {
        	// 分配用于解密的buffer
            sharedEncryptedBuffer = mDealer->allocate(elem.mBuffer->capacity());
        }
        // 创建ACodec::BufferInfo
        inputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, sharedEncryptedBuffer);
    }
    std::atomic_store(
            &mInputBuffers,
            std::make_shared<const std::vector<const BufferInfo>>(inputBuffers));
}

ACodecBufferChannel::BufferInfo::BufferInfo(
        const sp<MediaCodecBuffer> &buffer,
        IOMX::buffer_id bufferId,
        const sp<IMemory> &sharedEncryptedBuffer)
    : mClientBuffer(
          (sharedEncryptedBuffer == nullptr)
          ? buffer
          : new SharedMemoryBuffer(buffer->format(), sharedEncryptedBuffer)),
      mCodecBuffer(buffer),
      mBufferId(bufferId),
      mSharedEncryptedBuffer(sharedEncryptedBuffer) {
}

setInputBufferArray 中会判断是否需要解密或者解扰,这些内容是在 MediaCodec configure过程中设定的,如果需要会分配出一块用于解密的buffer,最后会创建一个ACodec::BufferInfo对象。继续来看BufferInfo的构造函数,如果sharedEncryptedBuffer这个参数不为NULL,那么mClientBuffer将会使用这块新分配的用于解密的buffer,否则就直接使用ACodec传来的MediaCodecBuffer。

到这我们应该就可以猜测了,如果上层需要对input进行解密/解扰,那么数据也需要加密传输,因此需要一块受保护的buffer,因此需要单独分配buffer给上层使用(buffer的拷贝过程也是加密的)。上层把填好数据的buffer(mClientBuffer)送给 BufferChannel,在这里会对数据进行解密,再将解密后的数据拷贝要input buffer当中(mCodecBuffer),最后送给 OMX 组件,因此这里使用两个MediaCodecBuffer来处理需要加密解密的情况,这种情况下mClientBuffer和mCodecBuffer指向的内容是不同的。

但是如果只是普通播放,那么mClientBuffer和mCodecBuffer会指向同一个地址,使用 == 判断是就会返回 true 了。

我们去阅读 ACodecBufferChannel::queueInputBuffer 和 ACodecBufferChannel::queueSecureInputBuffer 大致能能理解上面的意思了:

status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        return -ENOENT;
    }
    if (it->mClientBuffer != it->mCodecBuffer) {
        // Copy metadata from client to codec buffer.
        it->mCodecBuffer->meta()->clear();
        int64_t timeUs;
        CHECK(it->mClientBuffer->meta()->findInt64("timeUs", &timeUs));
        it->mCodecBuffer->meta()->setInt64("timeUs", timeUs);
        int32_t eos;
        if (it->mClientBuffer->meta()->findInt32("eos", &eos)) {
            it->mCodecBuffer->meta()->setInt32("eos", eos);
        }
        int32_t csd;
        if (it->mClientBuffer->meta()->findInt32("csd", &csd)) {
            it->mCodecBuffer->meta()->setInt32("csd", csd);
        }
    }
    ALOGV("queueInputBuffer #%d", it->mBufferId);
    sp<AMessage> msg = mInputBufferFilled->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->post();
    return OK;
}

我们来看普通模式,这时候mClientBuffer和mCodecBuffer中的mData指向的内容是相同的,所以不需要做buffer拷贝,但是可能有人又要问了,之前不是说 == 判断 是返回true的吗,这里要注意,== 返回true指的是把 MediaCodecBuffer 中的 mBuffer 进行比较。

简单来说是数据块相同,但是 meta 信息还是独立的,所以每次调用 queueInputBuffer 都是要拷贝 meta 数据的。

queueSecureInputBuffer 比较长,这里就不贴代码了,这里面主要做的就是把secure input buffer 中的数据安全的拷贝到 ACodec input buffer 当中。

这里抛出一个问题,是否使用 queueSecureInputBuffer 来输入 buffer,那么就一定用的 secure 组件呢?答案不是的。


关注公众号《青山渺渺》阅读完整内容; 如有问题可在公众号后台私信,也可进入音视频开发技术分享群一起讨论!

在这里插入图片描述

  • 42
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山渺渺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值