static int transcode_step(void)
{
OutputStream *ost;
InputStream *ist;
int ret;
//选出一路输出流中dts(解码时间戳)最小的。
ost = choose_output();//后面有详细介绍
if (!ost) {
if (got_eagain()) {
reset_eagain();
av_usleep(10000);
return 0;
}
av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n");
return AVERROR_EOF;
}
//判断是否配置了滤镜
if (ost->filter) {
if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0)
return ret;
if (!ist)
return 0;
} else {
av_assert0(ost->source_index >= 0);
ist = input_streams[ost->source_index];
}
//完成解码工作,并放入滤镜中
ret = process_input(ist->file_index);
if (ret == AVERROR(EAGAIN)) {
if (input_files[ist->file_index]->eagain)
ost->unavailable = 1;
return 0;
}
if (ret < 0)
return ret == AVERROR_EOF ? 0 : ret;
//从滤镜中获取数据,并完成编码工作,写入文件中
return reap_filters(0);
}
选出一路输出流中dts(解码时间戳)最小的。
其中av_rescale_q的解释参考ffmpeg中的时间
简单的说就是做一个不同时间基之间的转换
/**
* Select the output stream to process.
*
* @return selected output stream, or NULL if none available
*/
static OutputStream *choose_output(void)
{
int i;
int64_t opts_min = INT64_MAX;
OutputStream *ost_min = NULL;
for (i = 0; i < nb_output_streams; i++) {
OutputStream *ost = output_streams[i];
int64_t opts = ost->st->cur_dts == AV_NOPTS_VALUE ? INT64_MIN :
av_rescale_q(ost->st->cur_dts, ost->st->time_base,
AV_TIME_BASE_Q);
if (ost->st->cur_dts == AV_NOPTS_VALUE)
av_log(NULL, AV_LOG_DEBUG, "cur_dts is invalid (this is harmless if it occurs once at the start per stream)\n");
/*find the stream that opts is Minimum */
if (!ost->finished && opts < opts_min) {
opts_min = opts;
ost_min = ost->unavailable ? NULL : ost;
}
}
return ost_min;
}
对指定的滤镜做一步转码。
这个函数需要再分析完滤镜整体结构后才能解释。先放到这里。
/**
* Perform a step of transcoding for the specified filter graph.
*
* @param[in] graph filter graph to consider
* @param[out] best_ist input stream where a frame would allow to continue
* @return 0 for success, <0 for error
*/
static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist)
{
int i, ret;
int nb_requests, nb_requests_max = 0;
InputFilter *ifilter;
InputStream *ist;
*best_ist = NULL;
ret = avfilter_graph_request_oldest(graph->graph);
if (ret >= 0)
return reap_filters(0);
if (ret == AVERROR_EOF) {
ret = reap_filters(1);
for (i = 0; i < graph->nb_outputs; i++)
close_output_stream(graph->outputs[i]->ost);
return ret;
}
if (ret != AVERROR(EAGAIN))
return ret;
for (i = 0; i < graph->nb_inputs; i++) {
ifilter = graph->inputs[i];
ist = ifilter->ist;
if (input_files[ist->file_index]->eagain ||
input_files[ist->file_index]->eof_reached)
continue;
nb_requests = av_buffersrc_get_nb_failed_requests(ifilter->filter);
if (nb_requests > nb_requests_max) {
nb_requests_max = nb_requests;
*best_ist = ist;
}
}
if (!*best_ist)
for (i = 0; i < graph->nb_outputs; i++)
graph->outputs[i]->ost->unavailable = 1;
return 0;
}
/*
* Return
* - 0 -- one packet was read and processed
* - AVERROR(EAGAIN) -- no packets were available for selected file,
* this function should be called again
* - AVERROR_EOF -- this function should not be called again
*/
int Cffmpeg::process_input(int file_index)
{
InputFile *ifile = input_files[file_index];
AVFormatContext *is;
InputStream *ist;
AVPacket pkt;
int ret, i, j;
int64_t duration;
int64_t pkt_dts;
is = ifile->ctx;
//也就是调用av_read_frame(),其中对帧率仿真做了处理
ret = get_input_packet(ifile, &pkt);
if (ret == AVERROR(EAGAIN)) {
ifile->eagain = 1;
return ret;
}
/*ifile->loop针对参数"stream_loop",在open_input_file()中有赋值
*表示"set number of times input stream shall be looped"
*get_input_packet函数出错,就seek到文件的开始,ifile->loop表示循环次数,
*在seek_to_start中递减
*/
if (ret < 0 && ifile->loop) {
if ((ret = seek_to_start(ifile, is)) < 0)
return ret;
ret = get_input_packet(ifile, &pkt);
}
if (ret < 0) {
//出错
if (ret != AVERROR_EOF) {
print_error(is->filename, ret);
if (exit_on_error)
exit_program(1);
}
//文件结束
for (i = 0; i < ifile->nb_streams; i++) {
ist = input_streams[ifile->ist_index + i];
if (ist->decoding_needed) {
/*
调用流程如下:
process_input_packet()->decode_video()->avcodec_decode_video2();
在avcodec_decode_video2()函数的注释中有这样一段
* @note Codecs which have the AV_CODEC_CAP_DELAY capability set have a delay
* between input and output, these need to be fed with avpkt->data=NULL,
* avpkt->size=0 at the end to return the remaining frames.
*
* process_input_packet中将一个avpkt->data=NULL,avpkt->size=0的avpkt传给了avcodec_decode_video2
* 以返回剩余的帧。结束这路流的输入
*/
ret = process_input_packet(ist, NULL, 0);
if (ret>0)
return 0;
}
/* mark all outputs that don't go through lavfi as finished */
for (j = 0; j < nb_output_streams; j++) {
OutputStream *ost = output_streams[j];
if (ost->source_index == ifile->ist_index + i &&
(ost->stream_copy || ost->enc->type == AVMEDIA_TYPE_SUBTITLE))
finish_output_stream(ost);//设置结束标识。
}
}
ifile->eof_reached = 1;
return AVERROR(EAGAIN);
}
reset_eagain();
//对应参数"dump","dump each input packet"
if (do_pkt_dump) {
av_pkt_dump_log2(NULL, AV_LOG_INFO, &pkt, do_hex_dump,
is->streams[pkt.stream_index]);
}
/* the following test is needed in case new streams appear
dynamically in stream : we ignore them
如果获取的流在初始化的时候没有,就丢弃
*/
if (pkt.stream_index >= ifile->nb_streams) {
report_new_stream(file_index, &pkt);
goto discard_packet;
}
//找到读取的帧对应的流信息结构体。ifile->ist_index表示这个文件的流在input_streams中的开始小标
ist = input_streams[ifile->ist_index + pkt.stream_index];
ist->data_size += pkt.size;
ist->nb_packets++;
//配置中让丢弃的流就丢弃
if (ist->discard)
goto discard_packet;
//读取的帧损坏退出
if (exit_on_error && (pkt.flags & AV_PKT_FLAG_CORRUPT)) {
av_log(NULL, AV_LOG_FATAL, "%s: corrupt input packet in stream %d\n", is->filename, pkt.stream_index);
exit_program(1);
}
if (debug_ts) {
av_log(NULL, AV_LOG_INFO, "demuxer -> ist_index:%d type:%s "
"next_dts:%s next_dts_time:%s next_pts:%s next_pts_time:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s off:%s off_time:%s\n",
ifile->ist_index + pkt.stream_index, av_get_media_type_string(ist->dec_ctx->codec_type),
av_ts2str(ist->next_dts), av_ts2timestr(ist->next_dts, &AV_TIME_BASE_Q),
av_ts2str(ist->next_pts), av_ts2timestr(ist->next_pts, &AV_TIME_BASE_Q),
av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ist->st->time_base),
av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ist->st->time_base),
av_ts2str(input_files[ist->file_index]->ts_offset),
av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
}
//在设置了is->start_time的情况下才执行这里。对应参数"ss"
if(!ist->wrap_correction_done && is->start_time != AV_NOPTS_VALUE && ist->st->pts_wrap_bits < 64){
int64_t stime, stime2;
// Correcting starttime based on the enabled streams
// FIXME this ideally should be done before the first use of starttime but we do not know which are the enabled streams at that point.
// so we instead do it here as part of discontinuity handling
if ( ist->next_dts == AV_NOPTS_VALUE
&& ifile->ts_offset == -is->start_time
&& (is->iformat->flags & AVFMT_TS_DISCONT)) {
int64_t new_start_time = INT64_MAX;
for (i=0; i<is->nb_streams; i++) {
AVStream *st = is->streams[i];
if(st->discard == AVDISCARD_ALL || st->start_time == AV_NOPTS_VALUE)
continue;
new_start_time = FFMIN(new_start_time, av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q));
}
if (new_start_time > is->start_time) {
av_log(is, AV_LOG_VERBOSE, "Correcting start time by %"PRId64"\n", new_start_time - is->start_time);
ifile->ts_offset = -new_start_time;
}
}
stime = av_rescale_q(is->start_time, AV_TIME_BASE_Q, ist->st->time_base);
stime2= stime + (1ULL<<ist->st->pts_wrap_bits);
ist->wrap_correction_done = 1;
if(stime2 > stime && pkt.dts != AV_NOPTS_VALUE && pkt.dts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) {
pkt.dts -= 1ULL<<ist->st->pts_wrap_bits;
ist->wrap_correction_done = 0;
}
if(stime2 > stime && pkt.pts != AV_NOPTS_VALUE && pkt.pts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) {
pkt.pts -= 1ULL<<ist->st->pts_wrap_bits;
ist->wrap_correction_done = 0;
}
}
/* add the stream-global side data to the first packet */
if (ist->nb_packets == 1) {
if (ist->st->nb_side_data)
av_packet_split_side_data(&pkt);
for (i = 0; i < ist->st->nb_side_data; i++) {
AVPacketSideData *src_sd = &ist->st->side_data[i];
uint8_t *dst_data;
if (av_packet_get_side_data(&pkt, src_sd->type, NULL))
continue;
if (ist->autorotate && src_sd->type == AV_PKT_DATA_DISPLAYMATRIX)
continue;
dst_data = av_packet_new_side_data(&pkt, src_sd->type, src_sd->size);
if (!dst_data)
exit_program(1);
memcpy(dst_data, src_sd->data, src_sd->size);
}
}
if (pkt.dts != AV_NOPTS_VALUE)
pkt.dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts *= ist->ts_scale;
if (pkt.dts != AV_NOPTS_VALUE)
pkt.dts *= ist->ts_scale;
pkt_dts = av_rescale_q_rnd(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q, (enum AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
pkt_dts != AV_NOPTS_VALUE && ist->next_dts == AV_NOPTS_VALUE && !copy_ts
&& (is->iformat->flags & AVFMT_TS_DISCONT) && ifile->last_ts != AV_NOPTS_VALUE) {
int64_t delta = pkt_dts - ifile->last_ts;
if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE ||
delta > 1LL*dts_delta_threshold*AV_TIME_BASE){
ifile->ts_offset -= delta;
av_log(NULL, AV_LOG_DEBUG,
"Inter stream timestamp discontinuity %"PRId64", new offset= %"PRId64"\n",
delta, ifile->ts_offset);
pkt.dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
}
}
duration = av_rescale_q(ifile->duration, ifile->time_base, ist->st->time_base);
if (pkt.pts != AV_NOPTS_VALUE) {
pkt.pts += duration;
ist->max_pts = FFMAX(pkt.pts, ist->max_pts);
ist->min_pts = FFMIN(pkt.pts, ist->min_pts);
}
if (pkt.dts != AV_NOPTS_VALUE)
pkt.dts += duration;
pkt_dts = av_rescale_q_rnd(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q, (enum AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
pkt_dts != AV_NOPTS_VALUE && ist->next_dts != AV_NOPTS_VALUE &&
!copy_ts) {
int64_t delta = pkt_dts - ist->next_dts;
if (is->iformat->flags & AVFMT_TS_DISCONT) {
if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE ||
delta > 1LL*dts_delta_threshold*AV_TIME_BASE ||
pkt_dts + AV_TIME_BASE/10 < FFMAX(ist->pts, ist->dts)) {
ifile->ts_offset -= delta;
av_log(NULL, AV_LOG_DEBUG,
"timestamp discontinuity %"PRId64", new offset= %"PRId64"\n",
delta, ifile->ts_offset);
pkt.dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
}
} else {
if ( delta < -1LL*dts_error_threshold*AV_TIME_BASE ||
delta > 1LL*dts_error_threshold*AV_TIME_BASE) {
av_log(NULL, AV_LOG_WARNING, "DTS %"PRId64", next:%"PRId64" st:%d invalid dropping\n", pkt.dts, ist->next_dts, pkt.stream_index);
pkt.dts = AV_NOPTS_VALUE;
}
if (pkt.pts != AV_NOPTS_VALUE){
int64_t pkt_pts = av_rescale_q(pkt.pts, ist->st->time_base, AV_TIME_BASE_Q);
delta = pkt_pts - ist->next_dts;
if ( delta < -1LL*dts_error_threshold*AV_TIME_BASE ||
delta > 1LL*dts_error_threshold*AV_TIME_BASE) {
av_log(NULL, AV_LOG_WARNING, "PTS %"PRId64", next:%"PRId64" invalid dropping st:%d\n", pkt.pts, ist->next_dts, pkt.stream_index);
pkt.pts = AV_NOPTS_VALUE;
}
}
}
}
if (pkt.dts != AV_NOPTS_VALUE)
ifile->last_ts = av_rescale_q(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q);
if (debug_ts) {
av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s off:%s off_time:%s\n",
ifile->ist_index + pkt.stream_index, av_get_media_type_string(ist->dec_ctx->codec_type),
av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ist->st->time_base),
av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ist->st->time_base),
av_ts2str(input_files[ist->file_index]->ts_offset),
av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
}
sub2video_heartbeat(ist, pkt.pts);
process_input_packet(ist, &pkt, 0);
discard_packet:
av_packet_unref(&pkt);
return 0;
}
/**
* Get and encode new output from any of the filtergraphs, without causing
* activity.
*
* @return 0 for success, <0 for severe errors
*/
static int reap_filters(int flush)
{
AVFrame *filtered_frame = NULL;
int i;
/* Reap all buffers present in the buffer sinks */
for (i = 0; i < nb_output_streams; i++) {
OutputStream *ost = output_streams[i];
OutputFile *of = output_files[ost->file_index];
AVFilterContext *filter;
AVCodecContext *enc = ost->enc_ctx;
int ret = 0;
if (!ost->filter)
continue;
filter = ost->filter->filter;
if (!ost->filtered_frame && !(ost->filtered_frame = av_frame_alloc())) {
return AVERROR(ENOMEM);
}
filtered_frame = ost->filtered_frame;
while (1) {
double float_pts = AV_NOPTS_VALUE; // this is identical to filtered_frame.pts but with higher precision
ret = av_buffersink_get_frame_flags(filter, filtered_frame,
AV_BUFFERSINK_FLAG_NO_REQUEST);
if (ret < 0) {
if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
av_log(NULL, AV_LOG_WARNING,
"Error in av_buffersink_get_frame_flags(): %s\n", av_err2str(ret));
} else if (flush && ret == AVERROR_EOF) {
if (filter->inputs[0]->type == AVMEDIA_TYPE_VIDEO)
do_video_out(of->ctx, ost, NULL, AV_NOPTS_VALUE);
}
break;
}
if (ost->finished) {
av_frame_unref(filtered_frame);
continue;
}
if (filtered_frame->pts != AV_NOPTS_VALUE) {
int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
AVRational tb = enc->time_base;
int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16);
tb.den <<= extra_bits;
float_pts =
av_rescale_q(filtered_frame->pts, filter->inputs[0]->time_base, tb) -
av_rescale_q(start_time, AV_TIME_BASE_Q, tb);
float_pts /= 1 << extra_bits;
// avoid exact midoints to reduce the chance of rounding differences, this can be removed in case the fps code is changed to work with integers
float_pts += FFSIGN(float_pts) * 1.0 / (1<<17);
filtered_frame->pts =
av_rescale_q(filtered_frame->pts, filter->inputs[0]->time_base, enc->time_base) -
av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base);
}
//if (ost->source_index >= 0)
// *filtered_frame= *input_streams[ost->source_index]->decoded_frame; //for me_threshold
switch (filter->inputs[0]->type) {
case AVMEDIA_TYPE_VIDEO:
if (!ost->frame_aspect_ratio.num)
enc->sample_aspect_ratio = filtered_frame->sample_aspect_ratio;
if (debug_ts) {
av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n",
av_ts2str(filtered_frame->pts), av_ts2timestr(filtered_frame->pts, &enc->time_base),
float_pts,
enc->time_base.num, enc->time_base.den);
}
do_video_out(of->ctx, ost, filtered_frame, float_pts);
break;
case AVMEDIA_TYPE_AUDIO:
if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) &&
enc->channels != av_frame_get_channels(filtered_frame)) {
av_log(NULL, AV_LOG_ERROR,
"Audio filter graph output is not normalized and encoder does not support parameter changes\n");
break;
}
do_audio_out(of->ctx, ost, filtered_frame);
break;
default:
// TODO support subtitle filters
av_assert0(0);
}
av_frame_unref(filtered_frame);
}
}
return 0;
}
声明在libavutil\frame.h中
定义在libavutil\frame.c中
MAKE_ACCESSORS(AVFrame, frame, int64_t, best_effort_timestamp)
//libavutil\internal.h
#define MAKE_ACCESSORS(str, name, type, field) \
type av_##name##_get_##field(const str *s) { return s->field; } \
void av_##name##_set_##field(str *s, type v) { s->field = v; }
功能可见,就是获取和设置一个结构体中的一个成员的值。