FFMPEG视频开发教程(三)——使用ffmpeg把264文件数据解码为jpeg图片文件或者bmp图片

使用ffmpeg可以很容易把264文件数据解码为jpeg图片文件或者bmp图片。开发环境的搭建同样参考《Ffmpeg视频开发教程(一)——实现视频格式转换功能超详细版》(https://blog.csdn.net/zhangamxqun/article/details/80304494)。

环境搭建好,把主程序的cpp文件代码,换成下面的代码,即可进行测试。

lib库文件的路径,要根据自己的实际情况配置。

测试之前在exe同级目录下先放入test.264的测试264文件数据(csdn随便下载一个)。程序运行后可以在程序目录下看到大量图片被生成。

/** 
实现FFMPEG把264数据保存为jpeg图像或者bmp图像,作者自己测试正确可用 
作者:明天继续
使用的ffmpeg版本:ffmpeg-20180508-293a6e8-win32 
开发工具:vs2012 
**/  
  
#include "stdafx.h"  
#include <Windows.h>
#include <wingdi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" {  
#include <libavcodec/avcodec.h>  
#include <libavformat/avformat.h>  
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
 
}  
  
#define INBUF_SIZE 4096
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avcodec.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avformat.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avfilter.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/avutil.lib")  
#pragma comment(lib,"../ffmpeg-20180508-293a6e8-win32/ffmpeg-20180508-293a6e8-win32-dev/lib/swscale.lib") 
  
