【音视频开发(二)】---MediaCodec NDK编码

初始化编码器:


void VideoEncoder::Init(int width, int height, int buffNumber)
{
    DM_NATIVE_PRINT("VideoEncoder::Init Start... %dx%d", width, height);
    imageW = width;
    imageH = height;
    mMaxBufferNumber = buffNumber;

    mInputDataList = new DmSyncQueue<InputData_t>(buffNumber);
    mOutputDataList = new DmSyncQueue<InputData_t>(buffNumber);

    const char* mine = "video/avc";
    mMediaCodec =  AMediaCodec_createEncoderByType(mine);

    AMediaFormat* videoFormat = AMediaFormat_new();
    AMediaFormat_setString(videoFormat, "mime", mine);
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_WIDTH, width); // 视频宽度
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_HEIGHT, height); // 视频高度
   
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, 2135033992);//COLOR_FormatYUV422Flexible
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, height*width*4);//帧传输速率
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_FRAME_RATE, 25);
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 5);

    media_status_t status = AMediaCodec_configure(mMediaCodec, videoFormat, NULL, NULL, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
    if (status != AMEDIA_OK)
    {
        AMediaCodec_delete(mMediaCodec);
        mMediaCodec = NULL;
        opened = false;
    } else {
        status = AMediaCodec_start(mMediaCodec);
        if (status != AMEDIA_OK)
        {
            DM_NATIVE_ERR_PRINT("AMediaCodec_start: Could not start decoder.");
        }
        run = true;
        mLoopThread = new std::thread(&VideoEncoder::Loop, this);
        opened = true;
    }
    AMediaFormat_delete(videoFormat);
    DM_NATIVE_PRINT("VideoEncoder::Init Done");
}

编码器数据编码:

编码器线程:

void VideoEncoder::Loop()
{
    while(run) {
        Encode();
    }
}

void VideoEncoder::Encode()
{
#ifndef WIN32
    InputData_t input;
  
    if (mMediaCodec == NULL)
    {
        return;
    }
    if(mInputStreamDataList == NULL) {
        if(!mInputDataList->Take(input)) return;
    } else {
        if(!mInputStreamDataList->Take(input)) return;
    }

    ssize_t oBufidx = -1;
    size_t bufsize = 0;
    AMediaCodecBufferInfo info;

    uint8_t *buf = NULL;
    ssize_t iBufidx = -1;

    bool endofstream = false;
    /*First put our H264 bitstream into the decoder*/
    do
    {
        iBufidx = AMediaCodec_dequeueInputBuffer(mMediaCodec, TIMEOUT_US);
        //DM_NATIVE_DEBUG_PRINT("decoder iBufidx %d", iBufidx);
        if (iBufidx >= 0)
        {
            buf = AMediaCodec_getInputBuffer(mMediaCodec, iBufidx, &bufsize);
           
            if (buf)
            {
                bufsize = input.size;
                memcpy(buf, input.dataPtr, bufsize);
                //DM_NATIVE_DEBUG_PRINT("Decoder iBufidx %d size=%d %02X%02X%02X%02X%02X %p", iBufidx, bufsize, buf[0],buf[1],buf[2],buf[3],buf[4], buf);
            }
            // DM_NATIVE_DEBUG_PRINT("Decoder iBufidx %d size=%d %02X%02X%02X%02X%02X %p", iBufidx, bufsize, input.dataPtr[0],input.dataPtr[1],input.dataPtr[2],input.dataPtr[3],input.dataPtr[4], buf);
            int flag = 0;
            if(input.flag) {
                flag =  AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
                endofstream = true;
                DM_NATIVE_DEBUG_PRINT("Encode AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM %d", input.flag);
            }
            AMediaCodec_queueInputBuffer(mMediaCodec, iBufidx, 0, bufsize, GetTimestampUs(), flag);
        
            CALLBACK_InputDataFinished(input);
            if (input.needRealeaseMemory) {
                SAFE_DELETE_ARRAY(input.dataPtr);
            }

        }
        else if (iBufidx == -1)
        {
            break;
        }
        if (!mInputDataList->IsEmpty()) {
            if(!mInputDataList->Take(input)) break;
        } else {
            break;
        }
    }while (true);

    /*secondly try to get decoded frames from the decoder, this is performed every tick*/
    oBufidx = AMediaCodec_dequeueOutputBuffer(mMediaCodec, &info, 500);

    //DM_NATIVE_DEBUG_PRINT("Decoder oBufidx %d", oBufidx);
    while (oBufidx >= 0)
    {
        AMediaFormat *format;
        int color = 0;

        uint8_t *buf = AMediaCodec_getOutputBuffer(mMediaCodec, oBufidx, &bufsize);

        if (buf == NULL)
        {
            DM_NATIVE_ERR_PRINT("Encode: AMediaCodec_getOutputBuffer() returned NULL");
            //continue;
        }
        else
        {
            /*H264编码首帧,内部存有SPS和PPS信息,需要保留起来,然后,加在每个H264关键帧的前面。
            * 其中有个字段是flags,它有几种常量情况。
                flags = 4;End of Stream。
                flags = 2;首帧信息帧。
                flags = 1;关键帧。
                flags = 0;普通帧。*/
            if(info.flags == 2){//首帧,记录信息
                videoCfgData = new uint8_t [info.size];
                videoCfgDataSize = info.size;
                memcpy(videoCfgData, buf, videoCfgDataSize);
                if (m_hMP4File) {
                    Mp4v2Util::WriteConfigData(m_hMP4File, m_videoTrackId, videoCfgData, videoCfgDataSize, imageW, imageH, mFps);
                }
            }else if(info.flags == 1){ //关键帧
                m_videoOutputFrameId++;
                if(_pf != NULL) {
                    DM_NATIVE_DEBUG_PRINT("WriteH264Data: videoCfgDataSize size=%d\n", videoCfgDataSize);
                    DM_NATIVE_DEBUG_PRINT("WriteH264Data: I-frame data size=%d\n", info.size);
                    fwrite(videoCfgData, 1, videoCfgDataSize, _pf);
                    fwrite(buf, 1, info.size, _pf);
                }
                if (m_hMP4File) {
                    Mp4v2Util::WriteH264Data(m_hMP4File, m_videoTrackId, buf, info.size);
                }
            } else {
                m_videoOutputFrameId++;
                if(_pf != NULL) fwrite(buf, 1, info.size, _pf);
                if (m_hMP4File) {
                    Mp4v2Util::WriteH264Data(m_hMP4File, m_videoTrackId, buf, info.size);
                }
                DM_NATIVE_DEBUG_PRINT("--- [%d] WriteH264Data: data size=%d\n", m_videoOutputFrameId, info.size);
            }

        }
        AMediaCodec_releaseOutputBuffer(mMediaCodec, oBufidx, false);

        oBufidx = AMediaCodec_dequeueOutputBuffer(mMediaCodec, &info, 500);
        //DM_NATIVE_DEBUG_PRINT("Decoder oBufidx- %d", oBufidx);
    }
    if (endofstream) {
        if(_pf != NULL) fclose(_pf);
        _pf = NULL;
        Mp4v2Util::CloseOutputFile(m_hMP4File);
        m_videoTrackId = MP4_INVALID_TRACK_ID;
    }

    if (oBufidx == AMEDIA_ERROR_UNKNOWN)
    {
        DM_NATIVE_DEBUG_PRINT("Encode: AMediaCodec_dequeueOutputBuffer() had an exception");
    }
#endif // !WIN32
}

