暂时未有相关云产品技术能力~
Android、音视频、AI相关领域从业者
智能语音对话系统只是智能对话系统的一个延伸,对了语音输入和语音输出的能力,但核心还是“对话系统”,到底是“智能”还是“智障”还要看这个对话系统提供多少能力,拥有多少技能。
在实时视频编辑领域,头发变色、修改发型是很流行和受欢迎的场景。这种功能除了音视频相关的技术,还离不开AI能力的支持。而且这种场景本身对实时性要求高,很适合在端侧应用落地。上一篇文章我们基于谷歌的MediaPipe项目实现了本地实时人脸检测功能,本文我们再来一步一步跑通端侧实时染色功能。
AI在视觉领域最常用的就是人脸检测、人脸识别、活体检测、人体与行为分析、图像识别、图像增强等,而且目前都是比较成熟的技术,不论商业化的Paas平台还是开源的模型,都几乎一抓一大把。
体验过市面上这类产品的估计都有中”智障“的感觉,除了特殊的几类问题,几乎什么都不会,而且对Query的泛化也不是很好,同一个问题换个问法就不会了,给人的感觉就是”傻“。
在Python中函数有默认参数等,在C++11中我们发现C++也支持了默认参数;还有C++特有的内联函数、constexpr函数等知识都有不少细节,本文对这些知识做详细介绍。
函数返回值就是使用return语句终止正在执行的函数,看是很简单的问题有什么说的呢?因为越是简单的问题里面越是有一些不易发现的坑。
数组作为函数形参传递的是数组首元素的地址本来是很简单的知识点,但是在具体使用中还会有一些坑需要注意。
函数可以理解为功能的封装,很基础的功能单元,但是因为它虽然看似简单,但是里面涉及了不少知识点和技巧,我们花一篇文章来整理。
C++中模板本身不是类,是编译器生成类或者函数的说明,要使用模板需要编译器根据模板来创建类的实例化过程,我们代码中使用的是类,所以在使用模板是,要指定编译器将模板实例化成那种类型。
在C语言中我们操作String要相对麻烦些,每次字符串拼接都要重新开辟空间,再把数据拷贝进去,使用上没有那么便捷。
select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开 的文件描述符个数并不能改变select监听文件个数
目前移动机器人已得到了大范围应用,无论是在大型商场还是银行都可以看到移动机器人身影。移动机器人主要是移动加决策,移动方式主要以轮式和足式,在商场见到的主要以轮式拟人的形态出现,足式的主要以动物形象为主,前段时间还看到了上海一个小区,机器人上绑着喇叭在小区跑,提示人们注意做好个人防范等。
最近开发的机器人操作系统ROS基于Android,在里面做一些深度定制,其中运动控制与Server的交互需要双向通道,经过权衡和讨论我们最终选用MQTT作为长连接通信方案。
在Java中我们定义常量通常用final static TYPE variableName = xxx来实现,在C语言中我们通常用预编译宏来实现:#define MAX 100,在C++中虽然我们仍可以使用预编译宏,但是已经不推荐这么干了。
服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于 监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服 务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK 段,服务器收到后从accept()返回。
Java和C++在语法层面比较的时候就不得不提到C++的多继承,我们知道Android是单继承,C++是多继承。在大型项目中不可避免的会用到多继承,本文分析C++多继承的一些特征。
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描 述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调 用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表 示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。
整个结构还是比较简单的,从类内部到本包到子类到外部包权限越来越小,比较好理解也比较好记忆。但是在C++中访问控制要复杂很多,因为不仅有属性和方法的访问控制,还有继承时的派生列表访问说明符。今天我们着重了解访问控制。
OP面向对象程序设计的核心是数据抽象,继承和动态绑定。前面的文章我们介绍了使用virtual的虚类实现动态绑定的多态,有时候我们在做抽象时,对于抽象的实体不想让被人实例化,虚类没有这个功能,我们Java里面我们有抽象类,有接口来抽象一个实体的行为,而不允许被实例化,C++有没有这样的功能呢?C++怎么实现抽象类呢?
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运 行,可以调用以下库函数做网络字节序和主机字节序的转换。
如果times是非空指针,则存取时间和修改时间被设置为 times所指向的结构中的值。此 时,进程的有效用户ID必须等于该文件的所有者 ID,或者进程必须是一个超级用户进程。对 文件只具有写许可权是不够的
AVS Device SDK是主要应用在音响的控制台程序,而且代码是跨平台的,所以一是有很多为了跨平台做的冗余,二是有很多我们根本用不到的模块。比如为了做本地存储引入了一个Sqlite的动态库,我们本身也用不到本地存储,像闹钟设置之类的放到APP层即可,而且就算是需要存储也完全可以使用Android和iOS平台提供的Sqlite。删除用不到的模块是包体积优化空间最大最快的。
最近在做移动机器人项目,要给机器人做一个头台,搭载Android操作系统,要为系统做一些定制。在 好多年前,还是Android 5.0以下系统的时候做游戏画面采集做个AOSP的编译,那个时候电脑配置查,网络也差,搭建一次环境特别费劲。编译完还没有现成的设备可以给刷,一直也没有真正的framework和内核、驱动层的开发经验,现在有了实际的需求了,硬着头皮开搞。
JNI 定义了两个关键数据结构,即“JavaVM”和“JNIEnv”。两者本质上都是指向函数表的二级指针。(在 C++ 版本中,它们是一些类,这些类具有指向函数表的指针,并具有通过该函数表间接调用的 JNI 函数的成员函数。)JavaVM 提供“调用接口”函数,我们可以利用这些函数创建和销毁 JavaVM。理论上,每个进程可以有多个 JavaVM,但 Android 只允许有一个。
正常情况我们在方法里面申请的local reference会在方法执行完后自动释放,所以一直没有太在意local reference的释放,结果在一个线程的looper方法中有两个jstring使用完一直没释放,导致交互多轮后泄露崩溃。
基于Alexa的全链路智能语音SDK基于C++实现了跨平台特性,跑通了Android、Mac、Linux等设备,在兼容iOS时发现iOS未提供音频采集和播放的C++接口,所以需要改造SDK,允许SDK初始化时注入外部的采集器和播放器实现类,同时SDK中的Android播放器是基于ffmpeg解码 + opensl实现,但是考虑到包体积的问题,准备也基于这个接口在外部实现基于Android硬件解码的播放器。
libcurl是一个免费和易于使用的客户端URL传输库,支持DICT, FILE, FTP, FTPS, GOPHER, gopers, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET和TFTP。libcurl支持SSL证书,HTTP POST, HTTP PUT, FTP上传,HTTP表单上传,代理,HTTP/2, HTTP/3, cookie,用户+密码认证(基本,摘要,NTLM,协商,Kerberos)
之前建设的跨平台全链路智能语音交互系统一直只跑在Android系统(主要是Iot设备),没有iOS业务场景,最近经过不断的努力,终于要把这个能力推广到B端APP,面向B端用户,这个时候就有了iOS的述求,毕竟从能力建设之初就吹出去了,系统是跨平台的,这个时候不能掉链子。
例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱, 应用strtok_r版本,r表示可重入。
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非 空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针, 则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前 的信号屏蔽字为mask,下表说明了how参数的可选值。
kill函数或kill命令 不过,kill向调用者返回测试结果时,原来存在的被测试进程可能刚终 止
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存 地址,对文件的读写可以直接用指针来做而不需要read/write函数。
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不 到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用 户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程 间通信(IPC,InterProcess Communication)。
如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时 的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵 尸进程都立刻被父进程清理了,为了观察到僵尸进程
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支), 子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的 用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建 新进程,所以调用exec前后该进程的id并未改变。
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信 息,Linux内核的进程控制块是task_struct结构体。现在我们全面了解一下其中都有哪 些信息。
dup和dup2都可用来复制一个现存的文件描述符,使两个文件描述符指向同一个file结 构体。如果两个文件描述符指向同一个file结构体,File Status Flag和读写位置只保存一份在file结构体中,并且file结构体的引用计数是2。如果两次open同一文件得到两个文件 描述符,则每个描述符对应一个不同的file结构体,可以有不同的File Status Flag和读写 位置。请注意区分这两种情况。
我们知道,一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(例如某种 mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写 一些管理存储布局的信息。上图是一个磁盘分区格式化成ext2文件系统后的存储布局。
注意这个读写位置和使用C标准I/O库时的读写位置有可能不同,这个读写 位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置。比如用fgetc读一个字节,fgetc有可能从内核中预读1024个字节到I/O缓冲区中,再返回第一 个字节,这时该文件在内核中记录的读写位置是1024,而在FILE结构体中记录的读写位置是 1。
事实上Unbuffered I/O这个名词是有些误导的,虽然write系统调用位于C标准库I/O缓 冲区的底层,但在write的底层也可以分配一个内核I/O缓冲区,所以write也不一定是直接 写到文件的,也可能写到内核I/O缓冲区中,至于究竟写到了文件中还是内核缓冲区中对于 进程来说是没有差别的,如果进程A和进程B打开同一文件,进程A写到内核I/O缓冲区中的数 据从进程B也能读到,而C标准库的I/O缓冲区则不具有这一特性(想一想为什么)
assets目录是Android的一种特殊目录,用于放置APP所需的固定文件,且该文件被打包到APK中时,不会被编码到二进制文件。 Android还存在一种放置在res下的raw目录,该目录与assets目录不同。
在 Android 通过 JNI 去调用 Bitmap,通过 CMake 去编 so 动态链接库的话,需要添加 jnigraphics 图像库。
第四个参数为线程启动程序的参数,也就是函数的参数,如果不需要传递参数,它可以为 NULL 。 pthread_create 函数如果执行成功了则返回 0 ,如果返回其他错误代码。
Native 提供了 ExceptionOccurred 和 ExceptionCheck 方法来检测是否有异常发生,前者返回的是 jthrowable 类型,后者返回的是 jboolean 类型。
在 Native 代码中有时候会接收 Java 传入的引用类型参数,有时候也会通过 NewObject 方法来创建一个 Java 的引用类型变量。在编写 Native 代码时,要注意这个代表 Java 数据结构类型的引用在使用时会被 GC 回收的可能性。
通常我们通过 FindClass 、GetFieldID、GetMethodID 去找到对应的信息也是耗时操作,如果方法被频繁调用(特别是像音视频处理时循环的调用JNI方法传递音视频数据),每次都去查找对应的类和方法ID会很耗性能,所以我们必须将它们缓存起来,达到只创建一次,后面直接使用缓存内容的效果。
JNI不仅仅是Android特有的,它是属于Java平台的,它允许在Java虚拟机内运行的java代码与其他编程语言(如c, c++和汇编语言)编写的程序和库进行交互。Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是并不是说不能使用其他编程语言,只要调用约定受支持就可以了。不同语言编写的程序之间调用也不是Java和C++特有的,Java可以调用C++,python、go语言都可以。
Android JNI开发时经常遇到C/C++层访问Java层对象的,比如C/C++层创建一个String返回,或者访问Java层提供的MediaCodec等,此时我们就需要通过 JNI 来调用 Java 一个类的构造方法来创建这个 Java 类。
有了之前那些基础,就可以实现 Java 和 Native 的相互调用了,在 Native 中去访问 Java 类的字段并调用相应的方法。
在 Android JNI 基础知识篇提到了 Java 数组类型对应的 JNI 数组类型。比如,Java int 数组对应了 jintArray,boolean 数组对应了 jbooleanArray。