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

news/2025/11/7 19:25:43/文章来源:https://www.cnblogs.com/xcj26/p/19200687

一、基础知识
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.mzph.cn/news/959139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DP 总结

1.如果有类似与 \(f{i,j,k}=[0,1]\) 的而且要求 \(f{i,j,k}=1\) 时\(k:{max,min}\)情况,可以转成 \(f{i,j}=k\) ,如果有值就代表原来是 1。 2.如果区间 DP 有一个区间不好做,考虑对于另外一个区间进行 DP。可以自由转…

2025年11月7日

完成台式机上的编译环境准备

【开题答辩全过程】以 爱运动健身小程序的设计与实现为例,包含答辩的障碍和答案

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

高并发下如何保证 Caffeine + Redis 多级缓存的一致性问题?MySQL、Redis 缓存一致性问题? - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025-11-07 PQ v.Next日志记录

2025-11-07 PQ v.Next日志记录 项目核心信息目前初步预计在这里进行开发测试(后续到develop): 由于功能的替换,因此新的功能开发地址修改如下: https://z.gitee.cn/zgca/projects/777586/repos/zgca/aipq/tree/fe…

[python刷题记录]-轮转数组-普通数组-中等

[python刷题记录]-轮转数组-普通数组-中等链接:189. 轮转数组 - 力扣(LeetCode) 这里卡我的点是,不要return,要改nums的值。我搞了一个新数组res 但是用nums = res不行,用nums = res[:]也不行,用nums[:] = res才…

QT正在复兴?兰亭妙微带你看懂工业软件设计的新风口

QT正在复兴?兰亭妙微带你看懂工业软件设计的新风口当工业软件从 “能用就行” 转向 “好用易用”,界面设计与开发工具的价值被推向台前。2025 年 Qt 全球峰会中国站的火爆(吸引 300 + 研发精英参会)、能源行业 Qt …

英语_阅读_Predictions_待读

Science and technology will continue to develop. Nature and Business have made their predictions. Lets take a look. 科学和技术将继续发展,《自然》与《商业》已做出了预测。让我们来看一看。 Human-like fri…

低代码如何真正降低企业数字化转型成本?

提到数字化转型,很多企业的第一顾虑是 “成本太高”—— 传统开发模式下,人力成本、时间成本、维护成本层层叠加,让不少中小企业望而却步。而低代码的崛起,恰恰击中了 “降本” 这一核心痛点。但低代码的降本,绝非…

低代码开发的核心流程

很多企业认可低代码的价值,却卡在 “落地环节”—— 不知道该从何开始、流程如何设计、容易踩哪些坑。其实,低代码开发虽比传统开发简单,但并非 “随意拖拽就能成功”,它需要遵循科学的流程,同时规避关键误区,才…

字符串杂题

Trick对于子串问题,考虑转化成后缀的前缀或者前缀的后缀进行处理。(A task for substrings)题目 [OOI 2023] A task for substrings 询问 \(T\) 的子串是不好处理的,考虑变成前缀或者后缀问题。 对于一个询问 \([l…

低代码 vs 无代码:90% 的企业都分不清的核心差异

在数字化转型浪潮中,“低代码” 和 “无代码” 常常被混为一谈。两者都主打 “可视化开发”“快速交付”,让不少企业陷入选择困境:同样是拖拽组件做应用,到底该选低代码还是无代码? 事实上,低代码和无代码虽有相…

轻言轻语

不被喜欢的姑娘喜欢,是一件很伤心的事情,可天没有塌下来,该怎么活,还得怎么活。// run new Vue({el: #app,data: {timelineItems: [{content: 终于写完这个时间轴样式了 ,准备看下遮天,紫川休息一下 ,然后开始准…

NIFI 使用HTTP 作为数据源接收数据

NIFI 使用HTTP 作为数据源接收数据1.概述 在NIFI 中,可以 ListenHTTP 组件 启动一个HTTP服务,通过HTTP 服务接收 客户端 发送的信息,后续可以增加处理器,对请求进行处理。 我做了一个示例通过 ListenHTTP 接收信息…

CSPS 2025 游寄 / 反思

看着 T1 A / B 性质错误的排序以及仅剩的 1 分钟,我瘫坐在椅子上。纵使万般不甘,我知道这一切已经无法挽回,我不知道自己又有何脸面面对接下来的训练 我都干了什么?10min 时浏览完题面,开 T130min 时面对 \(O(n^3…

FCN-ResNet18 语义分割完整实现详解

好的!我来把这段代码整理成博客园风格的笔记,一段代码一段讲解: FCN-ResNet18 语义分割完整实现详解 1. 导入必要的库 import torch import torchvision from torch import nn from torch.nn import functional as …

《代码大全 2》观后感(六):错误处理 —— 代码的 “安全气囊”

过去写代码时,我总觉得 “错误处理” 是 “可有可无的附加项”—— 只要自己测试时没遇到报错,就不用写 try-catch,不用判断空值。但读了《代码大全 2》中 “错误处理” 的章节,才意识到错误处理是代码的 “安全气…

在龟骨的第二次课的讲解

本节课将围绕环境搭建→效率工具→目标管理三大模块,帮你从“新手”到“规范开发者”。效率加速器:快捷键实战 底层逻辑:快捷键的本质是减少鼠标操作 基础快捷键 Ctrl + C/V/X:复制/粘贴/剪切(举例:写文档时复制…

P5610 解题报告

P5610 解题报告 简要题意 一个长为 \(n\) 的非负整数序列 \(a\),支持以下两个操作:1 l r x:把区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\)。 2 l r:查询区间 \([l,r]\) 的和。本题强制在线。 数据范围: \(1\le…