0%

本系列的第一篇 中介绍了 AArch64 的基础指令、进程内存布局以及基础栈操作 等. 本文该系列的第二篇, 主要聊聊函数调用, 涉及到的就是 Function Call Convention. 初衷还是尽可能 “浅入深出” 地 got 到语言背后的本质, 这不是一个手册, 所以不是完备的.

1. 我们在聊函数调用的时候在聊什么?

至少我们应该把函数调用的几个问题搞清楚:

  1. 函数在汇编层是怎么调用的, 本质是什么?
  2. 函数的参数怎么传?
  3. 返回值写到哪里? 怎么传给 caller?
  4. 调用完之后, 怎么返回到原来的位置?

Function Call Convention 其实就是回答这些问题的, 接下里我们一一找到答案.

1.1. 函数调用本质是什么?

汇编层是没有函数的概念的, 我们需要把函数映射到汇编层来, 这样我们就知道了它的本质. 其实执行一个程序, 在汇编层来看就是不断的执行 CPU 指令, 都执行完了, 进程就结束了. 从第一篇的例子其实可以看出, 一个函数就是一个label, 等于代码段中该函数第一条指令的位置. 其实本质上函数调用, 就是程序从代码段的某一条指令, 跳转到另外一个地址上的指令去执行. 稍微复杂点的 C 程序都不是从头执行到尾就结束了, 会有条件判断, 函数调用. 函数调用和普通跳转不同的地方在于要处理传参、返回、以及寄存器的 backup 和恢复.

AArch64 提供给我们了一个 bl (branch with link) 指令, 用来执行指定的函数. 第一篇里, 我们介绍了 cmp 以及 b.le/b.ge 等, ‘b’ 在这两处都是 branch 跳转的意思.

只不过 bl 是跳转的函数地址上, bl 内部实现是这样的:

  1. 跳转之前会把函数调用后面地址(也就是bl的下一条指令的地址) 存放到 LR (Link register) 中
  2. PC 被 bl 的参数替换, 就是 PC 指向了 bl 的参数, 通常是一个函数 label, 对应着一个地址
  3. 目标函数开始执行
  4. 目标函数执行完, 调用 ret 指令, ret 会把 LR copy 回 PC
  5. 程序执行 PC, 也就是执行原来 bl 下一条指令了
Read more »

1. 什么是 ARM?

正式开始之前, 我们先来了解一下什么是 ARM, 以及对应的一些概念.

Wikipedia 上是这么介绍 ARM 的:

ARM (stylised in lowercase as arm, formerly an acronym for Advanced RISC Machines and originally Acorn RISC Machine) is a family of reduced instruction set computer (RISC) instruction set architectures for computer processors, configured for various environments.

ARM 是 高级-RISC(精简指令集)-机器 的缩写, 是精简指令集架构的家族. 同时 Arm Ltd. 也是开发和设计、授权这项技术的公司名称.

1.1. 有哪些指令集架构呢? (TRDR, 可跳过)

目前用的比较多的架构是 ARMv7 和 ARMv8, 这两个名字各自都是一个系列.

在 ARMv7 以及之前都是最多支持 32 位架构(更早还有 16 位, 甚至更低), 那么 32 位架构对应的 ISA 也就是指令集称为 A32. 32 位下指令的地址空间最大只有 4GB, 苹果系列的代表是 iPhone 4 使用的 A4 芯片, 以及 iPhone 4s 使用的 A5 芯片.

2011 年面世的 ARMv8-A 架构增加了对 64 位地址空间的支持, 对应的 ISA 称为 A64. 这里用的词是“增加”, 也就意味着在支持 32 位的基础上增加了对 64 位的支持. 所以也可以看出来所谓的 32/64 位指的就是可寻址的最大地址空间. 苹果系列从 iPhone 5s 开始的 A7 芯片一直到 A15, 以及 Apple M1 系列开始都是基于 ARMv8.x-A 规范的.

那我们见到的 AArch64 是什么呢? 其实它和 AArch32 被称为 “执行状态” (execution state), 那么我们可以说 ARMv8-A 同时支持 AArch32 和 AArch64 两种状态, 在 AArch64 状态下, 运行的是 A64 指令集.

这里要注意 ARMv7/ARMv8-A、AArch32/AArch64 以及 A32/A64 在概念上的的区别, 但很多时候, 描述的范围都挺笼统的, 有些也是可以互相指代的, 大家知道就好.

上面说到指令集, 指令集是做什么用的呢? 我们为什么要了解这些?

指令集本质上定义了 CPU 提供的“接口”, 软件通过这些“接口”调用 CPU 硬件的能力来实现编程. 编译器在这里起到很关键的角色, 它把上层代码根据对应的架构, 编译为由该架构支持的指令集对应的二进制代码, 最终运行在 CPU 上.

对 C 系语言来说, 我们说的跨平台, 其实就是通过同一份源码在编译时, 根据不同 target 架构指令集, 生成不同的二进制文件来实现的.

