Android MultiMedia框架——MediaCodec编码(下)

Posted VNanyesheshou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android MultiMedia框架——MediaCodec编码(下)相关的知识,希望对你有一定的参考价值。

Android MultiMedia框架——OMX服务启动
Android MultiMedia框架——OMXPlugin
Android MultiMedia框架——MediaCodec编码(上)
Android MultiMedia框架——MediaCodec编码(下)

这篇主要分析以下MeidaCodec获取可用输入buffer,buffer加入队列相关流程

MediaCodec.cpp中相关数据结构

List<size_t> mAvailPortBuffers[2];
std::vector<BufferInfo> mPortBuffers[2];

mAvailPortBuffers:可用buffer对应的index。mAvailPortBuffers[0]为输入,mAvailPortBuffers[1]为输出。

mPortBuffers:所有buffer缓冲区,包括可用和已占用。mPortBuffers[0]为输入buffer,mPortBuffers[1]为输出buffer。

这些数据缓冲区是在Mediacodec configure的时候进行的分配。

input

查看MediaCodec 输入缓冲区使用流程

1 dequeueInputBuffer

这里就不在贴java层代码了,直接看MediaCodec.cpp dequeueInputBuffer

status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) 
    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
    msg->setInt64("timeoutUs", timeoutUs);

    sp<AMessage> response;
    status_t err;
    if ((err = PostAndAwaitResponse(msg, &response)) != OK) 
        return err;
    

    CHECK(response->findSize("index", index));

    return OK;

这里发送AMessage消息,MediaCodec继承AHandler,通过onMessageReceived处理消息

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) 
    switch (msg->what()) 
            case kWhatDequeueInputBuffer:
        
            //.....
            if (handleDequeueInputBuffer(replyID, true /* new request */)) 
                break;
            
        
	//.......

接着看handleDequeueInputBuffer

bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) 
    if (!isExecuting() || (mFlags & kFlagIsAsync)
            || (newRequest && (mFlags & kFlagDequeueInputPending))) 
        PostReplyWithError(replyID, INVALID_OPERATION);
        return true;
     else if (mFlags & kFlagStickyError) 
        PostReplyWithError(replyID, getStickyError());
        return true;
    

    ssize_t index = dequeuePortBuffer(kPortIndexInput);

    if (index < 0) 
        CHECK_EQ(index, -EAGAIN);
        return false;
    

    sp<AMessage> response = new AMessage;
    response->setSize("index", index);
    response->postReply(replyID);

    return true;

  1. 检查状态及flag;
  2. dequeuePortBuffer 从mAvailPortBuffers获取可用输入buffer下标;
  3. 将index通过response返回

2 getInputBuffer

MediaCodec.java 中getInputBuffer,直接看jni的方法

static jobject android_media_MediaCodec_getBuffer(
        JNIEnv *env, jobject thiz, jboolean input, jint index) 

    jobject buffer;
    status_t err = codec->getBuffer(env, input, index, &buffer);
	//获取buffer,并return;
    if (err == OK) 
        return buffer;
    

    return NULL;


status_t JMediaCodec::getBuffer(
        JNIEnv *env, bool input, size_t index, jobject *buf) const 
    sp<MediaCodecBuffer> buffer;

    status_t err =
        input
            ? mCodec->getInputBuffer(index, &buffer)
            : mCodec->getOutputBuffer(index, &buffer);

    if (err != OK) 
        return err;
    

    return createByteBufferFromABuffer(
            env, !input /* readOnly */, input /* clearBuffer */, buffer, buf);

  1. 调用MediaCodec.cpp中getInputBuffer函数,获取id 对应buffer;
  2. 通过MediaCodecBuffer创建ByteBuffer,实现java和native共享内存;
  3. 返回ByteBuffer;

接着看mCodec->getInputBuffer

status_t MediaCodec::getInputBuffer(size_t index, sp<MediaCodecBuffer> *buffer) 
    sp<AMessage> format;
    return getBufferAndFormat(kPortIndexInput, index, buffer, &format);


