超详细解析FFplay之数据读取线程( 六 )


 
For循环读取数据
主要包括以下步骤:
(1)检测是否退出
if (is->abort_request)break;当退出事件发?时,调?do_exit() -> stream_close() -> 将is->abort_request置为1 。退出该for循环,并最终退出该线程 。
(2)检测是否暂停/继续 。
这?的暂停、继续只是对?络流有意义 。?如rtsp,av_read_pause 。
// 2 检测是否暂停/继续 if (is->paused != is->last_paused) {is->last_paused = is->paused;if (is->paused)is->read_pause_return = av_read_pause(ic);// ?络流的时候有?elseav_read_play(ic); }/* pause the stream */static int rtsp_read_pause(AVFormatContext *s){ RTSPState *rt = s->priv_data; 5 RTSPMessageHeader reply1, *reply = &reply1;if (rt->state != RTSP_STATE_STREAMING)return 0;else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subsc ription)) {//发送给服务器命令ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, N ULL);if (reply->status_code != RTSP_STATUS_OK) {return ff_rtsp_averror(reply->status_code, -1);}}rt->state = RTSP_STATE_PAUSED;return 0; }av_read_play 。
static int rtsp_read_play(AVFormatContext *s){RTSPState *rt = s->priv_data;RTSPMessageHeader reply1, *reply = &reply1;......//发送给服务器ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL);....rt->state = RTSP_STATE_STREAMING;return 0;}(3)检测是否需要seek 。
if (is->seek_req) {// 是否有seek请求int64_t seek_target = is->seek_pos;int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_ rel + 2: INT64_MIN;int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_ rel - 2: INT64_MAX;// FIXME the +-2 is due to rounding being not done in the correc t direction in generation// of the seek_pos/seek_rel variables// 修复由于四舍五?,没有再seek_pos/seek_rel变量的正确?向上进?ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek _max, is->seek_flags);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, 12 "%s: error while seekingn", is->ic->url);} else {/* seek的时候,要把原先的数据情况,并重启解码器(释放缓存),put flush_pkt的?的是 告知解码线程需要* reset decoder*/// 如果有?频流if (is->audio_stream >= 0) {// 清空packet队列数据packet_queue_flush(&is->audioq);// 放?flush pkt, ?来开起新的?个播放序列, 解码器读取到flush_pk t也清空解码器packet_queue_put(&is->audioq, &flush_pkt);}// 如果有字幕流if (is->subtitle_stream >= 0) {// 同上作用packet_queue_flush(&is->subtitleq);packet_queue_put(&is->subtitleq, &flush_pkt);}// 如果有视频流if (is->video_stream >= 0) {// 同上作用packet_queue_flush(&is->videoq);packet_queue_put(&is->videoq, &flush_pkt);}//按字节还是按时间,设置时钟 。if (is->seek_flags & AVSEEK_FLAG_BYTE) {//按字节set_clock(&is->extclk, NAN, 0);} else {//按时钟set_clock(&is->extclk, seek_target / (double)AV_TIME_BAS E, 0);}}is->seek_req = 0;is->queue_attachments_req = 1;is->eof = 0;if (is->paused)// 如果本身是pause状态的则显示?帧(seek到那里的帧)继续暂停step_to_next_frame(is);}主要的seek操作通过avformat_seek_file完成(该函数的具体使?在播放控制seek时做详解) 。根据avformat_seek_file的返回值,如果seek成功,需要做到以下2点:
清除PacketQueue的缓存,并放??个flush_pkt 。放?的flush_pkt可以让PacketQueue的serial增1,以区分seek前后的数据(PacketQueue函数的分析0),该flush_pkt也会触发解码器重新刷新解码器缓存,avcodec_flush_buffers(),以避免解码时使?了原来的buffer作为参考?出现?赛克 。
(4)检测video是否为attached_pic 。封面也相当于是一个Video Stream,相当于一个AVPacket 。AV_DISPOSITION_ATTACHED_PIC 是?个标志 。如果?个流中含有这个标志的话,那么就是说这个流是 *.mp3等 ?件中的?个 Video Stream。并且该流只有?个 AVPacket,也就是attached_pic。这个 AVPacket 中所存储的内容就是这个 *.mp3等 ?件的封?图? 。因此,也可以很好的解释了?章开头提到的为什么 st->disposition & AV_DISPOSITION_ATTACHED_PIC,这个操作可以决定是否可以继续向缓冲区中添加 AVPacket。
// 4 检测video是否为attached_picif (is->queue_attachments_req) {// attached_pic 附带的图? 。?如说?些MP3,AAC?频?件附带的专辑封?,所以 需要注意的是?频?件不?定只存在?频流本身if (is->video_st && is->video_st->disposition & AV_DISPOSITION_A TTACHED_PIC) {AVPacket copy = { 0 };if ((ret = av_packet_ref(©, &is->video_st->attached_pic) ) < 0)goto fail;packet_queue_put(&is->videoq, ©);packet_queue_put_nullpacket(&is->videoq, is->video_stream);}is->queue_attachments_req = 0;}


推荐阅读