liusuyi
6 天以前 307977cfb9fb88f845e36e4041c082ffdd691da5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package cn.org.hentai.jtt1078.server.audio;
 
import cn.org.hentai.jtt1078.entity.AudioSendData;
import cn.org.hentai.jtt1078.entity.enums.ProtocolVersion;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
 
@Slf4j
public class Jt1078AudioEncoder extends MessageToByteEncoder<AudioSendData> {
 
    @Override
    protected void encode(ChannelHandlerContext ctx, AudioSendData audioData, ByteBuf out) throws Exception {
        try {
            // 1. 转换音频格式为PCM
            byte[] pcmData = audioData.getAudioBytes();
 
            // 2. 编码为G.711 A-law
            byte[] g711Data = encodePcmToG711A(pcmData);
 
            // 3. 构建JT1078协议包
            buildJt1078Packet(out, audioData, g711Data);
 
        } catch (Exception e) {
            log.error("音频编码失败", e);
            throw e;
        }
    }
 
 
    private byte[] encodePcmToG711A(byte[] pcmData) {
        byte[] alawData = new byte[pcmData.length / 2];
        ByteBuffer buffer = ByteBuffer.wrap(pcmData);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
 
        for (int i = 0; i < alawData.length; i++) {
            short pcmSample = buffer.getShort();
            alawData[i] = linearToALaw(pcmSample);
        }
        return alawData;
    }
 
    private void buildJt1078Packet(ByteBuf out, AudioSendData audioData, byte[] g711Data) {
        // 1. JT1078 Header
        out.writeBytes(new byte[]{0x30, 0x31, 0x63, 0x64});
 
        // 2. Payload Type (06 = Audio Stream)
        out.writeByte(0x81);
        out.writeByte(0x86);
        // 3. Sequence Number
        out.writeShort(audioData.getSequenceNumber());
 
        // 4. SIM (BCD, 10 bytes)
        int simLength = (audioData.getProtocolVersion() == ProtocolVersion.V2013) ? 6 : 10;
        byte[] simBcd = encodeSimToBcd(audioData.getSim(), simLength);
       // byte[] simBcd = encodeSimToBcd(audioData.getSim(), 6); // 可能需要更长
        out.writeBytes(simBcd);
 
        // 5. Channel
        out.writeByte(audioData.getChannel());
 
        // 6. Data Type (Audio) + Frame Info (Atomic)
        out.writeByte(0x30);
 
        // 7. Timestamp (8 bytes)
        out.writeLong(System.currentTimeMillis());
 
        // 8. Data Length (2 bytes)
        out.writeShort(g711Data.length);
 
        // 9. Audio Data (G.711 A-law)
        out.writeBytes(g711Data);
 
        // 打印十六进制字符串(推荐)
        //log.info("发送的 JT1078 报文(Hex): {}", ByteBufUtil.hexDump(out));
 
    }
 
 
    private byte[] encodeSimToBcd(String sim, int length) {
        // BCD编码每个字节存2位数字,因此需要2倍长度
        int digitCount = length * 2;
 
        // 补全或截断SIM卡号
        String normalizedSim = sim.length() > digitCount
                ? sim.substring(0, digitCount)
                : String.format("%-" + digitCount + "s", sim).replace(' ', '0');
 
        byte[] bcd = new byte[length];
        for (int i = 0; i < digitCount; i += 2) {
            int high = Character.digit(normalizedSim.charAt(i), 10);
            int low = Character.digit(normalizedSim.charAt(i + 1), 10);
            bcd[i / 2] = (byte) ((high << 4) | low);
        }
        return bcd;
    }
 
 
    private byte linearToALaw(short pcm) {
        // G.711 A-law编码算法实现
        int sign = (pcm & 0x8000) >> 8;
        if (sign != 0) {
            pcm = (short) -pcm;
        }
        if (pcm > 32767) pcm = 32767;
 
        int exponent = 7;
        int mask = 0x4000;
        while ((pcm & mask) == 0 && exponent > 0) {
            exponent--;
            mask >>= 1;
        }
 
        int mantissa = (pcm >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F;
        byte alaw = (byte) (sign | (exponent << 4) | mantissa);
        return (byte) (alaw ^ 0xD5);
    }
}