#if _MSC_VER  
#define snprintf _snprintf_s  
#define PRIx64       "I64x"  
#define PRIX64       "I64X"  
#endif  

  int save_jpg(const char* filename, AVFrame* pFrame)  
  {  
	  // 分配AVFormatContext对象    
	  AVFormatContext* pFormatCtx = avformat_alloc_context();  

	  // 设置输出文件格式    
	  pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);  //mjpeg
	  // 创建并初始化一个和该路径相关的AVIOContext    
	  if (avio_open(&pFormatCtx->pb, filename, AVIO_FLAG_READ_WRITE) < 0)   
	  {  
		  printf("Couldn't open output file.");  
		  return -1;  
	  }  

	  // 构建一个新stream    
	  AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);  
	  if (pAVStream == NULL)   
	  {  
		  printf("avformat_new_stream error.");  
		  return -1;  
	  }  

	  // 设置该stream的信息    
	  AVCodecContext* pCodecCtx = pAVStream->codec;  

	  pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;  
	  pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;  
	  pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;  
	  pCodecCtx->width = pFrame->width;  
	  pCodecCtx->height = pFrame->height;  
	  pCodecCtx->time_base.num = 1;  
	  pCodecCtx->time_base.den = 25;   

	  // 查找解码器    
	  AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);  
	  if (!pCodec)   
	  {  
		  printf("Could not find codec.");  
		  return -1;  
	  }  
	  // 设置pCodecCtx的解码器为pCodec    
	  if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)   
	  {  
		  printf("Could not open codec.");  
		  return -1;  
	  }  

	  //写文件头    
	  int ret = avformat_write_header(pFormatCtx, NULL);  
	  if (ret < 0)  
	  {  
		  printf("avformat_write_header.\n");  
		  return -1;  
	  }  

	  int y_size = pCodecCtx->width * pCodecCtx->height;  
  
	  AVPacket pkt;  
	  av_new_packet(&pkt, y_size * 3);  

	  int got_picture = 0;  
	  ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);  
	  if (ret < 0)   
	  {  
		  printf("Encode Error.\n");  
		  return -1;  
	  }  
	  if (got_picture == 1)   
	  {    
		  ret = av_write_frame(pFormatCtx, &pkt);  
	  }  

	  av_free_packet(&pkt);  

	  //写入文件尾    
	  av_write_trailer(pFormatCtx);  

	  if (pAVStream)   
	  {  
		  avcodec_close(pAVStream->codec);  
	  }  
	  avio_close(pFormatCtx->pb);  
	  avformat_free_context(pFormatCtx);  
	  return 0;  
  }  



  //保存8位的bmp图
  static void bmp_8bit_save(unsigned char*buf, int wrap, int xsize, int ysize, char* filename)
  {
	  FILE *f;
	  int bitCount = 8;
	  unsigned int linelength = (xsize * bitCount + 31)/32*4;

	  char filsename_new[1024];
	  snprintf(filsename_new, sizeof(filsename_new), "%s.%s", filename,"bmp");
	  f = fopen(filsename_new,"w");

	  BITMAPFILEHEADER targetfileheader;
	  BITMAPINFOHEADER targetinfoheader;
	  memset(&targetfileheader,0,sizeof(BITMAPFILEHEADER));
	  memset(&targetinfoheader,0,sizeof(BITMAPINFOHEADER)); 

	  //构造灰度图的文件头
	  targetfileheader.bfOffBits=(unsigned long)sizeof(BITMAPFILEHEADER)+(unsigned long)sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256; 
	  targetfileheader.bfSize=linelength*ysize+sizeof(RGBQUAD)*256 +sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);   
	  targetfileheader.bfReserved1=0;
	  targetfileheader.bfReserved2=0;
	  targetfileheader.bfType=0x4d42;


	  //构造灰度图的信息头
	  targetinfoheader.biBitCount=8;
	  targetinfoheader.biSize=sizeof(BITMAPINFOHEADER);
	  targetinfoheader.biHeight=ysize;
	  targetinfoheader.biWidth=xsize;
	  targetinfoheader.biPlanes=1;
	  targetinfoheader.biCompression=BI_RGB;
	  targetinfoheader.biSizeImage=0;
	  targetinfoheader.biXPelsPerMeter=0;
	  targetinfoheader.biYPelsPerMeter=0;
	  targetinfoheader.biClrImportant=0;
	  targetinfoheader.biClrUsed=0;

	  fwrite( &targetfileheader, 1, sizeof(BITMAPFILEHEADER), f);//写入位图文件头
	  fwrite( &targetinfoheader, 1, sizeof(BITMAPINFOHEADER), f);//写入位图信息头
	  RGBQUAD rgbquad[256];

	  for(int j=0;j<256;j++)
	  {
		  rgbquad[j].rgbBlue=j;
		  rgbquad[j].rgbGreen=j;
		  rgbquad[j].rgbRed=j;
		  rgbquad[j].rgbReserved=0;
	  } 
	  fwrite(rgbquad , 1, sizeof(RGBQUAD)*256, f);

	  char* imagData = new char[linelength*ysize];
	  memset(imagData,0, linelength*ysize);

	  for (int i = 0; i < ysize; i++)
		  memcpy(imagData+i*linelength,buf+i*wrap, xsize);
	  fwrite(imagData  , 1, linelength*ysize, f);
	  delete []imagData;
	  fclose(f);
  }

 
  void writ2bmp(AVFrame *pFrameRGB, int width, int height, int bpp, const char* filename)  
  {   
	  BITMAPFILEHEADER bmpheader;  
	  BITMAPINFOHEADER bmpinfo;  
	  FILE *fp;  

 
	  if( (fp = fopen(filename,"wb+")) == NULL ) {  
		  printf ("open file failed!\n");  
		  return;  
	  } 

	  int widthStep = (width*bpp+31)/32*4;

	  bmpheader.bfType = 0x4d42;  
	  bmpheader.bfReserved1 = 0;  
	  bmpheader.bfReserved2 = 0;  
	  bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
	  bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;  

	  bmpinfo.biSize = sizeof(BITMAPINFOHEADER);  
	  bmpinfo.biWidth = width;  
	  bmpinfo.biHeight = height;  
	  bmpinfo.biPlanes = 1;  
	  bmpinfo.biBitCount = bpp;  
	  bmpinfo.biCompression = BI_RGB;  
	  bmpinfo.biSizeImage = widthStep*height;  
	  bmpinfo.biXPelsPerMeter = 100;  
	  bmpinfo.biYPelsPerMeter = 100;  
	  bmpinfo.biClrUsed = 0;  
	  bmpinfo.biClrImportant = 0;  

	  fwrite(&bmpheader, sizeof(bmpheader), 1, fp);  
	  fwrite(&bmpinfo, sizeof(bmpinfo), 1, fp);  

	  //上下翻转
	  unsigned char* tempData = new unsigned char[widthStep];
	  for (int i=0;i<height/2;i++)
	  {
		  memcpy(tempData,pFrameRGB->data[0] + i*widthStep, widthStep);

		  memcpy(pFrameRGB->data[0] + i*widthStep, pFrameRGB->data[0] + (height-1-i)*widthStep, widthStep);

		  memcpy(pFrameRGB->data[0] + (height-1-i)*widthStep,tempData, widthStep);
	  }
	  delete[]tempData;
	  fwrite(pFrameRGB->data[0], width*height*bpp/8, 1, fp);  

	  fclose(fp);  
  } 

