陆小曼超详细解析FFplay之音视频解码线程( 五 )

重点:
(1)有针对 flush_pkt 的处理
if (pkt.data =http://news.hoteastday.com/a/= flush_pkt.data) {//// when seeking or when switching to a different streamavcodec_flush_buffers(d->avctx); //清空??的缓存帧d->finished = 0; // 重置为0d->next_pts = d->start_pts; // 主要?在了audiod->next_pts_tb = d->start_pts_tb;// 主要?在了audio}了解过PacketQueue的代码 , 我们知道在往PacketQueue送??个flush_pkt后 , PacketQueue的serial值会加1 , ?送?的flush_pkt和PacketQueue的新serial值保持?致 。 所以如果有“过时(旧serial)”Packet , 过滤后 , 取到新的播放序列第?个pkt将是flush_pkt 。 起到了一个分割作用 。 根据api要求 , 此时需要调? avcodec_flush_buffers。
(2)avcodec_send_packet后出现AVERROR(EAGAIN) , 则说明我们要继续调?avcodec_receive_frame()将frame读取 , 再调?avcodec_send_packet发packet 。 由于出现AVERROR(EAGAIN)返回值解码器内部没有接收传?的packet , 但?没法放回PacketQueue , 所以我们就缓存到了?封装的Decoder的pkt(即是d->pkt)(暂存) , 并将 d->packet_pending = 1 , 以备下次继续使?该packet 。
if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet bot h returned EAGAIN, which is an API violation.\n");d->packet_pending = 1;av_packet_move_ref(&d->pkt, &pkt);}queue_picture()
上? , 我们就分析完video_thread中关键的 get_video_frame 函数 , 根据所分析的代码 , 已经可以取到正确解码后的?帧数据 。 接下来就要把这?帧放?FrameQueue:
// 4 计算帧持续时间和换算pts值为秒 // 1/帧率 = duration 单位秒, 没有帧率时则设置为0, 有帧率帧计算出帧间隔duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){fr ame_rate.den, frame_rate.num}) : 0);// 根据AVStream timebase计算出pts值, 单位为秒 pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);// 5 将解码后的视频帧插?队列ret = queue_picture(is, frame, pts, duration, frame->pkt_pos, is->vid dec.pkt_serial);// 6 释放frame对应的数据av_frame_unref(frame);主要调? queue_picture, 看看他做了什么:
static int queue_picture(VideoState *is, AVFrame *src_frame, double pts,double duration, int64_t pos, int serial){Frame *vp;if (!(vp = frame_queue_peek_writable(&is->pictq))) // 检测队列是否 有可写空间return -1; // Frame队列满了则返回-1// 执?到这步说已经获取到了可写?的Frame//进行填充vp->sar = src_frame->sample_aspect_ratio;vp->uploaded = 0;vp->width = src_frame->width;vp->height = src_frame->height;vp->format = src_frame->format;vp->pts = pts;vp->duration = duration;vp->pos = pos;vp->serial = serial;set_default_window_size(vp->width, vp->height, vp->sar);av_frame_move_ref(vp->frame, src_frame); // 将src中所有数据拷?到dst 中 , 并复位src 。frame_queue_push(&is->pictq); // 更新写索引位置return 0; }queue_picture 的代码很直观 。
?先 frame_queue_peek_writable 取FrameQueue的当前写节点 。
然后把该拷?的拷?给节点(struct Frame)保存 。
再 frame_queue_push, “push”节点到队列中 。 唯?需要关注的是 , AVFrame的拷?是通过av_frame_move_ref 实现的 , 所以拷?后 src_frame 就是?效的了 。


推荐阅读