03 ffmpeg 解码SDK调用 H264转YUV420

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了03 ffmpeg 解码SDK调用 H264转YUV420相关的知识,希望对你有一定的参考价值。


制作一个H264文件

[[email protected] ~]# cd /home/
[[email protected] home]# wget http://sh.yinyuetai.com/uploads/videos/common/0E3E014EBF3448D901AF3519C4A1D4E0.mp4
[[email protected] home]# /ffmpeg -t 20 -i 0E3E014EBF3448D901AF3519C4A1D4E0.mp4 -c copy 1920_1080.h264


同样的Makefile文件:

[[email protected] 03]# cat makefile 
FLAGS = -Wall -g
INCLUDEPATH = -I /home/ffmpeg_dev/include/
LIBPATH = -L  /home/ffmpeg_dev/lib/
LIBS= -l avcodec          -l pthread          -l avutil           -l m                -l dl               -l swresample 
exe=yuv2h264

$(exe):
	gcc main.c  ${FLAGS}  ${INCLUDEPATH} ${LIBPATH} ${LIBS} -o [email protected]

clean:
	rm -rf ${exe}

[[email protected] 03]#


同样的自动编译运行脚本:

[[email protected] 03]# cat auto.sh 
make clean
make
./yuv2h264 in.h264  out.yuv
[[email protected] 03]#



这是一个可以正常运行的H264转YUV的程序:

[[email protected] 03_01]# cat main.c 
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <sys/time.h>

//#define INBUF_SIZE 40960000
#define INBUF_SIZE 409600000

