Decodificación FFMPEG de H264 a YUV

Decodificación FFMPEG de H264 a YUV

1. Descarga de código fuente

Boge ha abierto esta parte del código en github, descárguelo en github. Enlace de
descarga: https://github.com/wangfengbo2020/ffmped_decode_h264_to_yuv
(Cometí un error aquí, ffmpeg se escribió como ffmped, gracias por su identificación y correcciones posteriores)

Teniendo en cuenta la pequeña cantidad de código fuente, todavía lo
publico aquí: videodecoder.c

#include <stdio.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include "videodecoder.h"


struct AVCodecContext *pAVCodecCtxDecoder = NULL;
struct AVCodec *pAVCodecDecoder;
struct AVPacket mAVPacketDecoder;
struct AVFrame *pAVFrameDecoder = NULL;
struct SwsContext* pImageConvertCtxDecoder = NULL;
struct AVFrame *pFrameYUVDecoder = NULL;

 
int ffmpeg_init_video_decoder(AVCodecParameters *codecParameters)
{
    
    
    if (!codecParameters) {
    
    
        printf("Source codec context is NULL.");   //printf需替换为printf
        return -1;
    }
    ffmpeg_release_video_decoder();
    avcodec_register_all();
    
    pAVCodecDecoder = avcodec_find_decoder(codecParameters->codec_id);
    if (!pAVCodecDecoder) {
    
    
        printf("Can not find codec:%d\n", codecParameters->codec_id);
        return -2;
    }
    
    pAVCodecCtxDecoder = avcodec_alloc_context3(pAVCodecDecoder);
    if (!pAVCodecCtxDecoder) {
    
    
        printf("Failed to alloc codec context.");
        ffmpeg_release_video_decoder();
        return -3;
    }
    
    if (avcodec_parameters_to_context(pAVCodecCtxDecoder, codecParameters) < 0) {
    
    
        printf("Failed to copy avcodec parameters to codec context.");
        ffmpeg_release_video_decoder();
        return -3;
    }
 
    
    if (avcodec_open2(pAVCodecCtxDecoder, pAVCodecDecoder, NULL) < 0){
    
    
        printf("Failed to open h264 decoder");
        ffmpeg_release_video_decoder();
        return -4;
    }
    
    av_init_packet(&mAVPacketDecoder);
    
    pAVFrameDecoder = av_frame_alloc();
    pFrameYUVDecoder = av_frame_alloc();

    return 0;
}
 
int ffmpeg_init_h264_decoder()
{
    
    
    avcodec_register_all();
    AVCodec *pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!pAVCodec){
    
    
        printf("can not find H264 codec\n");
        return -1;
    }
 
    AVCodecContext *pAVCodecCtx = avcodec_alloc_context3(pAVCodec);
    if (pAVCodecCtx == NULL) {
    
    
        printf("Could not alloc video context!\n");
        return -2;
    }
    
    AVCodecParameters *codecParameters = avcodec_parameters_alloc();
    if (avcodec_parameters_from_context(codecParameters, pAVCodecCtx) < 0) {
    
    
        printf("Failed to copy avcodec parameters from codec context.");
        avcodec_parameters_free(&codecParameters);
        avcodec_free_context(&pAVCodecCtx);
        return -3;
    }
    
    int ret = ffmpeg_init_video_decoder(codecParameters);
    avcodec_parameters_free(&codecParameters);
    avcodec_free_context(&pAVCodecCtx);
    
    return ret;
}
 
int ffmpeg_release_video_decoder() {
    
    
    if (pAVCodecCtxDecoder != NULL) {
    
    
        avcodec_free_context(&pAVCodecCtxDecoder);
        pAVCodecCtxDecoder = NULL;
    }
    
    if (pAVFrameDecoder != NULL) {
    
    
        av_packet_unref(&mAVPacketDecoder);
        av_free(pAVFrameDecoder);
        pAVFrameDecoder = NULL;
    }
    
    if (pFrameYUVDecoder) {
    
    
        av_frame_unref(pFrameYUVDecoder);
        av_free(pFrameYUVDecoder);
        pFrameYUVDecoder = NULL;
    }
    
    if (pImageConvertCtxDecoder) {
    
    
        sws_freeContext(pImageConvertCtxDecoder);
    }
    
    av_packet_unref(&mAVPacketDecoder);
    
    return 0;
}
 