上面Mp4v2Tuile是自己定义的libmp4v2的封装类。此处主要是在需要写成mp4文件时会调用。如果不需要写成mp4文件则可能会写入H264裸流文件,即_pf句柄。

int VideoEncoder::Open(const char *name, int fps) {
    int len = strlen(name);
    if (len > 4) {
        if (strncmp(&name[len-4], ".mp4", 4) == 0) {
            //.mp4格式
            if(0 == Mp4v2Util::OpenOutputFile(m_hMP4File, m_videoTrackId, name, imageW, imageH)) {
                DM_NATIVE_DEBUG_PRINT("Ready write to mp4 %s\n", name);
                mFps = fps;
                m_videoOutputFrameId = 0;
                return 0;
            }
        }
    }

    _pf = fopen(name, "wb+");
    if(_pf == NULL) {
        DM_NATIVE_ERR_PRINT("Error Cannot open %s\n", name);
        return -1;
    }
    return 0;
}

头文件:


#include "Flow.h"
#include <media/NdkMediaCodec.h>
#include <vector>
#include <thread>
#include "DmSyncQueue.h"
#include "InputData.h"
#include "Mp4v2Util.h"
using namespace  std;

class VideoEncoder : public Flow {

public:
    VideoEncoder();
    ~VideoEncoder();

    void Init(int width, int height, int buffNumber=10);
    void UnInit();
    void PushData(const unsigned char *data, int size, int copy, int flag=0);
    int Open(const char *name, int fps=25) ;
    //vector <unsigned char *> mOutputNV12List;
    bool opened = false;
    int  mMediaCnt = 0;
private:
    AMediaCodec *mMediaCodec;
    void Loop();
    void Encode();
    int mYUVSize;

    uint8_t *videoCfgData = nullptr;
    int videoCfgDataSize = 0;

    FILE *_pf = nullptr;

    MP4FileHandle m_hMP4File = NULL;
    MP4TrackId m_videoTrackId = MP4_INVALID_TRACK_ID;
    int mFps;
    int m_videoOutputFrameId;
};

注意:

Android mediacodec编码默认支持的profile为baseline版本,但是发现windows media player貌似无法播放此类的mp4文件,但是ffplay或者vlc是可以播放的。它应该仅支持播放High的profile格式,因此查看android设备支持哪些profile?可以通过以下java代码进行读取:

        int numCodecs = MediaCodecList.getCodecCount();
            for (int i = 0; i < numCodecs; i++) {
                // 编解码器相关性信息存储在MediaCodecInfo中
                MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
                // 判断是否为编码器
                if (!codecInfo.isEncoder()) {
                    continue;
                }

                // 获取编码器支持的MIME类型,并进行匹配
                String[] types = codecInfo.getSupportedTypes();
                boolean typ = false;
                for (int j = 0; j < types.length; j++) {
                    Log.e(TAG, "mime: "+types[j]);
                    MediaCodecInfo.CodecCapabilities b = codecInfo.getCapabilitiesForType(types[j]);
                    MediaCodecInfo.EncoderCapabilities e =  b.getEncoderCapabilities();
                    MediaCodecInfo.CodecProfileLevel[]  levels = b.profileLevels;
                    for(int n = 0; n < levels.length; n++) {
                        Log.e(TAG, "levels: "+levels[n].level + "profile: "+levels[n].profile);
                    }
                    MediaFormat format = b.getDefaultFormat();
                    Log.e(TAG, "codecInfo: "+format.toString());
                    if("video/avc".equalsIgnoreCase(types[0])) typ = true;

                }
                if (!typ) continue;



            }

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值