1. 程式人生 > >MPP-解碼示例

MPP-解碼示例

提取出編碼的程式碼後,現在提取解碼例程,供以後需要的時候使用。

完整的解碼程式碼如下,做了一些改動,指令引數全部去除,輸入H264資料,解碼後的資料儲存在本地,官方示例解碼後的資料是YUV_420sp和YUV_422sp格式,這裡將其改為了YUV_420p格式。

#if defined(_WIN32)
#include "vld.h"
#endif

#define MODULE_TAG "mpi_dec_test"

#include <string.h>

#include "utils.h"
#include "rk_mpi.h"
#include "mpp_log.h"
#include "mpp_mem.h"
#include "mpp_env.h"
#include "mpp_time.h"
#include "mpp_common.h"

#include "mpp_frame.h"
#include "mpp_buffer_impl.h"
#include "mpp_frame_impl.h"

#define MPI_DEC_STREAM_SIZE         (SZ_4K)
#define MPI_DEC_LOOP_COUNT          4
#define MAX_FILE_NAME_LENGTH        256
	
typedef struct 
{
    MppCtx          ctx;
    MppApi          *mpi;
    RK_U32          eos;
    char            *buf;

    MppBufferGroup  frm_grp;
    MppBufferGroup  pkt_grp;
    MppPacket       packet;
    size_t          packet_size;
    MppFrame        frame;

    FILE            *fp_input;
    FILE            *fp_output;
    RK_S32          frame_count;
    RK_S32          frame_num;
    size_t          max_usage;
} MpiDecLoopData;

typedef struct 
{
    char            file_input[MAX_FILE_NAME_LENGTH];
    char            file_output[MAX_FILE_NAME_LENGTH];
    MppCodingType   type;
    MppFrameFormat  format;
    RK_U32          width;
    RK_U32          height;
    RK_U32          debug;

    RK_U32          have_input;
    RK_U32          have_output;

    RK_U32          simple;
    RK_S32          timeout;
    RK_S32          frame_num;
    size_t          max_usage;
} MpiDecTestCmd;

size_t mpp_frame_get_buf_size(const MppFrame s)
{
    check_is_mpp_frame((MppFrameImpl*)s); 
    return ((MppFrameImpl*)s)->buf_size; 
}

void dump_mpp_frame_to_file(MppFrame frame, FILE *fp)
{
    RK_U32 width    = 0;
    RK_U32 height   = 0;
    RK_U32 h_stride = 0;
    RK_U32 v_stride = 0;
	
    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);
    buffer   = mpp_frame_get_buffer(frame);
	
    base = (RK_U8 *)mpp_buffer_get_ptr(buffer);
    RK_U32 buf_size = mpp_frame_get_buf_size(frame);
    size_t base_length = mpp_buffer_get_size(buffer);
    mpp_log("base_length = %d\n",base_length);

    RK_U32 i;
    RK_U8 *base_y = base;
    RK_U8 *base_c = base + h_stride * v_stride;
	
	//儲存為YUV420sp格式
    /*for (i = 0; i < height; i++, base_y += h_stride) 
    {
        fwrite(base_y, 1, width, fp);
    }
    for (i = 0; i < height / 2; i++, base_c += h_stride)
    {
        fwrite(base_c, 1, width, fp);
    }*/

	//儲存為YUV420p格式
    for(i = 0; i < height; i++, base_y += h_stride)
    {
        fwrite(base_y, 1, width, fp);
    }
    for(i = 0; i < height * width / 2; i+=2)
	{
	    fwrite((base_c + i), 1, 1, fp);
    }
    for(i = 1; i < height * width / 2; i+=2)
	{
	    fwrite((base_c + i), 1, 1, fp);
    }
}

size_t mpp_buffer_group_usage(MppBufferGroup group)
{
    if (NULL == group) 
    {
        mpp_err_f("input invalid group %p\n", group);
        return MPP_BUFFER_MODE_BUTT;
    }

    MppBufferGroupImpl *p = (MppBufferGroupImpl *)group;
    return p->usage;
}