status_t MediaCodec::getBufferAndFormat(
        size_t portIndex, size_t index,
        sp<MediaCodecBuffer> *buffer, sp<AMessage> *format) 
	//...
    std::vector<BufferInfo> &buffers = mPortBuffers[portIndex];
    if (index >= buffers.size()) 
        return INVALID_OPERATION;
    

    const BufferInfo &info = buffers[index];
    if (!info.mOwnedByClient) 
        return INVALID_OPERATION;
    

    *buffer = info.mData;
    *format = info.mData->format();

    return OK;

  1. mPortBuffers通过index区分输入输出,这里获取所有输入BufferInfo;
  2. 通过下标获取指定的BufferInfo;
  3. 将buffer指针指向BufferInfo的mData.

3 queueInputBuffer

static void android_media_MediaCodec_queueInputBuffer(
        JNIEnv *env,
        jobject thiz,
        jint index,
        jint offset,
        jint size,
        jlong timestampUs,
        jint flags) 
    sp<JMediaCodec> codec = getMediaCodec(env, thiz);

    if (codec == NULL) 
        throwExceptionAsNecessary(env, INVALID_OPERATION);
        return;
    

    AString errorDetailMsg;
    status_t err = codec->queueInputBuffer(
            index, offset, size, timestampUs, flags, &errorDetailMsg);
    throwExceptionAsNecessary(
            env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());


status_t JMediaCodec::queueInputBuffer(
        size_t index,
        size_t offset, size_t size, int64_t timeUs, uint32_t flags,
        AString *errorDetailMsg) 
    return mCodec->queueInputBuffer(
            index, offset, size, timeUs, flags, errorDetailMsg);

jni 方法android_media_MediaCodec_queueInputBuffer获取JMediaCodec,调用其queueInputBuffer,然后调用MediaCodec的queueInputBuffer函数

MediaCodec.cpp queueInputBuffer

status_t MediaCodec::queueInputBuffer(
        size_t index,
        size_t offset,
        size_t size,
        int64_t presentationTimeUs,
        uint32_t flags,
        AString *errorDetailMsg) 
    if (errorDetailMsg != NULL) 
        errorDetailMsg->clear();
    

    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
    msg->setSize("index", index);
    msg->setSize("offset", offset);
    msg->setSize("size", size);
    msg->setInt64("timeUs", presentationTimeUs);
    msg->setInt32("flags", flags);
    msg->setPointer("errorDetailMsg", errorDetailMsg);

    sp<AMessage> response;
    return PostAndAwaitResponse(msg, &response);


void MediaCodec::onMessageReceived(const sp<AMessage> &msg) 
    switch (msg->what()) 
	case kWhatQueueInputBuffer:
		//......
            status_t err = onQueueInputBuffer(msg);
            PostReplyWithError(replyID, err);
            break;
        
    

onMessageReceived接收消息kWhatQueueInputBuffer,调用onQueueInputBuffer函数

status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) 
   //......
    sp<MediaCodecBuffer> buffer = info->mData;
    status_t err = OK;
    if (hasCryptoOrDescrambler()) 
        
     else 
        err = mBufferChannel->queueInputBuffer(buffer);
    

    return err;

调用ACodecBufferChannel queueInputBuffer函数

status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) 
    if (mDealer != nullptr) 
        return -ENOSYS;
    
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) 
        return -ENOENT;
    
    sp<AMessage> msg = mInputBufferFilled->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->post();
    return OK;

暂不清楚ACodecBufferChannel 的用处是什么。。 发送kWhatInputBufferFilled message.

ACodec接收到kWhatInputBufferFilled信息后续

void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg)
    //.......
	 switch (mode) 
        case KEEP_BUFFERS:
        case RESUBMIT_BUFFERS:
            status_t err2 = mCodec->mOMXNode->emptyBuffer(
                        bufferID, OMXBuffer::sPreset, OMX_BUFFERFLAG_EOS, 0, info->mFenceFd);
            
         	break;
        
        case FREE_BUFFERS:
            break;

ACodec通知OMX,写入数据。

output

output 数据获取流程和input流程大致上都相似,这里就不在细说了。

以上是关于Android MultiMedia框架——MediaCodec编码(下)的主要内容,如果未能解决你的问题,请参考以下文章

Android MultiMedia框架——OMXPlugin

Android MultiMedia框架——ACodec加载OMX

Android MultiMedia框架——ALooper AHandler AMessage

Android MultiMedia框架——MediaCodec编码(下)

Android MultiMedia框架——mediaserver启动

Android MultiMedia框架——mediaserver启动