读取视频帧保存为ppm格式图片

读取视频帧保存为ppm格式图片

ffmpeg 4.0.1
Qt 5.9

ppm图像格式解析:
https://blog.csdn.net/gengshenghong/article/details/8648577
https://blog.csdn.net/kinghzkingkkk/article/details/70226214


////////////////////////////////////////////////////////////////////////////////////////
///                     将视频中的5帧保存为ppm格式图像
////////////////////////////////////////////////////////////////////////////////////////
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
#include <stdio.h>

///
/// \brief SaveFrame 保存rgb为ppm图像
/// \param pFrame
/// \param width
/// \param height
/// \param iFrame
///
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
    FILE *pFile;
    char szFilename[32];
    int  y;

    // 打开文件
    sprintf(szFilename, "frame%d.ppm", iFrame);
    pFile=fopen(szFilename, "wb");
    if(pFile==NULL)
    return;

    // 写入图片的头部
    fprintf(pFile, "P6\n%d %d\n255\n", width, height);

    // 写入图片的rgb数据
    for(y=0; y<height; y++)
    {
        fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
    }

    // 关闭文件
    fclose(pFile);
}


int main(int argc, char *argv[])
{
    AVFormatContext *pFormatCtx = NULL;
    int             i, videoStream;
    AVCodecContext  *pCodecCtx = NULL;
    AVCodec         *pCodec = NULL;
    AVFrame         *pFrame = NULL;
    AVFrame         *pFrameRGB = NULL;
    AVPacket        packet;
    int             frameFinished;
    int             numBytes;
    uint8_t         *buffer = NULL;

    AVDictionary    *optionsDict = NULL;
    struct SwsContext      *sws_ctx = NULL;


    char *filename = "C:/Users/Administrator/Desktop/test.wmv";

    /*
    if(argc < 2)
    {
        printf("Please provide a movie file\n");
        return -1;
    }
    */

    // 注册解码器
    av_register_all();

    // 打开视频文件
    if(avformat_open_input(&pFormatCtx, filename, NULL, NULL)!=0)
    {
        // 打开失败
        return -1;
    }

    // 检索流信息
    if(avformat_find_stream_info(pFormatCtx, NULL)<0)
    {
        // 无法找到刘信息
        return -1;
    }

    // 输出流信息
    av_dump_format(pFormatCtx, 0, filename, 0);

    // 寻找视频流
    videoStream=-1;
    for(i=0; i<pFormatCtx->nb_streams; i++)
    {
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoStream=i;
            break;
        }
    }
    if(videoStream==-1)
    {
        // 找不到视频流
        return -1;
    }

    // 获取视频流的编解码器上下文
    pCodecCtx=pFormatCtx->streams[videoStream]->codec;

    // 获取视频解码器
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
    {
        fprintf(stderr, "Unsupported codec!\n");
        // 找不到解码器
        return -1;
    }

    // 打开解码器
    if(avcodec_open2(pCodecCtx, pCodec, &optionsDict)<0)
    {
        // 无法打开解码器
        return -1;
    }

    // 分配帧空间
    pFrame=av_frame_alloc();

    // 分配帧空间
    pFrameRGB=av_frame_alloc();
    if(pFrameRGB==NULL)
        return -1;

    // 分配一张图像的空间
    numBytes=avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
    buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

    sws_ctx =
    sws_getContext
    (
        pCodecCtx->width,
        pCodecCtx->height,
        pCodecCtx->pix_fmt,
        pCodecCtx->width,
        pCodecCtx->height,
        AV_PIX_FMT_RGB24,
        SWS_BILINEAR,
        NULL,
        NULL,
        NULL
    );

    // 将适当的缓冲区部分分配给pFrameRGB中的图像
    // 请注意,pFrameRGB是一个AVFrame,但AVFrame是AVPicture的超集
    avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);

    // 读取并保存视频中的5张图像
    i=0;
    while(av_read_frame(pFormatCtx, &packet)>=0)
    {
        // Is this a packet from the video stream?
        if(packet.stream_index==videoStream)
        {
            // 解码视频帧
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet);

            // 是否获取到一帧图像
            if(frameFinished)
            {
                // 将图像从其原始格式转换为RGB
                sws_scale
                (
                    sws_ctx,
                    (uint8_t const * const *)pFrame->data,
                    pFrame->linesize,
                    0,
                    pCodecCtx->height,
                    pFrameRGB->data,
                    pFrameRGB->linesize
                );

                // 保存一帧图像
                if(++i<=5)
                {
                    SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,i);
                }
            }
        }

        // 释放packet分配的内存空间
        av_free_packet(&packet);
    }

    // 释放内存空间
    av_free(buffer);
    av_free(pFrameRGB);

    // 释放YUV frame内存空间
    av_free(pFrame);

    // 关闭解码器
    avcodec_close(pCodecCtx);

    // 关闭打开的文件
    avformat_close_input(&pFormatCtx);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/wyy626562203/article/details/81208965