static int decode_simple(MpiDecLoopData *data)
{
    RK_U32 pkt_done = 0;
    RK_U32 pkt_eos  = 0;
    RK_U32 err_info = 0;
	
    MPP_RET ret = MPP_OK;
    MppCtx ctx  = data->ctx;
    MppApi *mpi = data->mpi;
    char   *buf = data->buf;
	
    MppPacket packet = data->packet;
    MppFrame  frame  = NULL;
    size_t read_size = fread(buf, 1, data->packet_size, data->fp_input);
	
    if (read_size != data->packet_size || feof(data->fp_input)) 
    {
        mpp_log("found last packet\n");
        data->eos = pkt_eos = 1;
    }
    
    mpp_packet_write(packet, 0, buf, read_size);
    mpp_packet_set_pos(packet, buf);
    mpp_packet_set_length(packet, read_size);

    if (pkt_eos)
    {
        mpp_packet_set_eos(packet);
    }

    do {
        if (!pkt_done) 
		{
            ret = mpi->decode_put_packet(ctx, packet);
            if (MPP_OK == ret)
			{
                pkt_done = 1;
			}
        }
		
        do {
            RK_S32 get_frm = 0;
            RK_U32 frm_eos = 0;

            ret = mpi->decode_get_frame(ctx, &frame);
    
            if (frame) 
			{
                if (mpp_frame_get_info_change(frame))
				{
                    RK_U32 width = mpp_frame_get_width(frame);
                    RK_U32 height = mpp_frame_get_height(frame);
                    RK_U32 hor_stride = mpp_frame_get_hor_stride(frame);
                    RK_U32 ver_stride = mpp_frame_get_ver_stride(frame);
                    RK_U32 buf_size = mpp_frame_get_buf_size(frame);								

                    mpp_log("decode_get_frame get info changed found\n");
                    mpp_log("decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d", width, height, hor_stride, ver_stride, buf_size);        

                    if (NULL == data->frm_grp) 
					{                       
                        ret = mpp_buffer_group_get_internal(&data->frm_grp, MPP_BUFFER_TYPE_ION);
                        if (ret) 
						{
                            mpp_err("get mpp buffer group failed ret %d\n", ret);
                            break;
                        }							
                        ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, data->frm_grp);
                        if (ret) 
						{
                            mpp_err("set buffer group failed ret %d\n", ret);
                            break;
                        }
                    } 
					else 
					{                      
                        ret = mpp_buffer_group_clear(data->frm_grp);
                        if (ret)
						{
                            mpp_err("clear buffer group failed ret %d\n", ret);
                            break;
                        }
                    }
                    
                    ret = mpp_buffer_group_limit_config(data->frm_grp, buf_size, 24);
                    if (ret) 
					{
                        mpp_err("limit buffer group failed ret %d\n", ret);
                        break;
                    }                   
                    ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
                    if (ret) 
					{
                        mpp_err("info change ready failed ret %d\n", ret);
                        break;
                    }
                }
				else 
				{
                    err_info = mpp_frame_get_errinfo(frame) | mpp_frame_get_discard(frame);
                    if (err_info) 
					{
                        mpp_log("decoder_get_frame get err info:%d discard:%d.\n", mpp_frame_get_errinfo(frame), mpp_frame_get_discard(frame));
                    }
                    data->frame_count++;
                    mpp_log("decode_get_frame get frame %d\n", data->frame_count);
                    if (data->fp_output && !err_info)
					{
                        dump_mpp_frame_to_file(frame, data->fp_output);
					}
				}
                frm_eos = mpp_frame_get_eos(frame);
                mpp_frame_deinit(&frame);
                frame = NULL;
                get_frm = 1;
			}
           
            if (data->frm_grp) 
			{
                size_t usage = mpp_buffer_group_usage(data->frm_grp);
                if (usage > data->max_usage)
				{
                    data->max_usage = usage;
				}	
            }
         
            if (pkt_eos && pkt_done && !frm_eos) 
			{
                msleep(10);
                continue;
            }
            if (frm_eos) 
			{
                mpp_log("found last frame\n");
                break;
            }
            if (data->frame_num && data->frame_count >= data->frame_num) 
			{
                data->eos = 1;
                break;
            }
            if (get_frm)
			{
                continue;
			}
            break;
        } while (1);

        if (data->frame_num && data->frame_count >= data->frame_num)
		{
            data->eos = 1;
            mpp_log("reach max frame number %d\n", data->frame_count);
            break;
        }
        if (pkt_done)
		{
            break;
		}
    } while (1);

    return ret;
}

