ijkplayer源码分析 read_thread流程

前言

前言本文是流程分析的第三篇,分析ijkPlayer中的read_thread流程,如下流程图中所示。
在这里插入图片描述

read_thread干了些啥

  1. 准备阶段:打开文件,检测Stream信息
  2. 打开音频播放器,创建音频解码线程audio_thread;
    创建视频解码器,创建视频解码线程video_thread;
    创建字幕解码线程subtitle_thread;
  3. 循环读取Packet,解封装,并存入PacketQueue
static int read_thread(void *arg) {
    
    
    AVFormatContext *ic = NULL;
    ic = avformat_alloc_context();

    /*
      *  1. 打开url
      */
    if (ffp->iformat_name)
        is->iformat = av_find_input_format(ffp->iformat_name);
    err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts);

    /*
      *  2. 查找流
      */
    int i;
    int orig_nb_streams = ic->nb_streams;
    if (ffp->find_stream_info) {
    
    
        do {
    
    
            if (av_stristart(is->filename, "data:", NULL) && orig_nb_streams > 0) {
    
    
                for (i = 0; i < orig_nb_streams; i++) {
    
    
                    if (!ic->streams[i] || !ic->streams[i]->codecpar ||
                        ic->streams[i]->codecpar->profile == FF_PROFILE_UNKNOWN) {
    
    
                        break;
                    }
                }

                if (i == orig_nb_streams) {
    
    
                    break;
                }
            }
            err = avformat_find_stream_info(ic, opts);
        } while (0);
    }
    
    /*
      *  3.  遍历nb_streams,区分各个流,类似MediaExtractor中的trackIndex
      */
    int st_index[AVMEDIA_TYPE_NB];
    int video_stream_count = 0;
    int h264_stream_count = 0;
    int first_h264_stream = -1;

    for (i = 0; i < ic->nb_streams; i++) {
    
    
        AVStream *st = ic->streams[i];
        enum AVMediaType type = st->codecpar->codec_type;
        if (type >= 0 && ffp->wanted_stream_spec[type] && st_index[type] == -1)
            if (avformat_match_stream_specifier(ic, st, ffp->wanted_stream_spec[type]) > 0)
                st_index[type] = i;

        // choose first h264
        if (type == AVMEDIA_TYPE_VIDEO) {
    
    
            enum AVCodecID codec_id = st->codecpar->codec_id;
            video_stream_count++;
            if (codec_id == AV_CODEC_ID_H264) {
    
    
                h264_stream_count++;
                if (first_h264_stream < 0)
                    first_h264_stream = i;
            }
        }
    }

    /*
      *  4. 调用av_find_best_stream确定trackIndex,找到最佳流
      */
    if (video_stream_count > 1 && st_index[AVMEDIA_TYPE_VIDEO] < 0) {
    
    
        st_index[AVMEDIA_TYPE_VIDEO] = first_h264_stream;
    }
    if (!ffp->video_disable)
        st_index[AVMEDIA_TYPE_VIDEO] =
                av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
                                    st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
    if (!ffp->audio_disable)
        st_index[AVMEDIA_TYPE_AUDIO] =
                av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
                                    st_index[AVMEDIA_TYPE_AUDIO],
                                    st_index[AVMEDIA_TYPE_VIDEO],
                                    NULL, 0);
    if (!ffp->video_disable && !ffp->subtitle_disable)
        st_index[AVMEDIA_TYPE_SUBTITLE] =
                av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,
                                    st_index[AVMEDIA_TYPE_SUBTITLE],
                                    (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?
                                     st_index[AVMEDIA_TYPE_AUDIO] :
                                     st_index[AVMEDIA_TYPE_VIDEO]),
                                    NULL, 0);
    
    /*
      *  5. open the streams,读取各个数据流
      */
    if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
    
    
        stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]);
    }
    if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
    
    
        ret = stream_component_open(ffp, st_index[AVMEDIA_TYPE_VIDEO]);
    }
    if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
    
    
        stream_component_open(ffp, st_index[AVMEDIA_TYPE_SUBTITLE]);
    }

    if (!ffp->render_wait_start && !ffp->start_on_prepared)
        toggle_pause(ffp, 1);

    /*
      *  6. 读数据,存入packet_queue
      */
    for (;;) {
    
    
        if (is->abort_request)
            break;

        if (is->seek_req) {
    
    
            //...
        }

        // 如果不是无限制大小的话,并且overSize || 队列都慢了,则睡10ms,等一下;控制缓冲区大小
        // #define MAX_QUEUE_SIZE (15 * 1024 * 1024) 15M
        overSize = is->audioq.size + is->videoq.size + is->subtitleq.size > ffp->dcc.max_buffer_size;
        if (ffp->infinite_buffer < 1 && !is->seek_req
            && (overSize ||
                (stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq, MIN_FRAMES)                 
                 && stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq, MIN_FRAMES)
                 && stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq, MIN_FRAMES)))) {
    
    

            /* wait 10 ms */
            SDL_LockMutex(wait_mutex);
            SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
            SDL_UnlockMutex(wait_mutex);
            continue;
        }

        // 读取数据,存入packet_queue
        int ret = av_read_frame(ic, pkt);

        if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
    
       // 音频
            packet_queue_put(&is->audioq, pkt);
            SDL_SpeedSampler2Add(&ffp->stat.audio_bit_rate_sampler, pkt->size);
        } else if (pkt->stream_index == is->video_stream && pkt_in_play_range && !(is->video_st &&
                                                                                   (is->video_st->disposition &
                                                                                    AV_DISPOSITION_ATTACHED_PIC))) {
    
     // 视频
            packet_queue_put(&is->videoq, pkt);
            SDL_SpeedSampler2Add(&ffp->stat.video_bit_rate_sampler, pkt->size);
        } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
    
     // 字幕
            packet_queue_put(&is->subtitleq, pkt);
        } else {
    
    
            av_packet_unref(pkt);
        }        
    }
}

