陆小曼超详细解析FFplay之音视频解码线程( 三 )
decoder_decode_frame 的主?代码是?个循环 , 要拿到?帧解码数据 , 或解码出错、?件结束 , 才会返回 。
循环内可以分解为3个步骤:
(1)同?播放序列流连续的情况下 , 不断调?avcodec_receive_frame获取解码后的frame 。
a. d->queue 就是video PacketQueue(videoq)
b. d->pkt_serial 是最近?次取的packet的序列号 。 在判断完 d->queue->serial == d->pkt_serial 确保流连续后 , 循环调? avcodec_receive_frame, 有取到帧就返回 。 使还没送?新的Packet , 这是为了兼容?个Packet可以解出多个Frame的情况 。
(2)获取?个packet , 如果播放序列不?致(数据不连续)则过滤掉“过时”的packet 。 主要阻塞调?packet_queue_get () 。 另外 , 会在PacketQueue为空时 , 发送 empty_queue_cond 条件信号 , 通知读线程继续读数据 。 ( empty_queue_cond 就是 continue_read_thread, 可以参考read线程的分析 , 查看读线程何时会等待该条件量 。
【陆小曼超详细解析FFplay之音视频解码线程】(3)将packet送?解码器 。
先看avcodec_receive_frame的具体流程 , 这?先省略Audio的case:
// 1. 流连续情况下获取解码后的帧if (d->queue->serial == d->pkt_serial) {// 1.1 先判断是否是同?播放序列 的数据do {if (d->queue->abort_request)return -1; // 是否请求退出// 1.2. 获取解码帧switch (d->avctx->codec_type) {case AVMEDIA_TYPE_VIDEO:ret = avcodec_receive_frame(d->avctx, frame);//printf("frame pts:%ld, dts:%ld\n", frame->pts, fra me->pkt_dts);if (ret >= 0) {if (decoder_reorder_pts == -1) {frame->pts = frame->best_effort_timestamp;} else if (!decoder_reorder_pts) {frame->pts = frame->pkt_dts;}}break;case AVMEDIA_TYPE_AUDIO:ret = avcodec_receive_frame(d->avctx, frame);....break;}// 1.3. 检查解码是否已经结束 , 解码结束返回0if (ret == AVERROR_EOF) {d->finished = d->pkt_serial;printf("avcodec_flush_buffers %s(%d)\n", __FUNCTION__, _ _LINE__);// 调?该函数后可以再次解 码 , 只要有数据packet进?, 取出缓存avcodec_flush_buffers(d->avctx);return 0; }// 1.4. 正常解码返回1if (ret >= 0)return 1;} while (ret != AVERROR(EAGAIN));// 1.5 没帧可读时ret返回EAGIN ,需要继续送packet}注意返回值:注意返回值:
-1:请求退出解码器线程 。
0:解码器已经完全冲刷 , 没有帧可读 , 这?也说明对应码流播放结束 。
1:正常解码获取到帧 。
重点分析以下:
(1)decoder_reorder_pts
ret = avcodec_receive_frame(d->avctx, frame);if (ret >= 0) {if (decoder_reorder_pts == -1) {frame->pts = frame->best_effort_timestamp;} else if (!decoder_reorder_pts) {frame->pts = frame->pkt_dts;}}decoder_reorder_pts:让ffmpeg内部排序pts 。 这里表示解码后的frame的pts , 该用何值 。
0=off, 1=on ,-1=auto 。 默认为-1 。 (ffplay配置 -drp value进?设置) 。
0:frame的pts使?pkt_dts , 这种情况很少(涉及到时间基的变化) 。
1:frame保留??的pts 。
-1:frame的pts使?frame->best_effort_timestamp , best_effort_timestamp是经过算法计算出来的值 , 主要是“尝试为可能有错误的时间戳猜测出适当单调的时间戳” , ?部分情况下还是frame->pts , 或者就是frame->pkt_dts 。
(2)avcodec_flush_buffers
使?“空包”冲刷解码器后 , 如果要再次解码则需要调?avcodec_flush_buffers() , 之所以在这个节点调?avcodec_flush_buffers() , 主要是让我们在循环播放码流的时候可以继续正常解码 , 不刷空包 , 再次循环播放 , 解码时 , 有可能出错 。
推荐阅读
- 龙导说车|宝马X3奔驰GLC和奥迪Q5L,详细选车指南,豪华中型SUV
- 利兹联|周一精彩3C1:01荷乙恐爆冷、狼队有望大胜,另附一场瑞典超解析
- 前沿分析局|那美国会遵守吗?金灿荣解析,基辛格希望设立“交战规则”
- 登海种业|10月19日龙虎榜解析:若羽臣净买入额最多,还有19只个股被机构扫货
- 青年|《原神》“断罪皇女”菲谢尔全方位解析指南
- 九游网|《鸿图之下》益州怎么样 益州地形影响解析
- 央视新闻客户端|如何看待前三季度中国经济的基本态势?杨禹详细解读
- 杜兰特|NBA实力榜解析:掘金热火谁更强 勇士配不配进前5
- 穿搭|基础款怎么穿?通过"针织衫+半裙"解析,简单背后总是暗藏玄机
- 博森科技苏闻衫|CCR炒币机器人和手动炒币的优缺点解析
