FFmpeg基础:视频流转图片


在浏览视频的过程中,有时候我们需要将视频中的某一帧图像以图片的形式保存到本地。这时候我们就需要对视频流中的图像数据进行转码提取和数据存储了。视频流转图片的流程如下图所示:
在这里插入图片描述

1.定义图片RGB数据结构体

首先定义图片数据结构体用来存储转换之后的图片RGB数据

//定义RGB图片数据结构
typedef struct RGB888 {
    
    
	uint8_t r;
	uint8_t g;
	uint8_t b;
} RGB888;

typedef struct PICTURE {
    
    
	int32_t width;
	int32_t height;
	RGB888 *data;
} PICTURE;

2.定义分配和释放内存的C方法

通过封装的方法来对图片数据进行分配和释放。

//创建内存块
void *Memory_Alloc(size_t size)
{
    
    
	void *result = malloc(size);
	if (!result) {
    
    
		av_log(NULL,AV_LOG_DEBUG,"ERROR: Could not allocate enough memory");
	}
	memset(result, 0, size);
	return result;
}

//释放内存
void Memory_Free(void *memory)
{
    
    
	if (memory) {
    
    
		free(memory);
	}
}

//重新分配内存块
void *Memory_Realloc(void *memory, size_t size)
{
    
    
	void *result = realloc(memory, size);
	if (!result) {
    
    
		av_log(NULL, AV_LOG_DEBUG, "ERROR: Could not allocate enough memory");
	}
	return result;
}

//清空内存
void Memory_FreePointer(void *arg)
{
    
    
	void *memory;
	memcpy(&memory, arg, sizeof(void *));
	memcpy(arg, &(void *) {
    
     NULL }, sizeof(void *));
	Memory_Free(memory);
}

//根据宽高创建图片内存
PICTURE *Picture_Create(int width, int height)
{
    
    
	PICTURE *picture = Memory_Alloc(sizeof(PICTURE));
	picture->width = width;
	picture->height = height;
	picture->data = Memory_Alloc(width * height * sizeof(RGB888));
	return picture;
}

3.提取视频文件中的原始图像数据

将视频文件中的视频流提取出来,然后将视频流中的数据包解析成原始的YUV数据。

int main(int argc, char **argv)
{
    
    
	int ret;
	const char *filename, *outfilename;
	AVFormatContext *fmt_ctx = NULL;
	const AVCodec *codec;
	AVCodecContext *codec_ctx = NULL;
	AVStream *st = NULL;
	int stream_index;
	int frame_count;
	AVFrame *frame;
	struct SwsContext *img_convert_ctx;
	AVPacket avpkt;
	PICTURE* target_pic = NULL;
	if (argc <= 2) {
    
    
		fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
		exit(0);
	}

	filename = argv[1];
	outfilename = argv[2];

	//打开媒体文件的上下文
	if (avformat_open_input(&fmt_ctx, filename, NULL, NULL) < 0) {
    
    
		fprintf(stderr, "Could not open source file %s\n", filename);
		exit(1);
	}

	//获取流信息
	if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
    
    
		fprintf(stderr, "Could not find stream information\n");
		exit(1);
	}
	
	//查找视频流
	ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	if (ret < 0) {
    
    
		fprintf(stderr, "Could not find %s stream in input file '%s'\n",
			av_get_media_type_string(AVMEDIA_TYPE_VIDEO), filename);
		return ret;
	}

	stream_index = ret;
	st = fmt_ctx->streams[stream_index];

	//查找解码器
	codec = avcodec_find_decoder(st->codecpar->codec_id);
	if (!codec) {
    
    
		fprintf(stderr, "Failed to find %s codec\n",
			av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
		return AVERROR(EINVAL);
	}

	//设置编码器上下文的参数
	codec_ctx = avcodec_alloc_context3(NULL);
	if (!codec_ctx) {
    
    
		fprintf(stderr, "Could not allocate video codec context\n");
		exit(1);
	}

	if ((ret = avcodec_parameters_to_context(codec_ctx, st->codecpar)) < 0) {
    
    
		fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
			av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
		return ret;
	}

	//打开编码器
	if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
    
    
		fprintf(stderr, "Could not open codec\n");
		exit(1);
	}

	//初始化数据包
	av_init_packet(&avpkt);

	//初始化帧信息
	frame = av_frame_alloc();
	if (!frame) {
    
    
		fprintf(stderr, "Could not allocate video frame\n");
		exit(1);
	}

	//读取帧数据并保存成图片
	frame_count = 0;
	while (av_read_frame(fmt_ctx, &avpkt) >= 0) 
	{
    
    
		if (avpkt.stream_index == stream_index) 
		{
    
    
			int ret = decode_frame(codec_ctx, frame, &frame_count, &avpkt, &target_pic);
			if (ret == 0)
			{
    
    
				char buf[1024];
				snprintf(buf, sizeof(buf), "%s-%d.jpg", outfilename, frame_count);
				SavePictureToFile(target_pic, buf);
				Memory_FreePointer(&target_pic->data);
			}
		}
		av_packet_unref(&avpkt);
	}
	//数据清理
	avformat_close_input(&fmt_ctx);
	avcodec_free_context(&codec_ctx);
	av_frame_free(&frame);

	return 0;
}

