ijkplayer 源码分析系列(2)——read_thread流程

·  阅读 141

紧接上一篇总结,prepare方法中,主要分为两个流程

  • read_thread ,负责打开流,创建音视频解码线程,读取packet等流程
  • video_refresh_thread,主要负责音视频同步,以及音视频渲染显示流程。

这里把read_thread拿出来单独分析下

read_thread

位于ff_ffplay.c之中,先总结一下大概做的工作

  • 打开文件,检测Stream信息
  • 循环等待start接口调用,进入播放的流程
  • 打开音频播放器,创建音频解码线程audio_thread; 创建视频解码器,创建视频解码线程video_thread; 创建字幕解码线程subtitle_thread;
  • 循环读取Packet,解封装,并存入PacketQueue

删减下代码,大概如下

static int read_thread(void *arg)
{
    //Open an input stream and read the header. The codecs are not opened.
    //The stream must be closed with avformat_close_input().
    //打开输入流,并读取文件头部,解码器还未打开。主要作用是探测流的协议,如http还是rtmp等。
    err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts);
  
    // Read packets of a media file to get stream information. This
    // is useful for file formats with no headers such as MPEG. This
    // function also computes the real framerate in case of MPEG-2 repeat
    // frame mode.
    // The logical file position is not changed by this function;
    // examined packets may buffered for later processing. 
    //探测文件封装格式,音视频编码参数等信息。
    err = avformat_find_stream_info(ic, opts);
  
    // Find the "best" stream in the file.
    // The best stream is determined according to various heuristics as the most
    // likely to be what the user expects.
    // If the decoder parameter is non-NULL, av_find_best_stream will find the
    // default decoder for the stream's codec; streams for which no decoder can
    // be found are ignored.
    //根据 AVFormatContext,找到最佳的流。
    av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
                        st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
  
    //内部分别开启audio,video,subtitle的解码器的线程,开始各自的解码的工作。解码线程中分析这里的内容
    stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]);
    stream_component_open(ffp, st_index[AVMEDIA_TYPE_VIDEO]);
    stream_component_open(ffp, st_index[AVMEDIA_TYPE_SUBTITLE]);
​
    //开始无限循环,调用ffmpeg的av_read_frame()读取AVPacket,并入队。
      for (;;) {
      //AVPacket pkt;
      
      ret = av_read_frame(ic, pkt);
      
      //把网络读取到并解封装到的pkt包入队列。(稍后在解码线程会拿到这些pkt包去解码。)
      
            //如果是音频流的包
      packet_queue_put(&is->audioq, pkt);
      //如果是视频流的包
      packet_queue_put(&is->videoq, pkt);
      //如果是字幕流的包
      packet_queue_put(&is->subtitleq, pkt);
    }  
}
复制代码

完整代码如下

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) {
                    prev_io_tick_counter = io_tick_counter;
                    ffp_check_buffering_l(ffp);
                }
            }
        }
    }
}

复制代码

stream_component_open

解码线程在这里处理,省略版代码如下

static int stream_component_open(FFPlayer *ffp, int stream_index)
{
      AVCodecContext *avctx;//解码器上下文
        AVCodec *codec = NULL;//解码器
    //找到解码器
    codec = avcodec_find_decoder(avctx->codec_id);
  
    switch (avctx->codec_type) {
    case AVMEDIA_TYPE_AUDIO:
        ret = audio_open(ffp, channel_layout, nb_channels, sample_rate, &is->audio_tgt);
        //decoder初始化
        decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);
                //decoder启动,启动audio_thread线程
        if ((ret = decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec")) < 0)
            goto out;
        break;
    case AVMEDIA_TYPE_VIDEO:
        //decoder初始化
        decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
        ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
        if (!ffp->node_vdec)
          goto fail;
        //解码器开始
        if ((ret = decoder_start(&is->viddec, video_thread, ffp, "ff_video_dec")) < 0)
          goto out;
        break;
      case AVMEDIA_TYPE_SUBTITLE:
        //decoder初始化
        decoder_init(&is->subdec, avctx, &is->subtitleq, is->continue_read_thread);
        //解码器开始
        if ((ret = decoder_start(&is->subdec, subtitle_thread, ffp, "ff_subtitle_dec")) < 0)
            goto out;
        break;
}
复制代码

可以看到,主要就是对不同的流,进行各自的解码流程,后面针对不同的解码流程进行分析。

分类:
Android
标签: