国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

FFMPEG源碼之ffmpeg.c解析

這篇具有很好參考價值的文章主要介紹了FFMPEG源碼之ffmpeg.c解析。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

main()

int main(int argc, char **argv)
{
    int ret;
    BenchmarkTimeStamps ti;

    /* 初始化動態(tài)加載 */
    init_dynload();

    /* 注冊退出回調(diào)函數(shù) */
    register_exit(ffmpeg_cleanup);

    /* 設(shè)置stderr的緩沖模式(win32運行時需要) */
    setvbuf(stderr, NULL, _IONBF, 0);

    /* 設(shè)置日志打印選項 */
    av_log_set_flags(AV_LOG_SKIP_REPEATED);
    parse_loglevel(argc, argv, options);

#if CONFIG_AVDEVICE
    /* 注冊音視頻設(shè)備 */
    avdevice_register_all();
#endif

    /* 初始化網(wǎng)絡(luò)模塊 */
    avformat_network_init();

    /* 顯示ffmpeg的banner信息 */
    show_banner(argc, argv, options);

    /* 解析命令行選項并打開所有的輸入/輸出文件 */
    ret = ffmpeg_parse_options(argc, argv);
    if (ret < 0)
        exit_program(1);

    /* 檢查是否沒有指定輸出文件并且沒有輸入文件 */
    if (nb_output_files <= 0 && nb_input_files == 0) {
        show_usage();
        av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
        exit_program(1);
    }

    /* 檢查是否至少指定一個輸出文件 */
    if (nb_output_files <= 0) {
        av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
        exit_program(1);
    }

    current_time = ti = get_benchmark_time_stamps();

    /* 文件轉(zhuǎn)碼或抓取 */
    if (transcode() < 0)
        exit_program(1);

    if (do_benchmark) {
        int64_t utime, stime, rtime;
        current_time = get_benchmark_time_stamps();
        utime = current_time.user_usec - ti.user_usec;
        stime = current_time.sys_usec  - ti.sys_usec;
        rtime = current_time.real_usec - ti.real_usec;
        av_log(NULL, AV_LOG_INFO,
               "bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n",
               utime / 1000000.0, stime / 1000000.0, rtime / 1000000.0);
    }

    av_log(NULL, AV_LOG_DEBUG,
           "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n",
           decode_error_stat[0], decode_error_stat[1]);

    /* 檢查解碼錯誤是否超過了指定的錯誤率 */
    if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
        exit_program(69);

    /* 根據(jù)是否接收到信號確定程序的返回碼,并終止程序 */
    exit_program(received_nb_signals ? 255 : main_return_code);

    return main_return_code;
}

下面是對每個步驟的功能的詳細解釋:

  1. 初始化動態(tài)加載。
    • 調(diào)用init_dynload函數(shù),用于初始化動態(tài)加載庫的相關(guān)資源,以便在需要時加載需要的庫。
  2. 注冊退出回調(diào)函數(shù)。
    • 調(diào)用register_exit函數(shù),將ffmpeg_cleanup函數(shù)注冊為在程序退出時被調(diào)用的回調(diào)函數(shù)。
  3. 設(shè)置stderr的緩沖模式。
    • 調(diào)用setvbuf函數(shù),將stderr的緩沖模式設(shè)置為無緩沖模式,以確保錯誤信息可以立即顯示在終端上。
  4. 設(shè)置日志打印選項。
    • 調(diào)用av_log_set_flags函數(shù),設(shè)置日志打印選項。在這里設(shè)置
      AV_LOG_SKIP_REPEATED選項,表示日志會跳過重復的消息。
  5. 解析命令行參數(shù)中的日志級別選項。
    • 調(diào)用parse_loglevel函數(shù),解析命令行參數(shù)中的日志級別選項,并將其應用到日志系統(tǒng)中。
  6. 注冊音視頻設(shè)備。
    • 調(diào)用avdevice_register_all函數(shù),用于注冊所有的音視頻設(shè)備。
  7. 初始化網(wǎng)絡(luò)模塊。
    • 調(diào)用avformat_network_init函數(shù),初始化網(wǎng)絡(luò)模塊,以便進行網(wǎng)絡(luò)相關(guān)的操作,如打開網(wǎng)絡(luò)流。
  8. 顯示ffmpeg的banner信息。
    • 調(diào)用show_banner函數(shù),根據(jù)命令行參數(shù)、選項和程序信息,打印ffmpeg的banner信息。
  9. 解析命令行選項并打開所有的輸入/輸出文件。
    • 調(diào)用ffmpeg_parse_options函數(shù),解析命令行選項,并根據(jù)選項打開所有的輸入/輸出文件。
  10. 檢查是否沒有指定輸出文件并且沒有輸入文件。
    • 檢查nb_output_files和nb_input_files的值。
    • 若滿足條件,則打印用法信息和警告,并終止程序。
  11. 檢查是否至少指定一個輸出文件。
    • 檢查nb_output_files的值。
    • 若不滿足條件,則打印致命錯誤信息,并終止程序。
  12. 進行文件轉(zhuǎn)碼或抓取。
    • 調(diào)用transcode函數(shù),進行文件轉(zhuǎn)碼或抓取。
    • transcode函數(shù)返回值小于0表示出錯,并通過調(diào)用exit_program函數(shù)終止程序。
  13. 如果啟用了性能評測,輸出性能數(shù)據(jù)。
    • 如果do_benchmark為真,計算從開始到結(jié)束的用戶時間、系統(tǒng)時間和真實時間,并打印出來。
  14. 打印解碼幀數(shù)和解碼錯誤數(shù)。
    • 調(diào)用av_log函數(shù),打印成功解碼的幀數(shù)和解碼錯誤數(shù)。
  15. 檢查解碼錯誤是否超過了指定的錯誤率。
    • 檢查解碼錯誤數(shù)是否超過了最大錯誤率。
    • 若滿足條件,則通過調(diào)用exit_program終止程序。
  16. 根據(jù)是否接收到信號確定程序的返回碼,并終止程序。
  17. 返回main_return_code作為main函數(shù)的返回值。