Read more »

本系列的 第一篇 中介绍到了 AudioUnit 中和系统硬件交互的 IO Unit, 以及如何使用它进行音频的采集和播放. 本文是该系列的第二篇, 将会介绍 AudioUnit 中另外 四类 非常重要的 AudioUnit: MixingEffect UnitConverter Unit 以及 Generator Unit.

1. Mixing Unit

Mixing unit 在实际场景中非常的实用, 特别我们需要对多路音频做处理或者播放. 比如对于音频制作 app 来做, 通常要支持混入 N 多种乐器的声音和片段, 比如 吉他、钢琴、贝斯、人声、和声等等. 这个时候使用 Mixing unit 把这些 input bus 混成一路 output 交给 IO Unit 播放, 就是一个很必要且自然的结果.

Mixing Unit 是一个种类, 苹果内部提供了三个子类型:

1
2
3
4
5
CF_ENUM(UInt32) {
kAudioUnitSubType_MultiChannelMixing = 'mcmx',
kAudioUnitSubType_MatrixMixing = 'mxmx',
kAudioUnitSubType_SpatialMixing = '3dem',
};

1.1 MultiChannelMixing

MultiChannelMixing 是一个 多输入、单输出 的结构, 特点是:

  1. 支持任意多的 input bus, 每个 input 都可以有任意多的 channel(声道) 数
  2. 只有一路输出 ouput bus, 这一路 output bus 也可以有任意多的 channel 数

把这些 input bus 的声音混和, 从 output bus 输出, 每一路 input bus 可以独立设置数据源、音频格式、音量、mute 等, 这个是 Mixing 通用特点, 下同.

下面我们看一下它的结构, 相比 IO Unit 这个理解起来就比较简单了.

MixingNode

MultilChannelMixing 是 多输入、单输出 的结构, 可以自由配置 input bus 的数量, 配置完之后, 一个配置为 N 输入的 bus number 从 0 开始 到 N-1. Output bus 的个数只有一个, bus number 固定为 0.

每个 input bus 可以设置独立的 RenderCallback 或者连接前序的 AudioUnit 提供数据, 可以设置独立的音频格式参数, 以及控制当前 input 的音量和 mute 状态等等.

Read more »

Apple 平台上如果涉及到音频采集, 很难避开 AudioUnit 这个工具库, AudioUnit 是 Audio Toolbox 下的一套有年头的 C API, 功能相对也比较强大, 虽然苹果最近几年推出并逐渐在其基础之后完善了一套 AVAudioUnit 的 OC/Swift 的 API, 但 AudioUnit 依然有很广泛的使用, 而且了解这套 C API 也对理解 AVAudioUnit 内部的实现和使用有很大的帮助.

其实里面的概念并不是特别复杂, 但是因为文档比较老旧, 概念也比较绕, 上手并不易. 我此前做唱歌和直播 app 相关的工作, 对 AudioUnit 使用的也比较多, 积累了一些经验, 希望能够最大程度地把一些通用的概念和使用方法分享出来. 接下来将带大家剖析 AudioUnit 的内部原理和丰富多样的使用方式, 如果你在做涉及到声音采集和处理的工作, 希望能带大家深入浅出地摸透 AudioUnit.

关于 AudioUnit 的文章是一个系列, 我希望能够把之前的经验结合一些实际的场景来介绍, 大概分为一下四个部分:

  1. 熟悉 IO Unit 结构和运行机制, 使用它来进行录制和播放
  2. 熟悉其他类型的 AudioUnit, 比如 Mixer, Effect, Converter 等
  3. 使用 AUGraph 串联起来 AudioUnit, 以及常用的使用模式
  4. 熟悉使用 AVAudioUnit 进行音频采集和播放

本文中我们先来看第一部分.

1. AudioUnit 介绍

如下图, 可见 iOS 上所有的音频基础都是基于 AudioUnit 的, 比如 AudioToolbox、Media Player, AV Foundation 等都是在 AudioUnit 上做的封装. AudioUnit 本身处理效率非常高, 实时性也很强, 支持 VoIP 常见下进行回声消除、降噪等处理.

Image

Read more »

1. 函数指针

函数指针就是指向函数地址的指针

1
2
3
4
5
6
7
8
9
10
int Sum(int a, int b) {
return a + b;
}

typedef int(*SumFunc)(int x, int y);

// --------

SumFunc sum = Sum;
std::cout << sum(1, 2) << std::endl;

2. 函数对象

重载了 operator() 的类对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SumClass {
public:
SumClass(int padding): padding(padding){}

int operator()(int a, int b) {
return a + b + padding;
}

private:
int padding;
};

// ---------------------

SumClass sumObj(3);
std::cout << sumObj(1, 2) << std::endl;
Read more »