int ffmpeg_decode_h264(unsigned char *inbuf, int inbufSize, int *framePara, unsigned char **outRGBBuf, unsigned char *outYUVBuf)
{
    
    
    if (!pAVCodecCtxDecoder || !pAVFrameDecoder || !inbuf || inbufSize<=0 || !framePara || (!outRGBBuf && !outYUVBuf)) {
    
    
        return -1;
    }
    av_frame_unref(pAVFrameDecoder);
    av_frame_unref(pFrameYUVDecoder);
    
    framePara[0] = framePara[1] = 0;
    mAVPacketDecoder.data = inbuf;
    mAVPacketDecoder.size = inbufSize;
    
    int ret = avcodec_send_packet(pAVCodecCtxDecoder, &mAVPacketDecoder);
    if (ret == 0) {
    
    
        ret = avcodec_receive_frame(pAVCodecCtxDecoder, pAVFrameDecoder);
        if (ret == 0) {
    
    
            framePara[0] = pAVFrameDecoder->width;
            framePara[1] = pAVFrameDecoder->height;
            
            if (outYUVBuf) {
    
    
                //*outYUVBuf = (unsigned char *)pAVFrameDecoder->data;
                memcpy(outYUVBuf, pAVFrameDecoder->data, sizeof(pAVFrameDecoder->data));

                framePara[2] = pAVFrameDecoder->linesize[0];
                framePara[3] = pAVFrameDecoder->linesize[1];
                framePara[4] = pAVFrameDecoder->linesize[2];
            } else if (outRGBBuf) {
    
    
                pFrameYUVDecoder->data[0] = outRGBBuf;
                pFrameYUVDecoder->data[1] = NULL;
                pFrameYUVDecoder->data[2] = NULL;
                pFrameYUVDecoder->data[3] = NULL;
                int linesize[4] = {
    
     pAVCodecCtxDecoder->width * 3, pAVCodecCtxDecoder->height * 3, 0, 0 };
                pImageConvertCtxDecoder = sws_getContext(pAVCodecCtxDecoder->width, pAVCodecCtxDecoder->height, 
                                                            AV_PIX_FMT_YUV420P, 
                                                            pAVCodecCtxDecoder->width, 
                                                            pAVCodecCtxDecoder->height, 
                                                            AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, 
                                                            NULL, NULL, NULL);
                sws_scale(pImageConvertCtxDecoder, (const uint8_t* const *) pAVFrameDecoder->data, pAVFrameDecoder->linesize, 0, pAVCodecCtxDecoder->height, pFrameYUVDecoder->data, linesize);
                sws_freeContext(pImageConvertCtxDecoder);
            }
            return 1;
        } else if (ret == AVERROR(EAGAIN)) {
    
    
            return 0;
        } else {
    
    
            return -1;
        }
    }
    
    return 0;
}

videodecoder.h

#include <libavcodec/avcodec.h>
 
/**
 视频流解码器初始化
 @param ctx 解码参数结构体AVCodecParameters
 @see ffmpeg_init_video_decoder,此为解码H264视频流
 @return 初始化成功返回0,否则<0
 */
int ffmpeg_init_video_decoder(AVCodecParameters *ctx);
 
/**
 H264视频流解码器初始化
 @return 初始化成功返回0,否则<0
 */
int ffmpeg_init_h264_decoder(void);


/**
 释放解码器
 @return 初始化成功返回0,否则<0
 */
int ffmpeg_release_video_decoder(void);
 
//return 0:暂未收到解码数据,-1:解码失败,1:解码成功
 
/**
 解码视频流数据
 @param inbuf 视频裸流数据
 @param inbufSize 视频裸流数据大小
 @param framePara 接收帧参数数组:{width,height,linesize1,linesiz2,linesize3}
 @param outRGBBuf 输出RGB数据(若已申请内存)
 @param outYUVBuf 输出YUV数据(若已申请内存)
 @return 成功返回解码数据帧大小,否则<=0
 */
int ffmpeg_decode_h264(unsigned char * inbuf, int inbufSize, int *framePara, unsigned char **outRGBBuf, unsigned char *outYUVBuf);

decode264.c

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#include "videodecoder.h"

/* H264 source data,  I frame */
#define  H264_TEST_FILE  "./Iframe4test.h264"

/* Target yuv data, yuv 420 */
#define  TARGET_YUV_FILE "./target420.yuv"

int g_paramBuf[8] = {
    
    0};
char g_inputData[1*1024*1024] = {
    
    0};
char g_ouputData[4*1024*1024] = {
    
    0};



int read_raw_data(char *path, char *data, int maxBuf)
{
    
    
    FILE *fp = NULL;
    int len = 0;

    fp = fopen(path, "r");

    if (NULL != fp)
    {
    
       
        memset(data, 0x0, maxBuf);
        len = fread(data, 1, maxBuf, fp);
        fclose(fp);
    }    

    return len;
}