針對main函數(shù)中幾個重要的函數(shù),下面將逐步解析他們的具體實現(xiàn)

ffmpeg_parse_options()

// 解析命令行參數(shù)并設(shè)置選項
int ffmpeg_parse_options(int argc, char **argv)
{
    // 定義選項解析上下文和錯誤信息
    OptionParseContext octx;
    uint8_t error[128];
    int ret;

    memset(&octx, 0, sizeof(octx));

    /* split the commandline into an internal representation */
    // 將命令行參數(shù)拆分為內(nèi)部表示形式
    ret = split_commandline(&octx, argc, argv, options, groups,
                            FF_ARRAY_ELEMS(groups));
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: ");
        goto fail;
    }

    /* apply global options */
    // 應用全局選項
    ret = parse_optgroup(NULL, &octx.global_opts);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error parsing global options: ");
        goto fail;
    }

    /* configure terminal and setup signal handlers */
    // 配置終端并設(shè)置信號處理程序
    term_init();

    /* open input files */
    // 打開輸入文件
    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error opening input files: ");
        goto fail;
    }

    // 應用同步偏移量
    apply_sync_offsets();

    /* create the complex filtergraphs */
    // 創(chuàng)建復雜的濾波器圖
    ret = init_complex_filters();
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n");
        goto fail;
    }

    /* open output files */
    // 打開輸出文件
    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error opening output files: ");
        goto fail;
    }

    // 檢查濾波器的輸出
    check_filter_outputs();

fail:
    // 反初始化選項解析上下文
    uninit_parse_context(&octx);
    if (ret < 0) {
        // 如果有錯誤,將錯誤信息輸出到日志
        av_strerror(ret, error, sizeof(error));
        av_log(NULL, AV_LOG_FATAL, "%s\n", error);
    }
    return ret;
}

函數(shù)功能:該函數(shù)用于解析命令行參數(shù)并進行相應處理,包括拆分命令行參數(shù)為內(nèi)部表示形式、應用全局選項、配置終端和設(shè)置信號處理程序、打開輸入文件、應用同步偏移量、創(chuàng)建復雜的濾波器圖、打開輸出文件、檢查濾波器的輸出。如果出現(xiàn)錯誤,會將錯誤信息輸出到日志。


transcode()