4.将YUV数据转换成图片RGB数据

//提取帧数据中的YUV数据并转换成RGB888
int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *frame_count, AVPacket *pkt, PICTURE** target_pic)
{
    
    
	int ret, len = 0;
	char buf[1024];
	struct SwsContext *sws_ctx = NULL;
	uint8_t *dst_data[4] = {
    
     0 };
	int dst_linesize[4] = {
    
     0 };

	//解析数据包
	ret = avcodec_send_packet(avctx, pkt);

	if (ret < 0) {
    
    
		fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret));
		return ret;
	}
	//读取数据帧信息
	ret = avcodec_receive_frame(avctx, frame);
	if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		return;
	else if (ret < 0)
	{
    
    
		fprintf(stderr, "Error during decoding\n");
		return 1;
	}
    //分配图片内存
	*target_pic = Picture_Create(frame->width, frame->height);
	
	//数据格式转换上下文
	//YUV---RGB888
	sws_ctx = sws_getContext(
		avctx->width, avctx->height, avctx->pix_fmt,
		(*target_pic)->width, (*target_pic)->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,
		NULL, NULL, NULL);

	if (!sws_ctx) {
    
    
		Memory_FreePointer(&(*target_pic)->data);
		return 2;
	}

	ret = av_image_alloc(
		dst_data, dst_linesize, (*target_pic)->width, (*target_pic)->height,
		AV_PIX_FMT_RGB24, 1);

	if (ret < 0) {
    
    
		Memory_FreePointer(&(*target_pic)->data);
		return 3;
	}

	//转换数据格式
	sws_scale(
		sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0,
		frame->height, dst_data, dst_linesize);

	//拷贝数据内容
	av_image_copy_to_buffer(
		(uint8_t *)(*target_pic)->data,
		(*target_pic)->width *  (*target_pic)->height * sizeof(RGB888),
		(const uint8_t *const *)dst_data, dst_linesize, AV_PIX_FMT_RGB24,
		(*target_pic)->width, (*target_pic)->height, 1);

	//清理分配的内存
	fflush(stdout);
	av_freep(&dst_data[0]);
	if (sws_ctx) {
    
    
		sws_freeContext(sws_ctx);
	}
	(*frame_count)++;
	return 0;
}

5.将RGB数据保存成对应的图片

这里支持存储成png图片和jpg图片

