RTK_mmp_dec.c 10.7 KB
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


#include "JZsdkLib.h"
#include "./RTK_mmp_dec.h"

#ifdef RTK_MPP_STATUS_ON


void dump_frame(MppFrame frame, FILE *out_fp)
{
    printf("dump_frame_to_file\n");

    RK_U32 width    = 0;
    RK_U32 height   = 0;
    RK_U32 h_stride = 0;
    RK_U32 v_stride = 0;
    MppFrameFormat fmt  = MPP_FMT_YUV420SP;
    MppBuffer buffer    = NULL;
    RK_U8 *base = NULL;

    width    = mpp_frame_get_width(frame);
    height   = mpp_frame_get_height(frame);
    h_stride = mpp_frame_get_hor_stride(frame);
    v_stride = mpp_frame_get_ver_stride(frame);
    fmt      = mpp_frame_get_fmt(frame);
    buffer   = mpp_frame_get_buffer(frame);

    RK_U32 buf_size = mpp_frame_get_buf_size(frame);
    printf("w x h: %dx%d hor_stride:%d ver_stride:%d buf_size:%d\n",
           width, height, h_stride, v_stride, buf_size);
           
    if (NULL == buffer) {
        printf("buffer is null\n");
        return ;
    }

    base = (RK_U8 *)mpp_buffer_get_ptr(buffer);

    // MPP_FMT_YUV420SP
    if (fmt != MPP_FMT_YUV420SP) {
        printf("fmt %d not supported\n", fmt);
        return;
    }

    RK_U32 i;
    RK_U8 *base_y = base;
    RK_U8 *base_c = base + h_stride * v_stride;

    for (i = 0; i < height; i++, base_y += h_stride) {
        fwrite(base_y, 1, width, out_fp);
    }
    for (i = 0; i < height / 2; i++, base_c += h_stride) {
        fwrite(base_c, 1, width, out_fp);
    }

}

// void dump_frame_to_file(MppCtx ctx, MppApi *mpi, MppFrame frame, FILE *out_fp)
// {
//     printf("decode_and_dump_to_file\n");

//     MPP_RET ret;

//     if (mpp_frame_get_info_change(frame)) {
//         printf("mpp_frame_get_info_change\n");
//         /**
//          * 第一次解码会到这个分支,需要为解码器设置缓冲区.
//          * 解码器缓冲区支持3种模式。参考【图像内存分配以及交互模式】Rockchip_Developer_Guide_MPP_CN.pdf
//          * 这里使用纯内部模式。
//          */
//         ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
//         if (ret) {
//             printf("mpp_frame_get_info_change mpi->control error"
//                     "MPP_DEC_SET_INFO_CHANGE_READY %d\n", ret);
//         }
//         return;
//     }

//     RK_U32 err_info = mpp_frame_get_errinfo(frame);
//     RK_U32 discard = mpp_frame_get_discard(frame);    
//     printf("err_info: %u discard: %u\n", err_info, discard);

//     if (err_info) {
//         return;
//     }
        
//     // save
//     dump_frame(frame, out_fp);
//     return;
// }


