使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码

ffmpeg开源库,实现将bmp格式的图片编码成x264文件,并将编码好的H264文件解码保存为BMP文件。

实现将视频文件yuv格式保存的图片格式的测试,图像格式png,jpg, gif等等测试均OK

  自己根据博客的代码,vs2010搭建的测试环境。资源下载 

   具体代码:

 

 
  1. #define _AFXDLL

  2. #include<afxwin.h>

  3. #ifdef __cplusplus

  4. extern "C" {

  5. #endif

  6.  
  7. #include <libavcodec/avcodec.h>

  8. #include <libavformat/avformat.h>

  9. #include <libswscale/swscale.h>

  10. void main()

  11. {

  12. CFile file[5];

  13. BYTE *szTxt[5];

  14.  
  15. int nWidth = 0;

  16. int nHeight= 0;

  17.  
  18. int nDataLen=0;

  19.  
  20. int nLen;

  21.  
  22. CString csFileName;

  23. for (int fileI = 1; fileI <= 5; fileI ++)

  24. {

  25. csFileName.Format("%d.bmp", fileI);

  26. file[fileI - 1].Open(csFileName,CFile::modeRead | CFile::typeBinary);

  27. nLen = file[fileI - 1].GetLength();

  28.  
  29. szTxt[fileI -1] = new BYTE[nLen];

  30. file[fileI - 1].Read(szTxt[fileI - 1], nLen);

  31. file[fileI - 1].Close();

  32.  
  33. //BMP bmi;//BITMAPINFO bmi;

  34. //int nHeadLen = sizeof(BMP);

  35. BITMAPFILEHEADER bmpFHeader;

  36. BITMAPINFOHEADER bmpIHeader;

  37. memcpy(&bmpFHeader,szTxt[fileI -1],sizeof(BITMAPFILEHEADER));

  38.  
  39. int nHeadLen = bmpFHeader.bfOffBits - sizeof(BITMAPFILEHEADER);

  40. memcpy(&bmpIHeader,szTxt[fileI - 1]+sizeof(BITMAPFILEHEADER),nHeadLen);

  41.  
  42. nWidth = bmpIHeader.biWidth;// 464;// bmi.bmpInfo.bmiHeader.biWidth;// ;

  43. nHeight = bmpIHeader.biHeight;//362;// bmi.bmpInfo.bmiHeader.biHeight;// ;

  44.  
  45. szTxt[fileI - 1] += bmpFHeader.bfOffBits;

  46. nDataLen = nLen-bmpFHeader.bfOffBits;

  47. }

  48. getchar();

  49. av_register_all();

  50. avcodec_register_all();

  51. AVFrame *m_pRGBFrame = new AVFrame[1]; //RGB帧数据

  52. AVFrame *m_pYUVFrame = new AVFrame[1];; //YUV帧数据

  53. AVCodecContext *c= NULL;

  54. AVCodecContext *in_c= NULL;

  55. AVCodec *pCodecH264; //编码器

  56. uint8_t * yuv_buff;//

  57.  
  58. //查找h264编码器

  59. pCodecH264 = avcodec_find_encoder(CODEC_ID_H264);

  60. if(!pCodecH264)

  61. {

  62. fprintf(stderr, "h264 codec not found\n");

  63. getchar();

  64. exit(1);

  65. }

  66.  
  67. c= avcodec_alloc_context3(pCodecH264);

  68. c->bit_rate = 3000000;// put sample parameters

  69. c->width =nWidth;//

  70. c->height = nHeight;//

  71.  
  72. // frames per second

  73. AVRational rate;

  74. rate.num = 1;

  75. rate.den = 25;

  76. c->time_base= rate;//(AVRational){1,25};

  77. c->gop_size = 10; // emit one intra frame every ten frames

  78. c->max_b_frames=1;

  79. c->thread_count = 1;

  80. c->pix_fmt = PIX_FMT_YUV420P;//PIX_FMT_RGB24;

  81.  
  82. //av_opt_set(c->priv_data, /*"preset"*/"libvpx-1080p.ffpreset", /*"slow"*/NULL, 0);

  83. //打开编码器

  84. if(avcodec_open2(c,pCodecH264,NULL)<0){

  85. printf("avcodec_open2 failed\n");

  86. TRACE("不能打开编码库");

  87. getchar();

  88. }

  89.  
  90. int size = c->width * c->height;

  91.  
  92. yuv_buff = (uint8_t *) malloc((size * 3) / 2); // size for YUV 420

  93.  
  94. //将rgb图像数据填充rgb帧

  95. uint8_t * rgb_buff = new uint8_t[nDataLen];

  96.  
  97. //图象编码 outbuf_size太小会报错,图像清晰度也会差

  98. int outbuf_size = 900000;

  99. uint8_t * outbuf= (uint8_t*)malloc(outbuf_size);

  100. int u_size = 0;

  101. FILE *f=NULL;

  102. char * filename = "myData.h264";

  103. f = fopen(filename, "wb");

  104. if (!f)

  105. {

  106. TRACE( "could not open %s\n", filename);

  107. getchar();

  108. exit(1);

  109. }

  110.  
  111. //初始化SwsContext

  112. SwsContext * scxt = sws_getContext(c->width,c->height,PIX_FMT_BGR24,c->width,c->height,PIX_FMT_YUV420P,SWS_POINT,NULL,NULL,NULL);

  113.  
  114. AVPacket avpkt;

  115.  
  116. //AVFrame *pTFrame=new AVFrame

  117. for (int i=0;i<250;++i)

  118. {

  119.  
  120. //AVFrame *m_pYUVFrame = new AVFrame[1];

  121.  
  122. int index = (i / 25) % 5;

  123. memcpy(rgb_buff,szTxt[index],nDataLen);

  124.  
  125. avpicture_fill((AVPicture*)m_pRGBFrame, (uint8_t*)rgb_buff, PIX_FMT_RGB24, nWidth, nHeight);

  126.  
  127. //将YUV buffer 填充YUV Frame

  128. avpicture_fill((AVPicture*)m_pYUVFrame, (uint8_t*)yuv_buff, PIX_FMT_YUV420P, nWidth, nHeight);

  129.  
  130. // 翻转RGB图像

  131. m_pRGBFrame->data[0] += m_pRGBFrame->linesize[0] * (nHeight - 1);

  132. m_pRGBFrame->linesize[0] *= -1;

  133. m_pRGBFrame->data[1] += m_pRGBFrame->linesize[1] * (nHeight / 2 - 1);

  134. m_pRGBFrame->linesize[1] *= -1;

  135. m_pRGBFrame->data[2] += m_pRGBFrame->linesize[2] * (nHeight / 2 - 1);

  136. m_pRGBFrame->linesize[2] *= -1;

  137.  
  138.  
  139. //将RGB转化为YUV

  140. sws_scale(scxt,m_pRGBFrame->data,m_pRGBFrame->linesize,0,c->height,m_pYUVFrame->data,m_pYUVFrame->linesize);

  141.  
  142. static int got_packet_ptr = 0;

  143. av_init_packet(&avpkt);

  144. avpkt.data = outbuf;

  145. avpkt.size = outbuf_size;

  146. u_size = avcodec_encode_video2(c, &avpkt, m_pYUVFrame, &got_packet_ptr);

  147. m_pYUVFrame->pts++;

  148. if (u_size == 0)

  149. {

  150. fwrite(avpkt.data, 1, avpkt.size, f);

  151. }

  152. }

  153.  
  154. fclose(f);

  155. delete []m_pRGBFrame;

  156. delete []m_pYUVFrame;

  157. delete []rgb_buff;

  158. free(outbuf);

  159. avcodec_close(c);

  160. av_free(c);

  161.  
  162. }

  163.  
  164. #ifdef __cplusplus

  165. }

  166. #endif


 完全按照博客中的代码测试发现会报下面的信息,而且在播放过程中,画面都是模糊的。修改了outbuff_size的大小解决了这个问题。