void save_24bit_bmp(AVPixelFormat inputFormat,  AVFrame * inputFrame, const char* filename)
{
	AVFrame * pFrameRGB = av_frame_alloc();  
	int pictureSize = avpicture_get_size(AV_PIX_FMT_BGR24, inputFrame->width, inputFrame->height);  
	uint8_t *outBuff = (uint8_t*)av_malloc(pictureSize); 
	avpicture_fill((AVPicture *)pFrameRGB, outBuff, AV_PIX_FMT_BGR24, inputFrame->width, inputFrame->height);  
	struct SwsContext *pSwsCtx = sws_getContext(inputFrame->width, inputFrame->height, inputFormat,  
		inputFrame->width, inputFrame->height, AV_PIX_FMT_BGR24,  
		SWS_BICUBIC, NULL, NULL, NULL); 
	sws_scale(pSwsCtx, inputFrame->data,  
		inputFrame->linesize, 0, inputFrame->height,  
		pFrameRGB->data, pFrameRGB->linesize); 		
	writ2bmp(pFrameRGB, inputFrame->width, inputFrame->height,  24, filename);
	sws_freeContext (pSwsCtx);   
	av_free (pFrameRGB);   
}

static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
                   const char *filename)
{

    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);
        }

        printf("saving frame %3d\n", dec_ctx->frame_number);
        fflush(stdout);
        /* 图片是解码器分配的内存,不需要释放 */
        

		//保存8位的bmp灰度图
		//snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);
		//bmp_8bit_save(frame->data[0], frame->linesize[0],
		//	frame->width, frame->height, buf);

		//保存24位bmp图片
		char namebuf[1024]= {0};
		snprintf(namebuf, sizeof(namebuf), "%s-%d.bmp", filename, dec_ctx->frame_number);
		save_24bit_bmp(dec_ctx->pix_fmt,  frame, namebuf);
		

		
		//保存jpeg图片
		char filsename_new[1024];
		snprintf(filsename_new, sizeof(filsename_new), "%s%d.%s", filename,dec_ctx->frame_number,"jpg");
		save_jpg(filsename_new,frame); 
    }
}

  
int _tmain(int argc, _TCHAR* argvec[])  
{  

   const char *filename, *outfilename;
    const AVCodec *codec_264;
    AVCodecParserContext *parser;
    AVCodecContext *codecContext_264= 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;

	//输入的h264文件
    filename    = "test.264";

	//输出的jpe图片文件的前缀
    outfilename = "image";

	av_register_all();  

    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);

    /* buffer尾部数据置为0 */
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    /* 找到h264解码器,用于解码h264 */
    codec_264 = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec_264) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    parser = av_parser_init(codec_264->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        exit(1);
    }

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

    /* 打开解码器 */
    if (avcodec_open2(codecContext_264, codec_264, 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);
    }

    while (!feof(f)) {
        /* 从264文件读取数据 */
        data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size)
            break;

        /* 使用解析器把数据分割为包 */
        data = inbuf;
        while (data_size > 0) {
            ret = av_parser_parse2(parser, codecContext_264, &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(codecContext_264, frame, pkt, outfilename);
        }
    }

    /* 解码最后的数据 */
    decode(codecContext_264, frame, NULL, outfilename);

    fclose(f);

    av_parser_close(parser);
    avcodec_free_context(&codecContext_264);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    return 0;
}  

效果图



  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值