static int transcode(void)
{
    int ret, i;
    OutputStream *ost;
    InputStream *ist;
    int64_t timer_start;
    int64_t total_packets_written = 0;
	
	//執(zhí)行轉(zhuǎn)碼初始化操作,并將返回值賦給變量ret。
    ret = transcode_init();
    if (ret < 0)
        goto fail;
	
	//檢查是否啟用了從標準輸入進行交互的選項。
    if (stdin_interaction) {
        av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
    }

	// 記錄轉(zhuǎn)碼開始的時間,用于計算整個過程的耗時。
    timer_start = av_gettime_relative();

	//執(zhí)行輸入線程的初始化,啟動輸入線程來讀取輸入文件。
    if ((ret = init_input_threads()) < 0)
        goto fail;

	//持續(xù)循環(huán)進行轉(zhuǎn)碼操作,直到接收到停止信號。
    while (!received_sigterm) {
        int64_t cur_time= av_gettime_relative();	//記錄當前時間,用于計算轉(zhuǎn)碼過程中的耗時。

        /* if 'q' pressed, exits */
        /* 檢查是否啟用了從標準輸入進行交互的選項。如果啟用了與用戶交互的選項,
           將會檢查是否需要檢查鍵盤交互來停止轉(zhuǎn)碼。*/
        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;
        }
		
		// 執(zhí)行轉(zhuǎn)碼的一步操作,比如從輸入文件中讀取數(shù)據(jù),應用濾鏡,寫入輸出文件等等。
        ret = transcode_step();
        if (ret < 0 && ret != AVERROR_EOF) {
            av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret));
            break;
        }

        /* dump report by using the output first video and audio streams */
        // 根據(jù)輸出流的第一個視頻和音頻流打印轉(zhuǎn)碼報告。顯示轉(zhuǎn)碼的進度和統(tǒng)計信息。
        print_report(0, timer_start, cur_time);
    }
    free_input_threads();	//釋放輸入線程的資源。

    /* at the end of stream, we must flush the decoder buffers */
    //循環(huán)處理每個輸入流的解碼器緩沖區(qū),以確保在流結(jié)束時進行刷新。
    for (i = 0; i < nb_input_streams; i++) {
        ist = input_streams[i];
        if (!input_files[ist->file_index]->eof_reached) {
            process_input_packet(ist, NULL, 0);
        }
    }
    flush_encoders();	// 刷新編碼器的緩沖區(qū),將剩余的數(shù)據(jù)編碼為輸出幀。

    term_exit(); // 執(zhí)行終結(jié)操作,例如釋放資源和關(guān)閉設(shè)備。

    /* write the trailer if needed */
    // 循環(huán)寫入每個輸出文件的尾部。
    for (i = 0; i < nb_output_files; i++) {
        ret = of_write_trailer(output_files[i]); 
        if (ret < 0 && exit_on_error)
            exit_program(1);
    }

    /* dump report by using the first video and audio streams */
    // 根據(jù)第一個視頻和音頻流打印轉(zhuǎn)碼報告,顯示轉(zhuǎn)碼的最終進度和統(tǒng)計信息。
    print_report(1, timer_start, av_gettime_relative());

    /* close each encoder */
    // 循環(huán)關(guān)閉每個輸出流的編碼器。
    for (i = 0; i < nb_output_streams; i++) {
        uint64_t packets_written;
        ost = output_streams[i];
        packets_written = atomic_load(&ost->packets_written);
        total_packets_written += packets_written;
        if (!packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) {
            av_log(NULL, AV_LOG_FATAL, "Empty output on stream %d.\n", i);
            exit_program(1);
        }
    }
	
	// 如果輸出為空且設(shè)置了對空輸出流終止轉(zhuǎn)碼的標志,將會退出程序。
    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 */
    // 循環(huán)關(guān)閉每個輸入流的解碼器。
    for (i = 0; i < nb_input_streams; i++) {
        ist = input_streams[i];
        if (ist->decoding_needed) {
            avcodec_close(ist->dec_ctx);
        }
    }
	
	// 釋放所有的硬件設(shè)備。
    hw_device_free_all();

    /* finished ! */
    ret = 0;

 fail:
    free_input_threads();	// 釋放輸入線程所占用的資源。
	
	// 如果存在輸出流,將會釋放輸出流相關(guān)的資源。
    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);
            }
        }
    }
    return ret;
}


transcode()函數(shù)中每個流程的詳細操作步驟及功能,并與上下文的關(guān)系的說明:
  1. 初始化轉(zhuǎn)碼:調(diào)用transcode_init函數(shù)初始化轉(zhuǎn)碼過程。進行一些初始化操作,包括打開輸入和輸出文件,初始化輸入和輸出流等。該步驟在整個流程中只進行一次。
  2. 控制臺交互提示:如果啟用stdin_interaction參數(shù),打印控制臺交互提示信息。在控制臺輸出交互提示信息,指示用戶如何操作。根據(jù)參數(shù)stdin_interaction的設(shè)置,判斷是否需要進行控制臺交互提示。
  3. 初始化輸入線程:調(diào)用init_input_threads函數(shù)初始化輸入線程,準備進行輸入數(shù)據(jù)的讀取和處理。在轉(zhuǎn)碼過程中,可能需要使用多線程來進行輸入數(shù)據(jù)的讀取和處理。調(diào)用此函數(shù)來初始化輸入線程。
  4. 循環(huán)處理輸入數(shù)據(jù):在循環(huán)處理輸入數(shù)據(jù)的過程中,調(diào)用transcode_step函數(shù)來處理輸入數(shù)據(jù)。該函數(shù)通過選擇輸出流、處理濾鏡、處理輸入流等步驟,將輸入數(shù)據(jù)進行轉(zhuǎn)碼處理,并寫入到輸出流中。
  5. 打印報告:在轉(zhuǎn)碼過程中,定期調(diào)用print_report函數(shù)來打印轉(zhuǎn)碼報告,展示轉(zhuǎn)碼的進度和所消耗的時間。
  6. 釋放輸入線程::調(diào)用free_input_threads函數(shù)釋放輸入線程。在轉(zhuǎn)碼結(jié)束后,釋放輸入線程的資源,表示輸入數(shù)據(jù)的讀取和處理已經(jīng)完成。
  7. 刷新編碼器:調(diào)用flush_encoders函數(shù)刷新編碼器。在所有的輸入數(shù)據(jù)處理完畢后,需要將編碼器中剩余的數(shù)據(jù)進行編碼和寫入輸出流。調(diào)用flush_encoders函數(shù)來完成此操作。
  8. 退出程序:在轉(zhuǎn)碼流程結(jié)束后,進行一些資源的釋放和清理工作,然后調(diào)用term_exit函數(shù)退出程序。
  9. 寫入輸出文件尾:在轉(zhuǎn)碼結(jié)束后,需要將輸出文件的文件尾寫入。調(diào)用of_write_trailer函數(shù)完成此操作。
  10. 打印最終報告:在轉(zhuǎn)碼結(jié)束后,調(diào)用print_report函數(shù)來打印最終的轉(zhuǎn)碼報告,展示轉(zhuǎn)碼的總體進度和所消耗的時間。
  11. 關(guān)閉編碼器和解碼器:在轉(zhuǎn)碼結(jié)束后,需要關(guān)閉每個輸出流中的編碼器,以及每個輸入流中的解碼器,釋放相應的資源。