stream_component_open干了些啥

打开音频设备,创建音频解码线程audio_thread;
创建视频解码线程video_thread;
创建字幕解码线程subtitle_thread;

static int stream_component_open(FFPlayer *ffp, int stream_index) {
    
    
    if (stream_index < 0 || stream_index >= ic->nb_streams) {
    
    
        return -1;
    }
    
    // 固定写法,调用avcodec_open2打开解码器
    AVCodecContext *avctx = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
    av_codec_set_pkt_timebase(avctx, ic->streams[stream_index]->time_base);

    AVCodec *codec = avcodec_find_decoder(avctx->codec_id);    
    avcodec_open2(avctx, codec, &opts);

    switch (avctx->codec_type) {
    
    
        case AVMEDIA_TYPE_AUDIO:
            // 调用SDL_AoutOpenAudio打开音频设备
            audio_open(ffp, channel_layout, nb_channels, sample_rate, &is->audio_tgt);
            decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);
            decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec")break;
        case AVMEDIA_TYPE_VIDEO:
            decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
            // 创建解码器
            ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
            decoder_start(&is->viddec, video_thread, ffp, "ff_video_dec")
        break;
        case AVMEDIA_TYPE_SUBTITLE:
            decoder_init(&is->subdec, avctx, &is->subtitleq, is->continue_read_thread);
            decoder_start(&is->subdec, subtitle_thread, ffp, "ff_subtitle_dec")break;
    }
}

// 初始化Decoder
static void
decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, SDL_cond *empty_queue_cond) {
    
    
    memset(d, 0, sizeof(Decoder));
    d->avctx = avctx;
    d->queue = queue;
    d->empty_queue_cond = empty_queue_cond;
    d->start_pts = AV_NOPTS_VALUE;

    d->first_frame_decoded_time = SDL_GetTickHR();
    d->first_frame_decoded = 0;

    SDL_ProfilerReset(&d->decode_profiler, -1);
}

static int decoder_start(Decoder *d, int (*fn)(void *), void *arg, const char *name) {
    
    
    FFPlayer *ffp = arg;
    live_log(ffp->inject_opaque, NULL);
    packet_queue_start(d->queue);
    d->decoder_tid = SDL_CreateThreadEx(&d->_decoder_tid, fn, arg, name);
    return 0;
}

read_thread怎么被调用的

ijkplayer.c#ijkmp_prepare_async
-> ijkplayer.c#ijkmp_prepare_async_l
->ff_play.c#ffp_prepare_async_l
->ff_play.c#stream_open

  • stream_open方法作用:
    初始化音视频packet队列
    初始化音视频frame队列
    初始化时钟
    创建read_thread
    创建video_refresh_thread
static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat) {
    
    
    VideoState *is = av_mallocz(sizeof(VideoState));;
    
     // 初始化frame_queue
    frame_queue_init(&is->pictq, &is->videoq, ffp->pictq_size, 1);
    frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0);
    frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1);

    // 初始化packet_queue
    packet_queue_init(&is->videoq);
    packet_queue_init(&is->audioq);
    packet_queue_init(&is->subtitleq);

    // 初始化时钟
    init_clock(&is->vidclk, &is->videoq.serial);
    init_clock(&is->audclk, &is->audioq.serial);
    init_clock(&is->extclk, &is->extclk.serial);

    // 创建video_refresh_thread,用于渲染视频
    is->video_refresh_tid = SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");
    // 创建read_thread, 用于读取数据
    is->read_tid = SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read");
}

参考:
ffplay read线程分析

猜你喜欢

转载自blog.csdn.net/u014099894/article/details/112970426