所有由空雪梦见发布的文章

使用VS2022编译7zip的控制台程序

7zip除了官方下载安装的图形界面工具之外,是有独立控制台程序的,在CPP\7zip\Bundles里面。这个控制台程序还分为全功能版(7zz)的和精简的(7zr),但官方那边能下载到的只有精简的,全功能的那个要自己编译。

在用VS2022编译全功能版的时候我遇到了问题。虽然官方提供了nmake用的编译脚本,但是它跑起来总是报错,而且报错的地方在Windows SDK里面、而不是7z自己的代码。报的错误是C2279,出现在mapi.h头文件里、诸如“这个typedef地方不能写throw标识符”这样的东西。

网上搜索之后能找到的相关的东西不太多,有一个说法是C++标准的问题,现在的Windows SDK需要配合新版本的C++标准(C++17之类)才能正常使用。看了一下7zip源码里nmake脚本加的参数,也确实没有指定标准。

本来想试试换个旧版的Windows SDK行不行,一看要下载那么多东西……烦了,就没试。最后我的做法是,打开CPP下的Build.mk,找到定义 CFLAGS 的那一行,加个 /std:c++17。文件是只读的,要去掉只读属性才能保存。再编译会变成另外的错误,报异常规范不匹配。最简单的去掉警告视为错误(搜索,去掉-WX)可以编译通过。

另外我发现它没加链接时代码生成这样的优化,暂时不知道为什么。试着开了一下,用7zz b看跑分,和不开看不出区别。

线程调度和IO多路复用杂谈

做网络编程的话,经常会听到什么epoll(linux下)、kqueue(bsd和苹果下)、io完成端口(windows下)这样的名词,也许看了网上的资料也知道用法是什么样的,但是却对为什么这样做没有很感性的认识。我在尝试设计一个实验能够从线程调度的角度来对这个事情有直观认识,现在勉强也能算是搞出来了,所以写一篇讲讲是做了什么。

继续阅读线程调度和IO多路复用杂谈

在OBS直播软件中使用FDK-AAC编码器

