前言本文是流程分析的第三篇,分析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)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
打开音频设备,创建音频解码线程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;
}
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");
}