首先定义图片数据结构体用来存储转换之后的图片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;
通过封装的方法来对图片数据进行分配和释放。
//创建内存块
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;
}
将视频文件中的视频流提取出来,然后将视频流中的数据包解析成原始的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 , 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;
}
//提取帧数据中的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;
}
这里支持存储成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
#include
#include
#include
#include
#include
#include
#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 , 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;
}