用ffmpeg把H264数据流解码成YUV420P

在网上找了很久这方面的内容,发现网上的代码都太旧了,所使用的函数旧到连最新版本的ffmpeg都已经不包含了,所以对于我这个初学者来说太坑拉。不过经过多次查找ffmpeg的头文件和结合网上的内容,终于成功可以解码拉。现在贴出来。

首先是初始化一些参数

 

[cpp]  view plain copy
 
 
  1. //下面初始化h264解码库  
  2. avcodec_init();  
  3. av_register_all();  
  4.   
  5. AVFrame *pFrame_ = NULL;  
  6.   
  7. AVCodecContext *codec_ = avcodec_alloc_context();  
  8.   
  9. /* find the video encoder */  
  10. AVCodec *videoCodec = avcodec_find_decoder(CODEC_ID_H264);  
  11.   
  12. if (!videoCodec)   
  13. {  
  14.     cout << "codec not found!" << endl;  
  15.     return -1;  
  16. }  
  17.   
  18. //初始化参数,下面的参数应该由具体的业务决定  
  19. codec_->time_base.num = 1;  
  20. codec_->frame_number = 1; //每包一个视频帧  
  21. codec_->codec_type = AVMEDIA_TYPE_VIDEO;  
  22. codec_->bit_rate = 0;  
  23. codec_->time_base.den = 30;//帧率  
  24. codec_->width = 1280;//视频宽  
  25. codec_->height = 720;//视频高  
  26.   
  27. if(avcodec_open(codec_, videoCodec) >= 0)  
  28.     pFrame_ = avcodec_alloc_frame();// Allocate video frame  
  29. else  
  30.     return -1;  


下面是具体的解码的代码

 

[cpp]  view plain copy
 
 
  1. AVPacket packet = {0};  
  2. int frameFinished = dwBufsize;//这个是随便填入数字,没什么作用  
  3.   
  4. packet.data = pBuffer;//这里填入一个指向完整H264数据帧的指针  
  5. packet.size = dwBufsize;//这个填入H264数据帧的大小  
  6.   
  7. //下面开始真正的解码  
  8. avcodec_decode_video2(codec_, pFrame_, &frameFinished, &packet);  
  9. if(frameFinished)//成功解码  
  10. {  
  11.     int picSize = codec_->height * codec_->width;  
  12.     int newSize = picSize * 1.5;  
  13.   
  14.     //申请内存  
  15.     unsigned char *buf = new unsigned char[newSize];  
  16.   
  17.     int height = p->codec->height;  
  18.     int width = p->codec->width;  
  19.   
  20.   
  21.     //写入数据  
  22.     int a=0,i;   
  23.     for (i=0; i<height; i++)   
  24.     {   
  25.         memcpy(buf+a,pFrame_->data[0] + i * pFrame_->linesize[0], width);   
  26.         a+=width;   
  27.     }   
  28.     for (i=0; i<height/2; i++)   
  29.     {   
  30.         memcpy(buf+a,pFrame_->data[1] + i * pFrame_->linesize[1], width/2);   
  31.         a+=width/2;   
  32.     }   
  33.     for (i=0; i<height/2; i++)   
  34.     {   
  35.         memcpy(buf+a,pFrame_->data[2] + i * pFrame_->linesize[2], width/2);   
  36.         a+=width/2;   
  37.     }  
  38.   
  39.     //===============  
  40.     //到这里,buf里面已经是yuv420p的数据了,可以对它做任何的处理拉!  
  41.     //===============  
  42.     delete [] buf;  
  43. }  



 

 

不过我发现这样解码很耗cpu资源,我的Core2  E7400 2.8G的处理器,解码1920X1080分辨率每秒30帧的视频时,CPU占用率能用到差不多50%。

 

