ffmpeg源码学习笔记七

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);

    ...
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值