疑问:为什么要循环250次?有知道麻烦解答下!

for (int i=0;i<250;++i)

将H264视频保存为BMP图片,具体代码如下:

 
  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include <string.h>

  4. #include <windows.h>

  5.  
  6. #ifdef __cplusplus

  7. extern "C" {

  8. #endif

  9.  
  10. #include <libavcodec/avcodec.h>

  11. #include <libavformat/avformat.h>

  12. #include <libswscale/swscale.h>

  13.  
  14. void SaveAsBMP (AVFrame *pFrameRGB, int width, int height, int index, int bpp)

  15. {

  16. char buf[5] = {0};

  17. BITMAPFILEHEADER bmpheader;

  18. BITMAPINFOHEADER bmpinfo;

  19. FILE *fp;

  20.  
  21. char filename[20] = "";

  22. _itoa (index, buf, 10);

  23. strcat (filename, buf);

  24. strcat (filename, ".bmp");

  25.  
  26. if ( (fp = fopen(filename,"wb+")) == NULL )

  27. {

  28. printf ("open file failed!\n");

  29. return;

  30. }

  31.  
  32. bmpheader.bfType = 0x4d42;

  33. bmpheader.bfReserved1 = 0;

  34. bmpheader.bfReserved2 = 0;

  35. bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

  36. bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;

  37.  
  38. bmpinfo.biSize = sizeof(BITMAPINFOHEADER);

  39. bmpinfo.biWidth = width;

  40. bmpinfo.biHeight = height;

  41. bmpinfo.biPlanes = 1;

  42. bmpinfo.biBitCount = bpp;

  43. bmpinfo.biCompression = BI_RGB;

  44. bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;

  45. bmpinfo.biXPelsPerMeter = 100;

  46. bmpinfo.biYPelsPerMeter = 100;

  47. bmpinfo.biClrUsed = 0;

  48. bmpinfo.biClrImportant = 0;

  49.  
  50. fwrite (&bmpheader, sizeof(bmpheader), 1, fp);

  51. fwrite (&bmpinfo, sizeof(bmpinfo), 1, fp);

  52. fwrite (pFrameRGB->data[0], width*height*bpp/8, 1, fp);

  53.  
  54. fclose(fp);

  55. }

  56.  
  57.  
  58. int main (void)

  59. {

  60. unsigned int i = 0, videoStream = -1;

  61. AVCodecContext *pCodecCtx;

  62. AVFormatContext *pFormatCtx = NULL;

  63. AVCodec *pCodec;

  64. AVFrame *pFrame, *pFrameRGB;

  65. struct SwsContext *pSwsCtx;

  66. const char *filename = "myData.h264";

  67. AVPacket packet;

  68. int frameFinished;

  69. int PictureSize;

  70. uint8_t *buf;

  71.  
  72. av_register_all();

  73.  
  74. if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0 ){

  75. printf ("av open input file failed!\n");

  76. exit (1);

  77. }

  78.  
  79. if ( avformat_find_stream_info(pFormatCtx,NULL) < 0 ){

  80. printf ("av find stream info failed!\n");

  81. exit (1);

  82. }

  83.  
  84. for ( i=0; i<pFormatCtx->nb_streams; i++ ){

  85. if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ){

  86. videoStream = i;

  87. break;

  88. }

  89. }

  90.  
  91. if (videoStream == -1){

  92. printf ("find video stream failed!\n");

  93. exit (1);

  94. }

  95.  
  96. pCodecCtx = pFormatCtx->streams[videoStream]->codec;

  97.  
  98. pCodec = avcodec_find_decoder (pCodecCtx->codec_id);

  99. if (pCodec == NULL){

  100. printf ("avcode find decoder failed!\n");

  101. exit (1);

  102. }

  103.  
  104.  
  105.  
  106.  
  107. if ( avcodec_open2(pCodecCtx, pCodec,NULL)<0 ){

  108. printf ("avcode open failed!\n");

  109. exit (1);

  110. }

  111.  
  112. pFrame = avcodec_alloc_frame();

  113. pFrameRGB = avcodec_alloc_frame();

  114.  
  115. if ( (pFrame == NULL)||(pFrameRGB == NULL) ){

  116. printf("avcodec alloc frame failed!\n");

  117. exit (1);

  118. }

  119.  
  120. PictureSize = avpicture_get_size (PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

  121.  
  122. buf = (uint8_t *)av_malloc(PictureSize);

  123.  
  124. if ( buf == NULL ){

  125. printf( "av malloc failed!\n");

  126. exit(1);

  127. }

  128.  
  129. avpicture_fill ( (AVPicture *)pFrameRGB, buf, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

  130.  
  131. pSwsCtx = sws_getContext (pCodecCtx->width,

  132. pCodecCtx->height,

  133. pCodecCtx->pix_fmt,

  134. pCodecCtx->width,

  135. pCodecCtx->height,

  136. PIX_FMT_BGR24,

  137. SWS_BICUBIC,

  138. NULL, NULL, NULL);

  139.  
  140. i = 0;

  141.  
  142. while(av_read_frame(pFormatCtx, &packet) >= 0){

  143. if(packet.stream_index == videoStream){

  144. avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

  145.  
  146. if(frameFinished){

  147. //反转图像

  148. pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);

  149. pFrame->linesize[0] *= -1;

  150. pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);

  151. pFrame->linesize[1] *= -1;

  152. pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);

  153. pFrame->linesize[2] *= -1;

  154.  
  155. sws_scale (pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

  156.  
  157. SaveAsBMP (pFrameRGB, pCodecCtx->width, pCodecCtx->height, i++, 24);

  158. }

  159. }

  160. av_free_packet(&packet);

  161. }

  162.  
  163. while(1){

  164. packet.data = NULL;

  165. packet.size = 0;

  166. avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

  167.  
  168. if(frameFinished){

  169. //反转图像

  170. pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);

  171. pFrame->linesize[0] *= -1;

  172. pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);

  173. pFrame->linesize[1] *= -1;

  174. pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);

  175. pFrame->linesize[2] *= -1;

  176.  
  177. sws_scale (pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

  178.  
  179. SaveAsBMP (pFrameRGB, pCodecCtx->width, pCodecCtx->height, i++, 24);

  180. }else{

  181. break;

  182. }

  183.  
  184. av_free_packet(&packet);

  185. }

  186.  
  187. sws_freeContext (pSwsCtx);

  188. av_free (pFrame);

  189. av_free (pFrameRGB);

  190. avcodec_close (pCodecCtx);

  191. avformat_close_input (&pFormatCtx);

  192.  
  193. return 0;

  194. }

  195.  
  196. #ifdef __cplusplus

  197. }

  198. #endif


 