现在在直播中用的最多的软件可能就是OBS了。这个免费开源(https://github.com/jp9000/obs-studio)的直播软件功能很强大,也没有烦人的小广告,不要注册不要登录,所有基于RTMP的直播平台都能用。

这个直播软件在编码直播音频流的过程中,支持使用很多种不同的AAC编码器。它大概会按照 Core-audio(苹果iTunes里的那个)、FDK-AAC、Windows Media Foundation、FFMpeg这样的顺序一个个找下去找到能用的。虽然是这个顺序,但是实际上在正常安装的OBS里面,是找不到 obs-libfdk.dll 这样一个文件的。安装的时候就不带这个文件。其实我之前也比较纳闷,这个如果效果这么好,那么为什么网上比较少见到哪里直接下载这个编码器的EXE文件呢。下午在一个群里提了这样的疑问,结果收到的回答大概是FDK-AAC发布源代码可以,但是因为其中涉及到一些什么专利许可证的问题,发布EXE、DLL这样的二进制文件好像是不行的。但是这些编码器里面,FDK-AAC在低码率HE-AAC v2模式下是公认的效果比较好。有的时候如果想拿来玩玩看用用看试试效果的话,就要自己从源代码编译了。

继续阅读在OBS直播软件中使用FDK-AAC编码器

Windows下控制子进程的句柄继承

相信用 Windows API 写过重定向子进程的标准输入输出的人都弄过这东西。大概过程是不难,不过对初学者坑很多。几年前我写了个文大概说这个的 http://blog.sorayuki.net/?p=85 ,现在要说的也是这个话题,不过是另外的事。

之前的经验是这样的,比如我有一个进程,这个进程要开个子进程、并且发数据给子进程。那么做法是创建一对匿名管道,把管道读的那一端拿去给子进程,写的这一端自己留着。容易出错的地方在这个“自己拿着”和“拿去给子进程”。拿去给子进程可以理解为,这个句柄要设置成可继承的,子进程开启以后就带着这个句柄了,那么自己进程拿着的就可以关掉(CloseHandle)了;自己拿着,那么字面意思就是不继承给子进程了。API函数CreatePipe有个 SECURITY_ATTRIBUTES 参数,这个结构体里是可以设置创建的管道句柄能不能继承的,要么两个都能继承要么两个都不能继承。但是现在要的是写的那个不继承、读的那个继承。做法不止一种,不过思路是这么两个,一个是创建的时候两个都不能继承,然后把其中一个改成可以;另一个是创建的时候两个都能继承,然后把其中一个改成不行。改继承属性可以用 SetHandleInformation 也可以用 DuplicateHandle 复制一个能/不能继承的新句柄出来、随后把原来的关掉。我现在是用 SetHandleInformation 的方法,DuplicateHandle 的方法已经忘记是在哪里看的了,现在也不知道怎么比较好坏和必要性。

复杂一点的场景,是我要起两个进程,第一个进程的输出给第二个进程的输入。麻烦一点的办法可以两个都和自己通信、自己在中间做转发;简单的办法可以直接创建一对管道,一个子进程分一个、自己不留。现在这里要说的是后者,复杂性一个表现在管道是自己创建的,但是自己在起了子进程之后一个都不留;另一个表现在要刚好一个进程继承一个,如果有一个进程继承了两个,比如写的那一端一不小心两个进程都继承了,那么很不幸这一对进程永远跑不完了:写入数据的那个子进程写完了关掉了这个写入端,但是由于读取数据的那个子进程也持有这个写端,自己有自己都不知道,也就是说这个写端没法全部被关闭,那么读的那一端就永远在等待了。

虽然上面那个例子只要搞清楚关系、小心操作的话,也没有那么大问题。不过麻烦的事情在,假设我现在是一个有好几个线程在同时执行的程序,在其中一个线程A里,要调用 CreateProcess 的时候,它不知道其他线程比如B有没有创建什么会被继承的句柄,假设这个时候线程B也在调用 CreateProcess,那么线程B创建的这个子进程会把线程A创建的那些句柄也一起继承走。然后问题呢就会出在刚才说的那个场景上,比如一对管道,读取端永远阻塞因为写入端没有全部被关闭。也许会觉得这样的场景只要在要创建子进程的时候锁个整个进程范围的锁就好了,但如果这里面调用了什么第三方库,或者是工程上好多人同时开发一个项目,有些事情就不一定有那么可控了。而且这种Bug还可能在极偶然的情况下才会遇到那么一两次,调试的时候难以重现十分痛苦……啊别说了,害怕。

继续阅读Windows下控制子进程的句柄继承

谈谈我对复数极坐标表示的初步理解2

上次那篇搞完之后,第二天我尝试了一下算极坐标那个角度的方法,花了很大的功夫尝试了几次,发现特别容易写错,几次写的代码算出来都是错的。我用的那个复数的库,虽然提供了获取长度和获取角度的方法,但是拿到的结果却不是我想要的能直接用于 Aeix 形式的。因为反三角函数的值域并不是从0到2π,所以我后来根据实部和虚部的正负关系,配合诱导公式,自己写了实现来求那个角度。思考之后,究其原因,是这东西有二义性。因为我一直假定 A > 0 、也就是极坐标上距离关系那个变量一定是正的,那么 x 也就是固定的了。举个简单的例子,对于 -1 这个数,可以理解为是 1 旋转了 180° 得到的;但是,它也可以理解为是 -1 没有旋转。 -i 这个数,可以理解为 1 旋转了 270° ,也可以理解为 -1 旋转了 90°。所以按照这么说,如果没有对 A 或 x 进行限定,那么就会有不止一种的表示方式。如果计算过程中弄串了,那么算出来的结果就很可能错了(之所以说很可能,是因为也有可能正好是对的)。

继续阅读谈谈我对复数极坐标表示的初步理解2

谈谈我对复数极坐标表示的初步理解

这几天在看离散傅里叶变换。虽然傅里叶变换那边还没搞定,不过因为数学有好大一部分还给老师了,这甚至都不知道是高中的还是大学的内容,结果这会儿看到复数还得想办法重新去理解复数。当时充其量也只知道个 i2=-1,但是这种程度的理解我觉得显然没办法应付要搞傅里叶变换这种事。今天在学习波形的平移会怎么影响傅里叶变换的结果的时候,好像突然对复数的理解有一点进展了,就赶紧想办法写一写。

继续阅读谈谈我对复数极坐标表示的初步理解

条件变量的伪唤醒

起因是这样的,C++的新标准库添加了多线程的支持,有线程和线程同步的实现。我自己是做Windows开发比较多,自然用过Event这样的线程同步对象。但是这个东西,在C++的标准库里面没有。我想到说它有一个“条件变量”,而这个“条件变量”是有一个wait / signal 功能的,这个类看起来能实现 Event 的一部分功能。但是在前几天,有个同事说条件变量的 wait 会在没有被 signal 的时候也自己唤醒。毕竟平时不做 posix 那样系统下面的开发,所以这样的事情没有听说过。那会儿其实我是不信的,但是用关键字“condition variable fake wake”在网上搜了一下以后,看到确实是有这样的事情,然后英语中也不是叫 fake,管它叫 spurious wakeup。

继续阅读条件变量的伪唤醒

视频直播开发的技能树

现在直播什么的很火,还有拍短视频之类。很多人在开发这方面的软件。我在一个这方面开发的讨论群里看了一段时间以后,有这种感觉。就是很多人是对多媒体方面的知识不太够,但是公司又要做,只能硬着头皮上。找资料的时候就会遇到这样一个问题,一些基础知识不会,看文章就看得有点迷糊或者看不懂。找别人写好的函数库来调用的话,只能实现一些简单的需求。所以我在想,我以前在字幕组搞过一段时间视频处理,这方面的知识能不能组织一下列出来,并不是全都要会才能做,里面很多我也不熟,只是在遇到问题时候好有个方向,知道要去查什么。

括号里的英文是用来方便搜索的。因为中文互联网里的资料搜起来很多实在不堪入目,搜英语资料虽然看起来比较累,不过根据经验确实是能少踩点坑

有想到的不完整的地方可能还会来补充。

继续阅读视频直播开发的技能树