[普通]ffmpeg将h264编码的视频流保存为BMP或者JPEG图片

作者(passion) 阅读(1531次) 评论(0) 分类( c++)

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

所以我想是否可以将视频流保存为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  
[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


#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;  
}

这里需要对

MyWriteJPEG  

函数做点说明:

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

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


« 上一篇:wifi共享上网(至尊版wifi)
« 下一篇:mongocxx基本增删改查操作
在这里写下您精彩的评论
  • 微信

  • QQ

  • 支付宝

返回首页
返回首页 img
返回顶部~
返回顶部 img