int main(int argc, char** argv)
{

    FILE *pFileH264 = NULL;
    FILE *pFileYUV = NULL;

    pFileH264 = fopen(argv[1],"rb");
    if( NULL == pFileH264 )
    {
        fprintf(stderr,"Could not open file \n");
        exit(1);
    }

    pFileYUV = fopen(argv[2],"wb");
    if(NULL == pFileYUV)
    {
        fprintf(stderr,"Open test.yuv fail\n");
        exit(1);
    }


    avcodec_register_all();
    AVCodec *codec;
    AVCodecContext *c = NULL;
    AVFrame *frame;
    AVCodecParserContext *avParserContext;


    int frame_count = 0;
    //unsigned char buffer[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    unsigned char * inbuf = (unsigned char*)malloc(INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
    //unsigned char *inbuf = buffer;
    int got_frame;
    int read_size;

    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if( NULL == codec )
    {
        fprintf(stderr,"Codec not found\n");
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    if(NULL == c)
    {
        fprintf(stderr,"Could not allocate video codec context\n");
        exit(1);
    }

    //初始化解码器所需要的参数,其实不设置也是可以正常解码的,因为264流中有对应的pps,sps相关结构
    //里面包含解码所需要的相关信息
    c->width = 640;
    c->height = 360;
    c->bit_rate = 1000;
    c->time_base.num = 1;
    c->time_base.den = 25;
    c->codec_id = AV_CODEC_ID_H264;
    c->codec_type = AVMEDIA_TYPE_VIDEO;

    avParserContext = av_parser_init(AV_CODEC_ID_H264);
    if( NULL == avParserContext)
    {
        fprintf(stderr,"Could not init avParserContext\n");
        exit(1);
    }

    if(avcodec_open2(c,codec, NULL) < 0)
    {
        fprintf(stderr,"Could not open codec\n");
        exit(1);
    }

    frame = av_frame_alloc();
    if(NULL == frame)
    {
        fprintf(stderr,"Could not allocate video frame\n");
        exit(1);
    }


    while(1)
    {
        //解码还有问题,因为avpkt不是一帧数据,而是有很多帧的数据,
        //需要增加一个filter,一帧一帧过滤出来
        read_size = fread(inbuf, 1,INBUF_SIZE, pFileH264);
        printf("read_size_orig=%d\n",read_size);
        if(read_size == 0)
        {
            break;
        }

        while(read_size)
        {
            unsigned char *buf = 0;
            int buf_len = 0;
            int parse_len = av_parser_parse2(avParserContext, c, &buf, &buf_len,
                    inbuf, read_size,
                    AV_NOPTS_VALUE,
                    AV_NOPTS_VALUE,
                    AV_NOPTS_VALUE);
            printf("av_parser_parse2 len=[%d]\n", parse_len);
            inbuf += parse_len;
            read_size -= parse_len;
            //printf("read_size=%d, parse_len=%d, buf_len=%d\n",read_size, parse_len, buf_len);
            if(buf_len != 0)
            {
                AVPacket avpkt = {0};
                av_init_packet(&avpkt);
                avpkt.data = buf;
                avpkt.size = buf_len;
                time_t start ,end;
                start = clock();
                int decode_len = avcodec_decode_video2(c,frame, &got_frame, &avpkt);
                end = clock();
                printf("decoded frame used %d\n",end - start);
                if(decode_len < 0)
                    fprintf(stderr,"Error while decoding frame %d\n",frame_count);
                if(got_frame)
                {
                    fprintf(stderr,"decode success\n");
                    int width = frame->width;
                    int height = frame->height;
                    unsigned char* yuv_buf = (unsigned char*)malloc(width * height *1.5);//可以放在while循环外面,这样就不用每次都申请,释放了
                    int i = 0;

                    //把解码出来的数据存成YUV数据,方便验证解码是否正确
                    for(i = 0; i < height; i++)
                    {
                        memcpy(yuv_buf + width * i, frame->data[0] + frame->linesize[0]*i, width);
                        if(i < height >> 1)
                        {
                            memcpy(yuv_buf + width * height + width *i / 2, frame->data[1] + frame->linesize[1]*i, width / 2);
                            memcpy(yuv_buf + width * height * 5 /4 + width * i / 2 ,frame->data[2] + frame->linesize[2]*i, width / 2);
                        }
                    }
                    fwrite(yuv_buf, sizeof(unsigned char),width * height * 1.5 ,pFileYUV);
                    free(yuv_buf);
                    frame_count++;

                }
                else
                {
                    fprintf(stderr,"decode fail\n");
                }
                av_packet_unref(&avpkt);
            }
        }
    }

    //记得释放变量的空间
    return 0;
}



运行后产生的文件:

[[email protected] 03_01]# ll
total 1.4G
drwxr-xr-x. 2 root root   97 Oct 23 08:47 .
drwxr-xr-x. 7 root root 4.0K Oct 23 08:42 ..
-rwxr-xr-x. 1 root root   44 Oct 23 08:00 auto.sh
-rw-r--r--. 1 root root 3.9M Oct 23 07:59 in.h264
-rw-r--r--. 1 root root 5.1K Oct 23 08:38 main.c
-rwxr-xr-x. 1 root root  338 Oct 23 07:59 makefile
-rw-r--r--. 1 root root 1.4G Oct 23 08:46 out.yuv
-rwxr-xr-x. 1 root root  53K Oct 23 08:46 yuv2h264
[[email protected] 03_01]#

技术分享




这是一个还没有调试好的H264转YUV的程序

[[email protected] 03]# cat main.c 
#include <stdio.h>
#include <stdlib.h>

#include "libavcodec/avcodec.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/imgutils.h"
#include "libavutil/mathematics.h"
#include "libavutil/samplefmt.h"

#define INBUF_SIZE 4096000  //缓存区太小,太大都不行

FILE *pFin = NULL;
FILE *pFout = NULL;
AVCodec *pCodec = NULL;
AVCodecContext *pCodecContext = NULL;
AVCodecParserContext *pCodecParserContext = NULL;
AVFrame *frame = NULL;
AVPacket pkt;

static void finish()
{
    fclose(pFin);
    fclose(pFout);
    avcodec_close(pCodecContext);
    av_free(pCodecContext);
    av_frame_free(&frame);
}

static int open_codec()
{
    avcodec_register_all();
    av_init_packet(&pkt);

    pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);// 查找编解码器
    if(NULL == pCodec)
    {
        printf("Not find H264 Codec\n");
        return -1;
    }

    pCodecContext = avcodec_alloc_context3(pCodec);//分配AV context 空间
    if(NULL == pCodecContext)
    {
        printf("avcodec_alloc_context3 err\n");
        return -1;
    }

    if(pCodec->capabilities & AV_CODEC_CAP_TRUNCATED)//判断读取码流的状态 ,可能以截断的方式来读取
    {
        pCodecContext->flags |= AV_CODEC_CAP_TRUNCATED;
        printf("AV_CODEC_CAP_TRUNCATED \n");
    }

    pCodecParserContext = av_parser_init(AV_CODEC_ID_H264);//根据码流格式,初始化解析器
    if(NULL == pCodecParserContext)
    {
        printf("av_parser_init err\n");
        return -1;
    }

    if(avcodec_open2(pCodecContext, pCodec, NULL) < 0)
    {
        printf("avcodec_open2 ERR \n");
        return -1;
    }

    frame = av_frame_alloc();
    if(NULL == frame)
    {
        printf("av_frame err\n");
        return -1;
    }
    return 0;
}

