ffmpeg进程优雅退出

需求

有时我们会在程序中通过fork/execl方式调用ffmpeg进程执行某些命令,这通常比调用API更简单。然后我们等待这些命令执行完毕后ffmpeg进程自动就会结束,但如果我们的任务是需要长时间运行比如拉取rtsp流保存为mp4,我们希望这个工作可以随时终止,终止的时候ffmpeg要优雅的退出,即ffmpeg要可以在退出时执行写入mp4的元数据、正常关闭文件等清理工作,使他看起来就像是正常退出一样,ffmpeg可以吗?

实现

ffmpeg是可以的。

一旦我们以额外进程的方式运行ffmpeg,那么通知ffmpeg进程最简单的方式就是发送信号,ffmpeg进程优雅退出的也是这么实现的,即监听某些信号,在信号回调函数中终止正在进行的操作,进行优雅退出。我们知道ffmpg这个可执行程序对应的代码是由ffmpeg.c,下面我们就跟踪下代码看起其具体实现。

在ffmpeg.c的main函数中会调用ffmpeg_parse_options函数,ffmpeg_parse_options中会调用SIGNAL函数对SIGINT和SIGTERM这两个信号进行注册,回调函数为sigterm_handler

 由下面sigterm_handler的实现可知,当这两个信号触发时,会将信号值赋值给received_sigterm,然后进行计算,如果接收的信号次数大于3则直接调用exit()进行强制退出了,而ffmpeg就是通过对received_sigterm的判断实现的优雅退出。

 由ffmpeg.c代码可知其命令的执行都是通过main函数调用transcode实现的,通过下面的transcode代码我们知道其主要包括两部分:通过一个循环不断的调用transcode_step执行任务,之后进行flush解码器、写文件尾这样的清理工作,而调用transcode_step的循环条件就是判断received_sigterm,所以收到信号后就不会再进行额外的transcode_step,转而去进行优雅的退出。

static int transcode(int *err_rate_exceeded)
{
    int ret = 0, i;
    InputStream *ist;
    int64_t timer_start;

    print_stream_maps();

    *err_rate_exceeded = 0;
    atomic_store(&transcode_init_done, 1);

    if (stdin_interaction) {
        av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
    }

    timer_start = av_gettime_relative();

    while (!received_sigterm) {
        OutputStream *ost;
        int64_t cur_time= av_gettime_relative();

        /* if 'q' pressed, exits */
        if (stdin_interaction)
            if (check_keyboard_interaction(cur_time) < 0)
                break;

        ret = choose_output(&ost);
        if (ret == AVERROR(EAGAIN)) {
            reset_eagain();
            av_usleep(10000);
            ret = 0;
            continue;
        } else if (ret < 0) {
            av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");
            ret = 0;
            break;
        }

        ret = transcode_step(ost);
        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 */
        print_report(0, timer_start, cur_time);
    }

    /* at the end of stream, we must flush the decoder buffers */
    for (ist = ist_iter(NULL); ist; ist = ist_iter(ist)) {
        float err_rate;

        if (!input_files[ist->file_index]->eof_reached) {
            int err = process_input_packet(ist, NULL, 0);
            ret = err_merge(ret, err);
        }

        err_rate = (ist->frames_decoded || ist->decode_errors) ?
                   ist->decode_errors / (ist->frames_decoded + ist->decode_errors) : 0.f;
        if (err_rate > max_error_rate) {
            av_log(ist, AV_LOG_FATAL, "Decode error rate %g exceeds maximum %g\n",
                   err_rate, max_error_rate);
            *err_rate_exceeded = 1;
        } else if (err_rate)
            av_log(ist, AV_LOG_VERBOSE, "Decode error rate %g\n", err_rate);
    }
    ret = err_merge(ret, enc_flush());

    term_exit();

    /* write the trailer if needed */
    for (i = 0; i < nb_output_files; i++) {
        int err = of_write_trailer(output_files[i]);
        ret = err_merge(ret, err);
    }

    /* dump report by using the first video and audio streams */
    print_report(1, timer_start, av_gettime_relative());

    return ret;
}

代码示例

下面写一个linux示例代码演示如果让ffmpeg优雅的终止(不包含错误处理),编译方式:g++ main.cpp -o main

#include <stdlib.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        // 子进程
        // 使用execl调用ffmpeg命令,请提供完整的路径,例如:/usr/bin/ffmpeg
        execl("/usr/bin/ffmpeg", "ffmpeg", "-y", "-rtsp_transport", "tcp", "-i", "rtsp://example.com/stream", "-c", "copy", "-an", "output.mp4", NULL);

        exit(0);
    } else {
        // 父进程
        // 主线程休眠5秒
        sleep(5);

        // 向子进程发送SIGINT信号
        kill(pid, SIGINT);

        // 等待子进程终止
        int status;
        waitpid(pid, &status, 0);
    }

    return 0;
}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Python中使用FFmpeg进行多进程分割视频文件可以通过以下步骤实现: 1. 首先,你需要安装FFmpeg并确保它已经添加到系统的环境变量中。你可以从FFmpeg的官方网站(https://ffmpeg.org/)下载并安装它。 2. 在Python中,你可以使用`subprocess`模块来执行FFmpeg命令。首先,导入`subprocess`模块: ```python import subprocess ``` 3. 接下来,你可以使用`subprocess.Popen`函数来启动一个新的进程并执行FFmpeg命令。例如,你可以使用以下代码来分割视频文件: ```python def split_video(input_file, output_file, start_time, duration): command = ['ffmpeg', '-i', input_file, '-ss', start_time, '-t', duration, '-c', 'copy', output_file] process = subprocess.Popen(command) process.wait() ``` 在上面的代码中,`input_file`是输入视频文件的路径,`output_file`是输出视频文件的路径,`start_time`是分割开始的时间(以秒为单位),`duration`是分割的持续时间(以秒为单位)。 4. 如果你想要同时分割多个视频文件,你可以使用Python的多进程库(如`multiprocessing`)来实现并行处理。以下是一个示例代码: ```python import multiprocessing def split_video(input_file, output_file, start_time, duration): # 分割视频的代码 if __name__ == '__main__': input_files = ['video1.mp4', 'video2.mp4', 'video3.mp4'] output_files = ['output1.mp4', 'output2.mp4', 'output3.mp4'] start_times = [10, 20, 30] durations = [5, 10, 15] processes = [] for i in range(len(input_files)): process = multiprocessing.Process(target=split_video, args=(input_files[i], output_files[i], start_times[i], durations[i])) processes.append(process) process.start() for process in processes: process.join() ``` 在上面的代码中,`input_files`是输入视频文件的路径列表,`output_files`是输出视频文件的路径列表,`start_times`是分割开始的时间列表,`durations`是分割的持续时间列表。通过循环创建多个进程来同时处理多个视频文件的分割操作。 这样,你就可以使用FFmpeg和Python的多进程库来实现多进程分割视频文件了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值