1.FFmpeg源码分析: AVStream码流
2.HTTP/2åè®®ä¹Streamãåçç¬è®°ã
3.于——InputStream类源码详解
4.解析Stream foreach源码
5.parallelStream 基本原理
6.node stream源码分析 — Readable
FFmpeg源码分析: AVStream码流
在AVCodecContext结构体中,源码原理AVStream数组存储着所有视频、源码原理音频和字幕流的源码原理信息。每个码流包含时间基、源码原理时长、源码原理索引数组、源码原理图床源码 汉化编解码器参数、源码原理dts和元数据。源码原理索引数组用于保存帧数据包的源码原理offset、size、源码原理timestamp和flag,源码原理方便进行seek定位。源码原理
让我们通过ffprobe查看mp4文件的源码原理码流信息。该文件包含5个码流,源码原理是源码原理双音轨双字幕文件。第一个是video,编码为h,帧率为.fps,分辨率为x,像素格式为yuvp。第二个和第三个都是audio,编码为aac,采样率为,立体声,语言分别为印地语和英语。第四个和第五个都是subtitle,语言为英语,编码器为mov_text和mov_text。
调试实时数据显示,stream数组包含以下信息:codec_type(媒体类型)、codec_id、bit_rate、profile、level、width、height、sample_rate、灵域ol源码channels等编解码器参数。
我们关注AVCodecContext的编解码器参数,例如codec_type、codec_id、bit_rate、profile、level、width、height、sample_rate和channels。具体参数如下:codec_type - 视频/音频/字幕;codec_id - 编码器ID;bit_rate - 位率;profile - 编码器配置文件;level - 编码器级别;width - 宽度;height - 高度;sample_rate - 采样率;channels - 音道数。
AVStream内部的nb_index_entries(索引数组长度)和index_entries(索引数组)记录着offset、size、timestamp、flags和min_distance信息。在seek操作中,通过二分查找timestamp数组来定位指定时间戳对应的帧。seek模式有previous、next、nearest,通常使用previous模式向前查找。
时间基time_base在ffmpeg中用于计算时间戳。在rational.h中,AVRational结构体定义为一个有理数,用于时间计算。要将时间戳转换为真实时间,只需将num分子除以den分母。
HTTP/2åè®®ä¹Streamãåçç¬è®°ã
åé¢ä¸ç¯ä»ç»äºHPPT/2çâè¿æ¥åè¨âãâäºè¿å¶æ¡¢âãâ头é¨å缩âãæ¬æä»âæµåå¤è·¯å¤ç¨âãâæµç¶æâãâæµéæ§å¶âãâæµä¼å 级âãâHTTP/2æ©å±âä»ç»HTTP/2åè®®æµç¸å ³ç¥è¯ãæµ
åé¢ä»ç»æ¡¢æ ¼å¼æ¶ï¼æ¯ä¸ªæ¡¢é½æä¸ä¸ªæµæ 示ï¼æ è®°èªå·±å±äºåªä¸ªæµãéè¿å°ç¸åæµæ è¯çæ¡¢ç»è£ ï¼æ¡¢ä¹é´æ¶æä¸¥æ ¼é¡ºåºçï¼å³å½¢æäºâæµâã
å¤è·¯å¤ç¨
ä¸ä¸ªHTTP/2è¿æ¥å¯ä»¥å¹¶éå¾å¤ä¸ªæµï¼æµID顺åºéå¢ä¸äºç¸ç¬ç«ï¼å½¢æå¤è·¯å¤ç¨ãç±å®¢æ·ç«¯åèµ·çæµID为å¥æ°ï¼æå¡ç«¯åèµ·ç为å¶æ°ã
idle
æµç©ºé²ç¶æï¼å¯ä»¥åéæ¥æ¶HEADERS帧
open
æµå¼å¯ç¶æï¼idleåéæè æ¥åHEADERS帧åï¼ç¶æåæ´ä¸ºå¼å¯
half closed
åéå å«END_STREAMæ¡¢çä¸ç«¯æµè½¬ä¸ºæ¬å°åå ³éhalf closed(local)ï¼è¡¨ç¤ºå®¢æ·ç«¯åé请æ±æ°æ®å®æ¯ï¼çå¾ æå¡ç«¯ååºæ°æ®ï¼æ¥åå°æå¡ç«¯åéçEND_STREAMè¿å ¥closeå ³éç¶æãæ¥åEND_STREAMæ¡¢çå¦ä¸ç«¯ç§°ä¸ºè¿ç¨åå ³éç¶æhalf closed(remote)ï¼è¡¨ç¤ºæå¡ç«¯ç¥é客æ·ç«¯è¯·æ±å·²ç»åéå®æ¯ï¼å¤çç»æåå¯ä»¥åéååºæ°æ®ï¼å¹¶åéEND_STREAMå°å®¢æ·ç«¯ï¼è¿å ¥closeå ³éç¶æã
close
æµçå ³éç¶æãé¤äºhalf closedæ°æ®åéç»æå ³éå¤ï¼åéRST_STREAM(åçé误æåæ¶)ä¹å¯å ³éæµã
æµç¶æ交äºç¤ºæå¾
æµéæ§å¶æ¯ä¿æ¤æ¥æ¶æ¹çæºå¶ï¼éè¿é é¢æºå¶å®ç°ãåé端æ¯åéæ°æ®åwindowçªå£å¤§å°ç¸åºçåå°ãå½åé端æ¶å°æ¥æ¶ç«¯WINDOW_UPDATEæ¡¢åwindowçªå£å¢å ãwindowçäº0åä¸å¯ä»¥è¿è¡åéï¼çªå£åå§å¼ä¸ºåèã
éè¿åé端åæ¥æ¶ç«¯åéä¼å 级æéæå¾ æ¥æ¶ç«¯ç»äºèµæºåé æ¯æï¼æ¥å端ä¸ä¿è¯ä¸å®éµå®ï¼é»è®¤æé为ãä¼å 级表达å¯ä»¥éè¿HEADERSæè åç¬åéPRIORITY帧å®ç°ã
æµä¼å 级示å¾
客æ·ç«¯éè¿PRIORITY帧å¯ä»¥åè¯æå¡ç«¯å½åæµæä¾èµçæµï¼å½¢ææµä¾èµæ ãåä¸ç¶çº§çå个åèç¹éè¿æéåé èµæºï¼ç¶çº§å åé èµæºä¼ è¾ç»æåï¼ååé å级èµæºã
éHTTP/2çåç¯æç« ï¼å¯¹HTTP2å·¥ä½åçæäºå ¨å±ç认è¯ï¼ç¸ä¿¡åé 读HTTP/2ç¸å ³æç®ä¸åå°é¾ã
ä½è èæ¢ï¼åå°åºè¡é«çº§ææ¯ä¸å®¶ï¼åä¸äºãRocketMQææ¯å å¹ã审稿工ä½ãä¸æ³¨å端ä¸é´ä»¶æ¹åï¼å·²éç»å表RocketMQç³»åãKafkaç³»åãgRPCç³»åãSentinelç³»åãJava NIOç³»åãå ¶ä¸RocketMQç³»åå·²å表ä½ç¯ãæºç ãå®æãåçãè°ä¼æå¾ ä¸ä½ ä¸èµ·å¦ä¹ ã
于——InputStream类源码详解
InputStream类是字节输入流的基础,它作为所有字节输入流类的超类,提供了读取字节的基本功能。
从InputStream读取下一个数据字节时,返回的值位于0到的整数范围内,代表字节值。若流已到达末尾而无更多字节,会返回值-1。在获取数据、防泄漏系统源码遇到流终点或抛出异常之前,此方法始终处于阻塞状态。
实现InputStream接口的子类通常会根据具体的应用场景,扩展或修改InputStream的基础行为。例如,FileInputStream用于从文件读取字节,而ByteArrayInputStream用于从字节数组读取。
InputStream类提供了基础的读取操作,包括read()方法用于读取单个字节,read(byte[] b)方法用于读取多个字节到字节数组中,以及read(byte[] b, int off, int len)方法用于指定读取字节的位置和数量。这些方法共同构成InputStream类的核心功能。
通过使用InputStream类及其子类,开发者可以实现从文件、网络连接、设备输入或其他数据源的字节读取,为数据处理、文件操作和网络通信等提供了基础支持。
在实际应用中,开发者需谨慎处理异常情况,比如文件未找到、网络连接断开或读取操作超时等,并合理使用非阻塞读取机制,以提高程序的性能和响应速度。
总之,InputStream类作为字节输入流的基础,为各种应用场景提供了灵活和高效的数据读取能力。深入理解其内部机制和用法,对于开发高效、可靠的软件系统至关重要。
解析Stream foreach源码
本文深入解析Stream的foreach操作源码,主要关注串行流和并行流的区别,特别是并行流背后的ForkJoin框架。 在Stream中,操作可分为中间操作和结束操作,其中foreach属于结束操作。b站源码限流串行流与并行流的主要区别在于实现方式,串行流是线性执行,而并行流则利用了ForkJoin框架的分治策略。 对于串行流(如`stream`),其执行过程如下:获取ReferencePipeline.Head的Stream实现,内部包含ArrayListSpliterator对象。
通过ArrayListSpliterator的forEachRemaining方法逐一执行元素操作。
而并行流(如`parallelStream`)则更为复杂:同样获取ReferencePipeline.Head的Stream实现,内部有ArrayListSpliterator。
调用父类的forEach方法,构建一个ForEachTask。
在ForEachTask的invoke方法中,调用compute方法,利用ForkJoin框架的分治策略将任务拆分到commonPool中的线程池执行。
子任务通过拆分器的forEachRemaining方法,最终执行用户定义的action.accept(e)回调。
ForkJoin框架是JDK7新增的,它通过线程池执行任务,尤其适用于并行处理。在并行流中,任务会分配到Java 8中预定义的commonPool,该线程池基于计算机处理器数量进行配置,以实现高效的并行计算。parallelStream 基本原理
stream流是java在jdk1.8中引入的工具,旨在简化任务处理,让我们专注于实现细节,而无需关注任务调度,由stream自动实现调度。例如,如果我们需要对列表进行求和,只需通过列表生成流,使用reduce方法传递表达式即可,省去了for循环和if判断等繁琐操作。默认情况下,stream是单线程执行的。
对于需要并行执行的使用他人源码场景,例如同时计算多个任务结果,java提供了parallelStream。通过parallelStream,可将上述例子中先计算1+2,再计算3+3的方式,优化为同时计算1+2和3+3,利用处理器的多核性能提升效率。parallelStream内部通过ForkJoinPool的commonPool线程执行任务,大大简化了并行编程的复杂性。
探究parallelStream如何利用ForkJoinPool,首先,integers.parallelStream()返回Stream对象,parallel属性为true,表明这是并行流。接下来,调用stream.reduce方法处理流。在reduce()源码中,它主要通过调用evaluate()方法处理流。evaluate()首先检查linkedOrConsumed属性,确保流未被消费。对于并行流,evaluate()创建ReduceTask,并通过其invoke方法执行计算。
ReduceTask是ForkJoinTask的子类,它不重写invoke方法,因此调用ReduceTask.invoke()等同于调用ForkJoinTask.invoke()。这样,parallelStream通过ForkJoinTask实现了并行流处理,具体通过递归任务分解和并行执行实现。
node stream源码分析 — Readable
Stream在Node.js中是一种数据传输的抽象机制,它分为四种类型:流、可读流(Readable)、可写流(Writable)和可缓冲流(Transform)。其中,可读流(Readable)用于从外部数据源读取数据。
可读流有两种模式:流动模式和非流动模式。非流动模式在监听到'data'事件时,直接读取数据而不暂停,并不将数据存储到缓存区。流动模式则在监听到'readable'事件时,将数据放入缓存区,并等待'writable'调用来判断是否有空位,以此来决定是否暂停。
以下是对可读流(Readable)的源码分析。首先,让我们查看Readable的源码。源码文件位于'_stream_readable.js'中。
在'fs.js'文件中,我们可以看到创建读取流的源码,而'Readable'则位于'_stream_readable.js'文件中。
在'fs.js'文件中,我们可以通过调用`fs.createReadStream`来创建读取流。在'Readable'源码文件中,我们可以看到Node.js实现的可读流类,它提供了读取数据的功能,并且支持缓冲和流式读取。
从原理剖析带你理解Stream
Stream是Java 8提供的新特性,它允许我们以声明式的方式处理数据集合,简化了集合操作的代码结构。在项目中,集合是最常用的数据存储结构,当我们需要对集合内的元素进行过滤或其他操作时,传统的做法是使用for循环。Stream操作分为中间操作与结束操作两大类。中间操作仅进行记录,直到结束操作才会触发实际计算,这种特性称为懒加载,使得Stream在处理大规模对象迭代计算时非常高效。中间操作又分为有状态与无状态操作,有状态操作需要在处理所有元素后才能进行,无状态操作则不受之前元素的影响。
Stream结构分析揭示了其内部实现机制。每一次中间操作都会生成新的Stream对象,无状态操作的实现类为StatelessOp,有状态操作的实现类为StatefulOp。通过继承关系,我们可以观察到Stream结构的层次性。核心Sink概念在Stream API内部实现中扮演关键角色,Stream API通过重载Sink的接口方法实现了其功能。以filter或map方法为例,源码返回的StatelessOp或StatefulOp对象构成了一个复杂的结构,最终与Sink相关联。Sink对象在Stream执行流程中扮演关键角色,其作用在collect方法中得以体现,通过匿名内部类ReducingSink对象实现元素的收集与处理。动画理解Stream执行流程可以帮助我们更直观地了解其运行机制,从而深入掌握其高效处理数据集合的方法。
Java Stream流与Optional流浅析
Stream流
1. 操作类型
Stream API中的操作类型主要分为两大类:中间操作和终止操作。中间操作仅作为标记,实际计算会在触发终止操作时进行。
2. Stream的操作过程
首先,我们准备了一些示例代码。在TestStream类中,我们定义了一些测试lambda函数的方法。在main方法中,我们执行了一个相关的流操作,在控制台中并没有看到任何输出。这说明Stream并没有真正执行到对应的方法中,因为我们没有写入终止操作。由此可见,在终止操作之前,Stream并没有真正去执行每个中间操作,而是将中间操作记录了下来。在执行终止操作这一行代码时,再去执行中间操作。
2.1 记录过程
进入源码后,可以看到Collection的Stream方法调用了StreamSupport.stream()方法。在该方法中,返回了一个ReferencePipeline.Head对象,这是记录管道操作的头节点对象。这个Head对象继承了ReferencePipeline对象,所以后续的map、filter等方法实际上是ReferencePipeline对象的方法。在构造方法中,也调用了父类AbstractPipeline类的构造方法。
在Stream中,每一步操作都被定义为一个Stage。在构造方法中,定义了previousStage和sourceStage,即上一个节点和头节点。在类中还有一个nextStage对象。
Stream实际上构建了一个双向链表来记录每一步操作。接下来,我们看一下list.map()方法。
在该方法中,创建了一个StatelessOp对象,它代表无状态的中间操作。这个对象同样继承了ReferencePipeline。在该对象的构造方法中,将调用该初始化方法的节点定义为上一个节点,并且对应的深度depth也进行了+1操作。
我们总结一下,stream()方法得到的是HeadStage,之后每一个操作(Operation)都会创建一个新的Stage,并以双向链表的形式结合在一起。每个Stage都记录了本身的操作。Stream就以此方式实现了对操作的记录。注意,结束操作不算depth的深度,它也不属于stage。但是我们的示例语句中没有写结束操作的代码,所以在这里提一下Stream的Lazy机制。它的特点是:Stream直到调用终止操作时才会开始计算,没有终止操作的Stream将是一个静默的无操作指令。
Stage相关类如下
2.2 执行过程
在了解执行过程之前,我们应该先了解另一个接口Sink,它继承了Consumer接口。在调用map、filter等无状态操作中返回的StatelessOp对象中,覆盖了opWrapSink方法,返回了一个Sink对象,并且将参数中的Sink对象作为构造方法中的参数传入进去。
走进构造方法后,可以看到在该对象中定义了一个downstream,该对象也是一个Sink类型的对象,并且在定义Sink对象时,覆盖了Consumer接口中的accept方法。
不难看出,在执行accept方法时,就是将当前节点的操作结果传入给downstream继续执行,而这个downstream则是通过onWrapSink方法中传入过来的。
了解了以上这些概念,我们可以走进结束操作.collect(Collectors.toList());方法。在该方法中,通过Collectors定义了一个另一个ArrayList收集器,并且传入了collect方法中。
我们暂时只看非并行的部分。在这一行通过ReduceOps定义了一个ReduceOp对象。
在makeRef方法中,返回了一个ReduceOp对象,该对象覆盖了makeSink()方法,返回了一个ReducingSink对象。我们继续往下走,走进evaluate方法中。
可以看出,wrapsink方法中,是查找链表的头节点,并且调用每个节点的onWrapSink方法,在该方法中传入当前节点的sink对象,并且将传入的对象定义成自己的下游,形成一个从头节点到尾部节点的Sink单向链表。
在wrapSink中,通过一层层的前置包装,返回头节点的Sink类传入copyInto方法中。
在该方法中,先调用了wrappedSink.begin()方法,该方法默认实现为调用downstream的begin方法。相当于触发全部Sink的begin方法,做好运行前的准备。
具体循环的执行则是在spliterator.forEachRemaining(wrappedSink);方法中,操作如下
在forEachRemaining方法中,调用了accept方法,也就是在定义onWrapSink方法中初始化Sink对象后定义的accept方法,将自己的执行结果传入downstream继续执行,也就是说,在调用结束操作后才实际执行每个方法。在实际执行过后,在执行end方法进行结束操作。Stream整体的流操作大概就是如此。了解了大概过程后可以找一些常用的case来分析一下。
2.3 具体分析
一般情况下都会选择list作为排序容器,大部分情况下都是不知道容器大小的,于是采用RefSortingSink类作为当前节点处理类,该类代码如下。
可以看到该Sink中的accept方法中,并没有执行下游的accept方法,而是将所有的数据装入了一个ArrayList,在end方法利用arrayList进行排序,并且继续开启后续的循环操作。
3. 代码建议