Carlife培训

  20 Feb 2019


 

Carlife培训文档

(一)简介

  1. 百度carlife是将手机carlife屏幕,通过usb线或者wifi连接建立的socket通道传输到车机端进行H264视频解码并展示,同时同步将手机carlife的声音封装成pcm音频流传输到车机进行pcm解码并播放;目前官方carlife只支持carlife屏内投屏(已有破解版本支持手机全屏投屏,但适配不友好);

  2. 百度carlife数据传输使用protobuf传输, 相对xml,json而言,传输效率更高,数据更轻量级,集成更简单;

(二)protobuf入门

(1) protobuf简介

1.Protocol Buffers (ProtocolBuffer/ protobuf )是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。现阶段支持C++、JAVA、Python、go、c#,ruby等等语音,Carlife使用的protobuf版本为2.2(规定,升级使用会出异常)。 2.protobuf相比Xml的优点

  • 更简单
  • 数据描述文件只需原来的1/10至1/3
  • 解析速度是原来的20倍至100倍
  • 减少了二义性
  • 生成了更容易在编程中使用的数据访问类

(2) protobuf简单使用

1.生成.proto文件

syntax = "proto3";

message gps_data {
    int64 id = 1;
    string terminalId = 2;
    string dataTime = 3;
    double lon = 4;
    double lat = 5;
    float speed = 6;
    int32 altitude = 7;
    int32 locType = 8;
    int32 gpsStatus = 9;
    float direction = 10;
    int32 satellite = 11;
}

注:顶部必须申明句法的版本号,如果不申明,则默认是2.0的语法。 Message字段为业务需要的字段,等号后面为序号,必须指定,不同于java

2..proto文件生成相对应语言的文件 ` protoc–java_out=. *.proto` protobuf语音有相对应的编译器,会根据需要将.proto文件编译成相对应的语言文件 (1)对Java来说,编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。 (2)javaNano来说,编译器输出类似域java但是没有Builder类.

3.导入相关的文件即可使用 protobuf使用介绍

(三)carlife连接流程

(1)Android连接使用AOA(Android Open Accessary)连接

1.Android AOA协议摘要

AOA协议是Google公司推出的用于实现Android设备与外围设备之间USB通信的协议。该协议拓展了Android设备USB接口的功能,为基于Android系统的智能设备应用于数据采集和设备控制领域提供了条件。

2.Android USB通信模式

Android系统支持多种USB外围设备。根据Android设备在USB通信中充当的角色,可以将Android USB通信分为主机模式(Host Mode)和配件模式(Accessory Mode)两种模式。

2.1主机模式

