您的位置:首页 > 其它

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

2017-08-11 14:57 141 查看
转自:http://blog.csdn.net/oldmtn/article/details/46742555

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

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

参考:

1.最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)
http://blog.csdn href="http://lib.csdn.net/base/dotnet" target=_blank>.NET/leixiaohua1020/article/details/25346147

2.

视频帧保存为BMP

[cpp] view
plain copy

#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

[cpp] view
plain copy

[cpp] view
plain copy

//保存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);

}

[cpp] view
plain copy

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

[cpp] view
plain copy

#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

[cpp] view
plain copy

/**

* 将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;

}

[cpp] view
plain copy

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;

}

这里需要对

[cpp] view
plain copy

MyWriteJPEG

函数做点说明:
在调用avformat_new_stream前,pFormatCtx的nb_streams为0,表明当前没有流,调用avformat_new_stream后,该值为1.
表明我们新建了一个流,然后下面就是设定该流的相关信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: