ijkplayer源码分析 read_thread流程

本系列如下:

前言本文是流程分析的第三篇,分析ijkPlayer中的read_thread流程,如下流程图中所示。

read_thread干了些啥

准备阶段:打开文件,检测Stream信息
循环等待start调用,进入播放流程
打开音频播放器,创建音频解码线程audio_thread;
创建视频解码器,创建视频解码线程video_thread;
创建字幕解码线程subtitle_thread;
循环读取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. while循环等待start()调用,修改pause_req为0,进入读数据流程
     * start()必须在prepared后调用
	 */
	ffp->prepared = true;
    ffp_notify_msg1(ffp, FFP_MSG_PREPARED);
    if (!ffp->render_wait_start && !ffp->start_on_prepared) {
        while (is->pause_req && !is->abort_request) {
            SDL_Delay(20);
        }
    }

    /*
     *  7. 读数据,存入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;
        }

		// 判断是否播放结束
		if ((!is->paused || completed) &&
            (!is->audio_st || (is->auddec.finished == is->audioq.serial && frame_queue_nb_remaining(&is->sampq) == 0)) &&
            (!is->video_st || (is->viddec.finished == is->videoq.serial && frame_queue_nb_remaining(&is->pictq) == 0))) {
            

            if (ffp->loop != 1 && (!ffp->loop || --ffp->loop)) {
                // 循环播放几次,从1开始计算
                stream_seek(is, ffp->start_time != AV_NOPTS_VALUE ? ffp->start_time : 0, 0, 0);
            } else if (ffp->autoexit) {
            	// 自动结束,走销毁逻辑
                ret = AVERROR_EOF;
                goto fail;
            } else {
                ffp_statistic_l(ffp);
                if (completed) {
                    av_log(ffp,
                           AV_LOG_INFO, "ffp_toggle_buffering: eof\n");
                    SDL_LockMutex(wait_mutex);

					// 结束了,一直等
                    while (!is->abort_request && !is->seek_req)
                        SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 100);
                    SDL_UnlockMutex(wait_mutex);
                    if (!is->abort_request)
                        continue;
                } else {
                	// 第一次进来是0,再次循环走上面if逻辑
                    completed = 1;
                    ffp->auto_resume = 0;

                    ffp_toggle_buffering(ffp,0);
                    toggle_pause(ffp,
                                 1);
                    if (ffp->error) {
                        ffp_notify_msg1(ffp, FFP_MSG_ERROR);
                    } else {
                        ffp_notify_msg1(ffp, FFP_MSG_COMPLETED);
                    }
                }
            }
        }

        // 读取数据,存入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);
        }

        /*
         * 开启缓冲机制
         * 在for循环中,每读到一个包,都会检查是否进行缓冲
         */
		if (ffp->packet_buffering) {
            io_tick_counter = SDL_GetTickHR();
            if ((!ffp->first_video_frame_rendered && is->video_st) ||
                (!ffp->first_audio_frame_rendered && is->audio_st)) {
                // 首帧未显示前,50ms检测一次
                if (abs((int) (io_tick_counter - prev_io_tick_counter)) > FAST_BUFFERING_CHECK_PER_MILLISECONDS) {
                    prev_io_tick_counter = io_tick_counter;
                    ffp->dcc.current_high_water_mark_in_ms = ffp->dcc.first_high_water_mark_in_ms;
                    ffp_check_buffering_l(ffp);
                }
            } else {
                if (abs((int) (io_tick_counter - prev_io_tick_counter)) > BUFFERING_CHECK_PER_MILLISECONDS) {
                	// 首帧显示后,500ms检测一次
                    prev_io_tick_counter = io_tick_counter;
                    ffp_check_buffering_l(ffp);
                }
            }
        }
    }
}

本文福利, 免费领取C++音视频学习资料包、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

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_jni.c#IjkMediaPlayer_prepareAsync
->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");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值