int write_raw_data_to_file(char *path, char *data, int dataLen)
{
    
    
    FILE *fp = NULL;
    int len = 0;
    int left = dataLen;
    int cnts = 0;

    if (NULL == data || 0 == dataLen)
    {
    
    
        printf("para error return\n");
        return 0;
    }

    if (access(path, F_OK) == 0)
    {
    
    
        remove(path);
    }

    fp = fopen(path, "ab+");

    if (NULL != fp)
    {
    
       
        while(left)
        {
    
    
            if (left >= 4096)
            {
    
    
                len += fwrite((void *)data+cnts*4096, 1, 4096, fp);
                cnts++;
                left -= 4096;
                sync();
            }            
            else
            {
    
    
                len += fwrite(data+cnts*4096, 1, left, fp);

                break;
            }
        }
        
        fclose(fp);
    }    

    return len;
}


int main()
{
    
    
    int dataLen = 0;
    int index = 150;
    unsigned char *yuvData[10] = {
    
    NULL};

    char *rgbData = NULL;

    //Step 1: init
    int ret = ffmpeg_init_h264_decoder();
    printf("Step 1 finished, ret = %d\n", ret);

    //Step 2: get YUV data
    dataLen = read_raw_data(H264_TEST_FILE, g_inputData, sizeof(g_inputData));
    if (0 == dataLen)
    {
    
    
        printf("Step 2 failed, Read YUV data error\n");
        return;
    }
    printf("Step 2 finished, readDatalen = %d\n", dataLen);

    //Step 3: decode
    ret =  ffmpeg_decode_h264(g_inputData, dataLen, g_paramBuf, NULL, yuvData);
    printf("Step 3 finished, decode ret = %d width = %d height = %d\n", ret, g_paramBuf[0], g_paramBuf[1]);


    //Put the data to the target file according to the format 420.
    memcpy(g_ouputData, (char *)(yuvData[0]), g_paramBuf[0]*g_paramBuf[1]);
    memcpy(g_ouputData + g_paramBuf[0]*g_paramBuf[1] , (char *)(yuvData[1]), (g_paramBuf[0]*g_paramBuf[1])/4);
    memcpy(g_ouputData + ((g_paramBuf[0]*g_paramBuf[1]))*5/4 , (char *)(yuvData[2]), (g_paramBuf[0]*g_paramBuf[1])/4);

    //Step 4: write yuv data to file
    ret = write_raw_data_to_file(TARGET_YUV_FILE, g_ouputData, g_paramBuf[0]*g_paramBuf[1]*3/2);

    printf("Step 4 write finish len = %d\n", ret);

    return 0;
}

build.sh

ffmpeginstall=/home/eric/avtest/ffmpeg_install
x264install=/home/eric/avtest/x264_install
x265install=/home/eric/avtest/x265_install

ffmpeglib=$ffmpeginstall/lib
ffmpeginc=$ffmpeginstall/include
x264lib=$x264install/lib
x265lib=$x265install/lib

gcc -o decode264  decodeH264.c videodecoder.c -L$ffmpeglib  -L$x264lib -L$x265lib -I$ffmpeginc  -lavformat -lavcodec -lavutil -lswscale -lswresample -lx264 -lx265 -lm  -lpthread -ldl -lstdc++

2. Análisis del código fuente

2.1 Introducción al proyecto

Inserte la descripción de la imagen aquí

  • build.sh: compilar script, sin parámetros
  • videodecoder.h: API de transcodificación ffmpeg
  • videodecoder.c: Implementación de la API de transcodificación ffmpeg
  • decodeH264.c: demostración

2.2 Operación del proyecto

(1) Abra y modifique el archivo build.sh.
Inserte la descripción de la imagen aquí
Modifique la ruta anterior. Consulte el blog anterior de Boge para obtener información sobre la compilación de los archivos anteriores.
(2) Agregar el archivo de destino (marco H264 I)
Inserte la descripción de la imagen aquí
puede colocar el marco H264 I probado en el directorio raíz del proyecto y nombrarlo como se indicó anteriormente.

(3) Compilar el proyecto

sh build.sh

Después de que la compilación sea normal, genere decode264 en el directorio raíz
Inserte la descripción de la imagen aquí
(4) Ejecute el archivo ejecutable

./decode264

Inserte la descripción de la imagen aquí
Si se muestra el proceso anterior, la operación es normal y luego se genera el archivo target420.yuv bajo la grabación de seguimiento.

(5) Archivos generados por la prueba

ffplay -f rawvideo -video_size 1920x1080 target420.yuv 

Supongo que te gusta

Origin blog.csdn.net/weixin_42955871/article/details/111935756
Recomendado
Clasificación