//将图片数据写入到文件中
//根据文件后缀来确定图片的格式,支持png和jpg
int SavePictureToFile(const PICTURE *pic, const char *path)
{
    
    
	int ret = 0;
	int error_code = 0;
	const AVCodec *codec = NULL;
	AVCodecContext *codec_ctx = NULL;
	AVFrame *frame = NULL;
	AVPacket *packet = NULL;
	struct SwsContext *sws_ctx = NULL;
	FILE *fp = NULL;

	//源格式
	enum AVPixelFormat source_pix_fmt = AV_PIX_FMT_RGB24;

	//目标格式
	enum AVPixelFormat target_pix_fmt;
	enum AVCodecID codec_id;

	//判断输出文件的类型
	if (strstr(path, ".jpg")) 
	{
    
    
		target_pix_fmt = AV_PIX_FMT_YUVJ420P;
		codec_id = AV_CODEC_ID_MJPEG;
	}
	else if (strstr(path, ".png")) {
    
    
		target_pix_fmt = AV_PIX_FMT_RGB24;
		codec_id = AV_CODEC_ID_PNG;
	}
	else 
	{
    
    
		goto cleanup;
	}

	fp = fopen(path, "wb");
	if (!fp) {
    
    
		goto cleanup;
	}

	//查找图片编码器
	codec = avcodec_find_encoder(codec_id);
	if (!codec) {
    
    
		error_code = AVERROR_MUXER_NOT_FOUND;
		goto cleanup;
	}

	//设置编码器的参数
	codec_ctx = avcodec_alloc_context3(codec);
	if (!codec_ctx) {
    
    
		error_code = AVERROR(ENOMEM);
		goto cleanup;
	}
	codec_ctx->bit_rate = 400000;
	codec_ctx->width = pic->width;
	codec_ctx->height = pic->height;
	codec_ctx->time_base = (AVRational) {
    
     1, 25 };
	codec_ctx->pix_fmt = target_pix_fmt;

	if (codec_id == AV_CODEC_ID_MJPEG) 
	{
    
    
		// 9 JPEG quality
		codec_ctx->flags |= AV_CODEC_FLAG_QSCALE;
		codec_ctx->global_quality = FF_QP2LAMBDA * 9;
	}

	//打开编码器的上下文
	error_code = avcodec_open2(codec_ctx, codec, NULL);
	if (error_code < 0)
	{
    
    
		goto cleanup;
	}

	//创建帧
	frame = av_frame_alloc();
	if (!frame) {
    
    
		error_code = AVERROR(ENOMEM);
		goto cleanup;
	}
	frame->format = codec_ctx->pix_fmt;
	frame->width = codec_ctx->width;
	frame->height = codec_ctx->height;
	frame->pts = 0;

	error_code = av_image_alloc(
		frame->data, frame->linesize, codec_ctx->width, codec_ctx->height,
		codec_ctx->pix_fmt, 32);

	if (error_code < 0) {
    
    
		goto cleanup;
	}
	//创建数据包
	packet = av_packet_alloc();
	av_new_packet(packet, 0);

	//获取图片处理对象的上下文
	sws_ctx = sws_getContext(
		pic->width, pic->height, source_pix_fmt, frame->width, frame->height,
		target_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);

	if (!sws_ctx) {
    
    
		error_code = AVERROR_EXTERNAL;
		goto cleanup;
	}

	//将图片数据添加到帧中
	uint8_t *src_planes[4];
	int src_linesize[4];
	av_image_fill_arrays(
		src_planes, src_linesize, (const uint8_t *)pic->data, source_pix_fmt,
		pic->width, pic->height, 1);

	sws_scale(
		sws_ctx, (const uint8_t *const *)src_planes, src_linesize, 0,
		pic->height, frame->data, frame->linesize);

	//编码器对像素格式进行转换
	//将处理后的目标格式帧数据,写入到文件中
	error_code = avcodec_send_frame(codec_ctx, frame);
	if (error_code < 0) {
    
    
		goto cleanup;
	}
	while (error_code >= 0) {
    
    
		error_code = avcodec_receive_packet(codec_ctx, packet);
		if (error_code == AVERROR(EAGAIN) || error_code == AVERROR_EOF) {
    
    
			error_code = 0;
			break;
		}
		if (error_code < 0) {
    
    
			goto cleanup;
		}
		fwrite(packet->data, 1, packet->size, fp);
		av_packet_unref(packet);
	}
	//清理释放数据
cleanup:
	if (error_code) {
    
    
		av_log(NULL,AV_LOG_DEBUG,
			"Error while saving picture %s: %s", path, av_err2str(error_code));
	}
	if (fp) {
    
    
		fclose(fp);
		fp = NULL;
	}
	if (sws_ctx) {
    
    
		sws_freeContext(sws_ctx);
	}

	if (packet) {
    
    
		av_packet_free(&packet);
	}

	if (codec) {
    
    
		avcodec_close(codec_ctx);
		av_free(codec_ctx);
		codec_ctx = NULL;
	}
	if (frame) {
    
    
		av_freep(&frame->data[0]);
		av_frame_free(&frame);
	}
	return ret;
}

完整工程代码

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>

#pragma pack(2)

//定义RGB图片数据结构
typedef struct RGB888 {
    
    
	uint8_t r;
	uint8_t g;
	uint8_t b;
} RGB888;

typedef struct PICTURE {
    
    
	int32_t width;
	int32_t height;
	RGB888 *data;
} PICTURE;

//创建内存块
void *Memory_Alloc(size_t size)
{
    
    
	void *result = malloc(size);
	if (!result) {
    
    
		av_log(NULL,AV_LOG_DEBUG,"ERROR: Could not allocate enough memory");
	}
	memset(result, 0, size);
	return result;
}

//释放内存
void Memory_Free(void *memory)
{
    
    
	if (memory) {
    
    
		free(memory);
	}
}

//重新分配内存块
void *Memory_Realloc(void *memory, size_t size)
{
    
    
	void *result = realloc(memory, size);
	if (!result) {
    
    
		av_log(NULL, AV_LOG_DEBUG, "ERROR: Could not allocate enough memory");
	}
	return result;
}

//清空内存
void Memory_FreePointer(void *arg)
{
    
    
	void *memory;
	memcpy(&memory, arg, sizeof(void *));
	memcpy(arg, &(void *) {
    
     NULL }, sizeof(void *));
	Memory_Free(memory);
}

//根据宽高创建图片内存
PICTURE *Picture_Create(int width, int height)
{
    
    
	PICTURE *picture = Memory_Alloc(sizeof(PICTURE));
	picture->width = width;
	picture->height = height;
	picture->data = Memory_Alloc(width * height * sizeof(RGB888));
	return picture;
}


