转载地址1
转载地址2
模块:
libavcodec - 编码解码器
libavdevice - 输入输出设备的支持
libavfilter - 视音频滤镜支持
libavformat - 视音频等格式的解析
libavutil - 工具库
libpostproc - 后期效果处理
libswscale - 图像颜色、尺寸转换
大致流程:
调用注册函数;
open_input_file打开输入文件;
open_output_file打开输出文件;
init_filters() 过滤器初始化;
完成 avformatcontext—-avpacket—-avstream—-avcodeccontext—-avcodec 解析包逐步得到编解码类型 ,其中av_read_frame读取数据到packet;
读取的包处理:
(1)若含有过滤器:
时间戳设置,调用avcodec_decode_video2 或 avcodec_decode_audio4解码;
解码成功后,设置时间戳,调用filter_encode_write_frame编码
(2) 不含过滤器: 修改时基,将packet直接放入输出文件
更新过滤器filter_encode_write_frame(NULL, i); 更新编码器flush_encoder(i)
1.1 代码详解
int_tmain(int argc, _TCHAR* argv[])
{
int ret;
AVPacketpacket;
AVFrame *frame= NULL;
enum AVMediaType type;
unsigned intstream_index;
unsigned int i;
int got_frame;
int (dec_func)(AVCodecContext , AVFrame , int , const AVPacket*); //函数指针
if (argc != 3) {
av_log(NULL, AV_LOG_ERROR, “Usage: %s \n”, argv[0]); //用法说明
return 1;
}
av_register_all(); //注册函数
avfilter_register_all();
if ((ret = open_input_file(argv[1])) < 0)
goto end;
if ((ret = open_output_file(argv[2])) < 0)
goto end;
if ((ret = init_filters()) < 0)
goto end; //至此,都是输入参数检查, end:
/* read all packets */
while (1) {
/* avformatcontext—-avpacket—-avstream—-avcodeccontext—-avcodec 解析包逐步得到编解码类型 */
if ((ret= av_read_frame(ifmt_ctx, &packet)) < 0) /int av_read_frame(AVFormatContext *s, AVPacket *pkt); 读取码流中的音频若干帧或者视频一帧,s 是输入的AVFormatContext,pkt是输出的AVPacket。这里ifmt_ctx是一个AVFormatContext /
break;
stream_index = packet.stream_index; // int stream_index:标识该AVPacket所属的视频/音频流
type =ifmt_ctx->streams[packet.stream_index]->codec->codec_type; /* AVFormatContext:AVStream *streams:视音频流; AVStream: AVCodecContext *codec:指向该视频/音频流的AVCodecContext; AVCodecContext: enum AVMediaType codec_type:编解码器的类型(视频,音频…)/
av_log(NULL, AV_LOG_DEBUG, “Demuxergave frame of stream_index %u\n”,
stream_index);
if (filter_ctx[stream_index].filter_graph) { // static FilteringContext *filter_ctx 完整的过滤器结构体,包含AVFilterGraph *filter_graph过滤器图
av_log(NULL, AV_LOG_DEBUG, “Going to reencode&filter the frame\n”);
frame =av_frame_alloc(); // av_frame_alloc() 初始化 AVFrame
if (!frame) {
ret = AVERROR(ENOMEM);
break;
}
/* int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd);是计算 “a * b / c” 的值并分五种方式来取整.
用在FFmpeg中, 则是将以 “时钟基c” 表示的 数值a 转换成以 “时钟基b” 来表示。 */
packet.dts = av_rescale_q_rnd(packet.dts, // AVPacket:nt64_t dts解码时间戳
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet.pts = av_rescale_q_rnd(packet.pts, //AVPacket:int64_t pts显示时间戳
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 :
avcodec_decode_audio4; //avcodec_decodec_video2()视频解码函数,avcodec_decode_audio4 音频解码函数
/* int avcodec_decode_video2( AVCodecContext* avctx, AVFrame* picture,int* got_pitcure_ptr,const AVPacket* avpkt)
avctx:解码器 picture:保存输出的视频帧 got_picture_ptr: 0表示没有可以解码的, avpkt: 包含输入buffer的AVPacket
*/
ret =dec_func(ifmt_ctx->streams[stream_index]->codec, frame,
&got_frame, &packet);
if (ret < 0) { //解码失败
av_frame_free(&frame);
av_log(NULL, AV_LOG_ERROR, “Decodingfailed\n”);
break;
}
if (got_frame) { //解码成功
//
int64_t av_frame_get_best_effort_timestamp ( const AVFrame * frame )
frame->pts = av_frame_get_best_effort_timestamp(frame);
ret= filter_encode_write_frame(frame, stream_index); //filter_encode_write_frame():编码一个AVFrame
av_frame_free(&frame);
if (ret< 0)
goto end;
} else {
av_frame_free(&frame);
}
} else {
/* remux this frame without reencoding 重新复用frame,没有重新编码的情况下 * /
packet.dts = av_rescale_q_rnd(packet.dts,
ifmt_ctx->streams[stream_index]->time_base,
ofmt_ctx->streams[stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet.pts = av_rescale_q_rnd(packet.pts,
ifmt_ctx->streams[stream_index]->time_base,
ofmt_ctx->streams[stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
ret =av_interleaved_write_frame(ofmt_ctx, &packet); //av_interleaved_write_frame写入一个AVPacket到输出文件
if (ret < 0)
goto end;
}
av_free_packet(&packet);
}
/* flush filters and encoders 刷新过滤器和编码器 * /
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
/*刷新过滤器flush filter * /
if (!filter_ctx[i].filter_graph)
continue;
ret =filter_encode_write_frame(NULL, i);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Flushingfilter failed\n”);
goto end;
}
/* 刷新编码器 flush encoder , flush_encoder():输入文件读取完毕后,输出编码器中剩余的AVPacket* /
ret = flush_encoder(i);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Flushingencoder failed\n”);
goto end;
}
}
av_write_trailer(ofmt_ctx); //输出文件尾
end:
/* 释放个个结构体 packet,frame—-codec—-filter—-ifmt_ctx,ofmt_ctx */
av_free_packet(&packet); //av_free_packet, 释放AVPacket对象
av_frame_free(&frame); //av_frame_free, 释放AVFrame对象
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
avcodec_close(ifmt_ctx->streams[i]->codec);
if (ofmt_ctx && ofmt_ctx->nb_streams >i && ofmt_ctx->streams[i] &&ofmt_ctx->streams[i]->codec)
avcodec_close(ofmt_ctx->streams[i]->codec);
if(filter_ctx && filter_ctx[i].filter_graph)
avfilter_graph_free(&filter_ctx[i].filter_graph);
}
av_free(filter_ctx);
avformat_close_input(&ifmt_ctx);
if (ofmt_ctx &&!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0)
av_log(NULL, AV_LOG_ERROR, “Erroroccurred\n”);
return (ret? 1:0);
}
1.2 int _tmain(int argc, _TCHAR* argv[])
用过C的人都知道每一个C的程序都会有一个main(),但有时看别人写的程序发现主函数不是int main(),而是int _tmain(),而且头文件也不是
extern “C”
{
};
/* 三个重要的结构体: AVFormatContext *ifmt_ctx ,AVFormatContext *ofmt_ctx , FilteringContext *filter_ctx
*/
static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
typedef struct FilteringContext{
AVFilterContext*buffersink_ctx;
AVFilterContext*buffersrc_ctx;
AVFilterGraph*filter_graph;
} FilteringContext;
static FilteringContext *filter_ctx;
(该函数在ffmpeg文件 transcoding.c)
说明:open_input_file():打开输入文件,并初始化相关的结构体,
(1).打开媒体的的过程开始于avformat_open_input函数
int avformat_open_input(AVFormatContext** ps, const char* filename,AVInputFormat* fmt,AVDictionary** options):
作用: 打开输入流,读取头部; codecs没有打开,最后关闭流需要使用avformat_close_input(); 正确执行返回0
参数: ps: 指向用户提供的AVFormatContext(由avformat_alloc_context分配);可以指向NULL(当AVFormatContext由该函数分配,并写入到ps中)
filename: 打开输入流的名字 fmt:如果非null,即特定的输入形式,否则自动检测格式 option:其他
(2). 获取相关信息 avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext* ic, AVDictionary** options)
作用:读取媒体文件的包,得到流信息
参数: ic:处理的媒体文件,即输入的 AVFormatContext , option 可选项; 正常执行后返回值大于等于0。
(3).打开解码器avcodec_open2
int avcodec_open2(AVCodecContext* avctx, const AVCodec * codec, AVDictionary** options),正确执行返回0
作用:初始化AVCodecContext使得使用给定的编解码器AVCodec
说明:(1). 使用函数avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), avcodec_find_decoder() and avcodec_find_encoder()ti提供编解码器 ; (2).该函数不是线程安全的??? ; (3).通常在解码程序之前调用,比如avcodec_decode_video2()
(4).av_dump_format
void av_dump_format (AVFormatContext* ic, int index, const char* url, int is_output)
作用:打印输入输出格式的详细信息,比如持续时间duration,比特率 bitrate,流 streams,容器 container, 程序programs,元数据 metadata, side data, 编解码 器codec ,时基 time base.
参数: ic:要分析的context, index:流索引, url:打印的URL,比如源文件或者目标文件, is_output:选择指定的context是input(0) or output(1)
static int open_input_file(const char *filename)
{
int ret;
unsigned int i;
ifmt_ctx =NULL;
/* avformat_open_input 打开输入流 失败报错 */
if ((ret = avformat_open_input(&ifmt_ctx,filename, NULL, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot openinput file\n”);
return ret;
}
/* avformat_open_input 获取流信息 失败报错 */
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL))< 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot findstream information\n”);
return ret;
}
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream*stream;
AVCodecContext *codec_ctx;
/* AVFormatContext : ifmt_ctx--- AVStream: stream---AVCodecContext: codec_ctx */
stream =ifmt_ctx->streams[i];
codec_ctx =stream->codec;
/*Reencode video & audio and remux subtitles(字幕) etc */
if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
||codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
/* 打开解码器 open decoder */
ret =avcodec_open2(codec_ctx,
avcodec_find_decoder(codec_ctx->codec_id), NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Failed toopen decoder for stream #%u\n”, i);
return ret;
}
}
}
av_dump_format(ifmt_ctx, 0, filename, 0); //打印数据信息
return 0;
}
4.satic int open_output_file(const char *filename)———打开输出文件;输入输出的AVStream,AVFormatContex;视频音频分别设置输出数据,其他的直接拷贝设置; 设置编码器; 打印信息到目标文件 ; 写入头文件
(该函数即ffmpeg文件中 transcoding.c)
(1).avformat_alloc_output_context2
int avformat_alloc_output_context2(AVFormatContext** ctx,AVOutputFormat* oformat, const char* format_name, const char* filename)
作用:分配一个用于输出格式的AVFormatContext,可以使用avformat_free_context() 释放; 正确返回大于等于0
参数:ctx:函数成功后创建的AVFormatContext结构体;
oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由 FFmpeg猜测输出格式。使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。
format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。
filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。
(2).avformat_new_stream
AVStream* avformat_new_stream(AVFormatContext* s, const AVCodec* c)
作用:在给定的context中增加一个新的流,
(3).avcodec_copy_context
int avcodec_copy_context(AVCodecContext* dest, const AVCodecContext* src)
作用:拷贝源AVCodecContext的设置到目的AVCodecContext
(4).avformat_write_header
int avformat_write_header(AVFormatContext * s, AVDictionary** options)
作用:分配流的私有数据,并将流的头部写入到输出文件; 正确返回0
static int open_output_file(const char *filename)
{
AVStream*out_stream;
AVStream*in_stream;
AVCodecContext*dec_ctx, *enc_ctx;
AVCodec*encoder;
int ret;
unsigned int i;
ofmt_ctx =NULL;
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename); //创建输出 AVFormatContext格式的ofmt_ctx,
if (!ofmt_ctx) {
av_log(NULL, AV_LOG_ERROR, “Could notcreate output context\n”);
return AVERROR_UNKNOWN;
}
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
out_stream= avformat_new_stream(ofmt_ctx, NULL); //创建一条流
if (!out_stream) {
av_log(NULL, AV_LOG_ERROR, “Failedallocating output stream\n”);
return AVERROR_UNKNOWN;
}
in_stream =ifmt_ctx->streams[i];
dec_ctx =in_stream->codec; //dec_ctx: 输入AVFormatContext中的AVCodecContext
enc_ctx =out_stream->codec; //enc_ctx: 输出AVFormatContext中的AVCodecContext
if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
||dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
/*in this example, we choose transcoding to same codec 这里使用相同的编解码器 */
encoder= avcodec_find_encoder(dec_ctx->codec_id);
/* In this example, we transcode to same properties(picture size,
* sample rate etc.). These properties can be changed for output
* streams easily using filters */
if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { //视频:设置输出的高,宽,纵横比,pix_fms ,时基
enc_ctx->height = dec_ctx->height;
enc_ctx->width = dec_ctx->width;
enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
/* take first format from list of supported formats */
enc_ctx->pix_fmt = encoder->pix_fmts[0];
/* video time_base can be set to whatever is handy andsupported by encoder */
enc_ctx->time_base = dec_ctx->time_base;
} else { //音频:设置输出的采样率,声道,声道数,,sample_fms…
enc_ctx->sample_rate = dec_ctx->sample_rate;
enc_ctx->channel_layout = dec_ctx->channel_layout;
enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
/* take first format from list of supported formats */
enc_ctx->sample_fmt = encoder->sample_fmts[0];
AVRationaltime_base={1, enc_ctx->sample_rate}; // AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s)
enc_ctx->time_base = time_base;
}
/* Third parameter can be used to pass settings to encoder*/
ret =avcodec_open2(enc_ctx, encoder, NULL); // 为enc_ctx(输出AVFormatContext中的AVCodecContext) 初始化编码器
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot openvideo encoder for stream #%u\n”, i);
return ret;
}
} else if(dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {
av_log(NULL, AV_LOG_FATAL, “Elementarystream #%d is of unknown type, cannot proceed\n”, i);
return AVERROR_INVALIDDATA;
} else {
/*假设该流一定要 if this stream must be remuxed */
ret =avcodec_copy_context(ofmt_ctx->streams[i]->codec, //拷贝设置,即复用??
ifmt_ctx->streams[i]->codec);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Copyingstream context failed\n”);
return ret;
}
}
if (ofmt_ctx->oformat->flags &AVFMT_GLOBALHEADER)
enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
av_dump_format(ofmt_ctx, 0, filename, 1); //打印ofmt_ctx的信息到文件filename
if (!(ofmt_ctx->oformat->flags &AVFMT_NOFILE)) {
ret =avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Could notopen output file ‘%s’”, filename);
return ret;
}
}
/* init muxer, write output file header */
ret =avformat_write_header(ofmt_ctx, NULL); //写入头文件
if (ret < 0) {
av_log(NULL,AV_LOG_ERROR, “Error occurred when openingoutput file\n”);
return ret;
}
return 0;
}
5.intinit_filter函数———— 初始化结构体; 视频音频分别 获取buffer过滤器,buffersink过滤器,并且创建过滤器,存储视频音频信息; 创建过滤器端; 连接参数传递的过滤器到过滤器图中,配置过滤器图的链路, 最后生成FilteringContext
(该函数即ffmpeg文件中 transcoding.c)
解码后的画面--->buffer过滤器---->其他过滤器---->buffersink过滤器--->处理完的画面
1).所有的过滤器形成了过滤器链,一定要的两个过滤器是buffer过滤器和buffersink过滤器,前者的作用是将解码后的画面加载到过滤器链中,后者 的作用是将处理好的画面从过滤器链中读取出来.
2). 过滤器相关的结构体: AVFilterGraph: 管理所有的过滤器图像; AVFilterContext: 过滤器上下文; AVFilter过滤器;
3).本程序中的定义:
typedef struct FilteringContext {
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
} FilteringContext;
static FilteringContext *filter_ctx;
AVFilterInOut
4). AVFilterInOut 结构体
作用:过滤器链的输入输出链
This is mainly useful for avfilter_graph_parse() / avfilter_graph_parse2(), where it is used to communicate open (unlinked) inputs and outputs from and to the caller. This struct specifies, per each not connected pad contained in the graph, the filter context and the pad index required for establishing a link.
(1).avfilter_inout_alloc()——分配一个AVFilterInOut; 使用 avfilter_inout_free() 释放
(2).avfilter_graph_alloc——分配一个AVFilterGraph
(3).avfilter_get_by_name——获取过滤器
(4).snprintf(),
为函数原型int snprintf(char *str, size_t size, const char *format, ...),将可变个参数(...)按照format格式化成字符串,然后将其复制到str中
1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符(‘\0’);
2) 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0’),返回值为格式化后的字符串的长度。
char a[20];
i = snprintf(a, 9, “%012d”, 12345);
printf(“i = %d, a = %s”, i, a);
输出为:i = 12, a = 00000001
(5).avfilter_graph_create_filter——创建过滤器并且添加到存在的AVFilterGraph 中
(6). av_opt_set_bin ——用于设置参数
(7).av_get_default_channel_layout ——信道数?
(8).avfilter_graph_parse_ptr——连接过滤器,即添加字符串描述的过滤器 filters到整个 过滤器图 graph中
(9).avfilter_graph_config——检查有效性以及 graph中所有链路和格式的配置
static intinit_filter(FilteringContext* fctx, AVCodecContext *dec_ctx,
AVCodecContext *enc_ctx, const char *filter_spec)
{
char args[512];
int ret = 0;
AVFilter*buffersrc = NULL;
AVFilter*buffersink = NULL;
AVFilterContext*buffersrc_ctx = NULL;
AVFilterContext*buffersink_ctx = NULL;
AVFilterInOut*outputs = avfilter_inout_alloc();
AVFilterInOut*inputs = avfilter_inout_alloc();
AVFilterGraph*filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret =AVERROR(ENOMEM);
goto end;
} //初始化各个结构体
/* 视频 过滤器 */
if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
buffersrc =avfilter_get_by_name(“buffer”);
buffersink= avfilter_get_by_name(“buffersink”);
if (!buffersrc || !buffersink) {
av_log(NULL, AV_LOG_ERROR, “filteringsource or sink element not found\n”);
ret = AVERROR_UNKNOWN;
goto end;
} //获取buffer过滤器和 buffersink过滤器
_snprintf(args, sizeof(args),
“video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d”,
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
dec_ctx->time_base.num,dec_ctx->time_base.den,
dec_ctx->sample_aspect_ratio.num,
dec_ctx->sample_aspect_ratio.den); //字符数组args存储视频的相关信息
ret =avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, “in”,
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannotcreate buffer source\n”);
goto end;
} // 创建 buffersrc过滤器
ret =avfilter_graph_create_filter(&buffersink_ctx, buffersink, “out”,
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannotcreate buffer sink\n”);
goto end;
} //创建 buffersink过滤器
ret =av_opt_set_bin(buffersink_ctx, “pix_fmts”,
(uint8_t*)&enc_ctx->pix_fmt, sizeof(enc_ctx->pix_fmt), //pix_fmts 视频像素格式
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot setoutput pixel format\n”);
goto end;
}
/* 音频 过滤器 */
} else if(dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
buffersrc = avfilter_get_by_name(“abuffer”);
buffersink= avfilter_get_by_name(“abuffersink”);
if (!buffersrc || !buffersink) {
av_log(NULL, AV_LOG_ERROR, “filteringsource or sink element not found\n”);
ret =AVERROR_UNKNOWN;
goto end;
} //获取buffer过滤器和 buffersink过滤器
if (!dec_ctx->channel_layout)
dec_ctx->channel_layout =
av_get_default_channel_layout(dec_ctx->channels); //信道
_snprintf(args, sizeof(args),
“time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x”,
dec_ctx->time_base.num, dec_ctx->time_base.den,dec_ctx->sample_rate,
av_get_sample_fmt_name(dec_ctx->sample_fmt),
dec_ctx->channel_layout); //字符数组args存储音频的相关信息
ret =avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, “in”,
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannotcreate audio buffer source\n”);
goto end;
} // 创建 buffer过滤器
ret =avfilter_graph_create_filter(&buffersink_ctx, buffersink, “out”,
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannotcreate audio buffer sink\n”);
goto end;
} // 创建 buffersink过滤器
ret = av_opt_set_bin(buffersink_ctx, “sample_fmts”,
(uint8_t*)&enc_ctx->sample_fmt, sizeof(enc_ctx->sample_fmt),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot setoutput sample format\n”);
goto end;
} // sample_fmts音频样本格式
ret =av_opt_set_bin(buffersink_ctx, “channel_layouts”,
(uint8_t*)&enc_ctx->channel_layout,
sizeof(enc_ctx->channel_layout),AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot setoutput channel layout\n”);
goto end;
}
ret =av_opt_set_bin(buffersink_ctx, “sample_rates”,
(uint8_t*)&enc_ctx->sample_rate, sizeof(enc_ctx->sample_rate),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Cannot setoutput sample rate\n”);
goto end;
} // sample_rates 音频采样率
} else {
ret =AVERROR_UNKNOWN;
goto end;
}
/* ndpoints for the filter graph*过滤图的端设置/
outputs->name =av_strdup(“in”);
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup(“out”);
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if (!outputs->name || !inputs->name) {
ret =AVERROR(ENOMEM);
goto end;
}
if ((ret = avfilter_graph_parse_ptr(filter_graph,filter_spec, // 添加字符串filter_spec描述的过滤器到过滤器图中
&inputs, &outputs, NULL)) < 0)
goto end;
if ((ret = avfilter_graph_config(filter_graph, NULL))< 0) //检查有效性以及 graph中所有链路和格式的配置
goto end;
/* Fill FilteringContext */
fctx->buffersrc_ctx = buffersrc_ctx;
fctx->buffersink_ctx = buffersink_ctx;
fctx->filter_graph= filter_graph;
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return ret;
}
(1).av_malloc_array
static int init_filters(void)
{
const char*filter_spec;
unsigned int i;
int ret;
filter_ctx =(FilteringContext *)av_malloc_array(ifmt_ctx->nb_streams, sizeof(*filter_ctx)); //分配过滤器 ,大小是流数目乘以*filter_ctx;FilteringContext是自定义的过滤器大结构体
if (!filter_ctx)
return AVERROR(ENOMEM);
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
filter_ctx[i].buffersrc_ctx =NULL;
filter_ctx[i].buffersink_ctx= NULL;
filter_ctx[i].filter_graph =NULL;
if(!(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO //如果不是视频或者音频,开始下一次循环直接
||ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO))
continue;
if (ifmt_ctx->streams[i]->codec->codec_type== AVMEDIA_TYPE_VIDEO) //设置滤波器类型
filter_spec = “null”; /* passthrough (dummy) filter for video */ ?????
else
filter_spec = “anull”; /* passthrough (dummy) filter for audio */
ret =init_filter(&filter_ctx[i], ifmt_ctx->streams[i]->codec, //调用init_filter函数,生成滤波器图
ofmt_ctx->streams[i]->codec, filter_spec);
if (ret)
return ret;
}
return 0;
}
encode_write_frame函数 ———视音频选择编码函数; 编码过滤的帧 (初始化 packet的可选项,编码,释放编码过的帧); 准备packet用于复用, 复用编码帧( 交织写一个packet到输出文件)
Referenced by filter_encode_write_frame(), and flush_encoder()
(1).av_init_packet:默认值初始化packet的可选项; 不涉及data 和size 属性,两者需要分别初始化
(2).av_rescale_q_rnd:重新调整 64—bit的整数,根据2个有理数按照指定的舍入
(3).av_interleaved_write_frame:写一个packet到输出文件,确保正确的交织
static int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int*got_frame) {
int ret;
int got_frame_local;
AVPacket enc_pkt;
/*根据视频还是音频选择各自的 编码函数,即avcodec_encode_video2 :或者avcodec_encode_audio2 ,enc_func是函数指针 */
int (enc_func)(AVCodecContext , AVPacket , const AVFrame , int*) =
(ifmt_ctx->streams[stream_index]->codec->codec_type ==
AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;
if (!got_frame) //。。。。。。。。。
got_frame =&got_frame_local;
av_log(NULL,AV_LOG_INFO, “Encoding frame\n”);
/* 编码过滤的帧 encode filtered frame*/
enc_pkt.data =NULL;
enc_pkt.size =0;
av_init_packet(&enc_pkt); //初始化 AVPacket enc_pkt的可选项,不包括 data和size
ret =enc_func(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,
filt_frame, got_frame); //编码,输入的AVFrame 为 filt_frame,输出的AVPacket 为enc_pkt,其中got_frame成功编码一个AVPacket的时候设置为1
av_frame_free(&filt_frame); //释放编码过的帧
if (ret < 0)
return ret;
if (!(*got_frame))
return 0;
/* prepare packet for muxing 准备packet 用于复用 */
enc_pkt.stream_index = stream_index;
enc_pkt.dts =av_rescale_q_rnd(enc_pkt.dts, // 调整 包enc_pkt的 解码时间戳 dts
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
enc_pkt.pts =av_rescale_q_rnd(enc_pkt.pts, // 调整 包enc_pkt的 显示时间戳 pts
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
enc_pkt.duration = av_rescale_q(enc_pkt.duration, // 调整 包enc_pkt的 持续时间 duration
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base);
av_log(NULL,AV_LOG_DEBUG, “Muxing frame\n”);
/*mux encoded frame 复用 编码帧 */
ret =av_interleaved_write_frame(ofmt_ctx, &enc_pkt); //写一个packet到输出文件,确保正确的交织
return ret;
}
8.filter_encode_write_frame函数————将编码过的帧放入过滤器图,再从中拉出来,继而调用encode_write_frame函数编码(1).av_buffersrc_add_frame_flags: 添加一个帧到 buffer source(AVFilterContext )
(2).av_buffersink_get_frame:从buffersink(AVFilterContext )过滤过的数据中得到一个帧,并放倒frame中
(3).filt_frame->pict_type = AV_PICTURE_TYPE_NONE(其中filt_frame是一个AVFrame): 即帧的图像类型
static int filter_encode_write_frame(AVFrame *frame, unsignedint stream_index)
{
int ret;
AVFrame*filt_frame;
av_log(NULL,AV_LOG_INFO, “Pushing decoded frame tofilters\n”);
/*将解码过的帧输入到过滤器图 push the decoded frame into the filtergraph */
ret =av_buffersrc_add_frame_flags(filter_ctx[stream_index].buffersrc_ctx, //添加帧到bunffersrc(AVFilterContext )过滤器
frame,0);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, “Error whilefeeding the filtergraph\n”);
return ret;
}
/*将从过滤器图中拉过滤过的帧 pull filtered frames from the filtergraph */
while (1) {
filt_frame= av_frame_alloc();
if (!filt_frame) {
ret =AVERROR(ENOMEM);
break;
}
av_log(NULL, AV_LOG_INFO, “Pullingfiltered frame from filters\n”);
ret =av_buffersink_get_frame(filter_ctx[stream_index].buffersink_ctx, filt_frame);//从buffersink(AVFilterContext )过滤器中得到一个帧
if (ret < 0) {
/*
if nomore frames for output - returns AVERROR(EAGAIN)
if flushed and no more frames for output - returns AVERROR_EOF
rewrite retcode to 0 to show it as normal procedure completion
*/
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret= 0;
av_frame_free(&filt_frame);
break;
}
filt_frame->pict_type = AV_PICTURE_TYPE_NONE; //图像帧的类型 未定义
ret =encode_write_frame(filt_frame, stream_index, NULL); //编码
if (ret < 0)
break;
}
return ret;
}
9.flush_encoder函数———刷新 流的编码器?
static int flush_encoder(unsigned intstream_index)
{
int ret;
int got_frame;
if(!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities&
CODEC_CAP_DELAY))
return 0;
while (1) {
av_log(NULL, AV_LOG_INFO, “Flushing stream #%u encoder\n”, stream_index);
ret =encode_write_frame(NULL, stream_index, &got_frame);
if (ret < 0)
break;
if (!got_frame)
return 0;
}
return ret;
}