【VLC核心二】clock管理流程

简介: 一、前言 clock管理是vlc播放音视频的重要部分,从live555收流到decoder解码到render渲染,整个播放过程中均需依赖clock机制。

一、前言

clock管理是vlc播放音视频的重要部分,从live555收流到decoder解码到render渲染,整个播放过程中均需依赖clock机制。

二、涉及的类文件

src\input\input.c

modules\access\live555.cpp

src\input\es_out.c

src\input\decoder.c

src\input\clock.c

三、clock核心点备注

1、live555::CmdExecuteControl(ES_OUT_SET_PCR,p_sys->i_pcr+1)

注:p_sys->i_pcr+1作为input_clock_Update的i_ck_stream参数传入。p_sys->i_pcr在live555的StreamRead函数中赋值

2、clock中i_cr_average值的来源

clock中i_cr_average=配置文件中读取默认40ms * i_pts_delay / DEFAULT_PTS_DELAY;
DEFAULT_PTS_DELAY = 3*CLOCK_FREQ/10

3、当准备收流或回放拖动时,会触发设置PCR动作,对应ES_OUT_SET_PCR,继而调用clock::input_clock_Update(i_pcr, mdate()),更新clock机制,其核心处理如下

a、判断当前流时戳与上一帧时戳差值是否大于MAX_GAP,如果大于MAX_GAP,说明收到的帧已经跳变过大,则重置clock值ResetClock。(MAX_GAP宏的值为60s,可以调整宏代码,此值过大,应该调整为1s以内,否则在回放拖动时有bug,后续文章详解);

b、每隔20ms调用AvgUpdate计算一次 stream clock 和system clock间的漂移;

typedef struct
{
    mtime_t i_value;
    int     i_residue; //残余

    int     i_count;	
    int     i_divider; // 分割;分配
} average_t;

AvgUpdate详解:
//将当前mdate时间转换为流时戳
const mtime_t i_converted = ClockSystemToStream( cl, i_ck_system );
//用转换的流时戳-真实流时戳作为ivalue
AvgUpdate( &cl->drift, i_converted - i_ck_stream );

static void AvgUpdate( average_t *p_avg, mtime_t i_value )
{
    const int i_f0 = __MIN( p_avg->i_divider - 1, p_avg->i_count );//i_f0记录当前已存值的个数
    const int i_f1 = p_avg->i_divider - i_f0;//剩余个数
	//统计总值=已存个数*平均value + 剩余个数*新来i_value + 余数
	// 好处是:未存满值的时候,剩余个数均使用新的i_value填充
    const mtime_t i_tmp = i_f0 * p_avg->i_value + i_f1 * i_value + p_avg->i_residue;
	// 计算新平均值和余数
    p_avg->i_value   = i_tmp / p_avg->i_divider;
    p_avg->i_residue = i_tmp % p_avg->i_divider;

    p_avg->i_count++;
}

c、计算当前帧是否来晚了

当前系统时戳即mdate的当前时间- (当前系统时戳将流时戳+平均偏移,转换为系统时戳)。如果大于零,说明改帧比期望它到的时间来晚了。来晚了就更新到数组里。VLC缓存了三个late的值,为后续GetJitter获取的时候可以计算平均的late值。

摘录:如果late,存储到数组中
if( i_late > 0 )
    {
        cl->late.pi_value[cl->late.i_index] = i_late;
        cl->late.i_index = ( cl->late.i_index + 1 ) % INPUT_CLOCK_LATE_COUNT;
    }

摘录:input_clock_GetJitter
寻找中间的late值,这种方法可以规避掉bad values
const mtime_t *p = cl->late.pi_value;
    mtime_t i_late_median = p[0] + p[1] + p[2] - __MIN(__MIN(p[0],p[1]),p[2]) - __MAX(__MAX(p[0],p[1]),p[2]);
    mtime_t i_pts_delay = cl->i_pts_delay ;

d、如果晚了,在es_out.c中,调用clock::input_clock_GetJitter统计抖动,并调用clock:: input_clock_Reset和input_clock_SetJitter重置clock,重新调节计算漂移的参数。这样就可以重新缓存待解码的数据。


4、缓存数据的核心流程

a、每次es_out.c中SET_PCR中调用EsOutDecodersStopBuffering 如果是缓冲状态, 判断是否缓冲完?

1)计算流缓存时间:调用input_clock_GetState检查i_stream_duration值得到流缓存的时间
2)计算预设缓存时间:
const mtime_t i_buffering_duration = p_sys->i_pts_delay + i_preroll_duration + p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
实测中i_buffering_duration  = p_sys->i_pts_delay = 1000;其它值为0,待研究什么情况下其它值不为零。
3)如果i_stream_duration<i_buffering_duration,则继续缓冲,否则已缓冲满。

