您的位置:首页 > 其它

Windows10 + FFmpeg + VS2010 使用FFmpeg解码视频之保存图片

2019-02-13 15:38 381 查看

参考链接:http://blog.yundiantech.com/?log=blog&id=8

Windows10 + FFmpeg + VS2010 使用FFmpeg解码视频之保存图片

基于上一篇博文,配置好环境,并且小试牛刀之后,趁热打铁,再进行一个小工程。

打开VS2010,新建一个win32控制台应用程序,在debug64位下,进行编写。各种lib include dll的配置,参看上一篇博文。

功能内容:mp4的视频,截取视频流,保存成图片。代码如下:

[code]#include "stdafx.h"
#include<iostream>
#include<stdio.h>

using namespace std;

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
}

// 定义SaveFrame函数,把RGB信息搞到一个PPM格式的文件中
// 生成一个简单的PPM格式的文件
void SaveFrame(AVFrame *pFrame, int width, int height, int index)
{
    FILE *pFile;
    char szFilename[32];
    // open file
    sprintf(szFilename, "frame%d.ppm", index);
    pFile = fopen(szFilename, "wb");

    if(pFile == NULL)
        return ;

    //write header
    fprintf(pFile, "P6 %d %d 255", width, height);
    // write poxel data
    for(int y = 0; y < height; ++y)
    {
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
    }
    fclose(pFile);
}

int _tmain(int argc, _TCHAR* argv[])
{
    char *file_path = "D:\\Tkinter14.mp4";                    // 据说:打开的视频文件,不要有中文,否则会失败
    // 定义一些变量。
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    av_register_all();                                // 初始化FFMPEG, 调用了这个函数才能正常使用编码器和解码器
    
    pFormatCtx = avformat_alloc_context();            // 分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行

    if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0)
    {
        printf("Can not open the file.");
        return -1;
    }
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("Can not find stream information.");
        return -1;
    }

    int videoStream;
    videoStream = -1;

    // 循环查找视频中包含的流信息,知道找到视频类型的流
    // 便将其记录下来,保存到videoStream变量中,
    // 这里我们现在只处理视频流,音频流先不管
    for(int i = 0; i < pFormatCtx->nb_streams; ++i)
    {
        if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
        }
    }
    // 如果videoStream为-1,说明没有找到视频流
    if(videoStream == -1)
    {
        printf("Can not find a video stream.");
        return -1;
    }

    // *** 根据视频流,打开一个解码器来解码
    // 查找解码器
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec == NULL)
    {
        printf("Codec not found!");
        return -1;
    }
    // 打开解码器
    if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Can not open codec.");
        return -1;
    }

    pFrame = av_frame_alloc();        // 为其开辟空间
    pFrameRGB = av_frame_alloc();

    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
        pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);        // 原文件中,PIX_FMT_BGR24改为AV_PIX_FMT_BGR24,即可

    int numBytes;
    numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

    out_buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

    // *** 开始读取视频
    int y_size = pCodecCtx->width * pCodecCtx->height;
    packet = (AVPacket*)malloc(sizeof(AVPacket));                // 分配一个packet
    av_new_packet(packet, y_size);                                // 分配packet的数据

    av_dump_format(pFormatCtx, 0, file_path, 0);        // 输出视频信息

    int index = 0;
    int ret, got_picture;

    while(1)
    {
        if(av_read_frame(pFormatCtx, packet) < 0)
        {
            //return -1;
            break;            // 读取完视频,退出.
        }

        // *** 对视频进行解码
        if(packet->stream_index == videoStream)
        {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
            if(ret < 0)
            {
                printf("decode error.");
                return -1;
            }
            // *** 解码之后的图像数据都是YUV420格式,将其保存为图片文件,则需要将YUV420转换为RGB格式
            if(got_picture)
            {
                sws_scale(img_convert_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 
                    0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                // *** 将得到的RGB数据 直接写入文件。
                SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, ++index);        // 保存图片
                if (index > 20)
                {
                    return 0;        // 先保存20张图片
                }
            }
        }
        av_free_packet(packet);
    }

    // 释放空间

    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

进行测试代码时,每个return 语句处,打一个断点进行跟踪。

结果显示,无法找到文件。

使用下面这段代码,如截图中所示,添加,查看输出的错误是啥。

        // 此段主要是检查错误码用。
        char buf[] = "";
        int err_code = avformat_open_input(&pFormatCtx, file_path, NULL, NULL);
        av_strerror(err_code, buf, 1024);
        printf("Can not open the file.%s: %d(%s)\n", file_path, err_code, buf);

 

错误显示,找不到文件。原来是我的文件路径写错了,少了一个转译符\

修改后,即可用。(记录此错误,方便自己回顾。原文中的代码,没有问题。)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