PS:原来avcodec_decode_video2这个函数会修改codec_里面的参数的,也就是说如果原来里面填的分别率是1280X720,运行avcodec_decode_video2后codec_里面会变成实际视频的分辨率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言示例,它演示了如何使用ffmpeg4.0以上版本中的FIFO和解码器,将H264视频流解码YUV格式: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/avutil.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #define INBUF_SIZE 4096 int main(int argc, char *argv[]) { AVFormatContext *fmt_ctx = NULL; AVCodecContext *dec_ctx = NULL; AVCodec *dec = NULL; AVPacket pkt; AVFrame *frame = NULL; AVFrame *frame_yuv = NULL; uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; int inbuf_size; uint8_t *data[4]; int linesize[4]; int ret, i, j; if (argc <= 1) { printf("Usage: %s <input file>\n", argv[0]); return 0; } av_register_all(); avformat_network_init(); avcodec_register_all(); if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) < 0) { printf("Cannot open input file.\n"); return -1; } if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { printf("Cannot find stream information.\n"); return -1; } int video_stream_index = -1; for (i = 0; i < fmt_ctx->nb_streams; i++) { if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } } if (video_stream_index == -1) { printf("Cannot find video stream.\n"); return -1; } dec = avcodec_find_decoder(fmt_ctx->streams[video_stream_index]->codecpar->codec_id); if (!dec) { printf("Failed to find decoder.\n"); return -1; } dec_ctx = avcodec_alloc_context3(dec); if (!dec_ctx) { printf("Failed to allocate codec context.\n"); return -1; } if (avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar) < 0) { printf("Failed to copy codec parameters to context.\n"); return -1; } if (avcodec_open2(dec_ctx, dec, NULL) < 0) { printf("Failed to open codec.\n"); return -1; } frame = av_frame_alloc(); if (!frame) { printf("Failed to allocate frame.\n"); return -1; } frame_yuv = av_frame_alloc(); if (!frame_yuv) { printf("Failed to allocate YUV frame.\n"); return -1; } int numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, dec_ctx->width, dec_ctx->height, 1); uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t)); av_image_fill_arrays(frame_yuv->data, frame_yuv->linesize, buffer, AV_PIX_FMT_YUV420P, dec_ctx->width, dec_ctx->height, 1); struct SwsContext *sws_ctx = sws_getContext(dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, dec_ctx->width, dec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); if (!sws_ctx) { printf("Failed to create SwsContext.\n"); return -1; } av_init_packet(&pkt); pkt.data = NULL; pkt.size = 0; while (av_read_frame(fmt_ctx, &pkt) >= 0) { if (pkt.stream_index == video_stream_index) { ret = avcodec_send_packet(dec_ctx, &pkt); if (ret < 0) { printf("Error sending packet to decoder.\n"); break; } while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) { printf("Error receiving frame from decoder.\n"); goto end; } sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0, dec_ctx->height, frame_yuv->data, frame_yuv->linesize); printf("Decoded frame %d (%d bytes).\n", dec_ctx->frame_number, ret); } } av_packet_unref(&pkt); } end: avcodec_free_context(&dec_ctx); avformat_close_input(&fmt_ctx); avformat_free_context(fmt_ctx); av_frame_free(&frame); av_frame_free(&frame_yuv); sws_freeContext(sws_ctx); av_free(buffer); return 0; } ``` 这个示例程序使用了FIFO和解码器来读取H264视频流并将其解码YUV格式。它首先打开输入文件并查找视频流,然后为解码器分配上下文并打开它。然后,它创建一个AVFrame结构体来存储解码的帧,并创建一个AVFrame结构体来存储YUV格式的帧。它还分配了一个缓冲区来存储YUV帧的像素数据。 接下来,它创建一个SwsContext结构体,用于执行YUV格式转换。然后,它循环读取视频流中的数据包并将其发送到解码器。每当解码解码一帧时,它将使用SwsContext将帧转换为YUV格式,并将其输出到控制台。 最后,它清理并释放分配的内存,并关闭输入文件。 请注意,此示例程序仅用于演示如何使用ffmpeg4.0以上版本中的FIFO和解码器,实际应用中可能需要添加更多的错误处理和异常情况处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值