转自:http://blog.csdn.net/oldmtn/article/details/46742555
一般我们知道播放视频流的时候是有截图功能的。
所以我想是否可以将视频流保存为BMP或者JPEG
参考:
1.最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)
http://blog.csdn.NET/leixiaohua1020/article/details/25346147
2.
视频帧保存为BMP
- #define __STDC_CONSTANT_MACROS
- #ifdef _WIN32
- //Windows
- extern "C"
- {
- #include "libavcodec/avcodec.h"
- #include "libavformat/avformat.h"
- #include "libswscale/swscale.h"
- #include "SDL.h"
- };
- #else
- //Linux...
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- #include <SDL2/SDL.h>
- #ifdef __cplusplus
- };
- #endif
- #endif
- //保存BMP文件的函数
- void SaveAsBMP(AVFrame *pFrameRGB, int width, int height, int index, int bpp)
- {
- char buf[5] = {0};
- BITMAPFILEHEADER bmpheader;
- BITMAPINFOHEADER bmpinfo;
- FILE *fp;
- char *filename = new char[255];
- //文件存放路径,根据自己的修改
- sprintf_s(filename, 255, "%s%d.bmp", "E:/temp/", index);
- if( (fp = fopen(filename,"wb+")) == NULL ) {
- printf ("open file failed!\n");
- return;
- }
- 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 = (width*bpp+31)/32*4*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);
- fwrite(pFrameRGB->data[0], width*height*bpp/8, 1, fp);
- fclose(fp);
- }
- DWORD Work_Save2BMP()
- {
- int videoStream = -1;
- AVCodecContext *pCodecCtx;
- AVFormatContext *pFormatCtx;
- AVCodec *pCodec;
- AVFrame *pFrame, *pFrameRGB;
- struct SwsContext *pSwsCtx;
- const char *filename = "bigbuckbunny_480x272.h264";
- AVPacket packet;
- int frameFinished;
- int PictureSize;
- uint8_t *outBuff;
- //注册编解码器
- av_register_all();
- // 初始化网络模块
- avformat_network_init();
- // 分配AVFormatContext
- pFormatCtx = avformat_alloc_context();
- //打开视频文件
- if( avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0 ) {
- printf ("av open input file failed!\n");
- exit (1);
- }
- //获取流信息
- if( avformat_find_stream_info(pFormatCtx, NULL) < 0 ) {
- printf ("av find stream info failed!\n");
- exit (1);
- }
- //获取视频流
- for( int i = 0; i < pFormatCtx->nb_streams; i++ ) {
- if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
- videoStream = i;
- break;
- }
- }
- if( videoStream == -1 ) {
- printf ("find video stream failed!\n");
- exit (1);
- }
- // 寻找解码器
- pCodecCtx = pFormatCtx->streams[videoStream]->codec;
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
- if( pCodec == NULL ) {
- printf ("avcode find decoder failed!\n");
- exit (1);
- }
- //打开解码器
- if( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 ) {
- printf ("avcode open failed!\n");
- exit (1);
- }
- //为每帧图像分配内存
- pFrame = avcodec_alloc_frame();
- pFrameRGB = avcodec_alloc_frame();
- if( (pFrame == NULL) || (pFrameRGB == NULL) ) {
- printf("avcodec alloc frame failed!\n");
- exit (1);
- }
- // 确定图片尺寸
- PictureSize = avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
- outBuff = (uint8_t*)av_malloc(PictureSize);
- if( outBuff == NULL ) {
- printf("av malloc failed!\n");
- exit(1);
- }
- avpicture_fill((AVPicture *)pFrameRGB, outBuff, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
- //设置图像转换上下文
- pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
- pCodecCtx->width, pCodecCtx->height, PIX_FMT_BGR24,
- SWS_BICUBIC, NULL, NULL, NULL);
- int i = 0;
- while( av_read_frame(pFormatCtx, &packet) >= 0 ) {
- if( packet.stream_index == videoStream ) {
- avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
- if( frameFinished ) {
- //反转图像 ,否则生成的图像是上下调到的
- pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);
- pFrame->linesize[0] *= -1;
- pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);
- pFrame->linesize[1] *= -1;
- pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);
- pFrame->linesize[2] *= -1;
- //转换图像格式,将解压出来的YUV420P的图像转换为BRG24的图像
- sws_scale(pSwsCtx, pFrame->data,
- pFrame->linesize, 0, pCodecCtx->height,
- pFrameRGB->data, pFrameRGB->linesize);
- SaveAsBMP(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i ++, 24);
- }
- } else {
- int a=2;
- int b=a;
- }
- av_free_packet(&packet);
- }
- sws_freeContext (pSwsCtx);
- av_free (pFrame);
- av_free (pFrameRGB);
- avcodec_close (pCodecCtx);
- avformat_close_input(&pFormatCtx);
- return 0;
- }
打开视频文件,将里面的每一帧取出来,并将其转换为RGB格式。
H264---(解码)-->YUV420---(转码).-->RGB---(保存)--->BMP
视频帧保存为JPEG
保存为JPEG和BMP有点类似,但是也有区别,具体流程是:
H264---(解码)-->YUV420---(转码).---(保存)--->JPEG
- #define __STDC_CONSTANT_MACROS
- #ifdef _WIN32
- //Windows
- extern "C"
- {
- #include "libavcodec/avcodec.h"
- #include "libavformat/avformat.h"
- #include "libswscale/swscale.h"
- #include "SDL.h"
- };
- #else
- //Linux...
- #ifdef __cplusplus
- extern "C"
- {
- #endif
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- #include <SDL2/SDL.h>
- #ifdef __cplusplus
- };
- #endif
- #endif
- /**
- * 将AVFrame(YUV420格式)保存为JPEG格式的图片
- *
- * @param width YUV420的宽
- * @param height YUV42的高
- *
- */
- int MyWriteJPEG(AVFrame* pFrame, int width, int height, int iIndex)
- {
- // 输出文件路径
- char out_file[MAX_PATH] = {0};
- sprintf_s(out_file, sizeof(out_file), "%s%d.jpg", "E:/temp/", iIndex);
- // 分配AVFormatContext对象
- AVFormatContext* pFormatCtx = avformat_alloc_context();
- // 设置输出文件格式
- pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);
- // 创建并初始化一个和该url相关的AVIOContext
- if( avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
- printf("Couldn't open output file.");
- return -1;
- }
- // 构建一个新stream
- AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);
- if( pAVStream == NULL ) {
- return -1;
- }
- // 设置该stream的信息
- AVCodecContext* pCodecCtx = pAVStream->codec;
- pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
- pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
- pCodecCtx->pix_fmt = PIX_FMT_YUVJ420P;
- pCodecCtx->width = width;
- pCodecCtx->height = height;
- pCodecCtx->time_base.num = 1;
- pCodecCtx->time_base.den = 25;
- // Begin Output some information
- av_dump_format(pFormatCtx, 0, out_file, 1);
- // End Output some information
- // 查找解码器
- AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
- if( !pCodec ) {
- printf("Codec not found.");
- return -1;
- }
- // 设置pCodecCtx的解码器为pCodec
- if( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 ) {
- printf("Could not open codec.");
- return -1;
- }
- //Write Header
- avformat_write_header(pFormatCtx, NULL);
- int y_size = pCodecCtx->width * pCodecCtx->height;
- //Encode
- // 给AVPacket分配足够大的空间
- AVPacket pkt;
- av_new_packet(&pkt, y_size * 3);
- //
- int got_picture = 0;
- int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
- if( ret < 0 ) {
- printf("Encode Error.\n");
- return -1;
- }
- if( got_picture == 1 ) {
- //pkt.stream_index = pAVStream->index;
- ret = av_write_frame(pFormatCtx, &pkt);
- }
- av_free_packet(&pkt);
- //Write Trailer
- av_write_trailer(pFormatCtx);
- printf("Encode Successful.\n");
- if( pAVStream ) {
- avcodec_close(pAVStream->codec);
- }
- avio_close(pFormatCtx->pb);
- avformat_free_context(pFormatCtx);
- return 0;
- }
- DWORD Work_Save2JPG()
- {
- int videoStream = -1;
- AVCodecContext *pCodecCtx;
- AVFormatContext *pFormatCtx;
- AVCodec *pCodec;
- AVFrame *pFrame, *pFrameRGB;
- struct SwsContext *pSwsCtx;
- const char *filename = "bigbuckbunny_480x272.h264";
- AVPacket packet;
- int frameFinished;
- int PictureSize;
- uint8_t *outBuff;
- //注册编解码器
- av_register_all();
- // 初始化网络模块
- avformat_network_init();
- // 分配AVFormatContext
- pFormatCtx = avformat_alloc_context();
- //打开视频文件
- if( avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0 ) {
- printf ("av open input file failed!\n");
- exit (1);
- }
- //获取流信息
- if( avformat_find_stream_info(pFormatCtx, NULL) < 0 ) {
- printf ("av find stream info failed!\n");
- exit (1);
- }
- //获取视频流
- for( int i = 0; i < pFormatCtx->nb_streams; i++ ) {
- if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
- videoStream = i;
- break;
- }
- }
- if( videoStream == -1 ) {
- printf ("find video stream failed!\n");
- exit (1);
- }
- // 寻找解码器
- pCodecCtx = pFormatCtx->streams[videoStream]->codec;
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
- if( pCodec == NULL ) {
- printf ("avcode find decoder failed!\n");
- exit (1);
- }
- //打开解码器
- if( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 ) {
- printf ("avcode open failed!\n");
- exit (1);
- }
- //为每帧图像分配内存
- pFrame = avcodec_alloc_frame();
- pFrameRGB = avcodec_alloc_frame();
- if( (pFrame == NULL) || (pFrameRGB == NULL) ) {
- printf("avcodec alloc frame failed!\n");
- exit (1);
- }
- // 确定图片尺寸
- PictureSize = avpicture_get_size(PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);
- outBuff = (uint8_t*)av_malloc(PictureSize);
- if( outBuff == NULL ) {
- printf("av malloc failed!\n");
- exit(1);
- }
- avpicture_fill((AVPicture *)pFrameRGB, outBuff, PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);
- //设置图像转换上下文
- pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
- pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUVJ420P,
- SWS_BICUBIC, NULL, NULL, NULL);
- int i = 0;
- while( av_read_frame(pFormatCtx, &packet) >= 0 ) {
- if( packet.stream_index == videoStream ) {
- avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
- if( frameFinished ) {
- // 保存为jpeg时不需要反转图像
- static bool b1 = true;
- if( b1 ) {
- MyWriteJPEG(pFrame, pCodecCtx->width, pCodecCtx->height, i ++);
- b1 = false;
- }
- //SaveAsBMP(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i ++, 24);
- }
- } else {
- int a=2;
- int b=a;
- }
- av_free_packet(&packet);
- }
- sws_freeContext(pSwsCtx);
- av_free(pFrame);
- av_free(pFrameRGB);
- avcodec_close(pCodecCtx);
- avformat_close_input(&pFormatCtx);
- return 0;
- }
- MyWriteJPEG
函数做点说明:
在调用avformat_new_stream前,pFormatCtx的nb_streams为0,表明当前没有流,调用avformat_new_stream后,该值为1.
表明我们新建了一个流,然后下面就是设定该流的相关信息。