本文章介绍ffmpeg基本使用流程
1. 创建编码器
//通过枚举id选择编码器类型
AVCodec *avcodec_find_encoder(enum AVCodecID id);
enum AVCodecID id --通过枚举选择编码器类型 AV_CODEC_ID_H264
//通过文件名称后缀选择编码器类型
AVCodec *avcodec_find_encoder_by_name(const char *name);
const char *name --文件名称 "1.mp4"
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec)
{
cout << " avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
return -1;
}
2. 创建编码上下文
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);const AVCodec *codec --上面创建的编码器
AVCodecContext *c = avcodec_alloc_context3(codec);
if (!c)
{
cout << " avcodec_alloc_context3 failed!" << endl;
return -1;
}
c->bit_rate = 400000000;//压缩比特率 视频压缩比例
c->width = width; //视频宽度
c->height = height; //视频高度
c->time_base = { 1, fps }; //时间基准 基本不用
c->framerate = { fps, 1 }; //帧率
c->gop_size = 50; //画面组大小,关键帧
c->max_b_frames = 0; //B帧 同等比特率下B帧越多压缩率越高画面越清晰
c->pix_fmt = AV_PIX_FMT_YUV420P; //像素格式
c->codec_id = AV_CODEC_ID_H264; //解码器id
c->thread_count = 8; //开的线程数量
//全局的编码信息
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
3. 打开编码器
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);AVCodecContext *avctx --编码器上下文
const AVCodec *codec --编码格式
AVDictionary **options --NULL
4. 创建输出的上下文
avformat_alloc_output_context2 --详见第一章
5. 创建视频流信息
avformat_new_stream --详见第一章
6. 根据提供的编解码器中的值填充参数结构
int avcodec_parameters_from_context(AVCodecParameters *par, const AVCodecContext *codec);AVCodecParameters *par --par中的任何分配字段都将被释放并替换为重复字段
const AVCodecContext *codec --编解码器中相应字段
AVFormatContext *oc = NULL;
avformat_alloc_output_context2(&oc, 0, 0, outfile); //创建上下文
AVStream *st = avformat_new_stream(oc, NULL); //创建视频流信息
//st->codec = c;
st->id = 0;
st->codecpar->codec_tag = 0;
//将编码上下文c的参数信息赋值分配给st->codecpar(存到视频流里面 封装文件头部)
avcodec_parameters_from_context(st->codecpar, c);
cout << "===============================================" << endl;
av_dump_format(oc, 0, outfile, 1); //打印编码器信息
cout << "===============================================" << endl;
7. 视频格式转换
struct SwsContext *sws_getCachedContext(struct SwsContext *context,
int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
struct SwsContext *context --上下文
int srcW, int srcH, enum AVPixelFormat srcFormat --输入宽度,输入高度,输入格式
int dstW, int dstH, enum AVPixelFormat dstFormat --输出宽度,输出高度,输出格式
int flags --选择算法
SwsFilter *srcFilter,SwsFilter *dstFilter --视频过滤器
const double *param --默认参数
8. 分配输入空间和输出空间
//输入空间
unsigned char *rgb = new unsigned char[width*height * 4];
//输出的空间
AVFrame *yuv = av_frame_alloc(); //存放编码前没有经过压缩的原始数据
yuv->format = AV_PIX_FMT_YUV420P; //像素输出格式
yuv->width = width;
yuv->height = height;
ret = av_frame_get_buffer(yuv, 32);//空间分配 32-》对齐方式
if (ret < 0)
{
cout << " av_frame_get_buffer failed!" << endl;
return -1;
}
9. 写入mp4头部
avformat_write_header --详见第一章
10.
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);struct SwsContext *c --上下文
const uint8_t *const srcSlice[] --
const int srcStride[],
int srcSliceY, int srcSliceH --深度值,高度
uint8_t *const dst[] --输出
const int dstStride[]
11.
//5 wirte mp4 head
ret = avio_open(&oc->pb, outfile, AVIO_FLAG_WRITE); //打开视频文件
if (ret < 0)
{
cout << " avio_open failed!" << endl;
getchar();
return -1;
}
ret = avformat_write_header(oc, NULL); //写入视频文件头部
if (ret < 0)
{
cout << " avformat_write_header failed!" << endl;
getchar();
return -1;
}
int p = 0;
for (;;)
{
int len = fread(rgb, 1, width*height * 4, fp);
if (len <= 0)
{
break;
}
uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
indata[0] = rgb;
int inlinesize[AV_NUM_DATA_POINTERS] = { 0 };
inlinesize[0] = width * 4;
int h = sws_scale(ctx, indata, inlinesize, 0, height,
yuv->data, yuv->linesize
);
if (h <= 0)
break;
//6 encode frame 编码
yuv->pts = p;
//yuv->pict_type = AV_PICTURE_TYPE_I; //指定该帧为关键帧
p = p + 3600;
ret = avcodec_send_frame(c, yuv);
if (ret != 0)
{
continue;
}
AVPacket pkt; //存放已编码的数据
av_init_packet(&pkt);
ret = avcodec_receive_packet(c, &pkt);
if (ret != 0)
continue;
//av_write_frame(oc, &pkt);
//av_packet_unref(&pkt); //清理pkt
av_interleaved_write_frame(oc, &pkt); //等价上面两步
cout << "<"<<pkt.size<<">";
}
//写入视频索引(尾部)
av_write_trailer(oc);
//关闭视频输出io
avio_close(oc->pb);
//清理封装输出上下文
avformat_free_context(oc);
//关闭编码器
avcodec_close(c);
//清理编码器上下文
avcodec_free_context(&c);
//清理视频重采样上下文
sws_freeContext(ctx);