//rtk解码器初始化
//输入内容,编码器参数的地址
T_JZsdkReturnCode RTK_mmp_dec_Init(T_rtk_mmp_dec_info *dec_info)
{
    MPP_RET ret;
    MppParam param = NULL;

    //初始化编码器参数
    // 资源分配参数
    dec_info->packet_size = (3*dec_info->hor_stride*dec_info->ver_stride); //一般这个值够了

    // 运行时配置
    dec_info->need_split = 0;

    //如果是其他类型,dec_tpye为1 mjpeg则为0
    int VideoType =  (dec_info->Dec_type != MPP_VIDEO_CodingMJPEG) ? (1) : (0);

    // /*************************************
    // //创建输入输出的数组  
    // ************************************/
    if (VideoType == 1)  //普通的视频类型
    {
        dec_info->dataBuf = (unsigned char*)malloc(dec_info->packet_size);
        if(dec_info->dataBuf == NULL)
        {
            JZSDK_LOG_ERROR("摄像头的视频回复帧内存注册错误");
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }

        ret = mpp_packet_init(&dec_info->packet, dec_info->dataBuf, dec_info->packet_size);
        if (ret) {
            JZSDK_LOG_ERROR("mpp_packet_init failed");
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }
    }
    else //mjpeg下的视频类型
    {
        ret = mpp_buffer_group_get_internal(&dec_info->frm_grp, MPP_BUFFER_TYPE_ION);
        if (ret) {
            JZSDK_LOG_ERROR("failed to get buffer group for input frame ret %d", ret);
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }

        ret = mpp_buffer_group_get_internal(&dec_info->pkt_grp, MPP_BUFFER_TYPE_ION);
        if (ret) {
            JZSDK_LOG_ERROR("failed to get buffer group for output packet ret %d", ret);
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }

        ret = mpp_frame_init(&dec_info->frame); /* output frame */
        if (MPP_OK != ret) 
        {
            JZSDK_LOG_ERROR("mpp_frame_init failed");
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }

        /*
         * NOTE: For jpeg could have YUV420 and YUV422 the buffer should be
         * larger for output. And the buffer dimension should align to 16.
         * YUV420 buffer is 3/2 times of w*h.
         * YUV422 buffer is 2 times of w*h.
         * So create larger buffer with 2 times w*h.
         */
        ret = mpp_buffer_get(dec_info->frm_grp, &dec_info->frmBuf, dec_info->hor_stride * dec_info->ver_stride * 4);
        if (ret) {
            JZSDK_LOG_ERROR("failed to get buffer for input frame ret %d", ret);
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }

        // NOTE: for mjpeg decoding send the whole file

        ret = mpp_buffer_get(dec_info->pkt_grp, &dec_info->pktBuf, dec_info->packet_size);
        if (ret) {
            JZSDK_LOG_ERROR("failed to get buffer for input frame ret %d", ret);
            return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
        }

        mpp_packet_init_with_buffer(&dec_info->packet, dec_info->pktBuf);
        dec_info->dataBuf = mpp_buffer_get_ptr(dec_info->pktBuf);

        mpp_frame_set_buffer(dec_info->frame, dec_info->frmBuf);
    }
    
    printf("创建输入输出的数组初始化完毕\n");
    
    // 创建解码器  
    ret = mpp_create(&dec_info->ctx, &dec_info->mpi);  
    if (ret != MPP_OK) 
    {  
        free(dec_info);  
        JZSDK_LOG_ERROR("解码器创建失败");  
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
    }  
    else
    {
        printf("解码器创建成功\n");
    }

    /**
     *      配置解码器
     *      - 解码文件需要 split 模式
     *      - 设置非阻塞模式,0非阻塞(默认),-1阻塞,+val 超时(ms)
     */
    param = &(dec_info->need_split);
    ret = dec_info->mpi->control(dec_info->ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, param);  
    if (ret != MPP_OK) {  
        mpp_destroy(dec_info->ctx); // 销毁创建的编码器  
        free(dec_info);  
        JZSDK_LOG_ERROR("mpi->control error MPP_DEC_SET_PARSER_SPLIT_MODE");  
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
    }  
    else
    {
        printf("解码器配置创建成功\n");
    }

    // //设置超时
    // // NOTE: timeout value please refer to MppPollType definition
    // //  0   - non-block call (default)
    // // -1   - block call
    // // +val - timeout value in ms
    // if (timeout) {
    //     param = &timeout;
    //     ret = mpi->control(ctx, MPP_SET_OUTPUT_TIMEOUT, param);
    //     if (MPP_OK != ret) {
    //         mpp_err("Failed to set output timeout %d ret %d\n", timeout, ret);
    //         goto MPP_TEST_OUT;
    //     }
    // }
    
    //初始化mpp编解码器
    //参数1 mmp初始上下文
    //参数2 DEC解码 ENC编码
    //参数3 编解码的格式
        /*MPP_VIDEO_CodingAVC :   H.264
        MPP_VIDEO_CodingHEVC:   H.265
        MPP_VIDEO_CodingVP8 :   VP8
        MPP_VIDEO_CodingVP9 :   VP9
        MPP_VIDEO_CodingMJPEG : MJPEG */
    ret = mpp_init(dec_info->ctx, MPP_CTX_DEC, dec_info->Dec_type);  
    if (ret != MPP_OK) {  
        mpp_destroy(dec_info->ctx);  
        free(dec_info);  
        JZSDK_LOG_ERROR("mpp解码初始化失败");  
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
    }     
    else
    {
        printf("mpp解码初始化成功\n");
    } 

    //输出的码流格式
	//param = &out_format;
    //也不知道为什么只能设420sp没法设420p
    MppFrameFormat  format = dec_info->out_format;
    ret = dec_info->mpi->control(dec_info->ctx, MPP_DEC_SET_OUTPUT_FORMAT, &format);
    if (ret == MPP_OK)
    {
        printf("输出格式正确\n");
    }
    else
    {
        printf("输出格式错误\n");
    }
    

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}



