scrcpy 源代码渲染逻辑整理

背景

自己学习scrcpy自己写的协同工具遇到了一些问题,所以分析了一下源代码,结果我出现的问题只是参数搞错了。

分析

  1. stream.c
    a. run_stream 创建线程,不停读取数据,然后投递接受的packet,然后投递数据
    b. stream_parse 解析收到数据packet
    c. push_packet_to_sinks 投递数据到
  2. decoder.c
    a. decoder_init 注册解码函数
    b. decoder_push 解析推过来的packet
    c. push_frame_to_sinks 推送得到frame 数据帧
  3. screen.c
    a. sc_screen_init 创建窗口
    b. 绑定frame处理函数
    c. sc_screen_frame_sink_push 投递从decoder解码的数据
  4. video_buffer.c
    a. sc_video_buffer_init 主要初始化队列和一些信号量
    b. sc_video_buffer_start 创建线程,不停读取队列,
    c. run_buffering 不停读取队列帧,判断是否超时,然后进行投递。sc_video_buffer_on_new_frame 通过这个回调到sceen.c 对象里面。
  5. screen.c
    a. sc_video_buffer_on_new_frame 通过 SDL_PushEvent 获取当前数据的

    总结


    这种c代码看起来还是很爽,几个对象抽象挺好,我自己写代码只封装解码层,网络层,SDL处理层,事件通知层。
    scrcpy 队列我没有感受他的好处,开始因为我的卡是没有这个帧的队列,后面发现,根本不是这么一会事情,事情情况渲染速度远远大于传送网络传送过来帧,我自己也实现一个简单队列,检测到帧堆积,直接丢弃,只显示最新的帧,结果从来就没有丢弃。
    scrcpy app 最核心SurfaceControl 反射实现,其他的模拟可以借鉴adb 命令实现代码实现,不过这个SurfaceControl 有人分析android 核心代码可以找到实现,但我自己看了一下貌似没有看懂,直接就拿scrcpy反射实现,其他几个控制命令都是我自己实现,更加简单明了,它是实现几个点击滚动太过于复杂了。

format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);

这个代表帧率,这个Scrcpy其实动态的,这个跟延迟有一定关系,但我之前设置错参数出现反复点击一个表情会出现延迟,然后把这个设置120就延迟了,手机FPS是60.

format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, REPEAT_FRAME_DELAY_US); // µs

这个影响到延迟的,这个给我感觉保持一直采集有数据,之前就是我把这个设置错了,导致快速点击输入表情时候会出现明显的延迟。

format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
这个跟画面清晰度有关系,类似直播高清,蓝光4M 这些。

会删除掉复制tmp的jar包

我开始到目录找了好久,没有好到文件,但命令确实复制到tmp

我不知道scrcpy 用OS.write 有什么好处,我直接用websocket通信,也不走转发,我自己用Websocket跑的时候效果差不多,协议直接用JSON格式就可以了,也不用字节解析,视频流原始传递,这个才是消耗流量的地方,控制的消耗不了多少流量。

scrcpy 我觉得待补充的功能

  • 蓝牙【貌似分支有了】
  • 远程控制【我自己目前这种模式就可以支持远程,我目的就是远程控制手机】
  • 后台运行 【提供增强功能,可以给APP使用,市面已经这样子APP】
  • 输入法增强 【我不知道目前这个实现了没有】
  • 控制栏 【我自己用win32 实现了,感受最原始界面开发】
  • 鼠标点击录制【用来做脚本,结合辅助服务最好,不知道能不能直接反射使用,开启辅助是可以做到】,这个只要做到基本就是无敌了软件。
  • 鼠标模拟增强 【目前鼠标的模拟,如果玩游戏可以检测出来,应该跟属性有关系,这种要做专门优化】

自己开发的软件应该远程控制+手机增强功能,给自己开发用的软件。

app process 其实能做很多东西,
以后想到什么再补充吧。

jquery 为什么要这么写

jqery 生成对象写法,我们调用jquery $("div")然后调用对应的函数或者属性我们就能快乐写代码了,通过他历史版本发现他们思路。

历史版本

var jQuery = function(a,c) {
	// If the context is global, return a new object
	if ( window == this )
		return new jQuery(a,c);

	// Make sure that a selection was provided
	a = a || document;
	
	// HANDLE: $(function)
	// Shortcut for document ready
	if ( jQuery.isFunction(a) )
		return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
	
	// Handle HTML strings
	if ( typeof a  == "string" ) {
		// HANDLE: $(html) -> $(array)
		var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a);
		if ( m )
			a = jQuery.clean( [ m[1] ] );
		
		// HANDLE: $(expr)
		else
			return new jQuery( c ).find( a );
	}
	
	return this.setArray(
		// HANDLE: $(array)
		a.constructor == Array && a ||

		// HANDLE: $(arraylike)
		// Watch for when an array-like object is passed as the selector
		(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||

		// HANDLE: $(*)
		[ a ] );
};

1.1.3以下版本(包括),没有采用闭包,但还是用的匿名函数异常内部实现,这个里面直接写选择逻辑,这种是我们常见写法,这个时候他们还没有单独把选择器代码用init的函数单独封装起来。这里含有new的和选择代码,不同代码逻辑放在一个函数里面,这样子就比较混乱,还是后面代码清晰很多。

1.1.4版本

var jQuery = function(a,c) {
	// If the context is global, return a new object
	if ( window == this || !this.init )
		return new jQuery(a,c);
	
	return this.init(a,c);
};

jquery 为了用户 无论new不new都是返回jquery对象,后面高版本全部都直接new,这样子代码简答一点,同时进行代码隔离,Jquery 函数对象 与 new Jquery对象分开, 因为它的调用

new JQuery.fn.init();

new其实 init 对象,与jquery 没有关系, 为了使用jquery 原始链的方法,又把init 原始链替换成Jquery的原型链

开始init 只是作为入口函数而已,代码清晰好多。

return new jQuery( c ).find( a );

老代码查找用重新new 而已,后面用原型链感觉只是代码更加清晰,别的书籍说了为了隔离,我感觉不是很靠谱,基本上都是为了返回jquery对象而已。

jquery 历史老版本地址

Google Code Archive - Long-term storage for Google Code Project Hosting.

这个google地址,所以需要梯子

补充

直接复制自己笔记,后面可能会补充修改

今天用Java写网络代码,好久没有这么写代码,一气呵成写逻辑代码,感觉就没有停下来过。。写着突然明白以前不是很懂的东西,现在看来真简单。