紧接上一篇总结,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;
}
可以看到,主要就是对不同的流,进行各自的解码流程,后面针对不同的解码流程进行分析。