FFmepg 多线程解码历程 - 6:frame_thread_init

本文详细介绍了FFmpeg中frame_thread_init函数的工作原理,包括如何根据设备性能自动配置线程数量、初始化线程所需的资源及环境、创建并启动线程等过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//ff_thread_init选择帧线程初始化,就会进入frame_thread_init

static int frame_thread_init(AVCodecContext *avctx)

{
    int thread_count = avctx->thread_count;
    const AVCodec *codec = avctx->codec;
    AVCodecContext *src = avctx;
    FrameThreadContext *fctx;
    int i, err = 0;
//如果我们在初始化codec的时候没有设置thread_count或者设置为0,则就会自动根据手机设备来获取
    if (!thread_count) {
        int nb_cpus = ff_get_logical_cpus(avctx);
        if ((avctx->debug & (FF_DEBUG_VIS_QP | FF_DEBUG_VIS_MB_TYPE)) || avctx->debug_mv)
            nb_cpus = 1;
        // use number of cores + 1 as thread count if there is more than one
        if (nb_cpus > 1)
            thread_count = avctx->thread_count = FFMIN(nb_cpus + 1, MAX_AUTO_THREADS);  //#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
        else
            thread_count = avctx->thread_count = 1;
    }

    if (thread_count <= 1) {  //如果thread_count <= 1那么即使进入帧级线程初始化,在解码的时候也不会调用帧级解码函数的
        avctx->active_thread_type = 0;
        return 0;
    }

    avctx->thread_opaque = fctx = av_mallocz(sizeof(FrameThreadContext));

    fctx->threads = av_mallocz(sizeof(PerThreadContext) * thread_count);  //相当与初始化线程池,个数就是thread_count
    pthread_mutex_init(&fctx->buffer_mutex, NULL);
    fctx->delaying = 1;

    for (i = 0; i < thread_count; i++) {
        AVCodecContext *copy = av_malloc(sizeof(AVCodecContext));
        PerThreadContext *p  = &fctx->threads[i];

        pthread_mutex_init(&p->mutex, NULL);
        pthread_mutex_init(&p->progress_mutex, NULL);
        pthread_cond_init(&p->input_cond, NULL);
        pthread_cond_init(&p->progress_cond, NULL);
        pthread_cond_init(&p->output_cond, NULL);

        p->parent = fctx;
        p->avctx  = copy;

        if (!copy) {
            err = AVERROR(ENOMEM);
            goto error;
        }

        *copy = *src;
        copy->thread_opaque = p;
        copy->pkt = &p->avpkt;

        if (!i) {
            src = copy;

            if (codec->init)
                err = codec->init(copy);
        //更新下一个线程的AVCodecContext参考线程的上下文中的值
            update_context_from_thread(avctx, copy, 1);
        } else {
            copy->priv_data = av_malloc(codec->priv_data_size);
            if (!copy->priv_data) {
                err = AVERROR(ENOMEM);
                goto error;
            }
            memcpy(copy->priv_data, src->priv_data, codec->priv_data_size);
            copy->internal = av_malloc(sizeof(AVCodecInternal));
            if (!copy->internal) {
                err = AVERROR(ENOMEM);
                goto error;
            }
            *copy->internal = *src->internal;
            copy->internal->is_copy = 1;

            if (codec->init_thread_copy)
                err = codec->init_thread_copy(copy);
        }

        if (err) goto error;
///下面出创建线程,调用frame_worker_thread
        err = AVERROR(pthread_create(&p->thread, NULL, frame_worker_thread, p));
        p->thread_init= !err;
        if(!p->thread_init)
            goto error;
    }

    return 0;

error:
    frame_thread_free(avctx, i+1);

    return err;
}
### 关于 FFmpeg 多线程解码时帧时间戳错位的问题 FFmpeg 是一个多用途的多媒体处理工具,在多线程环境下进行视频解码时可能会遇到帧的时间戳(timestamp)错位问题。这种现象通常是由以下几个原因引起的: #### 1. **多线程解码机制** FFmpeg 支持通过 `-threads` 参数设置多个解码线程来加速解码过程。然而,当启用多线程解码时,不同线程之间的数据流可能存在不一致的情况,这可能导致某些帧的时间戳被错误分配[^1]。 #### 2. **时间戳计算逻辑** 在多线程模式下,FFmpeg 可能会因为并行处理而导致部分帧的时间戳未按顺序生成。具体来说,如果某个线程完成了解码但未能及时更新全局时间戳计数器,则后续帧的时间戳可能基于旧的状态而发生偏移[^3]。 #### 解决方案 以下是几种常见的解决方案用于解决 FFmpeg 多线程解码中的时间戳错位问题: #### 方法一:禁用多线程解码 最简单的方法之一是完全关闭多线程支持,强制使用单线程解码。可以通过命令行参数 `threads=1` 实现这一点: ```bash ffmpeg -i input.mp4 -c:v copy -threads 1 output.mp4 ``` 这种方法虽然可以有效避免时间戳错位,但由于只利用了一个 CPU 核心资源,因此性能较低。 #### 方法二:调整线程类型 除了控制总的线程数量外,还可以指定具体的线程工作模式。例如,尝试切换到切片级或多帧级平行化策略: ```bash ffmpeg -init_thread_copy true -thread_type slice -i input.mp4 -vf "setpts=N/FRAME_RATE/TB" output.mp4 ``` 这里的关键选项包括: - `-init_thread_copy`: 确保每个子线程拥有独立的数据副本。 - `-thread_type`: 定义如何分割任务给各个线程执行 (slice 或 frame)[^2]. #### 方法三:重新校准 PTS/DTS 即使存在轻微偏差,也可以通过对输出流应用滤镜重设每帧呈现时间戳(PTS) 来修正异常情况。下面是一个例子: ```bash ffmpeg -i input.mp4 -vf "setpts=N/(FRAME_RATE*TB)" corrected_output.mp4 ``` 此方法不会改变实际媒体内容本身,只是修改关联元数据以便播放更加流畅。 #### 方法四:升级至最新版本 开发者持续改进软件功能的同时也在修复已知缺陷;所以建议始终采用官方发布的最新稳定版 FFmpeg ,因为它很可能已经解决了早期版本中存在的此类 bug . --- ### 示例代码片段展示如何手动调整 PTS 值 如果你希望通过编程手段干预这一流程,那么可以用 Python 结合 PyAV 库来进行更精细的操作: ```python import av container = av.open('input.mp4') stream = container.streams.video[0] for packet in container.demux(stream): for frame in packet.decode(): # 手动设定新的 pts 值 frame.pts = int(frame.time * stream.time_base.denominator / stream.time_base.numerator) # 编码回原容器或其他目标位置... ``` 上述脚本遍历所有视频包及其内部帧,并逐一为其赋予经过标准化后的 presentation timestamp 数值[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值