ffmpeg H264转YUV420p

版本是ffmpeg4.3(高几个版本或者低几个版本都可以)

结果 :把H264转成yuv420p

使用的H264文件是D:/videos/264.dat(dat后缀名无所谓)
264.dat下载,不用积分

可以使用ffplay播放

ffplay  D:/videos/264.dat

最终生成文件yuv420p_1920_1080_264.yuv

可以使用ffplay播放

ffplay -pix_fmt yuv420p -s 1920x1080 D:/output/yuv420p_1920_1080_264.yuv

方法一:主要函数:av_read_frame()

方法二:主要函数:av_parser_parse2()

方法一和方法二的区别:

方法一是通过查找流的信息找到解码器ID。

方法二不需要查找流的信息,直接读取二进制文件,自己设置解码器ID

方法一:

主要函数:av_read_frame()

源码

#define  YUVFORMAT_YUV420P AV_PIX_FMT_YUV420P
void startTransform2YUV420p()
{
  int nRet = 0;
    const char *pInFileName = "C:/Users/zhou/Desktop/Mpeg2decoder/out.h264";
    const char *pOutFileName = "C:/Users/zhou/Desktop/Mpeg2decoder/out.yuv";
    AVDictionary *pDic = nullptr;
    AVFormatContext *pInFmtCtx = nullptr;
    nRet = avformat_open_input(&pInFmtCtx,pInFileName,nullptr,&pDic);
    if( nRet < 0)
    {
        printf("Could not open input file.");
        return;
    }
    avformat_find_stream_info(pInFmtCtx, nullptr);
    printf("===========Input Information==========\n");
    av_dump_format(pInFmtCtx, 0, pInFileName, 0);
    printf("======================================\n");
    //查找解码器   和下边的查找解码器都可用
//    int vudioStreamIndex = av_find_best_stream(pInFmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
//    if(-1 == vudioStreamIndex)
//    {
//        printf("Error: can't find a video stream!\n");
//        return;
//    }
    //查找解码器
    int vudioStreamIndex = -1;
    for(int i = 0; i < pInFmtCtx->nb_streams; ++i)
    {
        if(AVMEDIA_TYPE_VIDEO == pInFmtCtx->streams[i]->codecpar->codec_type)
        {
            vudioStreamIndex = i;
            break;
        }
    }
    AVStream * in_stream = pInFmtCtx->streams[vudioStreamIndex];
    AVCodec *pInCodec = avcodec_find_decoder(in_stream->codecpar->codec_id);
    if(nullptr == pInCodec)
    {
        printf("avcodec_find_decoder fail.");
        return;
    }
    AVCodecContext* pInCodecCtx = avcodec_alloc_context3(pInCodec); //??????????????
    nRet = avcodec_parameters_to_context(pInCodecCtx, in_stream->codecpar);
    if(nRet < 0)
    {
        printf("avcodec_parameters_to_context fail.");
        return;
    }
    //打开解码器
    if(avcodec_open2(pInCodecCtx, pInCodec, nullptr) < 0)
    {
        printf("Error: Can't open codec!\n");
        return ;
    }
    printf("width = %d\n", pInCodecCtx->width);
    printf("height = %d\n", pInCodecCtx->height);
    AVPacket *packet = av_packet_alloc();
    av_init_packet(packet);

    FILE *fp = fopen(pOutFileName, "wb+");
    size_t y_size = 0;
    int got_picture = 0;
    int nCount = 0;
    AVFrame *pFrame = av_frame_alloc();
    while(av_read_frame(pInFmtCtx, packet) >= 0)
    {
        if( vudioStreamIndex == packet->stream_index)
        {
            //avcodec_send_packet送原始数据给编码器进行编码
            //avcodec_receive_frame
            if(avcodec_send_packet(pInCodecCtx, packet)<0 || (got_picture =avcodec_receive_frame(pInCodecCtx, pFrame))<0)
            {
                continue;
            }
            if(!got_picture)//
            {
                int picSize = pFrame->height *pFrame->width;
                int newSize = picSize * 1.5;
                //申请内存
                unsigned char *buf = new unsigned char[newSize];
                int a=0,i;
                for (i=0; i<pFrame->height; i++)
                {
                    memcpy(buf+a,pFrame->data[0] + i * pFrame->linesize[0], pFrame->width);
                    a+=pFrame->width;
                }
                for (i=0; i<pFrame->height/2; i++)
                {
                    memcpy(buf+a,pFrame->data[1] + i * pFrame->linesize[1], pFrame->width/2);
                    a+=pFrame->width/2;
                }
                for (i=0; i<pFrame->height/2; i++)
                {
                    memcpy(buf+a,pFrame->data[2] + i * pFrame->linesize[2], pFrame->width/2);
                    a+=pFrame->width/2;
                }
                fwrite(buf, 1, newSize, fp);

                printf("Succeed to decode %d Frame!\n",nCount);
                nCount++;
            }
            av_packet_unref(packet);
        }
    }
    fflush(fp);
    //flush decoder
    //当av_read_frame 退出循环的时候,实际上解码器中可能还包含
    //剩余的几帧数据。直接调用avcodec_decode_video2获得AVFrame ,
    //而不再向解码器传递AVPacket
    while(1)
    {
        if(avcodec_send_packet(pInCodecCtx, packet)<0 || (got_picture =avcodec_receive_frame(pInCodecCtx, pFrame))<0)
        {
            std::cout<<"h264toyuv420p end";
            goto __end;
        }
        if(!got_picture)
        {
          break;
        }
        int picSize = pFrame->height *pFrame->width;
        int newSize = picSize * 1.5;
        //申请内存
        unsigned char *buf = new unsigned char[newSize];
        int a=0,i;
        for (i=0; i<pFrame->height; i++)
        {
            memcpy(buf+a,pFrame->data[0] + i * pFrame->linesize[0], pFrame->width);
            a+=pFrame->width;
        }
        for (i=0; i<pFrame->height/2; i++)
        {
            memcpy(buf+a,pFrame->data[1] + i * pFrame->linesize[1], pFrame->width/2);
            a+=pFrame->width/2;
        }
        for (i=0; i<pFrame->height/2; i++)
        {
            memcpy(buf+a,pFrame->data[2] + i * pFrame->linesize[2], pFrame->width/2);
            a+=pFrame->width/2;
        }
        fwrite(buf, 1, newSize, fp);
        printf("flush Succeed to decode %d Frame!\n",nCount);
        nCount++;
    }
__end:
    fflush(fp);
    fclose(fp);
    av_frame_free(&pFrame);
    avcodec_close(pInCodecCtx);
    avformat_close_input(&pInFmtCtx);
    std::cout<<"h264toyuv420p end";
}
int main()
{
	startTransform2YUV420p();
}