通過以上的流程,transcode()函數(shù)實現(xiàn)了將輸入數(shù)據(jù)進行轉(zhuǎn)碼處理,并將結(jié)果寫入輸出流中,最終完成轉(zhuǎn)碼操作。每個流程均有特定的功能,按照一定的順序進行處理,達到轉(zhuǎn)碼的目的。每個流程的操作都與上下文關(guān)系密切,根據(jù)上一個流程的處理結(jié)果和狀態(tài)來決定下一個流程的操作和流程邏輯。整個轉(zhuǎn)碼過程在循環(huán)中進行,直到所有的輸入數(shù)據(jù)處理完畢、輸出流刷新完畢,并將輸出文件的文件尾寫入,最后退出程序。

文章來源地址http://www.zghlxwxcb.cn/news/detail-605499.html

transcode_init()

static int transcode_init(void)
{
    int ret = 0, i, j, k;
    OutputStream *ost;
    InputStream *ist;
    char error[1024] = {0};
	
	// 遍歷 nb_filtergraphs 列表中的 FilterGraph 結(jié)構(gòu)體。
    for (i = 0; i < nb_filtergraphs; i++) {
        FilterGraph *fg = filtergraphs[i];
        for (j = 0; j < fg->nb_outputs; j++) {
            OutputFilter *ofilter = fg->outputs[j];
            if (!ofilter->ost || ofilter->ost->source_index >= 0) // 如果輸出的 ost 為空或 source_index 大于等于0,則跳過
                continue;
            if (fg->nb_inputs != 1)	// 如果 FilterGraph 的輸入數(shù)不等于1,則跳過。
                continue;
            for (k = nb_input_streams-1; k >= 0 ; k--) // 從后往前遍歷輸入流列表,找到與 FilterGraph 的第一個輸入匹配的輸入流。
                if (fg->inputs[0]->ist == input_streams[k])
                    break;
            ofilter->ost->source_index = k; // 將找到的輸入流的索引賦值給 ost 的 source_index 屬性
        }
    }

    /* init framerate emulation */
    // 初始化幀率模擬
    for (i = 0; i < nb_input_files; i++) {	// 遍歷輸入文件列表。
        InputFile *ifile = input_files[i];
        if (ifile->readrate || ifile->rate_emu) // 如果輸入文件的 readrate 或 rate_emu 不為0,則對其所有流進行幀率模擬的初始化。
            for (j = 0; j < ifile->nb_streams; j++)
                input_streams[j + ifile->ist_index]->start = av_gettime_relative(); // 初始化方法是設(shè)置輸入流的 start 屬性為當前系統(tǒng)時間。
    }

    /* init input streams */
    // 初始化輸入流
    for (i = 0; i < nb_input_streams; i++) // 遍歷輸入流列表。
        if ((ret = init_input_stream(i, error, sizeof(error))) < 0) // 調(diào)用 init_input_stream() 對輸入流進行初始化。
            goto dump_format;

    /*
     * initialize stream copy and subtitle/data streams.
     * Encoded AVFrame based streams will get initialized as follows:
     * - when the first AVFrame is received in do_video_out
     * - just before the first AVFrame is received in either transcode_step
     *   or reap_filters due to us requiring the filter chain buffer sink
     *   to be configured with the correct audio frame size, which is only
     *   known after the encoder is initialized.
     */
     //初始化流復制和字幕/數(shù)據(jù)流:
    for (i = 0; i < nb_output_streams; i++) { // 遍歷輸出流列表。
    	// 對于每個輸出流,檢查其是否為編碼的 AVFrame 流。如果是,則跳過初始化。
        if (output_streams[i]->enc_ctx &&
            (output_streams[i]->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
             output_streams[i]->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
            continue;

        ret = init_output_stream_wrapper(output_streams[i], NULL, 0); // 初始化輸出流。
        if (ret < 0)
            goto dump_format;
    }

    /* discard unused programs */
    // 丟棄未使用的程序:
    for (i = 0; i < nb_input_files; i++) { // 遍歷輸入文件列表。
        InputFile *ifile = input_files[i];
        for (j = 0; j < ifile->ctx->nb_programs; j++) { // 對于每個輸入文件,遍歷其所有程序。
            AVProgram *p = ifile->ctx->programs[j];
            int discard  = AVDISCARD_ALL;
			// 檢查每個程序關(guān)聯(lián)的流是否需要丟棄。
            for (k = 0; k < p->nb_stream_indexes; k++)
                if (!input_streams[ifile->ist_index + p->stream_index[k]]->discard) {
                    discard = AVDISCARD_DEFAULT;	// 如果有未被丟棄的流,則將程序的丟棄標志設(shè)置為默認丟棄。
                    break;
                }
            p->discard = discard;
        }
    }

 dump_format:
    /* dump the stream mapping */
    // 打印流映射信息:
    av_log(NULL, AV_LOG_INFO, "Stream mapping:\n");
    for (i = 0; i < nb_input_streams; i++) {
        ist = input_streams[i];

        for (j = 0; j < ist->nb_filters; j++) {
        	// 對于每個輸入流的濾鏡圖,如果不是簡單濾鏡圖,則打印其相關(guān)信息。
            if (!filtergraph_is_simple(ist->filters[j]->graph)) {
                av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d (%s) -> %s",
                       ist->file_index, ist->st->index, ist->dec ? ist->dec->name : "?",
                       ist->filters[j]->name);	
                if (nb_filtergraphs > 1)
                    av_log(NULL, AV_LOG_INFO, " (graph %d)", ist->filters[j]->graph->index);
                av_log(NULL, AV_LOG_INFO, "\n");
            }
        }
    }

	// 打印輸出流映射信息
    for (i = 0; i < nb_output_streams; i++) { // 遍歷輸出流列表。
        ost = output_streams[i];

		// 如果輸出流是附加的文件流,則打印其文件名和關(guān)聯(lián)的流信息。
        if (ost->attachment_filename) {	
            /* an attached file */
            av_log(NULL, AV_LOG_INFO, "  File %s -> Stream #%d:%d\n",
                   ost->attachment_filename, ost->file_index, ost->index);
            continue;
        }

		// 如果輸出流是來自復雜濾鏡圖的輸出流,則打印濾鏡圖和關(guān)聯(lián)的流信息。
        if (ost->filter && !filtergraph_is_simple(ost->filter->graph)) {
            /* output from a complex graph */
            av_log(NULL, AV_LOG_INFO, "  %s", ost->filter->name);
            if (nb_filtergraphs > 1)
                av_log(NULL, AV_LOG_INFO, " (graph %d)", ost->filter->graph->index);

            av_log(NULL, AV_LOG_INFO, " -> Stream #%d:%d (%s)\n", ost->file_index,
                   ost->index, ost->enc ? ost->enc->name : "?");
            continue;
        }
		
		// 否則,打印輸入流和輸出流的對應關(guān)系。
        av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d -> #%d:%d",
               input_streams[ost->source_index]->file_index,
               input_streams[ost->source_index]->st->index,
               ost->file_index,
               ost->index);
        if (ost->enc_ctx) {
            const AVCodec *in_codec    = input_streams[ost->source_index]->dec;
            const AVCodec *out_codec   = ost->enc;
            const char *decoder_name   = "?";
            const char *in_codec_name  = "?";
            const char *encoder_name   = "?";
            const char *out_codec_name = "?";
            const AVCodecDescriptor *desc;

            if (in_codec) {
                decoder_name  = in_codec->name;
                desc = avcodec_descriptor_get(in_codec->id);
                if (desc)
                    in_codec_name = desc->name;
                if (!strcmp(decoder_name, in_codec_name))
                    decoder_name = "native";
            }

            if (out_codec) {
                encoder_name   = out_codec->name;
                desc = avcodec_descriptor_get(out_codec->id);
                if (desc)
                    out_codec_name = desc->name;
                if (!strcmp(encoder_name, out_codec_name))
                    encoder_name = "native";
            }

            av_log(NULL, AV_LOG_INFO, " (%s (%s) -> %s (%s))",
                   in_codec_name, decoder_name,
                   out_codec_name, encoder_name);
        } else
            av_log(NULL, AV_LOG_INFO, " (copy)");
        av_log(NULL, AV_LOG_INFO, "\n");
    }

    if (ret) {
        av_log(NULL, AV_LOG_ERROR, "%s\n", error);
        return ret;
    }

    atomic_store(&transcode_init_done, 1);

    return 0;
}


