RTK_mmp_dec.c 12.6 KB
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


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

#if RTK_MPP_STATUS == VERSION_SWITCH_ON
    #include "rockchip/mpp_common.h"  //这个.h能在mpp的源码中找到
    #include "rockchip/mpp_packet.h"
    #include "rockchip/rk_mpi.h"
    #include "./RTK_mmp_dec.h"

typedef struct {
    //编码器编号,用于选择编码器
    int index;

    // 基础流上下文
    MppCtx ctx;
    MppApi *mpi;

    // 输入/输出
    MppPacket   packet; //用于存放h264,h265数据
    MppFrame    frame;  //用于存放yuv rgb数据

    // 资源分配参数
    unsigned int width;
    unsigned int height;
	unsigned int hor_stride;
    unsigned int ver_stride;
    MppCodingType type;

    // 运行时配置
    unsigned int need_split;  //输入分割设置,整帧输入,这里为0

    MppBufferGroup  frm_grp;
    MppBufferGroup  pkt_grp;
	MppBuffer      frmBuf;
	MppBuffer      pktBuf;
    unsigned char *dataBuf;
    unsigned int  packet_size; //写入的包大小

} MPP_DEC_CONFIG ;

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(void **index, MppCodingType int_type, MppFrameFormat out_format, int width, int height)
{
    MppParam param = NULL;

    //创建一解码器参数
    MPP_DEC_CONFIG *DecConfigInput = NULL;
    int ret;//返回值

    //为编码器参数注册内存
    DecConfigInput = (MPP_DEC_CONFIG *)malloc(sizeof(MPP_DEC_CONFIG));  
    if (DecConfigInput == NULL) {  
        JZSDK_LOG_ERROR("mpp编码器参数内存分配失败");  
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
    }  

    //初始化编码器参数
    // 资源分配参数
    DecConfigInput->height = height;
    DecConfigInput->width = width;

   	DecConfigInput->hor_stride = MPP_ALIGN(width, 16);
    DecConfigInput->ver_stride = MPP_ALIGN(height, 16); 

    DecConfigInput->type = int_type;
    DecConfigInput->packet_size = (3*DecConfigInput->hor_stride*DecConfigInput->ver_stride); //一般这个值够了

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

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


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

        ret = mpp_packet_init(&DecConfigInput->packet, DecConfigInput->dataBuf, DecConfigInput->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(&DecConfigInput->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(&DecConfigInput->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(&DecConfigInput->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(DecConfigInput->frm_grp, &DecConfigInput->frmBuf, DecConfigInput->hor_stride * DecConfigInput->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(DecConfigInput->pkt_grp, &DecConfigInput->pktBuf, DecConfigInput->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(&DecConfigInput->packet, DecConfigInput->pktBuf);
        DecConfigInput->dataBuf = mpp_buffer_get_ptr(DecConfigInput->pktBuf);

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

    /**
     *      配置解码器
     *      - 解码文件需要 split 模式
     *      - 设置非阻塞模式,0非阻塞(默认),-1阻塞,+val 超时(ms)
     */
    param = &(DecConfigInput->need_split);
    ret = DecConfigInput->mpi->control(DecConfigInput->ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, param);  
    if (ret != MPP_OK) {  
        mpp_destroy(DecConfigInput->ctx); // 销毁创建的编码器  
        free(DecConfigInput);  
        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(DecConfigInput->ctx, MPP_CTX_DEC, DecConfigInput->type);  
    if (ret != MPP_OK) {  
        mpp_destroy(DecConfigInput->ctx);  
        free(DecConfigInput);  
        JZSDK_LOG_ERROR("mpp解码初始化失败");  
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;  
    }     
    else
    {
        printf("mpp解码初始化成功\n");
    } 

    //输出的码流格式
	param = &out_format;
    ret = DecConfigInput->mpi->control(DecConfigInput->ctx, MPP_DEC_SET_OUTPUT_FORMAT, param);
    if (ret == MPP_OK)
    {
        printf("输出格式正确\n");
    }
    else
    {
        printf("输出格式错误\n");
    }
    

    //把编码器地址传递回去
    *index = (void *)DecConfigInput;
    printf("mmp解码器初始化完毕\n");

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}



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

    //1、获取编码器参数
    MPP_DEC_CONFIG *DecConfigInput = (MPP_DEC_CONFIG *)*index;  

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

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

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

	ret = DecConfigInput->mpi->dequeue(DecConfigInput->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, DecConfigInput->packet);
    mpp_task_meta_set_frame (task, KEY_OUTPUT_FRAME,  DecConfigInput->frame);

	ret = DecConfigInput->mpi->enqueue(DecConfigInput->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 = DecConfigInput->mpi->poll(DecConfigInput->ctx, MPP_PORT_OUTPUT, MPP_POLL_BLOCK);
    if (ret) 
	{
        JZSDK_LOG_ERROR("mpp output poll failed");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

	ret = DecConfigInput->mpi->dequeue(DecConfigInput->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, &DecConfigInput->frame);

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

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

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

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

	return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

#endif