int mpi_dec_test_decode(MpiDecTestCmd *cmd)
{
    MPP_RET ret         = MPP_OK;
    size_t file_size    = 0;

    MppCtx ctx          = NULL;
    MppApi *mpi         = NULL;

    MppPacket packet    = NULL;
    MppFrame  frame     = NULL;

    MpiCmd mpi_cmd      = MPP_CMD_BASE;
    MppParam param      = NULL;
    RK_U32 need_split   = 1;

    RK_U32 width        = cmd->width;
    RK_U32 height       = cmd->height;
    MppCodingType type  = cmd->type;

    char *buf           = NULL;
    size_t packet_size  = MPI_DEC_STREAM_SIZE;
    MppBuffer pkt_buf   = NULL;
    MppBuffer frm_buf   = NULL;

    MpiDecLoopData data;

    mpp_log("mpi_dec_test start\n");
    memset(&data, 0, sizeof(data));

    data.fp_input = fopen("test.h264", "rb");
    if (NULL == data.fp_input) 
    {
        mpp_err("failed to open input file %s\n", cmd->file_input);
        goto MPP_TEST_OUT;
    }
	
    fseek(data.fp_input, 0L, SEEK_END);
    file_size = ftell(data.fp_input);
    rewind(data.fp_input);
    mpp_log("input file size %ld\n", file_size);

    data.fp_output = fopen("output.yuv", "w+b");

    if (cmd->simple) 
    {
        buf = mpp_malloc(char, packet_size);

        ret = mpp_packet_init(&packet, buf, packet_size);
    } 
    mpp_log("mpi_dec_test decoder test start w %d h %d type %d\n", width, height, type);

    ret = mpp_create(&ctx, &mpi);

    mpi_cmd = MPP_DEC_SET_PARSER_SPLIT_MODE;
    param = &need_split;
	
    ret = mpi->control(ctx, mpi_cmd, param);
    if (MPP_OK != ret) 
    {
        mpp_err("mpi->control failed\n");
        goto MPP_TEST_OUT;
    }

    ret = mpp_init(ctx, MPP_CTX_DEC, type);
    if (MPP_OK != ret) 
    {
        mpp_err("mpp_init failed\n");
        goto MPP_TEST_OUT;
    }
	
    mpp_log("packet_size = %d\n", packet_size);

    data.ctx            = ctx;
    data.mpi            = mpi;
    data.eos            = 0;
    data.buf            = buf;
    data.packet         = packet;
    data.packet_size    = packet_size;
    data.frame          = frame;
    data.frame_count    = 0;
    data.frame_num      = cmd->frame_num;
    mpp_log("data.packet_size = %d\n", data.packet_size);

    if (cmd->simple) 
    {
        while (!data.eos) 
		{
            decode_simple(&data);
			mpp_log("data.eos = %d\n", data.eos);
        }
    } 

    cmd->max_usage = data.max_usage;
    ret = mpi->reset(ctx);
    if (MPP_OK != ret) 
    {
        mpp_err("mpi->reset failed\n");
        goto MPP_TEST_OUT;
    }

MPP_TEST_OUT:
    if (packet) 
    {
        mpp_packet_deinit(&packet);
        packet = NULL;
    }

    if (frame) 
    {
        mpp_frame_deinit(&frame);
        frame = NULL;
    }

    if (ctx) 
    {
        mpp_destroy(ctx);
        ctx = NULL;
    }

    if (cmd->simple) 
    {
        if (buf) 
		{
            mpp_free(buf);
            buf = NULL;
        }
    } 

    if (data.pkt_grp) 
    {
        mpp_buffer_group_put(data.pkt_grp);
        data.pkt_grp = NULL;
    }

    if (data.frm_grp) 
    {
        mpp_buffer_group_put(data.frm_grp);
        data.frm_grp = NULL;
    }

    if (data.fp_output)
    {
        fclose(data.fp_output);
        data.fp_output = NULL;
    }

    if (data.fp_input) 
    {
        fclose(data.fp_input);
        data.fp_input = NULL;
    }

    return ret;
}

int main(int argc, char **argv)
{
    RK_S32 ret = 0;
    MpiDecTestCmd  cmd_ctx;
    MpiDecTestCmd* cmd = &cmd_ctx;
	
    cmd->simple = 1;
    cmd->type = 7;
    cmd->width = 640;
    cmd->height = 480;
	
    ret = mpi_dec_test_decode(cmd);
	
    return ret;
}

 

具體分析:

1、MPI介面的結構和使用,可以參考上篇內容,解碼與編碼基本一致。

 

2、解碼器介面

decode_put_packet:

MPP_RET decode_put_packet(MppCtx ctx,MppPacket packet)

ctx:MPP解碼器例項;

packet:待輸入的碼流資料;

輸入碼流的方式:分幀與不分幀。裸碼流輸入有兩種,一種是按幀分段的資料,每一個輸入給decode_put_packet函式的資料包都包含完整的一幀,不多也不少。在這種情況下,MPP可以直接按包處理碼流。另一種是按長度讀取資料,無法判斷一個包的資料是否為完整的一幀,需要MPP內部進行分幀處理。在進行這種形式的輸入時,需要在mpp_init前,通過control介面的MPP_DEC_SET_PARSER_SPLIT_MODE命令,開啟need_split標誌。分幀方式效率高,但需要在輸入碼流前進行解析與分幀,不分幀方式使用簡單,效率會受影響。官方解碼示例採用的是不分幀方式,因此上述程式碼也是不分幀方式。

decode_get_frame:

MPP_RET decode_get_frame(MppCtx ctx,MppFrame *frame)

ctx:MPP解碼器例項;

frame:用於MppFrame例項的指標;

完整的解碼過程是上面兩個函式的結合。