//将图片数据写入到文件中
//根据文件后缀来确定图片的格式,支持png和jpg
int SavePictureToFile(const PICTURE *pic, const char *path)
{
    
    
	int ret = 0;
	int error_code = 0;
	const AVCodec *codec = NULL;
	AVCodecContext *codec_ctx = NULL;
	AVFrame *frame = NULL;
	AVPacket *packet = NULL;
	struct SwsContext *sws_ctx = NULL;
	FILE *fp = NULL;

	//源格式
	enum AVPixelFormat source_pix_fmt = AV_PIX_FMT_RGB24;

	//目标格式
	enum AVPixelFormat target_pix_fmt;
	enum AVCodecID codec_id;

	//判断输出文件的类型
	if (strstr(path, ".jpg")) 
	{
    
    
		target_pix_fmt = AV_PIX_FMT_YUVJ420P;
		codec_id = AV_CODEC_ID_MJPEG;
	}
	else if (strstr(path, ".png")) {
    
    
		target_pix_fmt = AV_PIX_FMT_RGB24;
		codec_id = AV_CODEC_ID_PNG;
	}
	else 
	{
    
    
		goto cleanup;
	}

	fp = fopen(path, "wb");
	if (!fp) {
    
    
		goto cleanup;
	}

	//查找图片编码器
	codec = avcodec_find_encoder(codec_id);
	if (!codec) {
    
    
		error_code = AVERROR_MUXER_NOT_FOUND;
		goto cleanup;
	}

	//设置编码器的参数
	codec_ctx = avcodec_alloc_context3(codec);
	if (!codec_ctx) {
    
    
		error_code = AVERROR(ENOMEM);
		goto cleanup;
	}
	codec_ctx->bit_rate = 400000;
	codec_ctx->width = pic->width;
	codec_ctx->height = pic->height;
	codec_ctx->time_base = (AVRational) {
    
     1, 25 };
	codec_ctx->pix_fmt = target_pix_fmt;

	if (codec_id == AV_CODEC_ID_MJPEG) 
	{
    
    
		// 9 JPEG quality
		codec_ctx->flags |= AV_CODEC_FLAG_QSCALE;
		codec_ctx->global_quality = FF_QP2LAMBDA * 9;
	}

	//打开编码器的上下文
	error_code = avcodec_open2(codec_ctx, codec, NULL);
	if (error_code < 0)
	{
    
    
		goto cleanup;
	}

	//创建帧
	frame = av_frame_alloc();
	if (!frame) {
    
    
		error_code = AVERROR(ENOMEM);
		goto cleanup;
	}
	frame->format = codec_ctx->pix_fmt;
	frame->width = codec_ctx->width;
	frame->height = codec_ctx->height;
	frame->pts = 0;

	error_code = av_image_alloc(
		frame->data, frame->linesize, codec_ctx->width, codec_ctx->height,
		codec_ctx->pix_fmt, 32);

	if (error_code < 0) {
    
    
		goto cleanup;
	}
	//创建数据包
	packet = av_packet_alloc();
	av_new_packet(packet, 0);

	//获取图片处理对象的上下文
	sws_ctx = sws_getContext(
		pic->width, pic->height, source_pix_fmt, frame->width, frame->height,
		target_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);

	if (!sws_ctx) {
    
    
		error_code = AVERROR_EXTERNAL;
		goto cleanup;
	}

	//将图片数据添加到帧中
	uint8_t *src_planes[4];
	int src_linesize[4];
	av_image_fill_arrays(
		src_planes, src_linesize, (const uint8_t *)pic->data, source_pix_fmt,
		pic->width, pic->height, 1);

	sws_scale(
		sws_ctx, (const uint8_t *const *)src_planes, src_linesize, 0,
		pic->height, frame->data, frame->linesize);

	//编码器对像素格式进行转换
	//将处理后的目标格式帧数据,写入到文件中
	error_code = avcodec_send_frame(codec_ctx, frame);
	if (error_code < 0) {
    
    
		goto cleanup;
	}
	while (error_code >= 0) {
    
    
		error_code = avcodec_receive_packet(codec_ctx, packet);
		if (error_code == AVERROR(EAGAIN) || error_code == AVERROR_EOF) {
    
    
			error_code = 0;
			break;
		}
		if (error_code < 0) {
    
    
			goto cleanup;
		}
		fwrite(packet->data, 1, packet->size, fp);
		av_packet_unref(packet);
	}
	//清理释放数据
cleanup:
	if (error_code) {
    
    
		av_log(NULL,AV_LOG_DEBUG,
			"Error while saving picture %s: %s", path, av_err2str(error_code));
	}
	if (fp) {
    
    
		fclose(fp);
		fp = NULL;
	}
	if (sws_ctx) {
    
    
		sws_freeContext(sws_ctx);
	}

	if (packet) {
    
    
		av_packet_free(&packet);
	}

	if (codec) {
    
    
		avcodec_close(codec_ctx);
		av_free(codec_ctx);
		codec_ctx = NULL;
	}
	if (frame) {
    
    
		av_freep(&frame->data[0]);
		av_frame_free(&frame);
	}
	return ret;
}

//提取帧数据中的YUV数据并转换成RGB888
int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *frame_count, AVPacket *pkt, PICTURE** target_pic)
{
    
    
	int ret, len = 0;
	char buf[1024];
	struct SwsContext *sws_ctx = NULL;
	uint8_t *dst_data[4] = {
    
     0 };
	int dst_linesize[4] = {
    
     0 };

	//解析数据包
	ret = avcodec_send_packet(avctx, pkt);

	if (ret < 0) {
    
    
		fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret));
		return ret;
	}
	//获取对应的数据帧,一个包里面可能有很多帧
	ret = avcodec_receive_frame(avctx, frame);
	if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		return;
	else if (ret < 0)
	{
    
    
		fprintf(stderr, "Error during decoding\n");
		return 1;
	}
	*target_pic = Picture_Create(frame->width, frame->height);
	
	//数据格式转换上下文
	//YUV---RGB888
	sws_ctx = sws_getContext(
		avctx->width, avctx->height, avctx->pix_fmt,
		(*target_pic)->width, (*target_pic)->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,
		NULL, NULL, NULL);

	if (!sws_ctx) {
    
    
		Memory_FreePointer(&(*target_pic)->data);
		return 2;
	}

	ret = av_image_alloc(
		dst_data, dst_linesize, (*target_pic)->width, (*target_pic)->height,
		AV_PIX_FMT_RGB24, 1);

	if (ret < 0) {
    
    
		Memory_FreePointer(&(*target_pic)->data);
		return 3;
	}

	//转换数据格式
	sws_scale(
		sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0,
		frame->height, dst_data, dst_linesize);

	//拷贝数据内容
	av_image_copy_to_buffer(
		(uint8_t *)(*target_pic)->data,
		(*target_pic)->width *  (*target_pic)->height * sizeof(RGB888),
		(const uint8_t *const *)dst_data, dst_linesize, AV_PIX_FMT_RGB24,
		(*target_pic)->width, (*target_pic)->height, 1);

	//将帧数据中第一个图片保存成文件
	fflush(stdout);
	av_freep(&dst_data[0]);
	if (sws_ctx) {
    
    
		sws_freeContext(sws_ctx);
	}
	(*frame_count)++;
	return 0;
}

