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

来源:互联网 发布:java init方法 编辑:程序博客网 时间:2024/05/21 17:55
转自:http://blog.csdn.net/oldmtn/article/details/46742555
一般我们知道播放视频流的时候是有截图功能的。

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

参考:

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

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

2. 

视频帧保存为BMP

[cpp] view plain copy
 
  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  
[cpp] view plain copy
 
  1.   
[cpp] view plain copy
 
  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. }  


[cpp] view plain copy
 
  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.     forint 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

[cpp] view plain copy
 
  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  
[cpp] view plain copy
 
  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. }  
[cpp] view plain copy
 
  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.     forint 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. }  
这里需要对
[cpp] view plain copy
 
  1. MyWriteJPEG  
函数做点说明:
在调用avformat_new_stream前,pFormatCtx的nb_streams为0,表明当前没有流,调用avformat_new_stream后,该值为1.
表明我们新建了一个流,然后下面就是设定该流的相关信息。
阅读全文
0 0
原创粉丝点击