transcode()函数位于ffmpeg.c
工作模式是每次读取一个packet,并处理。
可见《 FFmpeg情景分析 1. FFmpeg转码框架解析》
{
/* 对每个输出流,计算出正确的编码参数,并写好文件头 */
ret = transcode_init();
while (1) {
/* 如果没有数据需要输出了,则循环结束 */
if (!need_output()) {
break;
}
/* 转码用的主函数,
* 每次循环是读取一个packet并处理
*/
ret = transcode_step();
/* dump report by using the output first video and audio streams */
print_report(0, timer_start, cur_time);
}
/* at the end of stream, we must flush the decoder buffers */
flush_encoders();
/* write the trailer if needed and close file */
/* dump report by using the first video and audio streams */
print_report(1, timer_start, av_gettime_relative());
/* close each encoder */
/* close each decoder */
/* finished ! */
ret = 0;
fail:
return ret;
}
{
int ret, i;
AVFormatContext *os;
OutputStream *ost;
InputStream *ist;
int64_t timer_start;
int64_t total_packets_written = 0;
/* 对每个输出流,计算出正确的编码参数,
* 然后在命令行显示出每一路流所用到编码参数信息,
* 最后写文件头,如FLV文件,则会写FLV文件的第一个tag。
*/
ret = transcode_init();
if (ret < 0)
goto fail;
if (stdin_interaction) {
av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
}
timer_start = av_gettime_relative();
/* 多线程处理,见《FFmpeg源码剖析-多线程:input_threads》
#if HAVE_PTHREADS
if ((ret = init_input_threads()) < 0)
goto fail;
#endif
while (!received_sigterm) {
int64_t cur_time= av_gettime_relative();
/* 如果在转码进行中,命令行的键盘上点击"q",则任务结束。
* 见《ffmpeg键盘命令响应程序详解》
*/
if (stdin_interaction)
if (check_keyboard_interaction(cur_time) < 0)
break;
/* check if there's any stream where output is still needed */
if (!need_output()) {
av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");
break;
}
ret = transcode_step();
if (ret < 0 && ret != AVERROR_EOF) {
char errbuf[128];
av_strerror(ret, errbuf, sizeof(errbuf));
av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", errbuf);
break;
}
/* dump report by using the output first video and audio streams */
print_report(0, timer_start, cur_time);
}
#if HAVE_PTHREADS
free_input_threads();
#endif
/* at the end of stream, we must flush the decoder buffers */
for (i = 0; i < nb_input_streams; i++) {
ist = input_streams[i];
if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {
process_input_packet(ist, NULL, 0);
}
}
flush_encoders();
term_exit();
/* write the trailer if needed and close file */
for (i = 0; i < nb_output_files; i++) {
os = output_files[i]->ctx;
if (!output_files[i]->header_written) {
av_log(NULL, AV_LOG_ERROR,
"Nothing was written into output file %d (%s), because "
"at least one of its streams received no packets.\n",
i, os->filename);
continue;
}
if ((ret = av_write_trailer(os)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s", os->filename, av_err2str(ret));
if (exit_on_error)
exit_program(1);
}
}
/* dump report by using the first video and audio streams */
print_report(1, timer_start, av_gettime_relative());
/* close each encoder */
for (i = 0; i < nb_output_streams; i++) {
ost = output_streams[i];
if (ost->encoding_needed) {
av_freep(&ost->enc_ctx->stats_in);
}
total_packets_written += ost->packets_written;
}
if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) {
av_log(NULL, AV_LOG_FATAL, "Empty output\n");
exit_program(1);
}
/* close each decoder */
for (i = 0; i < nb_input_streams; i++) {
ist = input_streams[i];
if (ist->decoding_needed) {
avcodec_close(ist->dec_ctx);
if (ist->hwaccel_uninit)
ist->hwaccel_uninit(ist->dec_ctx);
}
}
av_buffer_unref(&hw_device_ctx);
/* finished ! */
ret = 0;
fail:
#if HAVE_PTHREADS
free_input_threads();
#endif
if (output_streams) {
for (i = 0; i < nb_output_streams; i++) {
ost = output_streams[i];
if (ost) {
if (ost->logfile) {
if (fclose(ost->logfile))
av_log(NULL, AV_LOG_ERROR,
"Error closing logfile, loss of information possible: %s\n",
av_err2str(AVERROR(errno)));
ost->logfile = NULL;
}
av_freep(&ost->forced_kf_pts);
av_freep(&ost->apad);
av_freep(&ost->disposition);
av_dict_free(&ost->encoder_opts);
av_dict_free(&ost->sws_dict);
av_dict_free(&ost->swr_opts);
av_dict_free(&ost->resample_opts);
}
}
}
return ret;
}
* Run a single step of transcoding.
*
* @return 0 for success, <0 for error
*/
static int transcode_step(void)
{
OutputStream *ost;
InputStream *ist;
int ret;
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];
}
/* 读取一个packet并处理 */
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);
}
1. 函数概述
它是ffmpeg的转码的主函数。工作模式是每次读取一个packet,并处理。
可见《 FFmpeg情景分析 1. FFmpeg转码框架解析》
2. 函数框架图
3. 函数伪码解析
static int transcode(void){
/* 对每个输出流,计算出正确的编码参数,并写好文件头 */
ret = transcode_init();
while (1) {
/* 如果没有数据需要输出了,则循环结束 */
if (!need_output()) {
break;
}
/* 转码用的主函数,
* 每次循环是读取一个packet并处理
*/
ret = transcode_step();
/* dump report by using the output first video and audio streams */
print_report(0, timer_start, cur_time);
}
/* at the end of stream, we must flush the decoder buffers */
flush_encoders();
/* write the trailer if needed and close file */
/* dump report by using the first video and audio streams */
print_report(1, timer_start, av_gettime_relative());
/* close each encoder */
/* close each decoder */
/* finished ! */
ret = 0;
fail:
return ret;
}
4. 代码分析
static int transcode(void){
int ret, i;
AVFormatContext *os;
OutputStream *ost;
InputStream *ist;
int64_t timer_start;
int64_t total_packets_written = 0;
/* 对每个输出流,计算出正确的编码参数,
* 然后在命令行显示出每一路流所用到编码参数信息,
* 最后写文件头,如FLV文件,则会写FLV文件的第一个tag。
*/
ret = transcode_init();
if (ret < 0)
goto fail;
if (stdin_interaction) {
av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
}
timer_start = av_gettime_relative();
/* 多线程处理,见《FFmpeg源码剖析-多线程:input_threads》
#if HAVE_PTHREADS
if ((ret = init_input_threads()) < 0)
goto fail;
#endif
while (!received_sigterm) {
int64_t cur_time= av_gettime_relative();
/* 如果在转码进行中,命令行的键盘上点击"q",则任务结束。
* 见《ffmpeg键盘命令响应程序详解》
*/
if (stdin_interaction)
if (check_keyboard_interaction(cur_time) < 0)
break;
/* check if there's any stream where output is still needed */
if (!need_output()) {
av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");
break;
}
ret = transcode_step();
if (ret < 0 && ret != AVERROR_EOF) {
char errbuf[128];
av_strerror(ret, errbuf, sizeof(errbuf));
av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", errbuf);
break;
}
/* dump report by using the output first video and audio streams */
print_report(0, timer_start, cur_time);
}
#if HAVE_PTHREADS
free_input_threads();
#endif
/* at the end of stream, we must flush the decoder buffers */
for (i = 0; i < nb_input_streams; i++) {
ist = input_streams[i];
if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {
process_input_packet(ist, NULL, 0);
}
}
flush_encoders();
term_exit();
/* write the trailer if needed and close file */
for (i = 0; i < nb_output_files; i++) {
os = output_files[i]->ctx;
if (!output_files[i]->header_written) {
av_log(NULL, AV_LOG_ERROR,
"Nothing was written into output file %d (%s), because "
"at least one of its streams received no packets.\n",
i, os->filename);
continue;
}
if ((ret = av_write_trailer(os)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s", os->filename, av_err2str(ret));
if (exit_on_error)
exit_program(1);
}
}
/* dump report by using the first video and audio streams */
print_report(1, timer_start, av_gettime_relative());
/* close each encoder */
for (i = 0; i < nb_output_streams; i++) {
ost = output_streams[i];
if (ost->encoding_needed) {
av_freep(&ost->enc_ctx->stats_in);
}
total_packets_written += ost->packets_written;
}
if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) {
av_log(NULL, AV_LOG_FATAL, "Empty output\n");
exit_program(1);
}
/* close each decoder */
for (i = 0; i < nb_input_streams; i++) {
ist = input_streams[i];
if (ist->decoding_needed) {
avcodec_close(ist->dec_ctx);
if (ist->hwaccel_uninit)
ist->hwaccel_uninit(ist->dec_ctx);
}
}
av_buffer_unref(&hw_device_ctx);
/* finished ! */
ret = 0;
fail:
#if HAVE_PTHREADS
free_input_threads();
#endif
if (output_streams) {
for (i = 0; i < nb_output_streams; i++) {
ost = output_streams[i];
if (ost) {
if (ost->logfile) {
if (fclose(ost->logfile))
av_log(NULL, AV_LOG_ERROR,
"Error closing logfile, loss of information possible: %s\n",
av_err2str(AVERROR(errno)));
ost->logfile = NULL;
}
av_freep(&ost->forced_kf_pts);
av_freep(&ost->apad);
av_freep(&ost->disposition);
av_dict_free(&ost->encoder_opts);
av_dict_free(&ost->sws_dict);
av_dict_free(&ost->swr_opts);
av_dict_free(&ost->resample_opts);
}
}
}
return ret;
}
4.1 transcode_step(void)
/*** Run a single step of transcoding.
*
* @return 0 for success, <0 for error
*/
static int transcode_step(void)
{
OutputStream *ost;
InputStream *ist;
int ret;
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];
}
/* 读取一个packet并处理 */
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);
}