該函數(shù)是一個用于初始化轉(zhuǎn)碼器的函數(shù)。下面是每個步驟的詳細操作步驟及功能,并與上下文的關(guān)系進行說明:
  1. 初始化source_index:該步驟的目的是為每個輸出流設(shè)置源輸入流的索引(source_index)。這個索引用于指示輸出流的源是哪個輸入流。根據(jù)轉(zhuǎn)碼器的邏輯,只有當輸出流沒有源或者它的源索引為負值時,才需要設(shè)置源索引。如果過濾圖的輸入數(shù)目不為1,則跳過該輸出流。然后,遍歷所有的輸入流,找到與當前過濾圖的輸入對應的輸入流索引并將其設(shè)置為源索引。
  2. 初始化幀率仿真:這一步驟的目的是為了在處理視頻幀時模擬輸入文件的特定幀速率。對于每個輸入文件,如果設(shè)置了readrate或rate_emu參數(shù),則遍歷該文件的每個流,設(shè)置其開始時間為相對于系統(tǒng)啟動時間的時間戳。
  3. 初始化輸入流:對于每個輸入流,調(diào)用init_input_stream函數(shù)進行初始化。如果初始化失敗,則跳轉(zhuǎn)到dump_format標簽,進行格式轉(zhuǎn)換。
  4. 初始化流復制和字幕/數(shù)據(jù)流:這一步驟的目的是初始化流的復制、字幕和數(shù)據(jù)流。對于每個輸出流,如果其對應的編碼上下文存在且流的類型是視頻或音頻,則跳過該輸出流。否則,調(diào)用init_output_stream_wrapper函數(shù)進行初始化。
  5. 丟棄未使用的節(jié)目:這一步驟的目的是丟棄未使用的節(jié)目,即那些沒有被任何輸入流參考的節(jié)目。對于每個輸入文件,遍歷其所有的節(jié)目,并檢查每個節(jié)目中的流索引。如果找到至少一個流不是丟棄的,則將該節(jié)目的discard設(shè)置為AVDISCARD_DEFAULT,否則設(shè)置為AVDISCARD_ALL。
  6. 打印流映射:輸出日志以打印流映射關(guān)系。依次遍歷每個輸入流,并對于每個輸入流中的濾鏡依次輸出相關(guān)信息。這一步驟的作用是在日志中顯示輸入流的濾鏡鏈。
  7. 打印輸出流映射:輸出日志以打印輸出流映射關(guān)系。依次遍歷每個輸出流,并輸出與該輸出流相關(guān)的信息,如附加文件信息、復雜圖結(jié)構(gòu)信息等。
  8. 檢查初始化結(jié)果:如果初始化過程中出現(xiàn)錯誤,則返回錯誤碼并輸出錯誤信息。否則,設(shè)置transcode_init_done標志為1,表示轉(zhuǎn)碼器已經(jīng)初始化完成,并返回0表示成功初始化。

