ffplay数据读取线程

本文深入探讨ffplay的读取线程,从创建线程互斥量到读取AVPacket,再到解封装上下文和媒体信息探测。重点阐述了如何判断实时流,并详细解析了for循环读取过程,包括检查退出请求、处理暂停和seek操作、刷新解码包队列、队列管理以及读取文件末尾的处理策略。
摘要由CSDN通过智能技术生成

在前面我们介绍了ffplay的总体架构和一些关键的数据结构。今天我们还是从这张图开始,主要介绍ffplay的读取线程部分。

 

图导出的可能有点模糊,再加上上传图床后不知道有没有更加模糊了,想要高清大图的可以后台留言,加v信索取。

从ffplay的main函数入口开始阅读源码,发现是在函数stream_open创建了资源读取线程,读取线程执行的函数是read_thread,所以要分析读取线程的工作内容,我们只需读懂函数read_thread即可。

下面是我加了注释的read_thread函数:

/**
 * 读取线程工作内容
 * @param arg
 * @return
 */
static int read_thread(void *arg)
{
    VideoState *is = arg;
    AVFormatContext *ic = NULL;
    int err, i, ret;
    int st_index[AVMEDIA_TYPE_NB];
    AVPacket *pkt = NULL;
    int64_t stream_start_time;
    int pkt_in_play_range = 0;
    const AVDictionaryEntry *t;
    // 互斥量,读取线程总不能一直读取吧,播放消费队列消费比不上读取的速度,队列满了就要陷入等待唤醒
    SDL_mutex *wait_mutex = SDL_CreateMutex();
    int scan_all_pmts_set = 0;
    int64_t pkt_ts;
​
    if (!wait_mutex) {
        av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
        ret = AVERROR(ENOMEM);
        goto fail;
    }
    // 所有流的索引都设置成-1
    memset(st_index, -1, sizeof(st_index));
    is->eof = 0;
    // 分配pack
    pkt = av_packet_alloc();
    if (!pkt) {
        av_log(NULL, AV_LOG_FATAL, "Could not allocate packet.\n");
        ret = AVERROR(ENOMEM);
        goto fail;
    }
    // 解封装上下文
    ic = avformat_alloc_context();
    if (!ic) {
        av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
        ret = AVERROR(ENOMEM);
        goto fail;
    }
    // 回调函数,防止读取过程中阻塞时间过长
    ic->interrupt_callback.callback = decode_interrupt_cb;
    ic->interrupt_callback.opaque = is;
    if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
        av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
        scan_all_pmts_set = 1;
    }
    // 打开
    err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
    if (err < 0) {
        print_error(is->filename, err);
        ret = -1;
        goto fail;
    }
    if (scan_all_pmts_set)
        av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
​
    if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
        av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
        ret = AVERROR_OPTION_NOT_FOUND;
        goto fail;
    }
    is->ic = ic;
​
    if (genpts)
        ic->flags |= AVFMT_FLAG_GENPTS;
​
    // 将其注入成一个变量
    av_format_inject_global_side_data(ic);
​
    if (find_stream_info) {
        AVDictionary **opts = setup_find_stream_info_opts(ic, codec_opts);
        int orig_nb_streams = ic->nb_streams;
​
        /*
         * 探测媒体类型,可得到当前文件的封装格式,音视频编码参数等信息
         * 调用该函数后得多的参数信息会比只调用avformat_open_input更为详细,
         * 其本质上是去做了decdoe packet获取信息的工作
         */
        err = avformat_find_stream_info(ic, opts);
​
        for (i = 0; i < orig_nb_streams; i++)
            av_dict_free(&opts[i]);
        av_freep(&opts);
​
        if (err < 0) {
            av_log(NULL, AV_LOG_WARNING,
                   "%s: could not find codec parameters\n", is->filename);
            ret = -1;
            goto fail;
        }
    }
​
    if (ic->pb)
        ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use avio_feof() to test for the end
​
    if (seek_by_bytes < 0)
        seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", ic->iformat->name);
​
    is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
​
    if (!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值