ffmpeg(7):将h264编码的视频流保存为BMP或者JPEG图片

一般我们知道播放视频流的时候是有截图功能的。

所以我想是否可以将视频流保存为BMP或者JPEG

 

参考:

1.最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)

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

2. 

 

视频帧保存为BMP

 
  1.  
  2. #define __STDC_CONSTANT_MACROS

  3.  
  4.  
  5. #ifdef _WIN32

  6. //Windows

  7. extern "C"

  8. {

  9. #include "libavcodec/avcodec.h"

  10. #include "libavformat/avformat.h"

  11. #include "libswscale/swscale.h"

  12. #include "SDL.h"

  13. };

  14. #else

  15. //Linux...

  16. #ifdef __cplusplus

  17. extern "C"

  18. {

  19. #endif

  20. #include <libavcodec/avcodec.h>

  21. #include <libavformat/avformat.h>

  22. #include <libswscale/swscale.h>

  23. #include <SDL2/SDL.h>

  24. #ifdef __cplusplus

  25. };

  26. #endif

  27. #endif


 
  1. //保存BMP文件的函数

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

  3. {

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

  5. BITMAPFILEHEADER bmpheader;

  6. BITMAPINFOHEADER bmpinfo;

  7. FILE *fp;

  8.  
  9. char *filename = new char[255];

  10.  
  11. //文件存放路径,根据自己的修改

  12. sprintf_s(filename, 255, "%s%d.bmp", "E:/temp/", index);

  13. if( (fp = fopen(filename,"wb+")) == NULL ) {

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

  15. return;

  16. }

  17.  
  18. bmpheader.bfType = 0x4d42;

  19. bmpheader.bfReserved1 = 0;

  20. bmpheader.bfReserved2 = 0;

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

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

  23.  
  24. bmpinfo.biSize = sizeof(BITMAPINFOHEADER);

  25. bmpinfo.biWidth = width;

  26. bmpinfo.biHeight = height;

  27. bmpinfo.biPlanes = 1;

  28. bmpinfo.biBitCount = bpp;

  29. bmpinfo.biCompression = BI_RGB;

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

  31. bmpinfo.biXPelsPerMeter = 100;

  32. bmpinfo.biYPelsPerMeter = 100;

  33. bmpinfo.biClrUsed = 0;

  34. bmpinfo.biClrImportant = 0;

  35.  
  36. fwrite(&bmpheader, sizeof(bmpheader), 1, fp);

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

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

  39.  
  40. fclose(fp);

  41. }


 

 
  1. DWORD Work_Save2BMP()

  2. {

  3. int videoStream = -1;

  4. AVCodecContext *pCodecCtx;

  5. AVFormatContext *pFormatCtx;

  6. AVCodec *pCodec;

  7. AVFrame *pFrame, *pFrameRGB;

  8. struct SwsContext *pSwsCtx;

  9. const char *filename = "bigbuckbunny_480x272.h264";

  10. AVPacket packet;

  11. int frameFinished;

  12. int PictureSize;

  13. uint8_t *outBuff;

  14.  
  15. //注册编解码器

  16. av_register_all();

  17. // 初始化网络模块

  18. avformat_network_init();

  19. // 分配AVFormatContext

  20. pFormatCtx = avformat_alloc_context();

  21.  
  22. //打开视频文件

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

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

  25. exit (1);

  26. }

  27.  
  28. //获取流信息

  29. if( avformat_find_stream_info(pFormatCtx, NULL) < 0 ) {

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

  31. exit (1);

  32. }

  33. //获取视频流

  34. for( int i = 0; i < pFormatCtx->nb_streams; i++ ) {

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

  36. videoStream = i;

  37. break;

  38. }

  39. }

  40. if( videoStream == -1 ) {

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

  42. exit (1);

  43. }

  44.  
  45. // 寻找解码器

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

  47. pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

  48. if( pCodec == NULL ) {

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

  50. exit (1);

  51. }

  52.  
  53. //打开解码器

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

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

  56. exit (1);

  57. }

  58.  
  59. //为每帧图像分配内存

  60. pFrame = avcodec_alloc_frame();

  61. pFrameRGB = avcodec_alloc_frame();

  62. if( (pFrame == NULL) || (pFrameRGB == NULL) ) {

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

  64. exit (1);

  65. }

  66.  
  67. // 确定图片尺寸

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

  69. outBuff = (uint8_t*)av_malloc(PictureSize);

  70. if( outBuff == NULL ) {

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

  72. exit(1);

  73. }

  74. avpicture_fill((AVPicture *)pFrameRGB, outBuff, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

  75.  
  76. //设置图像转换上下文

  77. pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,

  78. pCodecCtx->width, pCodecCtx->height, PIX_FMT_BGR24,

  79. SWS_BICUBIC, NULL, NULL, NULL);

  80.  
  81. int i = 0;

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

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

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

  85.  
  86. if( frameFinished ) {

  87. //反转图像 ,否则生成的图像是上下调到的

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

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

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

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

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

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

  94.  
  95. //转换图像格式,将解压出来的YUV420P的图像转换为BRG24的图像

  96. sws_scale(pSwsCtx, pFrame->data,

  97. pFrame->linesize, 0, pCodecCtx->height,

  98. pFrameRGB->data, pFrameRGB->linesize);

  99.  
  100. SaveAsBMP(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i ++, 24);

  101. }

  102. } else {

  103. int a=2;

  104. int b=a;

  105. }

  106.  
  107. av_free_packet(&packet);

  108. }

  109.  
  110. sws_freeContext (pSwsCtx);

  111. av_free (pFrame);

  112. av_free (pFrameRGB);

  113. avcodec_close (pCodecCtx);

  114. avformat_close_input(&pFormatCtx);

  115.  
  116. return 0;

  117. }

