[Rockit] 多媒体播放器使用C++对解码内存buffer实时分配的监听

Rockit多媒体播放器音频的解码buffer,正确的做法是实时分配,因为音频的解码buffer大小有可能在某些流不是固定的。而且某些流也不能在初始化的时候知道解码frame的size,如果提前预分配,很可能造成buffer的浪费或者分配buffer太小造成声音断音。所以需要根据解码frame实时分配。但是分配方式是插件创建,所以只有插件才有分配释放权,而具体的解码


Rockit音频的代码简单逻辑线:

buffer监听基类RTBufferListener:

class RTBufferListener {
 public:
    RTBufferListener() {}
    virtual ~RTBufferListener() {}

 public:
    // buffer callback.
 /*
 * section: override virtual methods of RTBufferListener.
 *    name: onBufferAvailable
 */
    virtual void onBufferAvailable(void* buffer) = 0;
 /*
 * section: override virtual methods of RTBufferListener.
 *    name: onBufferRealloc
 */
    virtual void onBufferRealloc(void* buffer, UINT32 size) = 0;
/*
 * section: override virtual methods of RTBufferListener.
 *    name: onBufferRelease
 */
    virtual void onBufferRelease(void* buffer, RT_BOOL render) = 0;
};

此三个函数的功能:

 void onBufferAvailable(void* buffer) : 可用buffer回调函数

void onBufferRealloc(void* buffer, UINT32 size) : buffer重分配回调函数

void onBufferRelease(void* buffer, RT_BOOL render) : buffer释放回调函数

音频解码插件RTNodeAudioCodec和buffer管理池RTMediaBufferPool分别继承了此监听基类:

class RTNodeAudioCodec : public RTNodeCodec, RTBufferListener {
 public:
    RTNodeAudioCodec();
    virtual ~RTNodeAudioCodec();
    ......
}

class RTMediaBufferPool : public RTBufferListener {
 public:
    /* create for external buffer pool */
    explicit RTMediaBufferPool(UINT32 max_buffer_count = 0);
    /* create for internal buffer pool */
    explicit RTMediaBufferPool(UINT32 max_buffer_count, UINT32 buffer_size);
    ~RTMediaBufferPool();
    ......
}

RTNodeAudioCodec在初始化init()中会new RTMediaBufferPool,并把自己注册通过setBufferListener(this)给RTMediaBufferPool

RTNodeAudoCodec 在prepare阶段会拿到分配方式(使用malloc的分配的方式),并且进行buffer的预分配:

RT_RET RTNodeAudioCodec::onPrepare() {
    // create malloc allocator for audio
    RTAllocatorStore::fetchAllocator(RTAllocatorStore::RT_ALLOC_TYPE_MALLOC, mMetaInput, &mLinearAllocator);
    RT_LOGD_IF(DEBUG_FLAG, "fetchAllocator(ALLOC_TYPE_MALLOC), mLinearAllocator = %p", mLinearAllocator);
    RT_ASSERT(RT_NULL != mLinearAllocator);

    // allocate input and output buffers.
    allocateBuffersOnPort(RT_PORT_INPUT);
    allocateBuffersOnPort(RT_PORT_OUTPUT);
    mFramePool->start();
    mPacketPool->start();

    return RT_OK;
}

buffer预分配,这里对解码轮转buffer进行空分配(仅new出轮转的RTMediaBuffer,并没有分配实际的内存),并且把buffer注册进内存池:

在RTMediaBufferPool->registerBuffer中,会把RTMediaBufferPool注册给RTMediaBuffer:


开始播放后,RTNodeAudioCodec->runtask->IAudioCodec->fa_xx_get_frame,此时会对buffer开始监听:

当buffer->getSize()小于解码frame的size或者buffer->getData()为空时,会对buffer进行重分配:

RT_RET fa_xx_get_frame(FABSEncoderContext* ctx, RTMediaBuffer *buffer) {
    UINT8  *dst   = RT_NULL;

    if (ctx->mSize > 0) {
        // reallocate MediaBuffer is if resample_size > buffer_size
        if (buffer->getSize() < ctx->mSize) {
            RT_LOGD_IF(DEBUG_FLAG, "reallocate buffer(this=%p, size=%d), old=%d", \
                             buffer, ctx->mSize, buffer->getSize());
        }

        buffer->signalBufferRealloc(ctx->mSize);
        if (buffer->getSize() < ctx->mSize) {
            RT_LOGD("fail to reallocate buffer(this=%p, size=%d) is smaller than ctx->mSize=%d", \
                             buffer, buffer->getSize(), ctx->mSize);
            ctx->mSize = buffer->getSize();
        }
       ......
    }
    ......
}

这里buffer->signalBufferRealloc(ctx->mSize);会调用:

void RTMediaBuffer::signalBufferRealloc(UINT32 size) {
    if (RT_NULL != mBufferListener) {
        mBufferListener->onBufferRealloc(this, size);
    }
}

这里的mBufferListener是上面RTMediaBufferPool->registerBuffer中截图部分注册进来的,所以这里回调到RTMediaBufferPool中的onBufferRealloc:

/*
 * section: override virtual methods of RTBufferListener.
 *    name: onBufferRealloc
 */
void RTMediaBufferPool::onBufferRealloc(void* buffer, UINT32 size) {
    RT_LOGD_IF(DEBUG_FLAG, "buffer(%p) is reallocated", buffer);
    if (RT_NULL != mBufferListener) {
        mBufferListener->onBufferRealloc(buffer, size);

        if (RT_NULL != buffer) {
            RTMediaBuffer* frame = reinterpret_cast<RTMediaBuffer*>(buffer);
            // need to increase the reference count when RTMediaBuffer is realloced,
            // otherwise the RTMediaBuffer will be probabilistically recycled and reused,
            // and objects that are using RTMediaBuffer will crash.
            if (0 == frame->refsCount()) {
                frame->addRefs();
            }

            // buffer pool manages this new buffer, so needs RTBufferListener.
            frame->setListener(this);
        }
    }
}

而RTMediaBufferPool中的mBufferListener是RTNodeAudioCodec注册进来的,所以这里调用RTNodeAudioCodec->onBufferRealloc:

/*
 * section: override virtual methods of RTBufferListener.
 *    name: onBufferRealloc
 */
void RTNodeAudioCodec::onBufferRealloc(void* buffer, UINT32 size) {
    RTMediaBuffer* frame = reinterpret_cast<RTMediaBuffer*>(buffer);
    if ((RT_NULL != frame) && (RT_NULL != mLinearAllocator)) {
        if ((frame->getSize() < size) || (RT_NULL == frame->getData())) {
            frame->release();
            mLinearAllocator->freeBuffer(&frame);
            mLinearAllocator->newBuffer(size, &frame);

            RT_LOGD_IF(DEBUG_FLAG, "reallocated buffer(this: %p, data: %p, size: %d)",
                   frame, frame->getData(), size);
            return;
        }
    }
}

这样就会根据RTNodeAudioCodec注册的分配方式判断是否进行重分配,实现了谁注册分配方式,谁管理的监听。


注意:在当前buffer进行重分配的时候,需要把buffer置上引用标记(见下面截图框1),因为这buffer有概率会回收或者再利用,造成crash.  并且buffer pool管理新分配的buffer,所以新分配的需要注册监听(下面截图框2)。

猜你喜欢

转载自blog.csdn.net/zhoudidong/article/details/103600143