ffmpeg C代码实现 把视频流转换成图片保存到本地

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qqqq245425070/article/details/87529209

用C代码实现,把视频中的帧转换成图片保存在本地。

#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include "libavutil/log.h"
int main(){
    // char* filename = "./jichi.mp4";
    //如果本地没有视频可以使用这个直播流地址
    char* filename = "http://weblive.hebtv.com/live/hbws_bq/index.m3u8";
    AVFormatContext* avf_cxt = avformat_alloc_context();
    int ret = avformat_open_input(&avf_cxt,filename,NULL,NULL);
    if(ret < 0){
        av_log(NULL,AV_LOG_ERROR,"不能打开文件\n");
        return -1;
    }
    ret = avformat_find_stream_info(avf_cxt,NULL);
    if(ret < 0){
        av_log(NULL,AV_LOG_ERROR,"找不到流数据\n");
        goto _end;
    }
    //打印视频信息
    av_dump_format(avf_cxt,0,filename,0);
    int video_index = -1;
    for(int i = 0 ; i < avf_cxt->nb_streams; i++){
        if(avf_cxt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            video_index = i;
            break;
        }
    }
    if(video_index == -1){
        av_log(NULL,AV_LOG_ERROR,"没有找到视频流\n");
        goto _end;
    }
    AVCodecContext* avc_cxt = avf_cxt->streams[video_index]->codec;
    enum AVCodecID codecId = avc_cxt->codec_id;
    AVCodec* codec = avcodec_find_decoder(codecId);
    if(!codec){
        av_log(NULL,AV_LOG_ERROR,"没有找到解码器\n");
        goto _end;
    }
    ret = avcodec_open2(avc_cxt,codec,NULL);
    if(ret < 0){
        av_log(NULL,AV_LOG_ERROR,"解码器无法打开\n");
        goto _end;
    }
    //为avpacket分配内存
    AVPacket* packet = av_packet_alloc();
    //为avFrame分配内存
    AVFrame* frame = av_frame_alloc();
    while(av_read_frame(avf_cxt,packet) >= 0){
        if(packet && packet->stream_index == video_index){
            int gotFrame = 0;
            ret = avcodec_decode_video2(avc_cxt,frame,&gotFrame,packet);
            if(gotFrame){
                ret = writeJPEG(frame,avc_cxt->width,avc_cxt->height);
                if(ret == 0){
                    break;
                }
            }
        }
    }

    _end:
    av_frame_free(frame);
    avcodec_close(avc_cxt);
    avformat_free_context(avf_cxt);
    return 0;
}
int writeJPEG(AVFrame* frame,int width,int height){
    const char* out_file = "hello_world.jpg";
    //新建一个输出的AVFormatContext 并分配内存
    AVFormatContext* output_cxt = avformat_alloc_context();
    avformat_alloc_output_context2(&output_cxt,NULL,"singlejpeg",out_file);

    //设置输出文件的格式
    // output_cxt->oformat = av_guess_format("mjpeg",NULL,NULL);

    //创建和初始化一个和该URL相关的AVIOContext
    if(avio_open(&output_cxt->pb,out_file,AVIO_FLAG_READ_WRITE) < 0){
        av_log(NULL,AV_LOG_ERROR,"不能打开文件  \n");
        return -1;
    }

    //构建新的Stream
    AVStream* stream = avformat_new_stream(output_cxt,NULL);
    if(stream == NULL){
        av_log(NULL,AV_LOG_ERROR,"创建AVStream失败  \n");
        return -1;
    }
    //初始化AVStream信息
    AVCodecContext* codec_cxt = stream->codec;

    codec_cxt->codec_id = output_cxt->oformat->video_codec;
    codec_cxt->codec_type = AVMEDIA_TYPE_VIDEO;
    codec_cxt->pix_fmt = AV_PIX_FMT_YUVJ420P;
    codec_cxt->height = height;
    codec_cxt->width = width;
    codec_cxt->time_base.num = 1;
    codec_cxt->time_base.den = 25;

    //打印输出文件信息
    av_dump_format(output_cxt,0,out_file,1);

    AVCodec* codec = avcodec_find_encoder(codec_cxt->codec_id);
    if(!codec){
        av_log(NULL,AV_LOG_ERROR,"找不到编码器  \n");
        return -1;
    }

    if(avcodec_open2(codec_cxt,codec,NULL) < 0){
        av_log(NULL,AV_LOG_ERROR,"不能打开编码器  \n");
        return -1;
    }
    avcodec_parameters_from_context(stream->codecpar,codec_cxt);

    //写入文件头
    avformat_write_header(output_cxt,NULL);
    int size = codec_cxt->width * codec_cxt->height;

    AVPacket* packet;
    av_new_packet(packet,size * 3);

    int got_picture = 0;
    int result = avcodec_encode_video2(codec_cxt,packet,frame,&got_picture);
    if(result < 0){
        av_log(NULL,AV_LOG_ERROR,"编码失败  \n");
        return -1;
    }
    printf("got_picture %d \n",got_picture);
    if(got_picture == 1){
        //将packet中的数据写入本地文件
        result = av_write_frame(output_cxt,packet);
    }
    av_free_packet(packet);
    //将流尾写入输出媒体文件并释放文件数据
    av_write_trailer(output_cxt);
    if(frame){
        av_frame_unref(frame);
    }
    avio_close(output_cxt->pb);
    avformat_free_context(output_cxt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qqqq245425070/article/details/87529209