一、基础知识
1、JT808,JT1078 协议了解
① JT/T 808 是中国交通运输行业关于车载终端与监管平台之间数据交换的通信协议,核心目标是实现车辆运行状态、报警事件、定位数据、远程控制等信息的标准化传输。
② JT/T 1078 是我国交通运输行业音视频监控领域的重要技术标准,已形成覆盖 终端接入、数据传输、安全认证 等全链路的技术体系。
2、二进制,八进制,十进制,十六进制的基础知识
JT/T 808,JT/T 1078 涉及到大量的 二进制编码, 十六进制编码,有必要对这两种的编码的理论知识,进制间的相互转换进行学习。
3、原码,反码,补码的基本操作
4、音视频编码与封装格式
常见的音频,视频编码格式 与 封装格式:在部标机通信设备中,H264视频编码,H265视频编码,AAC音频编码,G711A音频编码较为常见。
二、整体架构
1、实物架构
① 拾音器(Pick-up Cartridge)又称麦克风,话筒 是通过声电转换采集声音信号的电声学器件。
② 部标机:全称卫星定位汽车行驶记录仪,又称GPS/北斗汽车行驶记录仪,是集成行驶记录与车载终端的数字式电子装置,具备GPS实时定位,视频监控,语音对讲的功能。
2、数据流架构
① 监控室的电脑外接拾音器,收集声音信号。
② 网页通过 navigator.mediaDevices.getUserMedia 对象,接收到硬件设备的声电信号,一般返回一个 Float32Array 数组对象。
③ 把 Float32Array 数组对象 转换为 WAV 数据格式传输至云服务器,或者把 Float32Array 数组对象 转换为 PCM 数据格式传输至云端服务器。
④ 云端服务器对 PCM 音频数据进行编码(一般有AAC,G711A 等20多种音频数据格式),然后下发至设备端。
三、Web端技术分析
1、Web端的权限设置
浏览器要收集话筒数据- chrome://settings/content/camera
- chrome://settings/content/microphone
- chrome://flags/#unsafely-treat-insecure-origin-as-secure
复制代码 2、Web端的调试设置
debugger 模式下,Chrome不进入调试的解决方法:按下F12 -> Setting图标 -> Ignore list菜单 -> Custom exclusion rules 区域 把 node_modules/node 勾选去掉
3、Web端录音组件分析
对 recoder, js-audio-recorder,js-recorder-rtc,wavesurfer 等录音组件库进行了相关试用,最后还是选择了自己行实现。
audio/webm; codecs=opus:强烈推荐。OPUS 格式音质好、压缩率高、延迟低,是 WebRTC 和现代浏览器的标准。后端需要支持解码 OPUS(例如使用 ffmpeg)。
audio/mp3:兼容性好,文件小,但编码延迟高,不适合实时流。
audio/wav:音质无损,文件巨大,主要用于需要高质量音频且不需要考虑带宽的场景。
4、Web端的音频编码与解码
音频的编码,解码引用了如下两个音频处理库。- npm install wav-decoder
- npm install wav-encoder
复制代码 效果展示:
四、服务端
1、服务端对 JT/T 808,JT/T 1078 的协议进行分析
① readByte() 读取起始标识1个字节 —— 8位 —— 2位16进制
② readUnsignedShort() 读取 2个字节 —— 16位 —— 4位16进制
③ readUnsignedShort() 读取2个字节 —— 16位 —— 4位16进制 0~9位 消息体长度 第10 位是否加密 第13 位是否分包 第14 位 是否有版本标识
④ readByte() 读取版本协议号 1个字节 —— 8位 —— 2位16进制
⑤ readStringBCD() 读取手机号 10个字节 —— 80位 —— 20位16进制
⑥ readUnsignedShort() 读取流水号 2个字节 —— 16位 —— 4位16进制
⑦ readByteBuf() 根据消息长度读取消息内容
⑧ readByte() 读取较验码 1个字节 —— 8位 —— 2 位16进制
⑨ readByte() 读取结束标识1个字节 —— 8位 —— 2位16进制
2、1078协议,下发到设备端
参考实例:- 30 31 63 64 81 E2 10 88 01 12 34 56 78 10 01 10 00 00 01 6B B3 92 CA 7C
- 02 80 00 28 00 2E 00 00 00 01 61 E1 A2 BF 00 98 CF C0 EE 1E 17 28 34 07
- 78 8E 39 A4 03 FD DB D1 D5 46 BF B0 63 01 3F 59 AC 34 C9 7A 02 1A B9 6A
- 28 A4 2C 08
复制代码 根据 JT1078 解码后的数据为:- {
- "[30316364]头部": 808543076,
- "[10000001]object1[81]": {
- "(10)V[固定为2]": 2,
- "(0)P[固定为0]": 0,
- "(0)X[RTP头是否需要扩展位固定为0]": 0,
- "(0001)CC[固定为1]": 1
- },
- "[11100010]object2[E2]": {
- "(1110)M[确定是否是完整数据帧的边界]": 1,
- "(0010)PT[负载类型]": "H264"
- },
- "1088[序列号]": 4232,
- "[终端设备SIM卡号]": "011234567810",
- "01[逻辑通道号]": 1,
- "[00010000]object3[10]": {
- "(0001)[数据类型]": "视频P帧",
- "(0000)[分包处理标记]": "原子包_不可被拆分"
- },
- "0000016BB392CA7C[标识此RTP数据包当前帧的相对时间,单位毫秒(ms)]": 1562085870204,
- "0280[该帧与上一个关键帧之间的时间间隔,单位毫秒(ms)]": 640,
- "0028[该帧与上一个帧之间的时间间隔,单位毫秒(ms)]": 40,
- "002E[数据体长度]": 46,
- "[数据体]": "00 00 00 01 61 E1 A2 BF 00 98 CF C0 EE ......"
- }
复制代码
五、设备端
1、设备端 AAC 音频的解码 libfdk-aac.so 组件实现 AAC 的解码
- #include <stdio.h>
- #include <stdint.h>
- #include <fdk-aac/aacdecoder_lib.h>
- #define INPUT_BUF_SIZE 2048
- #define MAX_CHANNEL_NUM 6 // 最大通道数
- #define MAX_SAMPLE_RATE 48000 // 最大采样率
- int main(int argc, char *argv[])
- {
- uint8_t *input_buf = NULL;
- int buf_size = 0;
- uint8_t *input_ptr = NULL;
- HANDLE_AACDECODER h_aac_decoder = NULL;
- CStreamInfo stream_info;
- int aac_frame_size = 0;
- int ret = 0;
- // 读取AAC音频数据并存入input_buf
- // ...
- // 初始化AAC解码器
- h_aac_decoder = aacDecoder_Open(TT_MP4_ADTS, 1);
- if (!h_aac_decoder) {
- printf("Failed to open AAC decoder\n");
- return -1;
- }
- // 读取AAC音频数据并解码直到输入完整一帧
- input_buf = (uint8_t*)malloc(INPUT_BUF_SIZE);
- input_ptr = input_buf;
- buf_size = INPUT_BUF_SIZE;
- while (buf_size > 0 && aac_frame_size == 0) {
- // 将一部分数据拷贝至input_buf中
- // ...
- buf_size -= copy_size;
- // 向AAC解码器输入数据
- const unsigned char *in_data_ptrs[1] = { input_buf };
- int in_data_sizes[1] = { copy_size };
- ret = aacDecoder_Fill(h_aac_decoder, in_data_ptrs, in_data_sizes, buf_size);
- if (ret != AAC_DEC_OK) {
- printf("aacDecoder_Fill error: %d\n", ret);
- break;
- }
- // 解码获取stream_info和完整帧的大小
- ret = aacDecoder_DecodeFrame(h_aac_decoder, NULL, 0, 0);
- if (ret != AAC_DEC_OK && ret != AAC_DEC_NOT_ENOUGH_BITS) {
- printf("aacDecoder_DecodeFrame error: %d\n", ret);
- break;
- }
- // 获取当前帧的Size
- aac_frame_size = aacDecoder_AvailableSamples(h_aac_decoder);
- if (aac_frame_size > 0) {
- // 获取流信息
- aacDecoder_GetStreamInfo(h_aac_decoder, &stream_info);
- }
- // 移动输入指针到还未解码的数据位置
- input_ptr += copy_size - buf_size;
- memcpy(input_buf, input_ptr, buf_size);
- input_ptr = input_buf;
- }
- if (ret != AAC_DEC_OK) {
- printf("Decode error.\n");
- return -1;
- }
- // 解码并输出音频数据
- int16_t pcm_buf[MAX_CHANNEL_NUM * MAX_SAMPLE_RATE];
- memset(pcm_buf, 0, sizeof(pcm_buf));
- ret = aacDecoder_DecodeFrame(h_aac_decoder, (void*)pcm_buf, MAX_SAMPLE_RATE, 0);
- if (ret != AAC_DEC_OK) {
- printf("aacDecoder_DecodeFrame error: %d\n", ret);
- return -1;
- }
- // 输出解码后的音频数据至文件或音频设备
- // ...
- aacDecoder_Close(h_aac_decoder);
- free(input_buf);
- return 0;
- }
复制代码 六、总结
① 关于开发网页与部标机的AAC音频对讲,核心就在 AAC 的编码与解码上。 可选的AAC编码解码库也比较多,jaad,ffmpeg,faad2 音频库都能实现音频的编码解码。
② 需要对 JT808,JT1078 协议要有比较深的了解,特别是在不同版本上的差异。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |