版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}