视频文件保存图片的另外一个方法,看代码

 
  1. /*File : yuv2pic

  2. *Auth : sjin

  3. *Date : 20141123

  4. *Mail : [email protected]

  5. */

  6.  
  7. /*

  8. * 参考博客http://blog.csdn.net/leixiaohua1020/article/details/25346147

  9. *本程序实现了YUV420P像素数据编码为JPEG图片。是最简单的FFmpeg编码方面的教程。

  10. *通过学习本例子可以了解FFmpeg的编码流程。

  11. */

  12. #include <libavcodec/avcodec.h>

  13. #include <libavformat/avformat.h>

  14. #include <libswscale/swscale.h>

  15.  
  16. #define INPUT_FILE_NAME "yuv420p.yuv"

  17. #define OUTPUT_FILE_NAME "encode.png"

  18. #define INPUT_FILE_WDITH 176

  19. #define INPUT_FILE_HEIGHT 144

  20.  
  21. int main(int argc, char* argv[])

  22. {

  23. AVFormatContext* pFormatCtx;

  24. AVOutputFormat* fmt;

  25. AVStream* video_st;

  26. AVCodecContext* pCodecCtx;

  27. AVCodec* pCodec;

  28.  
  29. uint8_t* picture_buf;

  30. AVFrame* picture;

  31. int size;

  32.  
  33. FILE *in_file = fopen(INPUT_FILE_NAME, "rb"); //视频YUV源文件

  34. int in_w = INPUT_FILE_WDITH;

  35. int in_h = INPUT_FILE_HEIGHT; //宽高

  36. const char* out_file = OUTPUT_FILE_NAME; //输出文件路径

  37.  
  38. av_register_all();

  39. #if 0

  40. //方法1.组合使用几个函数

  41. pFormatCtx = avformat_alloc_context();

  42. //猜格式。用MJPEG编码

  43. fmt = av_guess_format("mjpeg", NULL, NULL);

  44. pFormatCtx->oformat = fmt;

  45. //注意:输出路径

  46. if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){

  47. printf("输出文件打开失败");

  48. return -1;

  49. }

  50. #else

  51. //方法2.更加自动化一些

  52. //分配一个输出(out_file)文件格式的AVFormatContext的上下文句柄

  53. avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);

  54. fmt = pFormatCtx->oformat;

  55.  
  56. video_st = avformat_new_stream(pFormatCtx,NULL);

  57. if (video_st==NULL){

  58. return -1;

  59. }

  60. #endif

  61. pCodecCtx = video_st->codec;

  62. pCodecCtx->codec_id = fmt->video_codec;

  63. pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;

  64. pCodecCtx->pix_fmt = PIX_FMT_YUVJ420P;

  65.  
  66. pCodecCtx->width = in_w;

  67. pCodecCtx->height = in_h;

  68.  
  69. pCodecCtx->time_base.num = 1;

  70. pCodecCtx->time_base.den = 25;

  71. //输出格式信息

  72. av_dump_format(pFormatCtx, 0, out_file, 1);

  73.  
  74. pCodec = avcodec_find_encoder(pCodecCtx->codec_id);

  75. if (!pCodec){

  76. printf("没有找到合适的编码器!");

  77. return -1;

  78. }

  79. if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){

  80. printf("编码器打开失败!");

  81. return -1;

  82. }

  83.  
  84. //申请解码后保存视频帧的空间,AVFrame结构体

  85. picture = avcodec_alloc_frame();

  86. //即使我们申请的一帧的内存,当转换的时候,我们仍需要内存去保存原始的数据

  87. //利用下面的函数来获得原始数据帧的大小,手动分配内存

  88. size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

  89. picture_buf = (uint8_t *)av_malloc(size);

  90. if (!picture_buf){

  91. return -1;

  92. }

  93. //设置指定图像的参数,并指着图像数据缓冲区

  94. avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

  95.  
  96. //写文件头

  97. avformat_write_header(pFormatCtx,NULL);

  98.  
  99. AVPacket pkt;

  100. int y_size = pCodecCtx->width * pCodecCtx->height;

  101. av_new_packet(&pkt,y_size*3);

  102. //读入YUV

  103. if (fread(picture_buf, 1, y_size*3/2, in_file) < 0){

  104. printf("文件读取错误");

  105. return -1;

  106. }

  107.  
  108. //翻转图像

  109. picture->data[0] = picture_buf; // 亮度Y

  110. picture->data[1] = picture_buf+ y_size; // U

  111. picture->data[2] = picture_buf+ y_size*5/4; // V

  112. int got_picture=0;

  113. //编码

  114. int ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);

  115. if(ret < 0){

  116. printf("编码错误!\n");

  117. return -1;

  118. }

  119. if (got_picture==1){

  120. pkt.stream_index = video_st->index;

  121. ret = av_write_frame(pFormatCtx, &pkt);

  122. }

  123.  
  124. av_free_packet(&pkt);

  125. //写文件尾

  126. av_write_trailer(pFormatCtx);

  127.  
  128. printf("编码成功!\n");

  129.  
  130. if (video_st){

  131. avcodec_close(video_st->codec);

  132. av_free(picture);

  133. av_free(picture_buf);

  134. }

  135.  
  136. avio_close(pFormatCtx->pb);

  137. avformat_free_context(pFormatCtx);

  138.  
  139. fclose(in_file);

  140.  
  141. return 0;

  142. }


下面是编译的时候,比较好用的Makefile文件

 
  1. # use pkg-config for getting CFLAGS and LDLIBS

  2. FFMPEG_LIBS= libavdevice \

  3. libavformat \

  4. libavfilter \

  5. libavcodec \

  6. libswresample \

  7. libswscale \

  8. libavutil \

  9.  
  10. CFLAGS += -Wall -O2 -g

  11. CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS)

  12. LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS)

  13.  
  14. EXAMPLES= yuv2pic

  15.  
  16. OBJS=$(addsuffix .o,$(EXAMPLES))

  17.  
  18. # the following examples make explicit use of the math library

  19. LDLIBS += -lx264 -m32 -pthread -lm -ldl

  20.  
  21. .phony:all clean

  22.  
  23. all: $(OBJS) $(EXAMPLES)

  24.  
  25. clean:

  26. rm $(EXAMPLES) $(OBJS)


 

参考资料:

1、http://blog.csdn.net/eightdegree/article/details/7425635#reply

2、http://blog.csdn.net/leixiaohua1020/article/details/25346147 

猜你喜欢

转载自blog.csdn.net/SUKHOI27SMK/article/details/81781177