transcode_step()

static int transcode_step(void)
{
    OutputStream *ost;         // 輸出流指針
    InputStream  *ist = NULL;  // 輸入流指針
    int ret;

    // 選擇輸出流
    ost = choose_output();
    if (!ost) {
        // 如果沒有輸出流
        if (got_eagain()) {  // 檢查是否發(fā)生 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 && !ost->filter->graph->graph) {
        // 如果輸出流有濾鏡,并且濾鏡圖沒有構(gòu)建,則進行濾鏡圖的配置
        if (ifilter_has_all_input_formats(ost->filter->graph)) {
            ret = configure_filtergraph(ost->filter->graph);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n");
                return ret;
            }
        }
    }

    if (ost->filter && ost->filter->graph->graph) {
        // 如果輸出流有濾鏡,并且濾鏡圖已經(jīng)構(gòu)建,則進行相關(guān)操作
        if (av_buffersink_get_type(ost->filter->filter) == AVMEDIA_TYPE_AUDIO)
            init_output_stream_wrapper(ost, NULL, 1);  // 音頻特殊處理,進行輸出流的初始化

        ret = transcode_from_filter(ost->filter->graph, &ist);  // 從濾鏡開始轉(zhuǎn)碼
        if (ret < 0)
            return ret;
        if (!ist)
            return 0;
    } else if (ost->filter) {
        // 如果輸出流有濾鏡,但濾鏡圖沒有構(gòu)建,則嘗試找到輸入流
        int i;
        for (i = 0; i < ost->filter->graph->nb_inputs; i++) {
            InputFilter *ifilter = ost->filter->graph->inputs[i];
            if (!ifilter->ist->got_output && !input_files[ifilter->ist->file_index]->eof_reached) {
                ist = ifilter->ist;
                break;
            }
        }
        if (!ist) {
            ost->inputs_done = 1;  // 輸入流處理完畢
            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)) {
        // 如果返回 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);  // 回收濾鏡
}

transcode_step函數(shù)實現(xiàn)了對輸入流的處理和轉(zhuǎn)碼操作。具體的處理方式和操作根據(jù)輸出流的關(guān)聯(lián)情況和濾鏡圖的配置狀態(tài)進行調(diào)整,以達到轉(zhuǎn)碼的功能。
  1. 選擇輸出流:調(diào)用choose_output函數(shù)選擇一個輸出流進行處理。如果沒有可用的輸出流,則檢查是否收到EAGAIN錯誤,如果是則等待一段時間后返回0,否則輸出日志并返回AVERROR_EOF表示已經(jīng)沒有更多的輸入需要讀取。
  2. 配置濾鏡圖:對輸出流關(guān)聯(lián)的濾鏡圖進行配置。如果輸出流關(guān)聯(lián)了濾鏡圖,并且濾鏡圖尚未被配置,則調(diào)用configure_filtergraph函數(shù)對濾鏡圖進行配置。
  3. 處理帶濾鏡的輸出流:對關(guān)聯(lián)了濾鏡圖的輸出流進行處理。如果輸出流關(guān)聯(lián)了濾鏡圖,并且濾鏡圖已經(jīng)被配置,就進行處理。這個步驟包含了對特殊情況的處理,主要是針對音頻的初始化。然后調(diào)用transcode_from_filter函數(shù)對濾鏡圖進行處理,并通過傳入的指針返回輸入流。
  4. 處理無濾鏡的輸出流:對沒有關(guān)聯(lián)濾鏡圖的輸出流進行處理。如果輸出流沒有關(guān)聯(lián)濾鏡圖,但關(guān)聯(lián)了輸出流的源輸入流,則遍歷濾鏡圖的輸入,找到第一個沒有輸出的輸入流,并將其賦值給ist指針。如果所有的輸入流都已經(jīng)輸出完畢(eof_reached標志為真),則將輸出流的inputs_done標志置為1表示輸入流處理完畢,并返回0。
  5. 處理輸入流:調(diào)用process_input函數(shù)處理輸入流。具體操作是讀取輸入幀,解碼并進行相應的處理。返回值可能為EAGAIN表示需要更多的輸入,所以直接返回0。
  6. 處理輸入錯誤:如果處理輸入流發(fā)生錯誤,則根據(jù)錯誤碼判斷是否為AVERROR_EOF,如果是則返回0表示輸入流處理完畢,否則返回錯誤碼。
  7. 回收濾鏡:調(diào)用reap_filters函數(shù)回收濾鏡。這個函數(shù)會檢查濾鏡圖中的每個濾鏡的輸出是否準備好,如果準備好則進行相關(guān)操作。返回0表示濾鏡回收完畢,繼續(xù)進行下一輪的處理。
    ,

到了這里,關(guān)于FFMPEG源碼之ffmpeg.c解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • FFmpeg5.0源碼閱讀——FFmpeg大體框架(以GIF轉(zhuǎn)碼為示例)

    FFmpeg5.0源碼閱讀——FFmpeg大體框架(以GIF轉(zhuǎn)碼為示例)

    ?? 摘要 :前一段時間熟悉了下FFmpeg主流程源碼實現(xiàn),對FFmpeg的整體框架有了個大概的認識,因此在此做一個筆記,希望以比較容易理解的文字描述FFmpeg本身的結(jié)構(gòu),加深對FFmpeg的框架進行梳理加深理解,如果文章中有紕漏或者錯誤歡迎指出。本文描述了FFmpeg編解碼框架的

    2024年02月10日
    瀏覽(23)
  • [FFmpeg] 源碼編譯

    git clone https://git.ffmpeg.org/ffmpeg.git git checkout -b 5.1 remotes/origin/release/5.1 ./configure --prefix=./OUT --enable-shared --disable-static make make install 默認安裝路徑見: /usr/local/bin /usr/local/include /usr/local/lib /usr/local/main/man1 /usr/local/main/man3 有 --prefix 參數(shù)的安裝路徑: [prefix]/bin [prefix]/include [prefix

    2024年02月10日
    瀏覽(18)
  • 源碼編譯FFmpeg4.3

    FreeSWITCH的mod_av模塊目前(1.10.11)暫不支持FFmpeg4.4(或者更高版本),但4.3就沒問題 最近試了試源碼編譯FFmpeg4.3,記錄如下(系統(tǒng)centos7.9): git clone?GitHub - BtbN/FFmpeg-Builds 找到4.4.sh,改成這樣: GIT_BRANCH=\\\"release/4.3\\\" ./build.sh linux64 gpl 4.4 cd ffbuild/ffmpeg ./configure?--enable-shared make

    2024年02月01日
    瀏覽(22)
  • FFmpeg源碼走讀之內(nèi)存管理模型

    FFmpeg源碼走讀之內(nèi)存管理模型

    數(shù)據(jù)包管理過程中當數(shù)據(jù)轉(zhuǎn)移到新的數(shù)據(jù)包時存在兩種操作一種是數(shù)據(jù)包之間相互獨立,當新創(chuàng)建一份數(shù)據(jù)包時,需要將原來的數(shù)據(jù)重新申請一個數(shù)據(jù)空間并且將數(shù)據(jù)拷貝到新的數(shù)據(jù)包中,具體過程如下圖所示。這種數(shù)據(jù)包的管理優(yōu)勢是在于數(shù)據(jù)之間相互獨立,不會存在數(shù)據(jù)

    2023年04月24日
    瀏覽(18)
  • FFmpeg aresample_swr_opts的解析

    FFmpeg aresample_swr_opts的解析

    aresample_swr_opts 是AVFilterGraph中的option。 因為是option,所以就想能不能將這個option配置到graph里面,分析代碼發(fā)現(xiàn), AVFilterGraph::aresample_swr_opts 在graph解析的時候不能當做filter的option解析。 因為graph load的在解析 graph 文本的過程,option來自filter的option, aresample_swr_opts 是AVFilterGr

    2024年02月15日
    瀏覽(14)
  • 了解FFmpeg音頻通道布局結(jié)構(gòu):AVChannelLayout結(jié)構(gòu)體解析

    了解FFmpeg音頻通道布局結(jié)構(gòu):AVChannelLayout結(jié)構(gòu)體解析

    FFmpeg是一套可以用來記錄、轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源計算機程序。它提供了錄制、轉(zhuǎn)換以及流化音視頻的完整解決方案。它包含了非常先進的音頻/視頻編解碼庫libavcodec,為了保證高可移植性和編解碼質(zhì)量,libavcodec里包含了很多編解碼器,這些編解碼器

    2024年02月08日
    瀏覽(19)
  • ffmpeg.c源碼與函數(shù)關(guān)系分析

    ffmpeg.c源碼與函數(shù)關(guān)系分析

    FFmpeg 是一個可以處理音視頻的軟件,功能非常強大,主要包括,編解碼轉(zhuǎn)換,封裝格式轉(zhuǎn)換,濾鏡特效。 FFmpeg支持各種網(wǎng)絡(luò)協(xié)議,支持 RTMP ,RTSP,HLS 等高層協(xié)議的推拉流,也支持更底層的TCP/UDP 協(xié)議推拉流。 FFmpeg 可以在 Windows,Linux,Mac,iOS,Android等操作系統(tǒng)上運行。 F

    2024年02月14日
    瀏覽(53)
  • ffmpeg.c(4.3.1)源碼剖析

    ffmpeg.c(4.3.1)源碼剖析

    本文對 ffmpeg.c 源碼進行學習及剖析。 鏈接:ffmpeg整體流程.jpg 下面對上述圖片進行介紹: 函數(shù)背景色 函數(shù)在圖中以方框的形式表現(xiàn)出來。不同的背景色標志了該函數(shù)不同的作用: 粉紅色背景函數(shù):FFmpeg 的 API 函數(shù)。 白色背景的函數(shù):FFmpeg 的內(nèi)部函數(shù)。 黃色背景的函數(shù):

    2024年02月03日
    瀏覽(14)
  • 深入淺出:FFmpeg 音頻解碼與處理AVFrame全解析

    深入淺出:FFmpeg 音頻解碼與處理AVFrame全解析

    FFmpeg 是一個開源的音視頻處理軟件,它包含了一系列的庫和程序,用于處理音頻、視頻和其他多媒體數(shù)據(jù)。FFmpeg 的名字來源于 “Fast Forward MPEG”,其中 MPEG 是一種常見的音視頻編碼標準。 FFmpeg 項目于 2000 年由 Fabrice Bellard 啟動,他是 QEMU(一種開源的計算機模擬器和虛擬機

    2024年02月04日
    瀏覽(41)
  • ffmpeg中filter_query_formats函數(shù)解析

    ffmpeg中filter_query_formats函數(shù)解析

    ffmpeg中filter_query_formats主要起一個pix fmt引用指定的功能。 下下結(jié)論: 先看幾個結(jié)構(gòu)體定義: 結(jié)構(gòu)體: AVFilterFormatsConfig 再來看函數(shù): 核心函數(shù): ff_set_common_formats 看宏定義: 接著看ref 主要看關(guān)鍵的三行代碼: 這就是最開始圖片指示的互相引用。

    2024年02月16日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包