主机模式是指Android设备充当USB主机并为总线供电。此模式下,Android设备需支持USB主机功能或OTG功能,此时Android设备的USB主机称为USB嵌入式主机EH(Embedded Host。与PC上的USB主机相比,EH设备可能无法为连接到其总线上的未识别外围设备加载驱动程序,因此它们对其目标外围设备列表TPL(Target Peripheral List)进行了定义。这些外围USB设备大部分为HID设备(Human Interface Device)、BOMS设备(Bulk Only Mass Storage,如U盘)和CDC设备(Communication Device Class,USB通信设备类,如打印机),其驱动程序已存在于Android平台的系统中(Linux Kernel),因此Android设备可以与其直接通信。

2.2 配件模式

配件模式是指Android设备充当USB从机,外部设备充当主机并为总线供电。此模式下,外部USB设备称为Android配件。该模式为不具备主机功能的Android设备提供与USB设备交互的能力。Android设备和Android配件都必须支持AOA协议。不支持AOA协议的设备可以通过Android配件开发板(ADK板)Google 官方提供了 adk2012 开发套件(包含 Android 端和 Accessory 端的源码,Accessory 的参考设计为 Arduino 平台)与Android设备连接,成为Android设备的间接配件。

其中配件协议分1.0以及2.0版本,主机模式和配件模式在Android 3.1(API level 12)及更高的平台中直接支持。配件模式作为一个附加库也被Android 2.3.4(API level 10)支持。设备厂商可以选择是否在设备的Linux系统镜像中包含附加库。对主机和配件模式的支持最终取决于设备的硬件,而不是Android平台的等级(软件)。

2.3 配件与Android设备握手的过程
  • 等待设备连接并对其进行检测。 × 检测是根据usbManager获取usbDevice,再获取uid,pid进行判断是否处于accessary模式,vid,pid满足如下条件:

  • 确定设备是否支持配件模式。 × 配件发送查询指令给 Android 设备。绝大多数 Android 设备,在缺省情况下都不挂载Accessory 驱动,即不会默认处于 Accessory Mode,在配件与 Android 设备建立 USB 连接时,配件会通过握手协议查询该设备是否为 Android 设备且是否支持 AOA 协议以及支持的版本号。配件向 endpoint 0 端口中写入 51 号指令,如果返回值为 1 或者 2,则说明Android 设备支持 AOA1.0 或者 AOA2.0,如果小于 1 或者大于 2,则说明连接设备不支持AOA 协议或者支持的 AOA 协议版本号不正确

  • 尝试以配件模式启动设备(如需要)。 × 配件向 Android 设备写入 52 号指令,并附带如下一些信息

配件也会向 Android 设备发出 53 号指令,请求 Android 设备切换到 AOA 模式,Android 设备会执行请求,将 USB 切换到 AOA 模式;在这个过程中,USB 连接会 出现一次逻辑插拔,配件会重新枚举设备,进入第(2)步。Android 设备接收到 配件通过 52 指令发送过来的参数信息以后,使用其中的三个参数Manufacturer、ModelName、Version 来确定绑定到该配件的 App。如果系统内无任何 App 可以匹配配件设备发来的上述三个参数,则 Android 设备会弹出一个对话框,向用户提供 Accessory 设备发送过来的描述信息和 URL 信息,用户可以点击 URL 访问它指向的 Web 页面。

  • 如果设备支持 AOA,则与该设备建立通信。 × 如果系统内有 App 可以匹配 Accessory 设备发来的握手信息,则 Android 系统会弹出一个对话框询问用户是否立刻启动该 App。如果用户选择OK则启动App;同时该对话框提供一个勾选框,勾选之后每次 Accessory 设备连接后会自动启动该 App。应该要求用户勾选该对话框,否则 App 启动后USBManager获Accessory 设备后可能因为 Permission 问题无法打开文件描述符建立通讯连接。 aoa配对流程如下:

2.4 AOA消息结构
  • 由于 AOA 实际上在传输的过程中只有一个通道,因此需要在原消息结构的基础上增加一个头用来标识该条消息是属于哪一个通道。具体结构如下图所示。

  • 当使用 AOA 将移动设备和车机建立连接以后,建议使用如下方式来和移动设备端建立数据通道:在车机端将各模块和底层数据收发模块之间建立六个socket 用于通信。。当使用 AOA 或者 libusb 等输入接口从 usb 端口中读入数据以后,进行拆包(去除 AOA 头,8 个字节),得到 CarLife Msg 结构(详见第 13 章节),根据 Channel ID 分发到对应通道的 socket 中,其他模块(比如Cmd、 Video、 Media 等)可以从 socket 中读入数据并对 CarLife Msg 进行解析,获取具体的数据。相反,当其他模块需要向移动设备端传输数据时,往对应的socket 中写入数据,AOA 连接模块通过 socket 接收数据以后,加上 AOA 头(Channel ID 和 Msg Len 共计 8 个字节)调用输出接口写入到 usb 端口中发送给移动设备端。这种方式利用 socket 实现了消息缓存机制,并且将底层连接模块和 CarLife 上层模块之间充分解耦。

(2)Iphone使用EAP(Extensible Authentication Protocol)连接

EAP连接主要是iap2等底层完成,上层应用只是调用几个api以及连接对应的socket

(3)WIFI连接

WIFI连接是基于UDP协议的不可靠数据传输发现可用IP地址,根据返回的IP地址对应port进行socket连接

WiFi 连接主要用的是 WiFi direct,场景可分为三类,其原理一致:

  1. 第三方路由热点,车机与手机均作为 ap 连接;
  2. 车机作为热点,手机作为 ap 连接(BM2718 项目车机不支持作为热点);
  3. 手机作为热点,车机作为 ap 连接。 Wifi_Direct 的大致配对流程如下:
    1. 通过 WifiP2pManager 提供的接口初始化环境;
    2. WifiP2pManager.discoverPeers()开始扫描设备;
    3. 获取扫描到的设备,选择其中一个设备进行连接 (使用方法 WifiP2pManager.connect);
    4. 连接成功后,根据 WifiP2pInfo.isGroupOwner 和 WifiP2pInfo.groupOwnerAddress 进行连接

*总结 : *

  1. 三种不同的连接方式共用一个socket client端口,AOA的server端位于carlife内部,Eap的server端底层库中,WIFI连接的server端位于手机端的carlife中
  2. Iphone还支持USBMuxD连接方案,主要也是将USB通信虚拟化为TCP通信。

#(四)数据解析

(一)数据拆分

1.1 AOA数据拆分

AOA数据接收到之后需要进行拆包,解析出AOA数据头,用channelID判断头数据里面属于哪一个通道,并将数据分发到socket当中。

车机端在发送 AOA 数据时需要注意将8个字节的AOA Head 和 CarLife Msg单独分开发送。

1.2 EAP数据拆分

EAP的数据拆分在底层做处理,libicalifeserver收到usb两个端点的数据之后,进行数据头的解析,根据不同的数据头,将数据write进对应的socket当中;上层socket client端较为简单,只需要再socke中write和read数据即可。

(二)数据交互

2.1 连接过程的cmd交互

注:务必严格按照文档执行顺序执行,查看文档P73

######2.1.1 MSG_CMD_HU_PROTOCOL_VERSION 第一条socket必须为此。

2.2 TTS播报交互过程

tts命令,tts_init,tts_normalData,tts_stop
2.2.1 tts_init

手机端开始导航时会发送tts_init给车机(包含参数sampleRate,channelConfig ,sampleFormat),车机收到之后根据此参数初始化audiotrack,申请音频焦点;openStream();

按照个人设计看是否需要reset tts_audiotrack为空,可不置空,下次tts_init时直接requestFocus
2.2.2 tts_normalData

tts_init 之后会不断发送tts_data,需要将data,write进tts_audioTrack当中

2.2.3 tts_stop

tts_属于短焦点,tts_stop 后需要closeStream();按需设计是否需要reset tts_trackaudio

2.3 VR播报交互过程

VR命令,vr_init,vr_normalData,vr_stop
2.3.1 vr_init

手机端开始导航时会发送tts_init给车机(包含参数sampleRate,channelConfig ,sampleFormat),车机收到之后根据此参数初始化audiotrack,申请音频焦点;openStream();

按照个人设计看是否需要reset vr_audiotrack为空,可不置空,下次vr_init时直接requestFocus
2.3.2 vr_normalData

vr_init 之后会不断发送vr_data,需要将data,write进vr_audioTrack当中

2.3.3 vr_stop

vr_属于短焦点,vr_stop 后需要closeStream();按需设计是否需要reset vr_trackaudio

2.3.4 录音控制时序

2.3.4.1 “小度小度唤醒”时序 1.语音唤醒启动时,手机端会先往车机端发送 MSG_CMD_MIC_RECORD_WAKEUP_START 消息,通知车机端语音唤醒功能开始,启动录音线程;

  1. 车机端会持续不断地往手机端发送 MSG_VR_DATA,传递语音数据包,每个包大小是 1KB;
  2. 当手机端语音引擎识别到“小度小度”时,就会结束本次唤醒,并发送 MSG_CMD_MIC_RECORD_END 消息通知车机端结束录音,继而开始一次语音识别过程
  3. 语音识别蒙层开启时,手机端会先往车机端发送 MSG_CMD_MODULE_STATUS 消息,通知车机端 VR 启动,车机端需要申请音频焦点,同时手机端会初始化语音识别引擎;

2.3.4.2 语音识别时序

  1. 语音识别蒙层开启时,手机端会先往车机端发送 MSG_CMD_MODULE_STATUS 消息,通知车机端 VR 启动,车机端需要申请音频焦点,同时手机端会初始化语音识别引擎;
  2. 语音识别引擎启动就绪后,手机端会发送 MSG_CMD_MIC_RECORD_RECOG_START 消息,车机端收到该消息后开启 MIC 录音线程,然后车机端应持续不断地往手机端发送MSG_VR_DATA 消息,用于传递车机 MIC 录音数据包,每个包大小是 1KB;
  3. 当手机端语音识别引擎有识别结果返回时,手机端将传输MSG_CMD_MIC_RECORD_END消息,告知车机端此时应停止 MIC 录音线程;
  4. 在 语 音 识 别 蒙 层 已 打 开 的 情 况 下 , 重 新 开 启 新 一 轮 语 音 识 别 时 , 手 机 端 只 会 重 发MSG_CMD_MIC_RECORD_RECOG_START 消息,不会再次发送 MSG_CMD_MODULE_STATUS消息(参数VR_STATUS_RECORD_RUNNING)备注:MSG_CMD_MODULE_STATUS 消息(参数值 VR_STATUS_RECORD_RUNNING)只是在语音识别蒙层打开时发送;
  5. 当 语 音 识 别 蒙 层 关 闭 时 , 手 机 端 会 停 止 语 音 识 别 引 擎 , 同 时 发 送MSG_CMD_MODULE_STATUS(参数值 VR_STATUS_RECORD_IDLE)给车机端,收到该消息后车机端需要释放音频焦点。另外,手机端也会发送MSG_CMD_MIC_RECORD_END 消息,车机端按需终止录音线程,保证不会再给手机端传输 MSG_VR_DATA 消息。
2.3.5 录音源定制时序

语音识别和语音唤醒功能均可使用车机 MIC 和手机 MIC 完成,如无特殊说明,默认使 用车机 MIC 来完成。如果车机端 MIC 的录音质量不能满足语音引擎要求,而考虑使用手机自带 MIC 的话,实现方式有两种,建议使用第一种方式统一完成配置:

  1. 当车机端向手机端发送 MSG_CMD_HU_FEATURE_CONFIG_RESPONSE 消息时, VOICE_MIC参 数 值 配 置 为USE_MOBILE_MIC , 具 体 请 参 考MSG_CMD_HU_FEATURE_CONFIG_RESPONSE 消息的定义;

  2. 可 以 发 送 MSG_CMD_MODULE_CONTROL 消 息 , ModuleId 参 数 值 是 CARLIFE_MIC_MODULE_ID, MicState 参数值是 MIC_STATUS_USE_MOBILE_MIC, 发送的时机是车机端收到 MSG_CMD_PROTOCOL_VERSION_MATCH_STATUS 后。

2.4 touch 反控

根据百度文档,触摸屏中透传方式消息响应更直接,推荐使用透传;

NO : 透传对消息的及时性有要求,使用action down;action move; action up;若消息发送的线程有sleep等阻塞操作,会导致手机端对手势的识别不全或者是识别位置出现偏差

####### 2.4.1 坐标换算

####### 2.4.2 硬按键 可通过按键板上面的上下一曲以及其他功能按键对carlife进行操作,通过touch 通道传输;按键对应key值查看pdf P117;

2.5 carlife 音乐

2.5.1 Carlife音乐命令

命令交互包含music_init ,music_stop等等,只需要响应对应的 状态,对music进行播放暂停即可

2.6 视频交互

Carlife视频交互流程图

Carlife视频消息结构

注: 1. timestamp为视频时间戳,主要作用是用于将视频和音频同步以及判断视频是否过期; 2. service type包含两个,视频数据以及视频心跳包,在carlife V4.0之后支持手机后台截屏,故也发送视频数据,对于部分手机不支持后台截屏的会发送心跳包,心跳包和视频数据结合判断数据是否断开 3. 视频数据为原始数据,其他命令以及service type为pb序列化之后的数据

查看pdf P93

(五)场景策略

  1. 对于某些手机,当手机退到后台时,无法对手机屏幕进行录制,会发送MSG_CMD_BACKGROUND给车机端,并停止发送视频数据,只发送心跳包,需要给予用户全屏提示,让用户将手机carlife拉到前台。
  2. 若手机端接收到来电,不需要展示情感页面。

(五)延伸点

5.1 AudioTrack

5.1.1 AudioTrack简介以及使用

AudioTrack只能播放pcm流格式的视频以及wav(wav格式的文件属于pcm,不需要解码),MediaPlayer可以播放多种格式的视频(会在framework层创建对应格式的解码器),Mediaplayer调用时也会在framework层创建Audiotrack,最后将pcm数据流传给audioflinger进行混音后播放;

5.1.2 AudioTrack的简单使用

AudioTrack的构造函数

  1. streamType

    STREAM_VOCIE_CALL:电话声音

    STREAM_SYSTEM:系统声音

    STREAM_RING:铃声

    STREAM_MUSCI:音乐声

    STREAM_ALARM:警告声

    STREAM_NOTIFICATION:通知声

  2. sampleRateInHz 采样率 (范围 4000Hz~192000Hz )
  3. channelConfig 常用的是 CHANNEL_IN_MONO(单通道)CHANNEL_IN_STEREO(双通道)
  4. audioFormat 数据位宽 常用 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit)
  5. bufferSizeInBytes 使用int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)获得。配置的是 AudioTrack 内部的音频缓冲区的大小,该缓冲区的值不能低于一帧“音频帧”(Frame)的大小,而前一篇文章介绍过,一帧音频帧的大小计算如下:

    int size = 采样率 x 位宽 x 采样时间 x 通道数

    采样时间一般取 2.5ms~120ms 之间,由厂商或者具体的应用决定,我们其实可以推断,每一帧的采样时间取得越短,产生的延时就应该会越小,当然,碎片化的数据也就会越多。 不同的厂商有不同的实现,get即可。

  6. mode
    AudioTrack 提供了两种播放模式,一种是 static 方式,一种是 streaming 方式,前者需要一次性将所有的数据都写入播放缓冲区,简单高效,通常用于播放铃声、系统提醒的音频片段; 后者则是按照一定的时间间隔不间断地写入音频数据,理论上它可用于任何音频播放的场景。
mAudioTrack = new AudioTrack(streamType,sampleRateInHz,channelConfig,audioFormat,mMinBufferSize,DEFAULT_PLAY_MODE);
mAudioTrack.write(audioData,offsetInBytes,sizeInBytes) ;
mAudioTrack.play();

5.2 ArrayBlockingQueue使用

5.2.1 ArrayBlockingQueue简介

Concurrent是java新增的多线程使用安全的工具包,常用的ArrayBlockingQueue以及ConcurrentHashMap等被包括其中;主要是工具内部已经实现了锁机制,不需要再次实现。 ArrayBlockingQueue内部使用ReentrantLock(扩展公平锁)锁同步了管理的机制,即数据存取使用的同一把锁管理; ArrayBlockingQueue内部使用数组实现(参考arraylist以及linkedlist);LinkedBlockingQueue使用node链表实现,内部使用分理锁机制,即数据存,取使用不同的锁,并且LinkedBlockingQueue的删减对gc的要求较高,需要回收大量的对象 ArrayBlockingQueue内部锁机制ReentrantLock以及AQS AQS基础Unsafe与CAS