当前位置: 首页 > news >正文

JT808,JT1078 —— AAC编码 —— 部标机语音对讲Java实现

一、基础知识
1、JT808,JT1078 协议了解
① JT/T 808 是中国交通运输行业关于车载终端与监管平台之间数据交换的通信协议,核心目标是实现车辆运行状态、报警事件、定位数据、远程控制等信息的标准化传输。

② JT/T 1078 是我国交通运输行业音视频监控领域的重要技术标准,已形成覆盖 终端接入、数据传输、安全认证 等全链路的技术体系。

2、二进制,八进制,十进制,十六进制的基础知识
JT/T 808,JT/T 1078 涉及到大量的 二进制编码, 十六进制编码,有必要对这两种的编码的理论知识,进制间的相互转换进行学习。

3、原码,反码,补码的基本操作

1

 

4、音视频编码与封装格式
常见的音频,视频编码格式 与 封装格式:在部标机通信设备中,H264视频编码,H265视频编码,AAC音频编码,G711A音频编码较为常见。

2

 

二、整体架构
1、实物架构

3

 


① 拾音器(Pick-up Cartridge)又称麦克风,话筒 是通过声电转换采集声音信号的电声学器件。

② 部标机:全称卫星定位汽车行驶记录仪,又称GPS/北斗汽车行驶记录仪,是集成行驶记录与车载终端的数字式电子装置,具备GPS实时定位,视频监控,语音对讲的功能。

2、数据流架构

4

 

① 监控室的电脑外接拾音器,收集声音信号。

② 网页通过 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:音质无损,文件巨大,主要用于需要高质量音频且不需要考虑带宽的场景。

5

 

4、Web端的音频编码与解码

音频的编码,解码引用了如下两个音频处理库。

npm install wav-decoder
npm install wav-encoder

效果展示:

6

 

四、服务端
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进制

7

 


2、1078协议,下发到设备端

8

参考实例:

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;}// 获取当前帧的Sizeaac_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 协议要有比较深的了解,特别是在不同版本上的差异。

http://www.jsqmd.com/news/34556/

相关文章:

  • DP 总结
  • 2025年11月7日
  • 【开题答辩全过程】以 爱运动健身小程序的设计与实现为例,包含答辩的障碍和答案
  • 高并发下如何保证 Caffeine + Redis 多级缓存的一致性问题?MySQL、Redis 缓存一致性问题? - 指南
  • 2025-11-07 PQ v.Next日志记录
  • [python刷题记录]-轮转数组-普通数组-中等
  • QT正在复兴?兰亭妙微带你看懂工业软件设计的新风口
  • 低代码如何真正降低企业数字化转型成本?
  • 低代码开发的核心流程
  • 字符串杂题
  • 低代码 vs 无代码:90% 的企业都分不清的核心差异
  • 轻言轻语
  • NIFI 使用HTTP 作为数据源接收数据
  • CSPS 2025 游寄 / 反思
  • FCN-ResNet18 语义分割完整实现详解
  • 《代码大全 2》观后感(六):错误处理 —— 代码的 “安全气囊”
  • 在龟骨的第二次课的讲解
  • P5610 解题报告
  • fcitx5里有趣的东西
  • 自定义MCP Server
  • 英语_错题集_25-11
  • Ai元人文随想:守护时光的印记
  • 浅谈模拟系列算法
  • 第三十六篇
  • Ai元人文随想:三值纠缠中的人文关怀
  • R语言实现多组样本两两t检验的完整教程
  • 实用指南:TensorFlow深度学习实战(40)——图神经网络(GNN)
  • 2023QDEZ男人八题赛后总结
  • 学习差的孩子,有必要用学习机吗?
  • CSP-S2023游记