T_JZsdkReturnCode RTK_mmp_dec_input(T_rtk_mmp_dec_info *dec_info ,unsigned char *input_data, unsigned int input_data_len, MppFrame *out_put_frame)
{
    int ret; //返回值
    int pktEos = 0;
	MppTask task = NULL;

    //2、将解码的数据放入划好的地址
    memset(dec_info->dataBuf,0,dec_info->packet_size);
    memcpy(dec_info->dataBuf , input_data ,input_data_len );
    
	mpp_packet_set_pos(dec_info->packet, dec_info->dataBuf); //设置packet的 有效数据 的开始位置
    mpp_packet_set_length(dec_info->packet, input_data_len);  //设置packet的 有效数据 的长度

	if(pktEos) //结束标志,这边估计是不会用了
	{
		mpp_packet_set_eos(dec_info->packet);
	}

    ret = dec_info->mpi->poll(dec_info->ctx, MPP_PORT_INPUT, MPP_POLL_BLOCK);
    if (ret) 
	{
        JZSDK_LOG_ERROR("mpp input poll failed");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

	ret = dec_info->mpi->dequeue(dec_info->ctx, MPP_PORT_INPUT, &task);  /* input queue */
    if (ret) 
	{
        JZSDK_LOG_ERROR("mpp task input dequeue failed");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    //mpp_assert(task);
	mpp_task_meta_set_packet(task, KEY_INPUT_PACKET, dec_info->packet);
    mpp_task_meta_set_frame (task, KEY_OUTPUT_FRAME,  dec_info->frame);

	ret = dec_info->mpi->enqueue(dec_info->ctx, MPP_PORT_INPUT, task);  /* input queue */
    if (ret) 
	{
        JZSDK_LOG_ERROR("mpp task input enqueue failed");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

	/* poll and wait here */
    ret = dec_info->mpi->poll(dec_info->ctx, MPP_PORT_OUTPUT, MPP_POLL_BLOCK);
    if (ret) 
	{
        JZSDK_LOG_ERROR("mpp output poll failed");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

	ret = dec_info->mpi->dequeue(dec_info->ctx, MPP_PORT_OUTPUT, &task); /* output queue */
    if (ret) 
	{
        JZSDK_LOG_ERROR("mpp task output dequeue failed");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    //mpp_assert(task);

	if (task)
	{
        mpp_task_meta_get_frame(task, KEY_OUTPUT_FRAME, &dec_info->frame);

		if (dec_info->frame) 
		{
            //把解码出来的数据传递回去
            *out_put_frame = dec_info->frame;

            //printf("解码成功\n");

            if (mpp_frame_get_eos(dec_info->frame))
            {
				printf("found eos frame\n");
			}
        }

		ret = dec_info->mpi->enqueue(dec_info->ctx, MPP_PORT_OUTPUT, task);
        if (ret)
        {
			JZSDK_LOG_ERROR("mpp task output enqueue failed\n");
		}
	}

	return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

#endif