17. 再次回到read_thread
static int read_thread(void *arg)
{
...
//前面有分析
err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
...
//前面有分析
err = avformat_find_stream_info(ic, opts);
...
/* open the streams */
//稍后分析 stream_component_open, 这个函数会创建decoder thread
//后面for (;;)只需要读取packet,丢进packet 队列中,然后decoder thread负责将packet data
//解码成frame data,然后render就可以了
if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
}
ret = -1;
if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
}
if (is->show_mode == SHOW_MODE_NONE)
is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
}
//等于while(1)
for (;;) {
...
//读取一个pkt
/*
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
...
if (!genpts) {
//如果s->packet_buffer list中有packet,就是用packet list中的packet,否则
//call read_frame_internal 来获取一个packet,前面有分析过
// read_frame_internal 这个函数
ret = s->packet_buffer
? read_from_packet_buffer(&s->packet_buffer,
&s->packet_buffer_end, pkt)
: read_frame_internal(s, pkt);
if (ret < 0)
return ret;
goto return_packet;
}
}
*/
ret = av_read_frame(ic, pkt);
...
//把读取到的packet放到packet queue里,稍后分析 packet_queue_put
if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
packet_queue_put(&is->audioq, pkt);
} else if (pkt->stream_index == is->video_stream && pkt_in_play_range
&& !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
packet_queue_put(&is->videoq, pkt);
} else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
packet_queue_put(&is->subtitleq, pkt);
} else {
av_free_packet(pkt);
}
}
...
}
18.stream_component_open
//以下以video stream为例
static int stream_component_open(VideoState *is, int stream_index)
{
...
//从codec_id找到codec
codec = avcodec_find_decoder(avctx->codec_id);
...
//avcodec_open2 这个之前有分析过. 参考学习笔记四
/*
之前在为了获取decoder 信息,在调用try_decode_frame 时候,ff_thread_init
会因为有些设置,不会创建thread.
但是此时在真正去decoder, ff_thread_init 会创建 n个 frame_worker_thread
static attribute_align_arg void *frame_worker_thread(void *arg)
{
...
while (1) {
//会等p->input_cond 的signal,这个在后面笔记中,我们会看到
while (p->state == STATE_INPUT_READY && !fctx->die)
pthread_cond_wait(&p->input_cond, &p->mutex);
...
//调用codec去做decode
p->result = codec->decode(avctx, p->frame, &p->got_frame, &p->avpkt);
//发送signal p->output_cond
pthread_cond_broadcast(&p->progress_cond);
pthread_cond_signal(&p->output_cond);
pthread_mutex_unlock(&p->progress_mutex);
}
pthread_mutex_unlock(&p->mutex);
...
}
*/
if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
goto fail;
}
...
switch (avctx->codec_type) {
case AVMEDIA_TYPE_AUDIO:
...
break;
case AVMEDIA_TYPE_VIDEO:
is->video_stream = stream_index;
is->video_st = ic->streams[stream_index];
//放入一个flush_packet(作个标记)
packet_queue_start(&is->videoq);
//init decoder,并创建 video_thread,这个thread专门处理decoder,将AVPacket==>AVFrame
/*
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; 将is->videoq 保存在d->queue中,后面会用到
d->empty_queue_cond = empty_queue_cond;
d->start_pts = AV_NOPTS_VALUE;
}*/
decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
//接下来分析 video_thread
is->video_tid = SDL_CreateThread(video_thread, is);
is->queue_attachments_req = 1;
break;
case AVMEDIA_TYPE_SUBTITLE:
...
break;
default:
break;
}
...
}
static int video_thread(void *arg)
{
...
for (;;) {
//这个thread就这一个函数需要分析
ret = get_video_frame(is, frame);
...
}
...
}
static int get_video_frame(VideoState *is, AVFrame *frame)
{
int got_picture;
//接下来分析 decoder_decode_frame
if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
return -1;
if (got_picture) {
...
if (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) {
//如果不是video master 处理frame drop
//ffmpeg做av sync有3种 ,一种是audio master,一种是clock同步
//人耳对声音比较敏感,如果stream包含有audio stream,都会选择audio master
//就是audio 不会丢数据,如果video 快了,让video 在render 时候丢frame,
//如果video 慢了,就让video 在render时候,pending前一帧数据,因为人眼视觉暂留
//丢一帧或pending一帧,人眼觉察不出来.
//如果只有video stream,就可以选择video master.
if (frame->pts != AV_NOPTS_VALUE) {
double diff = dpts - get_master_clock(is);
if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD &&
diff - is->frame_last_filter_delay < 0 &&
is->viddec.pkt_serial == is->vidclk.serial &&
is->videoq.nb_packets) {
is->frame_drops_early++;
av_frame_unref(frame);
got_picture = 0;
}
}
}
}
return got_picture;
}
static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
...
do {
...
//每次push一个flush_pkt, d->queue->serial 会+1,
//所以这段的意思就是从packet queue过滤掉flush packet和flush之前的packet,
//并取出一个packet,并将这个pkt复制给d->pkt
if (!d->packet_pending || d->queue->serial != d->pkt_serial) {
AVPacket pkt;
do {
if (d->queue->nb_packets == 0)
SDL_CondSignal(d->empty_queue_cond);
if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)
return -1;
if (pkt.data == flush_pkt.data) {
avcodec_flush_buffers(d->avctx);
d->finished = 0;
d->flushed = 1;
d->next_pts = d->start_pts;
d->next_pts_tb = d->start_pts_tb;
}
} while (pkt.data == flush_pkt.data || d->queue->serial != d->pkt_serial);
av_free_packet(&d->pkt);
d->pkt_temp = d->pkt = pkt;
d->packet_pending = 1;
}
//下面这段应该很熟悉了,前面已经讲解过,不过flow和之前不太一样,这次使用的多线程decoder
switch (d->avctx->codec_type) {
case AVMEDIA_TYPE_VIDEO:
ret = avcodec_decode_video2(d->avctx, frame, &got_frame, &d->pkt_temp);
...
break;
case AVMEDIA_TYPE_AUDIO:
ret = avcodec_decode_audio4(d->avctx, frame, &got_frame, &d->pkt_temp);
...
break;
case AVMEDIA_TYPE_SUBTITLE:
ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &d->pkt_temp);
break;
}
...
} while (!got_frame && !d->finished);
...
}