其实总结起来就几点,

打开视频文件,将里面的每一帧取出来,并将其转换为RGB格式。

H264---(解码)-->YUV420---(转码).-->RGB---(保存)--->BMP

 

 

视频帧保存为JPEG

保存为JPEG和BMP有点类似,但是也有区别,具体流程是:

H264---(解码)-->YUV420---(转码).---(保存)--->JPEG

 

 
  1. #define __STDC_CONSTANT_MACROS

  2.  
  3.  
  4. #ifdef _WIN32

  5. //Windows

  6. extern "C"

  7. {

  8. #include "libavcodec/avcodec.h"

  9. #include "libavformat/avformat.h"

  10. #include "libswscale/swscale.h"

  11. #include "SDL.h"

  12. };

  13. #else

  14. //Linux...

  15. #ifdef __cplusplus

  16. extern "C"

  17. {

  18. #endif

  19. #include <libavcodec/avcodec.h>

  20. #include <libavformat/avformat.h>

  21. #include <libswscale/swscale.h>

  22. #include <SDL2/SDL.h>

  23. #ifdef __cplusplus

  24. };

  25. #endif

  26. #endif


 

 

 
  1. /**

  2. * 将AVFrame(YUV420格式)保存为JPEG格式的图片

  3. *

  4. * @param width YUV420的宽

  5. * @param height YUV42的高

  6. *

  7. */

  8. int MyWriteJPEG(AVFrame* pFrame, int width, int height, int iIndex)

  9. {

  10. // 输出文件路径

  11. char out_file[MAX_PATH] = {0};

  12. sprintf_s(out_file, sizeof(out_file), "%s%d.jpg", "E:/temp/", iIndex);

  13.  
  14. // 分配AVFormatContext对象

  15. AVFormatContext* pFormatCtx = avformat_alloc_context();

  16.  
  17. // 设置输出文件格式

  18. pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);

  19. // 创建并初始化一个和该url相关的AVIOContext

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

  21. printf("Couldn't open output file.");

  22. return -1;

  23. }

  24.  
  25. // 构建一个新stream

  26. AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);

  27. if( pAVStream == NULL ) {

  28. return -1;

  29. }

  30.  
  31. // 设置该stream的信息

  32. AVCodecContext* pCodecCtx = pAVStream->codec;

  33.  
  34. pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;

  35. pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;

  36. pCodecCtx->pix_fmt = PIX_FMT_YUVJ420P;

  37. pCodecCtx->width = width;

  38. pCodecCtx->height = height;

  39. pCodecCtx->time_base.num = 1;

  40. pCodecCtx->time_base.den = 25;

  41.  
  42. // Begin Output some information

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

  44. // End Output some information

  45.  
  46. // 查找解码器

  47. AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);

  48. if( !pCodec ) {

  49. printf("Codec not found.");

  50. return -1;

  51. }

  52. // 设置pCodecCtx的解码器为pCodec

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

  54. printf("Could not open codec.");

  55. return -1;

  56. }

  57.  
  58. //Write Header

  59. avformat_write_header(pFormatCtx, NULL);

  60.  
  61. int y_size = pCodecCtx->width * pCodecCtx->height;

  62.  
  63. //Encode

  64. // 给AVPacket分配足够大的空间

  65. AVPacket pkt;

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

  67.  
  68. //

  69. int got_picture = 0;

  70. int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);

  71. if( ret < 0 ) {

  72. printf("Encode Error.\n");

  73. return -1;

  74. }

  75. if( got_picture == 1 ) {

  76. //pkt.stream_index = pAVStream->index;

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

  78. }

  79.  
  80. av_free_packet(&pkt);

  81.  
  82. //Write Trailer

  83. av_write_trailer(pFormatCtx);

  84.  
  85. printf("Encode Successful.\n");

  86.  
  87. if( pAVStream ) {

  88. avcodec_close(pAVStream->codec);

  89. }

  90. avio_close(pFormatCtx->pb);

  91. avformat_free_context(pFormatCtx);

  92.  
  93. return 0;

  94. }

 

 
  1. DWORD Work_Save2JPG()

  2. {

  3. int videoStream = -1;

  4. AVCodecContext *pCodecCtx;

  5. AVFormatContext *pFormatCtx;

  6. AVCodec *pCodec;

  7. AVFrame *pFrame, *pFrameRGB;

  8. struct SwsContext *pSwsCtx;

  9. const char *filename = "bigbuckbunny_480x272.h264";

  10. AVPacket packet;

  11. int frameFinished;

  12. int PictureSize;

  13. uint8_t *outBuff;

  14.  
  15. //注册编解码器

  16. av_register_all();

  17. // 初始化网络模块

  18. avformat_network_init();

  19. // 分配AVFormatContext

  20. pFormatCtx = avformat_alloc_context();

  21.  
  22. //打开视频文件

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

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

  25. exit (1);

  26. }

  27.  
  28. //获取流信息

  29. if( avformat_find_stream_info(pFormatCtx, NULL) < 0 ) {

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

  31. exit (1);

  32. }

  33. //获取视频流

  34. for( int i = 0; i < pFormatCtx->nb_streams; i++ ) {

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

  36. videoStream = i;

  37. break;

  38. }

  39. }

  40. if( videoStream == -1 ) {

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

  42. exit (1);

  43. }

  44.  
  45. // 寻找解码器

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

  47. pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

  48. if( pCodec == NULL ) {

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

  50. exit (1);

  51. }

  52.  
  53. //打开解码器

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

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

  56. exit (1);

  57. }

  58.  
  59. //为每帧图像分配内存

  60. pFrame = avcodec_alloc_frame();

  61. pFrameRGB = avcodec_alloc_frame();

  62. if( (pFrame == NULL) || (pFrameRGB == NULL) ) {

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

  64. exit (1);

  65. }

  66.  
  67. // 确定图片尺寸

  68. PictureSize = avpicture_get_size(PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);

  69. outBuff = (uint8_t*)av_malloc(PictureSize);

  70. if( outBuff == NULL ) {

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

  72. exit(1);

  73. }

  74. avpicture_fill((AVPicture *)pFrameRGB, outBuff, PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);

  75.  
  76. //设置图像转换上下文

  77. pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,

  78. pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUVJ420P,

  79. SWS_BICUBIC, NULL, NULL, NULL);

  80.  
  81. int i = 0;

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

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

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

  85.  
  86. if( frameFinished ) {

  87. // 保存为jpeg时不需要反转图像

  88. static bool b1 = true;

  89. if( b1 ) {

  90. MyWriteJPEG(pFrame, pCodecCtx->width, pCodecCtx->height, i ++);

  91.  
  92. b1 = false;

  93. }

  94.  
  95. //SaveAsBMP(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i ++, 24);

  96. }

  97. } else {

  98. int a=2;

  99. int b=a;

  100. }

  101.  
  102. av_free_packet(&packet);

  103. }

  104.  
  105. sws_freeContext(pSwsCtx);

  106.  
  107. av_free(pFrame);

  108. av_free(pFrameRGB);

  109. avcodec_close(pCodecCtx);

  110. avformat_close_input(&pFormatCtx);

  111.  
  112. return 0;

  113. }


这里需要对

MyWriteJPEG

函数做点说明:

在调用avformat_new_stream前,pFormatCtx的nb_streams为0,表明当前没有流,调用avformat_new_stream后,该值为1.

表明我们新建了一个流,然后下面就是设定该流的相关信息。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值