int main(int argc, char **argv)
{
    
    
	int ret;
	const char *filename, *outfilename;
	AVFormatContext *fmt_ctx = NULL;
	const AVCodec *codec;
	AVCodecContext *codec_ctx = NULL;
	AVStream *st = NULL;
	int stream_index;
	int frame_count;
	AVFrame *frame;
	struct SwsContext *img_convert_ctx;
	AVPacket avpkt;
	PICTURE* target_pic = NULL;
	if (argc <= 2) {
    
    
		fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
		exit(0);
	}

	filename = argv[1];
	outfilename = argv[2];

	//打开媒体文件的上下文
	if (avformat_open_input(&fmt_ctx, filename, NULL, NULL) < 0) {
    
    
		fprintf(stderr, "Could not open source file %s\n", filename);
		exit(1);
	}

	//获取流信息
	if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
    
    
		fprintf(stderr, "Could not find stream information\n");
		exit(1);
	}
	
	//查找视频流
	ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	if (ret < 0) {
    
    
		fprintf(stderr, "Could not find %s stream in input file '%s'\n",
			av_get_media_type_string(AVMEDIA_TYPE_VIDEO), filename);
		return ret;
	}

	stream_index = ret;
	st = fmt_ctx->streams[stream_index];

	//查找解码器
	codec = avcodec_find_decoder(st->codecpar->codec_id);
	if (!codec) {
    
    
		fprintf(stderr, "Failed to find %s codec\n",
			av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
		return AVERROR(EINVAL);
	}

	//设置编码器上下文的参数
	codec_ctx = avcodec_alloc_context3(NULL);
	if (!codec_ctx) {
    
    
		fprintf(stderr, "Could not allocate video codec context\n");
		exit(1);
	}

	if ((ret = avcodec_parameters_to_context(codec_ctx, st->codecpar)) < 0) {
    
    
		fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
			av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
		return ret;
	}

	//打开编码器
	if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
    
    
		fprintf(stderr, "Could not open codec\n");
		exit(1);
	}

	//初始化数据包
	av_init_packet(&avpkt);

	//初始化帧信息
	frame = av_frame_alloc();
	if (!frame) {
    
    
		fprintf(stderr, "Could not allocate video frame\n");
		exit(1);
	}

	//读取帧数据并保存成图片
	frame_count = 0;
	while (av_read_frame(fmt_ctx, &avpkt) >= 0) 
	{
    
    
		if (avpkt.stream_index == stream_index) 
		{
    
    
			int ret = decode_frame(codec_ctx, frame, &frame_count, &avpkt, &target_pic);
			if (ret == 0)
			{
    
    
				char buf[1024];
				snprintf(buf, sizeof(buf), "%s-%d.jpg", outfilename, frame_count);
				SavePictureToFile(target_pic, buf);
				Memory_FreePointer(&target_pic->data);
			}
		}
		av_packet_unref(&avpkt);
	}
	//数据清理
	avformat_close_input(&fmt_ctx);
	avcodec_free_context(&codec_ctx);
	av_frame_free(&frame);

	return 0;
}

猜你喜欢

转载自blog.csdn.net/yang1fei2/article/details/127271163