使用ffmpeg 保存视频帧到png里面

本文介绍了一个使用FFmpeg库解析视频并提取特定帧将其保存为PNG图片的程序实例。该程序首先打开视频文件,查找视频流信息,然后初始化解码器进行视频解码,并将解码后的YUV图像数据转换为RGB格式,最终保存为PNG图片。
gcc main.c -o a.out -I$HOME/ffmpeg_build/include -L$HOME/ffmpeg_build/lib -lavformat -lavcodec -lavutil -lswscale -lavfilter -lswresample -lavdevice -lfdk-aac -lx264 -lx265 -lpthread -lm -lz -lva -lva-drm -lva-x11 -lX11 -lXext -lvdpau -lSDL2 -lpng && ./a.out
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/mathematics.h"
#include <png.h>


int main()
{
    AVFormatContext *pFormatCtx = avformat_alloc_context();
    if (avformat_open_input(&pFormatCtx, "../video_resources/test.mp4", NULL, NULL) != 0)
    {
        printf("Couldn't open input stream.\r\n");
        return -1;
    }
    // 获取视频信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("Couldn't find stream information.\r\n");
        return -1;
    }
    // 移动到视频的最后几帧
    int videoStream = -1;
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1)
    {
        printf("Didn't find a video stream.\r\n");
        return -1;
    }
    // 获取视频编码器
    AVCodecParameters *pCodecPar = pFormatCtx->streams[videoStream]->codecpar;
    AVCodec *pCodec = avcodec_find_decoder(pCodecPar->codec_id);
    if (pCodec == NULL)
    {
        printf("Codec not found.\r\n");
        return -1;
    }
    // 打开视频编码器
    AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec); // avcodec_alloc_context3的作用是分配一个AVCodecContext并设置默认值
    if (avcodec_parameters_to_context(pCodecCtx, pCodecPar) < 0)
    {
        printf("Couldn't copy codec context.\r\n");
        return -1;
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Couldn't open codec.\r\n");
        return -1;
    }

    AVFrame *pFrame = av_frame_alloc();
    AVFrame *pFrameRGB = av_frame_alloc();

    // 给视频解码
    AVPacket packet;
    int frameFinished;
    int i = 0;
    while (av_read_frame(pFormatCtx, &packet) >= 0)
    {
        if (packet.stream_index == videoStream)
        {
            avcodec_send_packet(pCodecCtx, &packet);
            frameFinished = avcodec_receive_frame(pCodecCtx, pFrame);
            // 保存packet
            if (frameFinished == 0)
            {
                if(i > 10){
                char filename[1024];
                //////////////把yuv数据保存为png图片
                sprintf(filename, "frame%d.png", i);
                FILE *fp = fopen(filename, "wb");
                if (fp == NULL)
                {
                    printf("open file error\r\n");
                    return -1;
                }
                png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
                if (png_ptr == NULL)
                {
                    printf("png_create_write_struct error\r\n");
                    return -1;
                }
                png_infop info_ptr = png_create_info_struct(png_ptr);
                if (info_ptr == NULL)
                {
                    printf("png_create_info_struct error\r\n");
                    return -1;
                }
                if (setjmp(png_jmpbuf(png_ptr)))
                {
                    printf("setjmp error\r\n");
                    return -1;
                }
                png_init_io(png_ptr, fp);
                png_set_IHDR(png_ptr, info_ptr, pCodecCtx->width, pCodecCtx->height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
                png_write_info(png_ptr, info_ptr);
                // pFrame->data[0]是Y分量,pFrame->data[1]是U分量,pFrame->data[2]是V分量 转换为RGB
                uint8_t *out_buffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1));
                if(out_buffer == NULL)
                {
                    printf("av_malloc error\r\n");
                    return -1;
                }
                av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
                struct SwsContext *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);
                if(img_convert_ctx == NULL)
                {
                    printf("sws_getContext error\r\n");
                    return -1;
                }
                sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                sws_freeContext(img_convert_ctx);
                //////////////把yuv数据保存为png图片
                png_bytep row_pointers[pCodecCtx->height]; // 每一行的指针 数组 用于写入 png 图片  
                for (int i = 0; i < pCodecCtx->height; i++) 
                {
                    row_pointers[i] = pFrameRGB->data[0] + i * pFrameRGB->linesize[0]; // 
                }
               

              
               

                // yuv有三个通道,分别是Y,U,V
                // Y通道的数据是连续的,U和V通道的数据是交错的
                // YUV420P的数据格式是YYYYYYYYUUVV
                // 现在要把YUV420P的数据可以直接存入png图片么 
                // for (int i = 0; i < pCodecCtx->height; i++)
                // {
                //     row_pointers[i] = pFrame->data[0] + i * pFrame->linesize[0];
                // }
                png_write_image(png_ptr, row_pointers);
                png_write_end(png_ptr, NULL);
                png_destroy_write_struct(&png_ptr, &info_ptr);
                printf("save frame%d.png\r\n", i);
                fclose(fp);



                // 保存yuv数据到图片文件
                // sprintf(filename, "../video_resources/frame%d.yuv", i);
                // FILE *pFile = fopen(filename, "wb");
                // if (pFile == NULL)
                // {
                //     printf("Couldn't open file.\r \n");
                //     return -1;
                // }
                // // 打印图片宽高
                // printf("width: %d, height: %d\r\n", pFrame->width, pFrame->height);
                // fwrite(pFrame->data[0], 1, pFrame->linesize[0] * pCodecCtx->height, pFile);
                // fwrite(pFrame->data[1], 1, pFrame->linesize[1] * pCodecCtx->height / 2, pFile);
                // fwrite(pFrame->data[2], 1, pFrame->linesize[2] * pCodecCtx->height / 2, pFile);
                // fclose(pFile);


                }
                
                i++;
                printf("frame%d\r\n", i);


               
            }

            // if (frameFinished == 0) // 
            // {
            //     i++;
            //     if (i > 100) 
            //     {
            //         break;
            //     }
            // }
        }
        av_packet_unref(&packet);
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值