方法二:

主要函数为:

结构体AVCodecParser:用于解析输入的数据流并把它分成一帧一帧的压缩编码数据。比较形象的说法就是把长长的一段连续的数据“切割”成一段段的数据。

av_parser_init(): 初始化AVCodecParserContext。

av_parser_parse2():解析获得一个Packet。相当于av_read_frame。

static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
                   FILE *fo)
{
    char buf[1024];
    int ret;
    ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0) {
        fprintf(stderr, "Error sending a packet for decoding\n");
        exit(1);
    }
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }
        size_t y_size = 1920*1080;
        fwrite(frame->data[0], 1, y_size, fo);
        fwrite(frame->data[1], 1, y_size/4, fo);
        fwrite(frame->data[2], 1, y_size/4, fo);
    }
}
static int test(const char* filename,const char* outfilename)
{
    const AVCodec *codec;
    AVCodecParserContext *parser;
    AVCodecContext *c= NULL;
    FILE *f;
    AVFrame *frame;
    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data;
    size_t   data_size;
    int ret;
    AVPacket *pkt;

    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);
    /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
    /* find the MPEG-1 video decoder */
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        exit(1);
    }
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */
    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }
    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    FILE *fo;
    fo = fopen(outfilename,"wb+");
    while (!feof(f)) {
        /* read raw data from the input file */
        data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size)
            break;
        /* use the parser to split the data into frames */
        data = inbuf;
        while (data_size > 0) {
            ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                                   data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
            data      += ret;
            data_size -= ret;
            if (pkt->size)
                decode(c, frame, pkt, fo);
        }
    }
    /* flush the decoder */
    decode(c, frame, NULL, fo);
    fclose(f);
    fclose(fo);
    av_parser_close(parser);
    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);
    return 0;
}
void main()
{
    test("D:/videos/264.dat","D:/output/decode_video_h264.yuv");
    std::cout<<"decode end";
}

如果出现条纹状或者阶梯状,请参考

https://blog.csdn.net/asdasfdgdhh/article/details/112831581

原文 ffmpeg H264转YUV420p

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

猜你喜欢

转载自blog.csdn.net/yinshipin007/article/details/131947468