b、input_DecoderWaitBuffering缓冲完通知decoder模块。解码模块队列中循环解码,可参考文章《【VLC核心一】播放流程梳理


5、clock在live555收流拼帧部分相关工作

a、拼帧完成后送SteamRead,SteamRead中的处理

int64_t i_pts = (int64_t)pts.tv_sec * INT64_C(1000000) +(int64_t)pts.tv_usec;
i_pts &= INT64_C(0x00ffffffffffffff);

if( p_sys->i_pcr < i_pts )
{
    p_sys->i_pcr = i_pts;
}

四、核心流程时序图


目录
相关文章
|
缓存 数据安全/隐私保护 iOS开发
2023最新mac开启ntfs读写功能 ntfs硬盘如何在mac上读写教程
在日常的工作中,总是避免不了跨平台的传输文件、文件共享等,例如一些用户使用Mac电脑修图或者剪辑视频之后需要拷贝到Windows电脑上查看。对于需要同时使用Mac和Windows的用户来说,系统之间不兼容是很大的阻碍,尤其是使用NTFS移动硬盘,用户会遇到Mac电脑无法写入NTFS硬盘的情况,本文就来教大家ntfs硬盘如何在mac上读写以及mac如何移动硬盘的文件。
4862 0
2023最新mac开启ntfs读写功能 ntfs硬盘如何在mac上读写教程
|
XML 存储 JSON
如何快速实现XML与JSON转换
XML与JSON之间的转换常常用于以下场景: 1.数据交换:当需要在不同的系统、平台或服务之间进行数据交换时,常常会使用XML或JSON进行数据的序列化和反序列化。比如,一个Web服务可能需要返回数据给一个移动应用,这时,数据就可以通过XML或JSON格式进行传输。
|
12月前
|
Java 开发者 Kotlin
华为仓颉语言初识:并发编程之线程的基本使用
本文详细介绍了仓颉语言中线程的基本使用,包括线程创建(通过`spawn`关键字)、线程名称设置、线程执行控制(使用`get`方法阻塞主线程以获取子线程结果)以及线程取消(通过`cancel()`方法)。文章还指出仓颉线程与Java等语言的差异,例如默认不提供线程名称。掌握这些内容有助于开发者高效处理并发任务,提升程序性能。
358 2
|
安全 Java API
基于Spring Boot的企业级应用架构设计
基于Spring Boot的企业级应用架构设计
|
JSON 前端开发 数据格式
12306火车票查询--Python可以这么玩!!!
12306火车票查询--Python可以这么玩!!!
|
Linux C++ iOS开发
VLC源码解析:视频播放速度控制背后的技术
VLC源码解析:视频播放速度控制背后的技术
1468 0
|
存储 Docker 容器
在Docker中,容器退出后,通过docker ps命令查看不到,数据会丢失么?
在Docker中,容器退出后,通过docker ps命令查看不到,数据会丢失么?
1225 15
|
编解码 芯片 数据库管理
如何解决电子墨水屏标签的误唤醒和吵醒问题?
随着电商和物流快递行业的高速发展,人们网上购物越来越方便,线下实体店受到了越来越强烈的冲击。作为传统的商业模式,零售和商超自然不甘堕落,也在不断的学习互联网打法的精髓。经过多年不断的升级改造,其购物环境,价格体系,结算体验,服务意识方面都获得很大的提升,在某些区域或针对某些特定人群,其吸引力甚至远远超过了网上购物。
|
前端开发 开发者
解决Edge输入document.querySelector(‘video‘).playbackRate = 2.5视频无法加速的问题,‘Uncaught (in promise) TypeErro’
解决Edge输入document.querySelector(‘video‘).playbackRate = 2.5视频无法加速的问题,‘Uncaught (in promise) TypeErro’
|
网络虚拟化 网络架构
不同网段通过静态路由实现互通
静态路由是一种需要管理员手工配置的特殊路由。静态路由比动态路由使用更少的带宽,并且不占用CPU资源来计算和分析路由更新。但是当网络发生故障或者拓扑发生变化后,静态路由不会自动更新,必须手动重新配置。静态路由有5个主要的参数:目的地址和掩码、出接口和下一跳、优先级。 使用静态路由的好处是配置简单、可控性高,当网络结构比较简单时,只需配置静态路由就可以使网络正常工作。在复杂网络环境中,还可以通过配置静态路由改进网络的性能,并且可以为重要的应用保证带宽。
758 0
不同网段通过静态路由实现互通

热门文章

最新文章