低延迟编码对于很多视频app来说都很重要,特别是对实时音视频场景。苹果在 WWDC 2021 在 VideoToolbox 里推出了一种新的低延迟编码模式。低延迟编码模式的主要目的是为实时通讯场景优化现有的编码流程。

低延迟视频编码有以下的特点,从而对一个实时视频通讯app进行优化。

  1. 处理效率高,最小化端到端的延迟
  2. 新增两种 profile: CBP & CHP,增强互操作性
  3. 引入时域伸缩编码(Temporal Scalability),当会话中有多个参与者的时候,提供高效的编码流程
  4. 支持设置最大帧量化参数(Max Frame QP),展示最好的视频质量
  5. 引入长期参考帧 LTR,提供一个可靠的机制从网络丢包错误中恢复通讯

1.低延迟视频编码一览

下图是苹果平台上视频编码管线的简图:

Image

Read more »

REF: WWDC 2021 - Evaluate videos whith the Advanced Video Quality Tool

AVQTAdvanced Video Quality Tool 的缩写,是苹果在 WWDC 21 上推出的一款评估视频感知质量的工具。


一、背景介绍(非WWDC内容)

1.1 视频质量评估的现状

在正式开始之前,我想跟大家科普几个概念和行业现状,这些对理解本次的内容很有帮助。

视频质量评估是个老话题了,主流的分为下面几类:

  1. 主观评测,也就是人工评估,准确率高,但成本大,难规模化
  2. 客观评测,纯依靠算法,比如PSNR(Peak Signal-to-Noise Ratio峰值信噪比),SSIM(Structural SIMilarity 结构相似性),准确率低,成本小,容易规模化
  3. 感知质量评测,代表是Netflix 的VMAF,VMAF 是基于机器学习算法,根据人工的识别结果训练模型,目的是要模拟真人评测,结果上达到接近人工评估的准确度,这也是“感知”一词的含义。优点是准确率高,也容易规模化。我们今天要聊的 AVQT 也属于此类。

还有一种分类是有源评估无源评估,有源评估顾名思义,需要有参考源,比如有一个未压缩的超清Raw视频,它作为参考源,然后在进行处理编码之后,变成一个低分辨率、低码率的的视频,这个作为评估的对象,对比参考源视频,打出分数。感知质量评测的工具都属于有源评估范畴,即需要参考源来进行评估打分。

Read more »

Xcode Cloud 是什么?

Xcode Cloud 是一个搭建在苹果的开发工具链之上的 CI/CD 系统,和苹果的 Xcode、TestFlight 以及 App Store Connect 整个开发工具和生态进行打通。Xcode Cloud 使用 Git 作为项目的代码管理工具,通过 CI/CD,帮助开发者打造更高质量、更稳定的 app。从 Xcode 13 版本开始支持,目前在 beta 阶段,免费限量内测申请,2022年对所有人开放,具体价格待公布。

Xcode Cloud 能做什么?

  1. 自动构建和运行测试
  2. 自动在模拟器里运行测试程序
  3. 接收 Xcode Cloud 返回的构建结果通知,提前发现问题
  4. 通过 TestFlight 分发新版本给测试用户
  5. 创建供苹果审核的新版本
  6. 使用 Xcode 和苹果的云基础设施协同开发
Read more »

什么是 Karabiner-Elements ?

Karabiner-Elements (下面我们简称为Karabiner)官网对自己的描述是 “A powerful and stable keyboard customizer for macOS.”,我使用下来的感受是 Karabiner-Elements 是 macOS 平台上一款非常强大的键位映射工具,没有吹嘘的成分,买家秀和卖家秀是一样的。

这个介绍我会分为两个部分:

  • part1 介绍 Karabiner 的核心功能,以及我自己使用 Karabiner 帮助我高效使用键盘的一个思路,不涉及具体的配置
  • part2 根据实例详细介绍使用 Karabiner 高级映射的配置和高级用法,满足一些高级自定义的需求

下面我尽量使用通俗易懂的语言来表达,简单来划分 Karabiner 核心功能的话,Karabiner 可以分为 简单修改Simple modifications) 和 复杂修改Complex modifications),我更倾向于称之为 简单映射高级映射

Read more »

avformat_find_stream_info() 函数的作用

先来看一下 avformat_find_stream_info() 的头文件里的注释对该函数的介绍,本文我们基于 FFmpeg n4.2 版本的源码分析。

1
2
3
4
5
6
7
8
9
10
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also computes the real framerate in case of MPEG-2 repeat
* frame mode.
* The logical file position is not changed by this function;
* examined packets may be buffered for later processing.
* ...
*/
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

注释里说这个方法通过读取媒体文件中若干个 packet 来获取流信息,对于 MPEG 这种没有 header 的文件格式比较有用,也可以计算像 MPEG-2 这种支持 repeat mode 的真实帧率。(MPEG-2 支持对于大量静止的画面设置 repeat mode,重复的帧不用编码和存储,可以减少体积)

Read more »