• Android codec2 视频框架 之应用


    应用流程

    在这里插入图片描述

    外部主动获取输入和输出buffer

    解码的调用流程,以android原生的一个bin来说明
    android 原生代码位置: frameworks/av/cmds/stagefright/codec.cpp
    frameworks/av/cmds/stagefright/SimplePlayer.cpp

    编译出来的是codec的bin,使用bin播放mp4, 可以使用如下的命令进行调试。

    screenrecord /sdcard/test.mp4
    codec -pSR /sdcard/test.mp4
    
    • 1
    • 2
    • prepare: 跟编码同样的流程

      createByType: 根据MIME创建解码器。

      configure:配置视频宽高、fromat信息到解码器。

      start:启动解码器进行解码。
      获取输入和输出的buffer 队列:先dequeue有效的输入buffer,然后将extract到的csd数据放入到这块buffer 中, 将这块buffer在queue到codec中(作用是 将解码所需要的额外的数据 给到解码器,类似于ffmpeg中的extradata)。

    • start: dequeue所有可用的input 和output buffer。然后解析封装读取数据 将数据拷贝到dequeue出来的input buffer 中, 拷贝完成后 queue到解码器, 同时从解码器中尽可能多的取输出的buffer, 对每个输出buffer 获取显示的时间戳。跟现有的时间比较,到显示时间的范围内则调用renderOutputBufferAndRelease将buffer 显示出去。

            state->mCodec = MediaCodec::CreateByType(
                    looper, mime.c_str(), false /* encoder */);
            CHECK(state->mCodec != NULL);
            err = state->mCodec->configure(
                    format, isVideo ? surface : NULL,
                    NULL /* crypto */,
                    0 /* flags */);
    
            CHECK_EQ((status_t)OK, codec->start());
            CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers));
            CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers))
    
             err = state->mCodec->queueInputBuffer(
                                index,
                                0 /* offset */,
                                buffer->size(),
                                timeUs,
                                bufferFlags);
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    外部设置回调

    大部分应用都不会主动去获取解码输入和输出buffer。 类似NuPlayer是通过外部设置回调。MediaCodec内部有输出或者输入buffer的时候 回调到应用层。

    NuPlayerDecoder.cpp
    void NuPlayer::Decoder::onConfigure(const sp &format) {
        sp reply = new AMessage(kWhatCodecNotify, this);
        mCodec->setCallback(reply);
    }
    void NuPlayer::Decoder::onMessageReceived(const sp &msg) {
        switch (msg->what()) {
            case kWhatCodecNotify:
            {
                switch (cbID) {
                    case MediaCodec::CB_INPUT_AVAILABLE:
                    {
                        int32_t index;
                        CHECK(msg->findInt32("index", &index));
                        handleAnInputBuffer(index);
                        break;
                    }
    
                    case MediaCodec::CB_OUTPUT_AVAILABLE:
                    {
                        CHECK(msg->findInt32("index", &index));
                        CHECK(msg->findSize("offset", &offset));
       
                        handleAnOutputBuffer(index, offset, size, timeUs, flags);
                        break;
                    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    内部流程

    • create 和configure

    create:mediacodeclist中遍历所有的解码器 然后将所有match的解码器返回。 这个里面放的是component名字如c2.android.vorbis.decoder等等。 找到之后 就用这个名字来init mediacodec。

    configure: 将format 和 surface 配置到解码器当中,。

    • format的configure

    其中format可以存储很多信息 比如基本的图像宽高,解码器的类型等等。
    configure将编解码的一些关键信息配置到编解码器 比如宽高、MIME、bitrate、frameRate 等等。

    • surface 的configure surface
    1. 在设置surface的流程中操作为nativeWindowConnect、设置一个surface的generaration,然后又重新connect、disconnect。
    2. 调用codec的setSurface, codec直接调用的channel 的setSurface。
    3. channel的setSurface不会调用mComponent->setOutputSurface, 因为初始化的时候对应的outputPoolId为0.
    • mediacodec 的start()
    1. 调用component 的start, 调用对应的SimpleC2Component的start(), 对应的每个解码器实现的onInit(),。
    2. mChannel->start, 调用channel的start
      这里有一个问题,如何决定要申请的buffer 是graphic 还是liner类型的。
      这在simpleC2interface中已经定义,会按照音视频的类型进行决定,视频 grahic,音频liner
        addParameter(
                DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
                .withConstValue(new C2StreamBufferTypeSetting::input(
                        0u, isEncoder ? rawBufferType : codedBufferType))
                .build());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.1 在Channel的start 中会根据是编码输入的surface 还是解码输出的surface 配置申请buffer的个数, 以及调用Component->createBlockPool,创建graphic的pool。会返回pool的allocatorID 和outputPoolId
    对应于在底层的实现是在C2AllocatedGraphic。 同时为生成的pools->outputPoolId生成poolIdsTuning并将这个Tuning 设置到componet中。

    2.2 调用mComponent->setOutputSurface: 设置surface到解码模块,使得后续可以从对应的nativewindow中申请buffer。

  • 相关阅读:
    《游戏系统设计十五》游戏房间服的设计
    每日一题:地下城游戏
    Spring Boot——yml和properties详解
    Java进行多线程编程?(lambda表达式~)
    基于python的神经网络在图像识别和自然语言处理上的应用
    采用python中的opencv2的库来运用机器视觉移动物体
    LVS集群
    现有库存(on-hand inventory),库存水平(inventory level),库存位置(inventory position)
    Evil.js(罪恶的) —— 代码
    PPT的使用技巧(一):对齐、文字填充、柱状图填充
  • 原文地址:https://blog.csdn.net/H2008066215019910120/article/details/134254640