zoukankan      html  css  js  c++  java
  • 【学习ffmpeg】打开视频文件,帧分析,并bmp保存关键帧

    http://www.tuicool.com/articles/jiUzua

    第一次接触ffmpeg,可以算是hello world程序。

    下面的代码全部都是直接可以使用的,借鉴了官方学习样例,也算是翻译吧。

    但是解决了,保存bmp图像时,图像颠倒和色彩异常问题。

    // x_ffmpeg.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <windows.h>
    #include <iostream>
    #include <iosfwd>
    #include <fstream>
    using namespace std;
    
    #define FILE_OUT
    #ifdef FILE_OUT
    std::ofstream file_debugout("frameandpacketinfo.txt");
    #endif
    
    static int av_create_bmp(char* filename,uint8_t *pRGBBuffer,int width,int height,int bpp)
    {
        BITMAPFILEHEADER bmpheader;
        BITMAPINFO bmpinfo;
        FILE *fp;
    
        fp = fopen(filename,"wb");
        if(!fp)return -1;
    
        bmpheader.bfType = ('M'<<8)|'B';
        bmpheader.bfReserved1 = 0;
        bmpheader.bfReserved2 = 0;
        bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
        bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;
        bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmpinfo.bmiHeader.biWidth = width;
        /*----注意,这里的bmpinfo.bmiHeader.biHeight变量的正负决定bmp文件的存储方式,如果
        为负值,表示像素是倒过来的*/
        bmpinfo.bmiHeader.biHeight = -height;
        bmpinfo.bmiHeader.biPlanes = 1;
        bmpinfo.bmiHeader.biBitCount = bpp;
        bmpinfo.bmiHeader.biCompression = BI_RGB;
        bmpinfo.bmiHeader.biSizeImage = 0;
        bmpinfo.bmiHeader.biXPelsPerMeter = 100;
        bmpinfo.bmiHeader.biYPelsPerMeter = 100;
        bmpinfo.bmiHeader.biClrUsed = 0;
        bmpinfo.bmiHeader.biClrImportant = 0;
    
        fwrite(&bmpheader,sizeof(BITMAPFILEHEADER),1,fp);
        fwrite(&bmpinfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
        fwrite(pRGBBuffer,width*height*bpp/8,1,fp);
        fclose(fp);
    
        return 0;
    }
    
    static int av_create_bmp(char* filename, AVFrame *pRGBBuffer,int width,int height,int bpp)
    {
        BITMAPFILEHEADER bmpheader;
        BITMAPINFO bmpinfo;
        FILE *fp;
    
        fp = fopen(filename, "wb");
        if(!fp)return -1;
    
        bmpheader.bfType = ('M'<<8)|'B';
        bmpheader.bfReserved1 = 0;
        bmpheader.bfReserved2 = 0;
        bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
        bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;
        bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmpinfo.bmiHeader.biWidth = width;
        bmpinfo.bmiHeader.biHeight = -height;
        bmpinfo.bmiHeader.biPlanes = 1;
        bmpinfo.bmiHeader.biBitCount = 24;
        bmpinfo.bmiHeader.biCompression = BI_RGB;
        bmpinfo.bmiHeader.biSizeImage = 0;
        bmpinfo.bmiHeader.biXPelsPerMeter = 100;
        bmpinfo.bmiHeader.biYPelsPerMeter = 100;
        bmpinfo.bmiHeader.biClrUsed = 0;
        bmpinfo.bmiHeader.biClrImportant = 0;
        fwrite(&bmpheader,sizeof(BITMAPFILEHEADER),1,fp);
        fwrite(&bmpinfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
        //fwrite(pRGBBuffer,width*height*bpp/8,1,fp);
        for(int y=0; y<height; y++)
            fwrite(pRGBBuffer->data[0] + y*pRGBBuffer->linesize[0], 1, width*3, fp);
        fclose(fp);
        return 0;
    }
    
    static void print_packet_info(AVPacket info)
    {
    #ifdef FILE_OUT
        file_debugout << "print_packet_info" << " convergence_duration:" << info.convergence_duration
            << " dts:" << info.dts 
            << " duration:" << info.duration
            << " flags:" << info.flags 
            << " pos:" << info.pos
            << " pts:" << info.pts
            << " size:" << info.size
            << " stream_index:" << info.stream_index << endl;
    #else
        cout << "print_packet_info" << " convergence_duration:" << info.convergence_duration
            << " dts:" << info.dts 
            << " duration:" << info.duration
            << " flags:" << info.flags 
            << " pos:" << info.pos
            << " pts:" << info.pts
            << " size:" << info.size
            << " stream_index:" << info.stream_index << endl;
    #endif
    }
    
    static void print_frame_info(AVFrame* pinfo)
    {
    #ifdef FILE_OUT
        file_debugout << "print_frame_info" << " coded_picture_number:" << pinfo->coded_picture_number
            << " display_picture_number:" << pinfo->display_picture_number 
            << " type:" << pinfo->type << endl;
    #else
        cout << "print_frame_info" << " coded_picture_number:" << pinfo->coded_picture_number
            << " display_picture_number:" << pinfo->display_picture_number 
            << " type:" << pinfo->type << endl;
    #endif
    
    }
    
    int decode_video_packet(AVFormatContext * fmt_ctx_for_decode, 
                             AVCodecContext* dec_ctx, int video_stream_index)
    {
        int ret = 0;
    
        AVFrame* pFrame=avcodec_alloc_frame();
        AVFrame* pFrameRGB = avcodec_alloc_frame();
    
        int numBytes=avpicture_get_size(PIX_FMT_BGR24, dec_ctx->width,dec_ctx->height); 
        uint8_t* buffer = new(std::nothrow) uint8_t[numBytes];
    
        avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_BGR24,
            dec_ctx->width, dec_ctx->height);
    
        SwsContext *pSWSCtx = sws_getContext(dec_ctx->width, dec_ctx->height, 
            dec_ctx->pix_fmt, dec_ctx->width, dec_ctx->height, PIX_FMT_BGR24,
            SWS_BICUBIC, NULL, NULL, NULL);
    
        if (NULL == pFrame || NULL == pFrameRGB || NULL == buffer || NULL == pSWSCtx)
        {
            ret = -1;
            goto exit;
        }
    
        AVPacket packet;
        int key_frame_picture_count = 0;
    
        while (av_read_frame(fmt_ctx_for_decode, &packet) >= 0)
        {
            if (packet.stream_index == video_stream_index)
            {
                int got_frame = 0;
                avcodec_decode_video2(dec_ctx, pFrame,&got_frame, &packet);
    
                if (got_frame) //一个完整的帧
                {
                    if (pFrame->key_frame)
                    {
                        sws_scale(pSWSCtx, pFrame->data, pFrame->linesize,0, 
                            dec_ctx->height, pFrameRGB->data, pFrameRGB->linesize);
    
                        // 保存到磁盘 
                        char pic[200]; 
                        sprintf(pic,"keyframe%d.bmp", ++key_frame_picture_count);
                        av_create_bmp(pic,pFrameRGB->data[0],dec_ctx->width,dec_ctx->height,24);
                        //av_create_bmp(pic, pFrameRGB, dec_ctx->width,dec_ctx->height,24);
                    }
                    print_frame_info(pFrame);
    
                }
    
                print_packet_info(packet);
            }
    
        }
    
    exit:
        avcodec_free_frame(&pFrame);
        avcodec_free_frame(&pFrameRGB);
        delete [] buffer;
        sws_freeContext(pSWSCtx);
        return ret;
    }
    
    static int open_input_file(const char *filename)
    {
        int ret;
        bool video_codec_init = false;
        int video_stream_index = -1;
        AVCodecContext* suitable_dec_ctx = NULL;
        AVFormatContext *video_fmt_ctx = NULL;
    
        if ((ret = avformat_open_input(&video_fmt_ctx, filename, NULL, NULL)) < 0) {
            av_log(NULL, AV_LOG_ERROR, "Cannot open input file
    ");
            return ret;
        }
    
        if ((ret = avformat_find_stream_info(video_fmt_ctx, NULL)) < 0) {
            av_log(NULL, AV_LOG_ERROR, "Cannot find stream information
    ");
            avformat_close_input(&video_fmt_ctx);
            return ret;
        }
    
        for (int i = 0; i < video_fmt_ctx->nb_streams; i++)
        {
            // 找到视频码流
            if (video_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                video_stream_index = i;
                // 初始化解码器信息
                if (!video_codec_init)
                {
                    suitable_dec_ctx = video_fmt_ctx->streams[i]->codec;
    
                     AVCodec* pcodec = avcodec_find_decoder(suitable_dec_ctx->codec_id);
    
                    if (NULL == pcodec)
                    {
                        printf("cannot find decoder");
                        avformat_close_input(&video_fmt_ctx);
                        return 1;
                    }
    
                    if(0 != avcodec_open2(suitable_dec_ctx, pcodec, NULL))
                    {
                        printf("open codecer failed");
                        avformat_close_input(&video_fmt_ctx);
                        return 1;
                    }
    
                    video_codec_init = true;
                }
    
            }
        }
    
        // 解码视频
        if (video_codec_init && suitable_dec_ctx)
        {
            decode_video_packet(video_fmt_ctx, suitable_dec_ctx, video_stream_index);
        }
    
        // 关闭文件
        avformat_close_input(&video_fmt_ctx);
    
        return 0;
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        // 注册库中所有可能有用的文件格式和编码器
        av_register_all();
        open_input_file("C:\Users\xukaijun.HIK\Desktop\hikvison.mp4");
    
    #ifdef FILE_OUT
        file_debugout.close();
    #endif
    
        return 0;
    }
  • 相关阅读:
    GreenPlum failover,primary和mirror切换实验 -- 重要
    Greenplum 激活standby 和恢复 master 原有角色
    GreenPlum 常用命令
    Greenplum 添加mirror步骤
    PostgreSQL 多版本的实现与Innodb和oracle的差别
    Oracle 与 postgreSQL 事务处理区别(多版本与undo区别)
    服务器使用bbr加速配置
    线表之队列
    线性表之栈
    线性表之单链表
  • 原文地址:https://www.cnblogs.com/jukan/p/7045296.html
Copyright © 2011-2022 走看看