static int init(int argc, char **argv)
{
    const char *inputFileName = argv[1];
    const char *oututFileName = argv[2];

    pFin = fopen(inputFileName, "rb+");
    if(NULL == pFin)
    {
        printf("open [%s],fail \n", inputFileName);
        return -1;
    }
    printf("open [%s],OK \n", inputFileName);

    pFout = fopen(oututFileName, "wb+");
    if(NULL == pFout)
    {
        printf("open [%s],fail \n", oututFileName);
        return -1;
    }
    printf("open [%s],OK\n", oututFileName);
    return 0;
}

static void write_out_yuv_frame(AVFrame *frame)
{
    int *pStride = frame->linesize;
    int i;

    for( i= 0; i < 3; i++ )
    {
        int nWidth  = i == 0 ? frame->width  :  frame->width/2;
        int nHeight = i == 0 ? frame->height :  frame->height/2;
        int y;
        for(y = 0; y < nHeight; y++)
        {
            fwrite(frame->data[i], 1, nWidth, pFout);
            frame->data[i] += pStride[i];
        }
        fflush(pFout);
    }

}

int main(int argc, char **argv)
{
    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    unsigned int uDataSize = 0;
    int got_frame = 0;
    uint8_t *pDataPtr = NULL;
    uint8_t len = 0;


    if(init(argc, argv) != 0)
    {
        printf("init err \n");
        return -1;
    }
    printf("init OK\n");

    if(open_codec() != 0)
    {
        printf("open_codec err \n");
        return -1;
    }
    printf("open_codec OK\n");

    while(1)
    {
        memset(inbuf, 0, AV_INPUT_BUFFER_PADDING_SIZE + INBUF_SIZE);
        uDataSize = fread(inbuf, 1, INBUF_SIZE, pFin);
        printf("read size=[%d] ----------------------------------- \n", uDataSize);
        if(uDataSize == 0)
        {
            printf("break while\n");
            break;
        }
        pDataPtr = inbuf;
        while(uDataSize > 0)
        {
            printf("into while uDataSize=[%d]\n", uDataSize);
            //int av_parser_parse2(AVCodecParserContext *s,
            //                     AVCodecContext *avctx,
            //                     uint8_t **poutbuf, int *poutbuf_size,
            //                     const uint8_t *buf, int buf_size,
            //                     int64_t pts, int64_t dts,
            //                     int64_t pos);

            len = av_parser_parse2(pCodecParserContext,
                    pCodecContext,
                    &pkt.data, &pkt.size,
                    pDataPtr, uDataSize,
                    AV_NOPTS_VALUE,
                    AV_NOPTS_VALUE,
                    AV_NOPTS_VALUE);
            printf("av_parser_parse2 len=[%d]\n", len);

            pDataPtr += len;
            uDataSize -= len;
            if(pkt.size != 0)
            {
                //成功的解析出一个包的数据
                printf("Parser Packet! uDataSize=[%d]\n", uDataSize);
                //int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
                //                         int *got_picture_ptr,
                //                         const AVPacket *avpkt);

                int ret = avcodec_decode_video2(pCodecContext, frame, &got_frame, &pkt);
                printf("avcodec_decode_video2 return[%d]***************************\n", ret);
                if(ret < 0)
                {
                    printf("decode Err\n");
                    return -1;
                }
                if(got_frame)
                {
                    printf("width*Height:[%d]*[%d]\n", frame->width, frame->height);
                    write_out_yuv_frame(frame);
                }
            }
            else
            {
                printf(".");
                continue;
            }
        }
    }
    pkt.data = NULL;
    pkt.size = 0;
    while(1)
    {
        int ret = avcodec_decode_video2(pCodecContext, frame, &got_frame, &pkt);
        if(ret < 0)
        {
            printf("decode fial\n");
            return -1;
        }
        if(got_frame)
        {
            printf("fflush width*Height:[%d]*[%d]", frame->width, frame->height);
            write_out_yuv_frame(frame);
        }
        else
        {
            break;
        }

    }

    finish();


    printf("everything will be OK! \n");

    return 0;
}

[[email protected] 03]#






本文出自 “李春利” 博客,转载请与作者联系!

以上是关于03 ffmpeg 解码SDK调用 H264转YUV420的主要内容,如果未能解决你的问题,请参考以下文章

FFMpeg SDK使用6调用FFmpeg SDK实现视频文件的转封装

FFmpeg解码H264及swscale缩放详解

FFMpeg SDK使用3调用FFmpeg SDK实现视频编码

FFMpeg Android Stagefright SIGSEGV 错误(h264 解码)

视频学习笔记:Android ffmpeg解码多路h264视频并显示

嵌入式linux------ffmpeg移植 解码H264(am335x解码H264到yuv420并通过SDL显示)