Commit 4bfd97b73e4e3819a1884ff0218518b4a81f7597
1 parent
f82dd1bd
优化
Showing
37 changed files
with
4827 additions
and
5 deletions
Too many changes to show.
To preserve performance only 37 of 74 files are displayed.
pom.xml
| @@ -100,6 +100,19 @@ | @@ -100,6 +100,19 @@ | ||
| 100 | <artifactId>spring-boot-starter-data-redis</artifactId> | 100 | <artifactId>spring-boot-starter-data-redis</artifactId> |
| 101 | </dependency> | 101 | </dependency> |
| 102 | <dependency> | 102 | <dependency> |
| 103 | + <groupId>io.netty</groupId> | ||
| 104 | + <artifactId>netty-all</artifactId> | ||
| 105 | + <version>4.1.42.Final</version> | ||
| 106 | + </dependency> | ||
| 107 | + | ||
| 108 | + <dependency> | ||
| 109 | + <groupId>de.sciss</groupId> | ||
| 110 | + <artifactId>jump3r</artifactId> | ||
| 111 | + <version>1.0.5</version> | ||
| 112 | + </dependency> | ||
| 113 | + | ||
| 114 | + | ||
| 115 | + <dependency> | ||
| 103 | <groupId>org.springframework.boot</groupId> | 116 | <groupId>org.springframework.boot</groupId> |
| 104 | <artifactId>spring-boot-starter-web</artifactId> | 117 | <artifactId>spring-boot-starter-web</artifactId> |
| 105 | </dependency> | 118 | </dependency> |
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
| @@ -17,6 +17,7 @@ import javax.servlet.ServletException; | @@ -17,6 +17,7 @@ import javax.servlet.ServletException; | ||
| 17 | import javax.servlet.SessionCookieConfig; | 17 | import javax.servlet.SessionCookieConfig; |
| 18 | import javax.servlet.SessionTrackingMode; | 18 | import javax.servlet.SessionTrackingMode; |
| 19 | import java.util.Collections; | 19 | import java.util.Collections; |
| 20 | +import java.util.Objects; | ||
| 20 | 21 | ||
| 21 | /** | 22 | /** |
| 22 | * 启动类 | 23 | * 启动类 |
| @@ -62,6 +63,6 @@ public class VManageBootstrap extends SpringBootServletInitializer { | @@ -62,6 +63,6 @@ public class VManageBootstrap extends SpringBootServletInitializer { | ||
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | public static <T> T getBean(Class<T> tClass){ | 65 | public static <T> T getBean(Class<T> tClass){ |
| 65 | - return context.getBean(tClass); | 66 | + return Objects.isNull(context)?null:context.getBean(tClass); |
| 66 | } | 67 | } |
| 67 | } | 68 | } |
src/main/java/com/genersoft/iot/vmp/jt1078/app/CacheMapUtil.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.app; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | + | ||
| 5 | +import java.util.HashMap; | ||
| 6 | +import java.util.Map; | ||
| 7 | + | ||
| 8 | +public class CacheMapUtil { | ||
| 9 | + public Map<Integer, VideoServerApp.VideoServer> videoServerMap = new HashMap(); | ||
| 10 | + public Map<Integer, Channel> portChannel = new HashMap(); | ||
| 11 | + | ||
| 12 | + private static CacheMapUtil cacheMapUtil; | ||
| 13 | + | ||
| 14 | + public static CacheMapUtil getCacheMapUtil() { | ||
| 15 | + if (null == cacheMapUtil) { | ||
| 16 | + cacheMapUtil = new CacheMapUtil(); | ||
| 17 | + } | ||
| 18 | + return cacheMapUtil; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + private CacheMapUtil() { | ||
| 22 | + | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/app/VideoServerApp.java
0 → 100644
| 1 | +// | ||
| 2 | +// Source code recreated from a .class file by IntelliJ IDEA | ||
| 3 | +// (powered by FernFlower decompiler) | ||
| 4 | +// | ||
| 5 | + | ||
| 6 | +package com.genersoft.iot.vmp.jt1078.app; | ||
| 7 | + | ||
| 8 | +import com.genersoft.iot.vmp.jt1078.http.GeneralResponseWriter; | ||
| 9 | +import com.genersoft.iot.vmp.jt1078.http.NettyHttpServerHandler; | ||
| 10 | +import com.genersoft.iot.vmp.jt1078.publisher.PublishManager; | ||
| 11 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Handler; | ||
| 12 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078MessageDecoder; | ||
| 13 | +import com.genersoft.iot.vmp.jt1078.util.Configs; | ||
| 14 | +import io.netty.bootstrap.ServerBootstrap; | ||
| 15 | +import io.netty.channel.Channel; | ||
| 16 | +import io.netty.channel.ChannelFuture; | ||
| 17 | +import io.netty.channel.ChannelHandler; | ||
| 18 | +import io.netty.channel.ChannelInitializer; | ||
| 19 | +import io.netty.channel.ChannelOption; | ||
| 20 | +import io.netty.channel.ChannelPipeline; | ||
| 21 | +import io.netty.channel.EventLoopGroup; | ||
| 22 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 23 | +import io.netty.channel.socket.SocketChannel; | ||
| 24 | +import io.netty.channel.socket.nio.NioServerSocketChannel; | ||
| 25 | + | ||
| 26 | +import java.net.InetAddress; | ||
| 27 | +import java.util.Date; | ||
| 28 | +import java.util.HashMap; | ||
| 29 | +import java.util.Map; | ||
| 30 | +import java.util.Objects; | ||
| 31 | + | ||
| 32 | +import io.netty.handler.codec.http.HttpObjectAggregator; | ||
| 33 | +import io.netty.handler.codec.http.HttpRequestDecoder; | ||
| 34 | +import io.netty.handler.codec.http.HttpResponseEncoder; | ||
| 35 | +import org.apache.commons.collections4.MapUtils; | ||
| 36 | +import org.slf4j.Logger; | ||
| 37 | +import org.slf4j.LoggerFactory; | ||
| 38 | +import sun.misc.Signal; | ||
| 39 | +import sun.misc.SignalHandler; | ||
| 40 | + | ||
| 41 | +public class VideoServerApp { | ||
| 42 | + private static Logger logger = LoggerFactory.getLogger(VideoServerApp.class); | ||
| 43 | + | ||
| 44 | + public VideoServerApp() { | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public void createListenter() throws Exception { | ||
| 48 | + Configs.init("/app.properties"); | ||
| 49 | + PublishManager.init(); | ||
| 50 | + String tagMapping = null; | ||
| 51 | + final VideoServer videoServer = new VideoServer(); | ||
| 52 | + final HttpServer httpServer = new HttpServer(); | ||
| 53 | + Signal.handle(new Signal("TERM"), new SignalHandler() { | ||
| 54 | + public void handle(Signal signal) { | ||
| 55 | + videoServer.shutdown(); | ||
| 56 | + HttpServer var10000 = httpServer; | ||
| 57 | + VideoServerApp.HttpServer.shutdown(); | ||
| 58 | + } | ||
| 59 | + }); | ||
| 60 | + VideoServerApp.VideoServer.getInstance().start(11078, (String) tagMapping, 3333); | ||
| 61 | + VideoServerApp.HttpServer.start((String) tagMapping, 3333); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + public void newVideoServer(final String key, int port, final int httpPort) throws Exception { | ||
| 65 | + VideoServer videoServer = new VideoServer(); | ||
| 66 | + HttpServer httpServer = null; | ||
| 67 | + boolean videoFlag = false; | ||
| 68 | + boolean httpFlag = false; | ||
| 69 | + long nw = (new Date()).getTime(); | ||
| 70 | + VideoServer sourceVideoServer = (VideoServer) CacheMapUtil.getCacheMapUtil().videoServerMap.get(port); | ||
| 71 | + if (Objects.isNull(sourceVideoServer)) { | ||
| 72 | + videoServer = new VideoServer(); | ||
| 73 | + videoFlag = true; | ||
| 74 | + CacheMapUtil.getCacheMapUtil().videoServerMap.put(port, videoServer); | ||
| 75 | + } | ||
| 76 | + if (videoFlag) { | ||
| 77 | + videoServer.start(port, key, httpPort); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + public static void stopServer(int port, Integer httpPort) { | ||
| 82 | + stopVideoServer(port); | ||
| 83 | +// stopHttpServer(httpPort); | ||
| 84 | +// channelMappingMap.remove(httpPort); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + public void stopServer2(int port, int httpPort) { | ||
| 88 | + stopVideoServer(port); | ||
| 89 | +// stopHttpServer(httpPort); | ||
| 90 | +// channelMappingMap.remove(httpPort); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + | ||
| 94 | + private static void stopVideoServer(Integer port) { | ||
| 95 | + VideoServer videoServer = (VideoServer) CacheMapUtil.getCacheMapUtil().videoServerMap.get(port); | ||
| 96 | + if (Objects.nonNull(videoServer)) { | ||
| 97 | + videoServer.shutdown(); | ||
| 98 | + CacheMapUtil.getCacheMapUtil().videoServerMap.remove(port); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + private static void stopHttpServer(Integer port) { | ||
| 104 | + | ||
| 105 | + | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + static class HttpServer { | ||
| 109 | + private static ServerBootstrap serverBootstrap; | ||
| 110 | + private static EventLoopGroup bossGroup; | ||
| 111 | + private static EventLoopGroup workerGroup; | ||
| 112 | + private static Integer httpPort; | ||
| 113 | + | ||
| 114 | + HttpServer() { | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + private static void start(final String tagMapping, final int port) throws Exception { | ||
| 118 | + bossGroup = new NioEventLoopGroup(); | ||
| 119 | + workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()); | ||
| 120 | + httpPort = port; | ||
| 121 | + ServerBootstrap bootstrap = new ServerBootstrap(); | ||
| 122 | + ((ServerBootstrap) ((ServerBootstrap) bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)).childHandler(new ChannelInitializer<SocketChannel>() { | ||
| 123 | + public void initChannel(SocketChannel ch) throws Exception { | ||
| 124 | + ch.pipeline().addLast(new ChannelHandler[]{new GeneralResponseWriter(), new HttpResponseEncoder(), new HttpRequestDecoder(), new HttpObjectAggregator(65536), new NettyHttpServerHandler(tagMapping, port)}); | ||
| 125 | + } | ||
| 126 | + }).option(ChannelOption.SO_BACKLOG, 1024)).childOption(ChannelOption.SO_KEEPALIVE, true); | ||
| 127 | + | ||
| 128 | + try { | ||
| 129 | + InterruptedException e; | ||
| 130 | + try { | ||
| 131 | + ChannelFuture f = bootstrap.bind(InetAddress.getByName("0.0.0.0"), port).sync(); | ||
| 132 | + VideoServerApp.logger.info("HTTP Server started at: {}", port); | ||
| 133 | + | ||
| 134 | + f.channel().closeFuture().sync(); | ||
| 135 | + e = null; | ||
| 136 | + } catch (InterruptedException var7) { | ||
| 137 | + e = var7; | ||
| 138 | + VideoServerApp.logger.error("http server error", e); | ||
| 139 | + } | ||
| 140 | + } finally { | ||
| 141 | + shutdown(); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + private static void shutdown() { | ||
| 147 | + | ||
| 148 | + try { | ||
| 149 | + bossGroup.shutdownGracefully(); | ||
| 150 | + } catch (Exception e) { | ||
| 151 | + e.printStackTrace(); | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + bossGroup = null; | ||
| 155 | + | ||
| 156 | + try { | ||
| 157 | + workerGroup.shutdownGracefully(); | ||
| 158 | + } catch (Exception e) { | ||
| 159 | + e.printStackTrace(); | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + workerGroup = null; | ||
| 163 | + } | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + static class VideoServer { | ||
| 167 | + private ServerBootstrap serverBootstrap; | ||
| 168 | + private EventLoopGroup bossGroup; | ||
| 169 | + private EventLoopGroup workerGroup; | ||
| 170 | + private static Integer port; | ||
| 171 | + | ||
| 172 | + public void start(int port, final String tagMapping, final Integer httpPort) throws Exception { | ||
| 173 | + VideoServerApp.VideoServer.port = port; | ||
| 174 | + serverBootstrap = new ServerBootstrap(); | ||
| 175 | + serverBootstrap.option(ChannelOption.SO_BACKLOG, Configs.getInt("server.backlog", 102400)); | ||
| 176 | + bossGroup = new NioEventLoopGroup(Configs.getInt("server.worker-count", Runtime.getRuntime().availableProcessors())); | ||
| 177 | + workerGroup = new NioEventLoopGroup(); | ||
| 178 | + serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class); | ||
| 179 | + serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { | ||
| 180 | + protected void initChannel(SocketChannel channel) throws Exception { | ||
| 181 | + ChannelPipeline p = channel.pipeline(); | ||
| 182 | + p.addLast(new ChannelHandler[]{new Jtt1078MessageDecoder()}); | ||
| 183 | + p.addLast(new ChannelHandler[]{new Jtt1078Handler(tagMapping, httpPort)}); | ||
| 184 | + } | ||
| 185 | + }); | ||
| 186 | + Channel ch = serverBootstrap.bind(InetAddress.getByName("0.0.0.0"), port).sync().channel(); | ||
| 187 | + CacheMapUtil.getCacheMapUtil().portChannel.put(port, ch); | ||
| 188 | + VideoServerApp.logger.info("Video Server started at: {}", port); | ||
| 189 | + ch.closeFuture(); | ||
| 190 | + ch = null; | ||
| 191 | + CacheMapUtil.getCacheMapUtil().portChannel.remove(port); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + public void shutdown() { | ||
| 195 | + Channel channel = (Channel) CacheMapUtil.getCacheMapUtil().portChannel.get(port); | ||
| 196 | + Exception e; | ||
| 197 | + if (Objects.nonNull(channel)) { | ||
| 198 | + try { | ||
| 199 | + channel.closeFuture(); | ||
| 200 | + } catch (Exception var5) { | ||
| 201 | + e = var5; | ||
| 202 | + e.printStackTrace(); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + channel = null; | ||
| 206 | + CacheMapUtil.getCacheMapUtil().portChannel.remove(port); | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + try { | ||
| 210 | + bossGroup.shutdownGracefully(); | ||
| 211 | + } catch (Exception var4) { | ||
| 212 | + e = var4; | ||
| 213 | + e.printStackTrace(); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + try { | ||
| 217 | + workerGroup.shutdownGracefully(); | ||
| 218 | + } catch (Exception var3) { | ||
| 219 | + e = var3; | ||
| 220 | + e.printStackTrace(); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + public static VideoServer getInstance() { | ||
| 226 | + return new VideoServer(); | ||
| 227 | + } | ||
| 228 | + } | ||
| 229 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/ADPCMCodec.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Decoder; | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.util.ByteHolder; | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | ||
| 6 | + | ||
| 7 | +import java.io.ByteArrayInputStream; | ||
| 8 | +import java.io.ByteArrayOutputStream; | ||
| 9 | +import java.io.FileInputStream; | ||
| 10 | +import java.io.FileOutputStream; | ||
| 11 | +import java.util.Arrays; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * Created by houcheng on 2019-12-05. | ||
| 15 | + * ADPCM 和 PCM转换 | ||
| 16 | + */ | ||
| 17 | +public final class ADPCMCodec extends AudioCodec | ||
| 18 | +{ | ||
| 19 | + static int[] indexTable = { | ||
| 20 | + -1, -1, -1, -1, 2, 4, 6, 8, | ||
| 21 | + -1, -1, -1, -1, 2, 4, 6, 8 | ||
| 22 | + }; | ||
| 23 | + | ||
| 24 | + static int[] stepsizeTable = { | ||
| 25 | + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, | ||
| 26 | + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, | ||
| 27 | + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, | ||
| 28 | + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, | ||
| 29 | + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, | ||
| 30 | + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, | ||
| 31 | + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, | ||
| 32 | + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, | ||
| 33 | + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 | ||
| 34 | + }; | ||
| 35 | + | ||
| 36 | + public static class State | ||
| 37 | + { | ||
| 38 | + public short valprev; | ||
| 39 | + public byte index; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public byte[] toPCM(byte[] data) | ||
| 43 | + { | ||
| 44 | + State state = new State(); | ||
| 45 | + int dlen = data.length / 2; | ||
| 46 | + byte[] temp; | ||
| 47 | + // 如果前四字节是00 01 52 00,则是海思头,需要去掉,否则就视为普通的ADPCM编码 | ||
| 48 | + if (data[0] == 0x00 && data[1] == 0x01 && (data[2] & 0xff) == (data.length - 4) / 2 && data[3] == 0x00) | ||
| 49 | + { | ||
| 50 | + dlen = (data.length - 8); | ||
| 51 | + temp = new byte[data.length - 8]; | ||
| 52 | + System.arraycopy(data, 8, temp, 0, temp.length); | ||
| 53 | + | ||
| 54 | + state.valprev = (short)(((data[5] << 8) & 0xff00) | (data[4] & 0xff)); | ||
| 55 | + state.index = data[6]; | ||
| 56 | + } | ||
| 57 | + else | ||
| 58 | + { | ||
| 59 | + dlen = data.length - 4; | ||
| 60 | + temp = new byte[data.length - 4]; | ||
| 61 | + System.arraycopy(data, 4, temp, 0, temp.length); | ||
| 62 | + | ||
| 63 | + state.valprev = (short)(((data[1] << 8) & 0xff00) | (data[0] & 0xff)); | ||
| 64 | + state.index = data[2]; | ||
| 65 | + } | ||
| 66 | + short[] outdata = new short[dlen * 2]; | ||
| 67 | + adpcm_decoder(temp, outdata, dlen * 2, state); | ||
| 68 | + temp = new byte[dlen * 4]; | ||
| 69 | + for (int i = 0, k = 0; i < outdata.length; i++) | ||
| 70 | + { | ||
| 71 | + short s = outdata[i]; | ||
| 72 | + temp[k++] = (byte)(s & 0xff); | ||
| 73 | + temp[k++] = (byte)((s >> 8) & 0xff); | ||
| 74 | + } | ||
| 75 | + return temp; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + public byte[] fromPCM(byte[] data) | ||
| 79 | + { | ||
| 80 | + return null; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + public static void adpcm_coder(short[] indata, byte[] outdata, int len, State state) | ||
| 84 | + { | ||
| 85 | + int val; /* Current input sample value */ | ||
| 86 | + int sign; /* Current adpcm sign bit */ | ||
| 87 | + int delta; /* Current adpcm output value */ | ||
| 88 | + int diff; /* Difference between val and valprev */ | ||
| 89 | + int step; /* Stepsize */ | ||
| 90 | + int valpred; /* Predicted output value */ | ||
| 91 | + int vpdiff; /* Current change to valpred */ | ||
| 92 | + int index; /* Current step change index */ | ||
| 93 | + int outputbuffer = 0; /* place to keep previous 4-bit value */ | ||
| 94 | + int bufferstep; /* toggle between outputbuffer/output */ | ||
| 95 | + | ||
| 96 | + byte[] outp = outdata; | ||
| 97 | + short[] inp = indata; | ||
| 98 | + | ||
| 99 | + valpred = state.valprev; | ||
| 100 | + index = state.index; | ||
| 101 | + step = stepsizeTable[index]; | ||
| 102 | + | ||
| 103 | + bufferstep = 1; | ||
| 104 | + | ||
| 105 | + int k = 0; | ||
| 106 | + for ( int i = 0; len > 0 ; len--, i++) { | ||
| 107 | + val = inp[i]; | ||
| 108 | + | ||
| 109 | + /* Step 1 - compute difference with previous value */ | ||
| 110 | + diff = val - valpred; | ||
| 111 | + sign = (diff < 0) ? 8 : 0; | ||
| 112 | + if ( sign != 0) diff = (-diff); | ||
| 113 | + | ||
| 114 | + /* Step 2 - Divide and clamp */ | ||
| 115 | + /* Note: | ||
| 116 | + ** This code *approximately* computes: | ||
| 117 | + ** delta = diff*4/step; | ||
| 118 | + ** vpdiff = (delta+0.5)*step/4; | ||
| 119 | + ** but in shift step bits are dropped. The net result of this is | ||
| 120 | + ** that even if you have fast mul/div hardware you cannot put it to | ||
| 121 | + ** good use since the fixup would be too expensive. | ||
| 122 | + */ | ||
| 123 | + delta = 0; | ||
| 124 | + vpdiff = (step >> 3); | ||
| 125 | + | ||
| 126 | + if ( diff >= step ) { | ||
| 127 | + delta = 4; | ||
| 128 | + diff -= step; | ||
| 129 | + vpdiff += step; | ||
| 130 | + } | ||
| 131 | + step >>= 1; | ||
| 132 | + if ( diff >= step ) { | ||
| 133 | + delta |= 2; | ||
| 134 | + diff -= step; | ||
| 135 | + vpdiff += step; | ||
| 136 | + } | ||
| 137 | + step >>= 1; | ||
| 138 | + if ( diff >= step ) { | ||
| 139 | + delta |= 1; | ||
| 140 | + vpdiff += step; | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + /* Step 3 - Update previous value */ | ||
| 144 | + if ( sign != 0 ) | ||
| 145 | + valpred -= vpdiff; | ||
| 146 | + else | ||
| 147 | + valpred += vpdiff; | ||
| 148 | + | ||
| 149 | + /* Step 4 - Clamp previous value to 16 bits */ | ||
| 150 | + if ( valpred > 32767 ) | ||
| 151 | + valpred = 32767; | ||
| 152 | + else if ( valpred < -32768 ) | ||
| 153 | + valpred = -32768; | ||
| 154 | + | ||
| 155 | + /* Step 5 - Assemble value, update index and step values */ | ||
| 156 | + delta |= sign; | ||
| 157 | + | ||
| 158 | + index += indexTable[delta]; | ||
| 159 | + if ( index < 0 ) index = 0; | ||
| 160 | + if ( index > 88 ) index = 88; | ||
| 161 | + step = stepsizeTable[index]; | ||
| 162 | + | ||
| 163 | + /* Step 6 - Output value */ | ||
| 164 | + if ( bufferstep != 0 ) { | ||
| 165 | + outputbuffer = (delta << 4) & 0xf0; | ||
| 166 | + } else { | ||
| 167 | + outp[k++] = (byte)((delta & 0x0f) | outputbuffer); | ||
| 168 | + } | ||
| 169 | + bufferstep = bufferstep == 0 ? 1 : 0; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + /* Output last step, if needed */ | ||
| 173 | + if ( bufferstep == 0 ) | ||
| 174 | + outp[k++] = (byte)outputbuffer; | ||
| 175 | + | ||
| 176 | + state.valprev = (short)valpred; | ||
| 177 | + state.index = (byte)index; | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + | ||
| 181 | + public static void adpcm_decoder(byte[] indata, short[] outdata, int len, State state) | ||
| 182 | + { | ||
| 183 | + // signed char *inp; /* Input buffer pointer */ | ||
| 184 | + // short *outp; /* output buffer pointer */ | ||
| 185 | + int sign; /* Current adpcm sign bit */ | ||
| 186 | + int delta; /* Current adpcm output value */ | ||
| 187 | + int step; /* Stepsize */ | ||
| 188 | + int valpred; /* Predicted value */ | ||
| 189 | + int vpdiff; /* Current change to valpred */ | ||
| 190 | + int index; /* Current step change index */ | ||
| 191 | + int inputbuffer = 0; /* place to keep next 4-bit value */ | ||
| 192 | + int bufferstep; /* toggle between inputbuffer/input */ | ||
| 193 | + | ||
| 194 | + short[] outp = outdata; | ||
| 195 | + byte[] inp = indata; | ||
| 196 | + | ||
| 197 | + valpred = state.valprev; | ||
| 198 | + index = state.index; | ||
| 199 | + if ( index < 0 ) index = 0; | ||
| 200 | + if ( index > 88 ) index = 88; | ||
| 201 | + step = stepsizeTable[index]; | ||
| 202 | + | ||
| 203 | + bufferstep = 0; | ||
| 204 | + | ||
| 205 | + int k = 0; | ||
| 206 | + for ( int i = 0; len > 0 ; len-- ) { | ||
| 207 | + | ||
| 208 | + /* Step 1 - get the delta value */ | ||
| 209 | + if ( bufferstep != 0 ) { | ||
| 210 | + delta = inputbuffer & 0xf; | ||
| 211 | + } else { | ||
| 212 | + inputbuffer = inp[i++]; | ||
| 213 | + delta = (inputbuffer >> 4) & 0xf; | ||
| 214 | + } | ||
| 215 | + bufferstep = bufferstep == 0 ? 1 : 0; | ||
| 216 | + | ||
| 217 | + /* Step 2 - Find new index value (for later) */ | ||
| 218 | + index += indexTable[delta]; | ||
| 219 | + if ( index < 0 ) index = 0; | ||
| 220 | + if ( index > 88 ) index = 88; | ||
| 221 | + | ||
| 222 | + /* Step 3 - Separate sign and magnitude */ | ||
| 223 | + sign = delta & 8; | ||
| 224 | + delta = delta & 7; | ||
| 225 | + | ||
| 226 | + /* Step 4 - Compute difference and new predicted value */ | ||
| 227 | + /* | ||
| 228 | + ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment | ||
| 229 | + ** in adpcm_coder. | ||
| 230 | + */ | ||
| 231 | + vpdiff = step >> 3; | ||
| 232 | + if ( (delta & 4) > 0 ) vpdiff += step; | ||
| 233 | + if ( (delta & 2) > 0 ) vpdiff += step>>1; | ||
| 234 | + if ( (delta & 1) > 0 ) vpdiff += step>>2; | ||
| 235 | + | ||
| 236 | + if ( sign != 0 ) | ||
| 237 | + valpred -= vpdiff; | ||
| 238 | + else | ||
| 239 | + valpred += vpdiff; | ||
| 240 | + | ||
| 241 | + /* Step 5 - clamp output value */ | ||
| 242 | + if ( valpred > 32767 ) | ||
| 243 | + valpred = 32767; | ||
| 244 | + else if ( valpred < -32768 ) | ||
| 245 | + valpred = -32768; | ||
| 246 | + | ||
| 247 | + /* Step 6 - Update step value */ | ||
| 248 | + step = stepsizeTable[index]; | ||
| 249 | + | ||
| 250 | + /* Step 7 - Output value */ | ||
| 251 | + outp[k++] = (short)valpred; | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + state.valprev = (short)valpred; | ||
| 255 | + state.index = (byte)index; | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + | ||
| 259 | + public static void main(String[] args) throws Exception | ||
| 260 | + { | ||
| 261 | + ByteArrayInputStream bais = null; | ||
| 262 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 1024 * 4); | ||
| 263 | + | ||
| 264 | + int len = -1; | ||
| 265 | + byte[] block = new byte[512]; | ||
| 266 | + FileInputStream fis = new FileInputStream("d:\\test\\g711\\streamax.bin"); | ||
| 267 | + FileOutputStream fos = new FileOutputStream("d:\\test\\g711\\111111111122222222222222.pcm"); | ||
| 268 | + | ||
| 269 | + ADPCMCodec codec = new ADPCMCodec(); | ||
| 270 | + | ||
| 271 | + Jtt1078Decoder decoder = new Jtt1078Decoder(); | ||
| 272 | + while ((len = fis.read(block)) > -1) | ||
| 273 | + { | ||
| 274 | + decoder.write(block, 0, len); | ||
| 275 | + while (true) | ||
| 276 | + { | ||
| 277 | + Packet p = decoder.decode(); | ||
| 278 | + if (p == null) break; | ||
| 279 | + | ||
| 280 | + int lengthOffset = 28; | ||
| 281 | + int dataType = (p.seek(15).nextByte() >> 4) & 0x0f; | ||
| 282 | + // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段 | ||
| 283 | + if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2; | ||
| 284 | + else if (dataType == 0x03) lengthOffset = 28 - 4; | ||
| 285 | + | ||
| 286 | + // FFMpegManager.getInstance().feed(publisherId, packet.seek(lengthOffset + 2).nextBytes()); | ||
| 287 | + if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02) | ||
| 288 | + { | ||
| 289 | + | ||
| 290 | + } | ||
| 291 | + else | ||
| 292 | + { | ||
| 293 | + byte[] data = p.seek(lengthOffset + 2).nextBytes(); | ||
| 294 | + fos.write(codec.toPCM(data)); | ||
| 295 | + fos.flush(); | ||
| 296 | + } | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + fos.flush(); | ||
| 301 | + | ||
| 302 | + fis.close(); | ||
| 303 | + fos.close(); | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/AudioCodec.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.entity.MediaEncoding; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * Created by houcheng on 2019-12-11. | ||
| 7 | + */ | ||
| 8 | +public abstract class AudioCodec | ||
| 9 | +{ | ||
| 10 | + public abstract byte[] toPCM(byte[] data); | ||
| 11 | + public abstract byte[] fromPCM(byte[] data); | ||
| 12 | + | ||
| 13 | + public static AudioCodec getCodec(int encoding) | ||
| 14 | + { | ||
| 15 | + if (MediaEncoding.Encoding.ADPCMA.ordinal() == encoding) return new ADPCMCodec(); | ||
| 16 | + else if (MediaEncoding.Encoding.G711A.ordinal() == encoding) return new G711Codec(); | ||
| 17 | + else if (MediaEncoding.Encoding.G711U.ordinal() == encoding) return new G711UCodec(); | ||
| 18 | + else if (MediaEncoding.Encoding.G726.ordinal() == encoding) return new G726Codec(); | ||
| 19 | + // else if (Audio.Encoding.G726.equals(encoding)) ; | ||
| 20 | + else return new SilenceCodec(); | ||
| 21 | + } | ||
| 22 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/G711Codec.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * 核心转换,PCM转G711 | ||
| 6 | + * Created by onlygx | ||
| 7 | + */ | ||
| 8 | +public class G711Codec extends AudioCodec | ||
| 9 | +{ | ||
| 10 | + private final static int SIGN_BIT = 0x80; | ||
| 11 | + private final static int QUANT_MASK = 0xf; | ||
| 12 | + private final static int SEG_SHIFT = 4; | ||
| 13 | + private final static int SEG_MASK = 0x70; | ||
| 14 | + static short[] seg_end = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; | ||
| 15 | + private final static int cClip = 32635; | ||
| 16 | + private static final byte[] aLawCompressTable = new byte[]{1, 1, 2, 2, 3, 3, 3, | ||
| 17 | + 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
| 18 | + 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
| 19 | + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 20 | + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 21 | + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
| 22 | + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; | ||
| 23 | + | ||
| 24 | + private static byte linearToALawSample(short sample) { | ||
| 25 | + int sign; | ||
| 26 | + int exponent; | ||
| 27 | + int mantissa; | ||
| 28 | + int s; | ||
| 29 | + | ||
| 30 | + sign = ((~sample) >> 8) & 0x80; | ||
| 31 | + if (!(sign == 0x80)) { | ||
| 32 | + sample = (short) -sample; | ||
| 33 | + } | ||
| 34 | + if (sample > cClip) { | ||
| 35 | + sample = cClip; | ||
| 36 | + } | ||
| 37 | + if (sample >= 256) { | ||
| 38 | + exponent = aLawCompressTable[(sample >> 8) & 0x7F]; | ||
| 39 | + mantissa = (sample >> (exponent + 3)) & 0x0F; | ||
| 40 | + s = (exponent << 4) | mantissa; | ||
| 41 | + } else { | ||
| 42 | + s = sample >> 4; | ||
| 43 | + } | ||
| 44 | + s ^= (sign ^ 0x55); | ||
| 45 | + return (byte) s; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * PCM转G711 | ||
| 50 | + * | ||
| 51 | + * @param src 编码前的数据 | ||
| 52 | + * @return 编码后res数组长度应为编码前src数组长度的一半 | ||
| 53 | + */ | ||
| 54 | + public static byte[] encode(byte[] src) { | ||
| 55 | + int j = 0; | ||
| 56 | + int len = src.length; | ||
| 57 | + int count = len / 2; | ||
| 58 | + byte[] res = new byte[count]; | ||
| 59 | + short sample = 0; | ||
| 60 | + for (int i = 0; i < count; i++) { | ||
| 61 | + sample = (short) (((src[j++] & 0xff) | (src[j++]) << 8)); | ||
| 62 | + res[i] = linearToALawSample(sample); | ||
| 63 | + } | ||
| 64 | + return res; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + static short search(short val, short[] table, short size) { | ||
| 68 | + | ||
| 69 | + for (short i = 0; i < size; i++) { | ||
| 70 | + if (val <= table[i]) { | ||
| 71 | + return i; | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + return size; | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + public static byte linear2alaw(short pcm_val) { | ||
| 78 | + short mask; | ||
| 79 | + short seg; | ||
| 80 | + char aval; | ||
| 81 | + if (pcm_val >= 0) { | ||
| 82 | + mask = 0xD5; | ||
| 83 | + } else { | ||
| 84 | + mask = 0x55; | ||
| 85 | + pcm_val = (short) (-pcm_val - 1); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + /* Convert the scaled magnitude to segment number. */ | ||
| 89 | + seg = search(pcm_val, seg_end, (short) 8); | ||
| 90 | + | ||
| 91 | + /* Combine the sign, segment, and quantization bits. */ | ||
| 92 | + | ||
| 93 | + if (seg >= 8) /* out of range, return maximum value. */ return (byte) (0x7F ^ mask); | ||
| 94 | + else { | ||
| 95 | + aval = (char) (seg << SEG_SHIFT); | ||
| 96 | + if (seg < 2) aval |= (pcm_val >> 4) & QUANT_MASK; | ||
| 97 | + else aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; | ||
| 98 | + return (byte) (aval ^ mask); | ||
| 99 | + } | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + | ||
| 103 | + public static short alaw2linear(byte a_val) { | ||
| 104 | + short t; | ||
| 105 | + short seg; | ||
| 106 | + | ||
| 107 | + a_val ^= 0x55; | ||
| 108 | + | ||
| 109 | + t = (short) ((a_val & QUANT_MASK) << 4); | ||
| 110 | + seg = (short) ((a_val & SEG_MASK) >> SEG_SHIFT); | ||
| 111 | + switch (seg) { | ||
| 112 | + case 0: | ||
| 113 | + t += 8; | ||
| 114 | + break; | ||
| 115 | + case 1: | ||
| 116 | + t += 0x108; | ||
| 117 | + break; | ||
| 118 | + default: | ||
| 119 | + t += 0x108; | ||
| 120 | + t <<= seg - 1; | ||
| 121 | + } | ||
| 122 | + return (a_val & SIGN_BIT) != 0 ? t : (short) -t; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + // 由G.711转至PCM | ||
| 126 | + public static byte[] _toPCM(byte[] g711data) { | ||
| 127 | + byte[] pcmdata = new byte[g711data.length * 2]; | ||
| 128 | + for (int i = 0, k = 0; i < g711data.length; i++) { | ||
| 129 | + short v = alaw2linear(g711data[i]); | ||
| 130 | + pcmdata[k++] = (byte) (v & 0xff); | ||
| 131 | + pcmdata[k++] = (byte) ((v >> 8) & 0xff); | ||
| 132 | + } | ||
| 133 | + return pcmdata; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + // 由PCM转至G.711 | ||
| 137 | + public static byte[] _fromPCM(byte[] pcmData) { | ||
| 138 | + return encode(pcmData); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + @Override | ||
| 142 | + public byte[] toPCM(byte[] data) { | ||
| 143 | + byte[] temp; | ||
| 144 | + // 如果前四字节是00 01 52 00,则是海思头,需要去掉 | ||
| 145 | + if (data[0] == 0x00 && data[1] == 0x01 && (data[2] & 0xff) == (data.length - 4) / 2 && data[3] == 0x00) { | ||
| 146 | + temp = new byte[data.length - 4]; | ||
| 147 | + System.arraycopy(data, 4, temp, 0, temp.length); | ||
| 148 | + } else temp = data; | ||
| 149 | + | ||
| 150 | + return _toPCM(temp); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + @Override | ||
| 154 | + public byte[] fromPCM(byte[] data) { | ||
| 155 | + return encode(data); | ||
| 156 | + } | ||
| 157 | +} | ||
| 0 | \ No newline at end of file | 158 | \ No newline at end of file |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/G711UCodec.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | ||
| 4 | + | ||
| 5 | +import java.io.ByteArrayOutputStream; | ||
| 6 | +import java.io.FileInputStream; | ||
| 7 | +import java.io.FileOutputStream; | ||
| 8 | +/* | ||
| 9 | + * This source code is a product of Sun Microsystems, Inc. and is provided | ||
| 10 | + * for unrestricted use. Users may copy or modify this source code without | ||
| 11 | + * charge. | ||
| 12 | + * | ||
| 13 | + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING | ||
| 14 | + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR | ||
| 15 | + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. | ||
| 16 | + * | ||
| 17 | + * Sun source code is provided with no support and without any obligation on | ||
| 18 | + * the part of Sun Microsystems, Inc. to assist in its use, correction, | ||
| 19 | + * modification or enhancement. | ||
| 20 | + * | ||
| 21 | + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE | ||
| 22 | + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE | ||
| 23 | + * OR ANY PART THEREOF. | ||
| 24 | + * | ||
| 25 | + * In no event will Sun Microsystems, Inc. be liable for any lost revenue | ||
| 26 | + * or profits or other special, indirect and consequential damages, even if | ||
| 27 | + * Sun has been advised of the possibility of such damages. | ||
| 28 | + * | ||
| 29 | + * Sun Microsystems, Inc. | ||
| 30 | + * 2550 Garcia Avenue | ||
| 31 | + * Mountain View, California 94043 | ||
| 32 | + */ | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * Created by houcheng on 2019-12-11. | ||
| 36 | + */ | ||
| 37 | +public class G711UCodec extends AudioCodec | ||
| 38 | +{ | ||
| 39 | + static final int ULAW = 1; | ||
| 40 | + static final int ALAW = 2; | ||
| 41 | + | ||
| 42 | + /* 16384 entries per table (16 bit) */ | ||
| 43 | + static byte[] linear_to_ulaw = new byte[65536]; | ||
| 44 | + | ||
| 45 | + /* 16384 entries per table (8 bit) */ | ||
| 46 | + static short[] ulaw_to_linear = new short[256]; | ||
| 47 | + | ||
| 48 | + static final int SIGN_BIT = 0x80; | ||
| 49 | + static final int QUANT_MASK = 0x0f; | ||
| 50 | + static final int NSEGS = 0x08; | ||
| 51 | + static final int SEG_SHIFT = 0x04; | ||
| 52 | + static final int SEG_MASK = 0x70; | ||
| 53 | + | ||
| 54 | + static final int BIAS = 0x84; | ||
| 55 | + static final int CLIP = 8159; | ||
| 56 | + | ||
| 57 | + static short[] seg_aend = { 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF }; | ||
| 58 | + static short[] seg_uend = { 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF }; | ||
| 59 | + | ||
| 60 | + int[] _u2a = { /* u- to A-law conversions */ | ||
| 61 | + 1, 1, 2, 2, 3, 3, 4, 4, | ||
| 62 | + 5, 5, 6, 6, 7, 7, 8, 8, | ||
| 63 | + 9, 10, 11, 12, 13, 14, 15, 16, | ||
| 64 | + 17, 18, 19, 20, 21, 22, 23, 24, | ||
| 65 | + 25, 27, 29, 31, 33, 34, 35, 36, | ||
| 66 | + 37, 38, 39, 40, 41, 42, 43, 44, | ||
| 67 | + 46, 48, 49, 50, 51, 52, 53, 54, | ||
| 68 | + 55, 56, 57, 58, 59, 60, 61, 62, | ||
| 69 | + 64, 65, 66, 67, 68, 69, 70, 71, | ||
| 70 | + 72, 73, 74, 75, 76, 77, 78, 79, | ||
| 71 | + /* corrected: | ||
| 72 | + 81, 82, 83, 84, 85, 86, 87, 88, | ||
| 73 | + should be: */ | ||
| 74 | + 80, 82, 83, 84, 85, 86, 87, 88, | ||
| 75 | + 89, 90, 91, 92, 93, 94, 95, 96, | ||
| 76 | + 97, 98, 99, 100, 101, 102, 103, 104, | ||
| 77 | + 105, 106, 107, 108, 109, 110, 111, 112, | ||
| 78 | + 113, 114, 115, 116, 117, 118, 119, 120, | ||
| 79 | + 121, 122, 123, 124, 125, 126, 127, 128}; | ||
| 80 | + | ||
| 81 | + int[] _a2u = { /* A- to u-law conversions */ | ||
| 82 | + 1, 3, 5, 7, 9, 11, 13, 15, | ||
| 83 | + 16, 17, 18, 19, 20, 21, 22, 23, | ||
| 84 | + 24, 25, 26, 27, 28, 29, 30, 31, | ||
| 85 | + 32, 32, 33, 33, 34, 34, 35, 35, | ||
| 86 | + 36, 37, 38, 39, 40, 41, 42, 43, | ||
| 87 | + 44, 45, 46, 47, 48, 48, 49, 49, | ||
| 88 | + 50, 51, 52, 53, 54, 55, 56, 57, | ||
| 89 | + 58, 59, 60, 61, 62, 63, 64, 64, | ||
| 90 | + 65, 66, 67, 68, 69, 70, 71, 72, | ||
| 91 | + /* corrected: | ||
| 92 | + 73, 74, 75, 76, 77, 78, 79, 79, | ||
| 93 | + should be: */ | ||
| 94 | + 73, 74, 75, 76, 77, 78, 79, 80, | ||
| 95 | + 80, 81, 82, 83, 84, 85, 86, 87, | ||
| 96 | + 88, 89, 90, 91, 92, 93, 94, 95, | ||
| 97 | + 96, 97, 98, 99, 100, 101, 102, 103, | ||
| 98 | + 104, 105, 106, 107, 108, 109, 110, 111, | ||
| 99 | + 112, 113, 114, 115, 116, 117, 118, 119, | ||
| 100 | + 120, 121, 122, 123, 124, 125, 126, 127}; | ||
| 101 | + | ||
| 102 | + static | ||
| 103 | + { | ||
| 104 | + // 初始化ulaw表 | ||
| 105 | + for (int i = 0; i < 256; i++) ulaw_to_linear[i] = ulaw2linear((byte)i); | ||
| 106 | + // 初始化ulaw2linear表 | ||
| 107 | + for (int i = 0; i < 65535; i++) linear_to_ulaw[i] = linear2ulaw((short)i); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + public static short ulaw2linear(byte u_val) | ||
| 111 | + { | ||
| 112 | + short t; | ||
| 113 | + u_val = (byte)(~u_val); | ||
| 114 | + t = (short)(((u_val & QUANT_MASK) << 3) + BIAS); | ||
| 115 | + t <<= (u_val & SEG_MASK) >>> SEG_SHIFT; | ||
| 116 | + | ||
| 117 | + return ((u_val & SIGN_BIT) > 0 ? (short)(BIAS - t) : (short)(t - BIAS)); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + public static byte linear2ulaw(short pcm_val) | ||
| 121 | + { | ||
| 122 | + short mask; | ||
| 123 | + short seg; | ||
| 124 | + byte uval; | ||
| 125 | + | ||
| 126 | + pcm_val = (short)(pcm_val >> 2); | ||
| 127 | + if (pcm_val < 0) | ||
| 128 | + { | ||
| 129 | + pcm_val = (short)(-pcm_val); | ||
| 130 | + mask = 0x7f; | ||
| 131 | + } | ||
| 132 | + else | ||
| 133 | + { | ||
| 134 | + mask = 0xff; | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + if (pcm_val > CLIP) pcm_val = CLIP; | ||
| 138 | + pcm_val += (BIAS >> 2); | ||
| 139 | + | ||
| 140 | + seg = search(pcm_val, seg_uend, (short)8); | ||
| 141 | + | ||
| 142 | + if (seg >= 8) | ||
| 143 | + { | ||
| 144 | + return (byte)(0x7f ^ mask); | ||
| 145 | + } | ||
| 146 | + else | ||
| 147 | + { | ||
| 148 | + uval = (byte) ((seg << 4) | ((pcm_val >> (seg + 1)) & 0xF)); | ||
| 149 | + return (byte)(uval ^ mask); | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + static short search(short val, short[] table, short size) | ||
| 154 | + { | ||
| 155 | + for (short i = 0; i < size; i++) | ||
| 156 | + { | ||
| 157 | + if (val <= table[i]) return i; | ||
| 158 | + } | ||
| 159 | + return size; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + static void ulaw_to_pcm16(int src_length, byte[] src_samples, byte[] dst_samples) | ||
| 163 | + { | ||
| 164 | + for (int i = 0, k = 0; i < src_length; i++) | ||
| 165 | + { | ||
| 166 | + short s = ulaw_to_linear[src_samples[i] & 0xff]; | ||
| 167 | + dst_samples[k++] = (byte)(s & 0xff); | ||
| 168 | + dst_samples[k++] = (byte)((s >> 8) & 0xff); | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + static void pcm16_to_ulaw(int src_length, byte[] src_samples, byte[] dst_samples) | ||
| 173 | + { | ||
| 174 | + short[] s_samples = ByteUtils.toShortArray(src_samples); | ||
| 175 | + for (int i = 0, k = 0; i < s_samples.length; i++) | ||
| 176 | + { | ||
| 177 | + dst_samples[k++] = linear2ulaw(s_samples[i]); | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + @Override | ||
| 182 | + public byte[] toPCM(byte[] data) | ||
| 183 | + { | ||
| 184 | + byte[] temp; | ||
| 185 | + // 如果前四字节是00 01 52 00,则是海思头,需要去掉 | ||
| 186 | + if (data[0] == 0x00 && data[1] == 0x01 && (data[2] & 0xff) == (data.length - 4) / 2 && data[3] == 0x00) | ||
| 187 | + { | ||
| 188 | + temp = new byte[data.length - 4]; | ||
| 189 | + System.arraycopy(data, 4, temp, 0, temp.length); | ||
| 190 | + } | ||
| 191 | + else temp = data; | ||
| 192 | + | ||
| 193 | + byte[] dest = new byte[temp.length * 2]; | ||
| 194 | + ulaw_to_pcm16(temp.length, temp, dest); | ||
| 195 | + return dest; | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + @Override | ||
| 199 | + public byte[] fromPCM(byte[] data) | ||
| 200 | + { | ||
| 201 | + byte[] dest = new byte[data.length / 2]; | ||
| 202 | + pcm16_to_ulaw(data.length, data, dest); | ||
| 203 | + return dest; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + public static void main(String[] args) throws Exception | ||
| 207 | + { | ||
| 208 | + FileInputStream fis = new FileInputStream("d:\\fuck121212121.pcm"); | ||
| 209 | + int len = -1; | ||
| 210 | + byte[] buff = new byte[320]; | ||
| 211 | + AudioCodec codec = new G711UCodec(); | ||
| 212 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(4096 * 1024); | ||
| 213 | + while ((len = fis.read(buff)) > -1) | ||
| 214 | + { | ||
| 215 | + baos.write(buff, 0, len); | ||
| 216 | + } | ||
| 217 | + new FileOutputStream("D:\\temp\\fuckfuckfuck.g711u").write(codec.fromPCM(baos.toByteArray())); | ||
| 218 | + } | ||
| 219 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/G726Codec.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.codec.g726.*; | ||
| 5 | + | ||
| 6 | +import java.io.FileInputStream; | ||
| 7 | +import java.io.FileOutputStream; | ||
| 8 | + | ||
| 9 | +public class G726Codec extends AudioCodec { | ||
| 10 | + | ||
| 11 | + // pcm采样率 | ||
| 12 | + private static final int PCM_SAMPLE = 8000; | ||
| 13 | + | ||
| 14 | + // pcm采样点 | ||
| 15 | + private static final int PCM_POINT = 320; | ||
| 16 | + | ||
| 17 | + // 音频通道数 | ||
| 18 | + private static final int CHANNEL = 1; | ||
| 19 | + | ||
| 20 | + // 码率 | ||
| 21 | + private static final int G726_BIT_RATE_16000 = 16000; | ||
| 22 | + private static final int G726_BIT_RATE_24000 = 24000; | ||
| 23 | + private static final int G726_BIT_RATE_32000 = 32000; | ||
| 24 | + private static final int G726_BIT_RATE_40000 = 40000; | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public byte[] toPCM(byte[] data) { | ||
| 30 | + int pos = 0; | ||
| 31 | + // 如果前四字节是00 01 52 00,则是海思头,需要去掉 | ||
| 32 | + if (data[0] == 0x00 && data[1] == 0x01 && (data[2] & 0xff) == (data.length - 4) / 2 && data[3] == 0x00) { | ||
| 33 | + pos = 4; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + int length = data.length - pos; | ||
| 37 | + | ||
| 38 | + int point = PCM_POINT; | ||
| 39 | + | ||
| 40 | + // 计算G726的码率 | ||
| 41 | + int rateBit = length * 8 * PCM_SAMPLE/point; | ||
| 42 | + | ||
| 43 | + G726 g726 = null; | ||
| 44 | + | ||
| 45 | + // 码率 | ||
| 46 | + if (rateBit == G726_BIT_RATE_40000) { | ||
| 47 | + g726 = new G726_40(); | ||
| 48 | + } | ||
| 49 | + else if (rateBit == G726_BIT_RATE_32000) { | ||
| 50 | + g726 = new G726_32(); | ||
| 51 | + } | ||
| 52 | + else if (rateBit == G726_BIT_RATE_24000) { | ||
| 53 | + g726 = new G726_24(); | ||
| 54 | + } | ||
| 55 | + else if (rateBit == G726_BIT_RATE_16000) { | ||
| 56 | + g726 = new G726_16(); | ||
| 57 | + } | ||
| 58 | + else { | ||
| 59 | + return null; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + int pcmSize = point * CHANNEL * 2; | ||
| 63 | + byte[] pcm = new byte[pcmSize]; | ||
| 64 | + | ||
| 65 | + int ret = g726.decode(data,pos,length,G726.AUDIO_ENCODING_LINEAR,pcm,0); | ||
| 66 | + if (ret < 0) { | ||
| 67 | + return null; | ||
| 68 | + } | ||
| 69 | + return pcm; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Override | ||
| 73 | + public byte[] fromPCM(byte[] data) { | ||
| 74 | + // TODO: | ||
| 75 | + return new byte[0]; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + private static void readWrite(String in,String out,int size) throws Exception { | ||
| 79 | + FileInputStream f = new FileInputStream(in); | ||
| 80 | + FileOutputStream o = new FileOutputStream(out); | ||
| 81 | + int len = -1; | ||
| 82 | + byte[] buff = new byte[size]; | ||
| 83 | + G726Codec g726Codec = new G726Codec(); | ||
| 84 | + int index = 0; | ||
| 85 | + while ((len = f.read(buff,index,buff.length)) > -1) { | ||
| 86 | + o.write(g726Codec.toPCM(buff)); | ||
| 87 | + } | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + // mac下在终端中输入 /Applications/VLC.app/Contents/MacOS/VLC --demux=rawaud --rawaud-channels 1 --rawaud-samplerate 8000 ${path} | ||
| 91 | + // 修改${path} 的值为pcm路径,即可播放转码后的pcm文件 | ||
| 92 | + public static void main(String[] args) throws Exception { | ||
| 93 | + | ||
| 94 | + readWrite(Thread.currentThread().getContextClassLoader().getResource("g726/in_40.g726").getPath(), | ||
| 95 | + "/Users/tmyam/Downloads/out_40.pcm",200); | ||
| 96 | + | ||
| 97 | + | ||
| 98 | + readWrite(Thread.currentThread().getContextClassLoader().getResource("g726/in_32.g726").getPath(), | ||
| 99 | + "/Users/tmyam/Downloads/out_32.pcm",160); | ||
| 100 | + | ||
| 101 | + | ||
| 102 | + readWrite(Thread.currentThread().getContextClassLoader().getResource("g726/in_24.g726").getPath(), | ||
| 103 | + "/Users/tmyam/Downloads/out_24.pcm",120); | ||
| 104 | + | ||
| 105 | + | ||
| 106 | + readWrite(Thread.currentThread().getContextClassLoader().getResource("g726/in_16.g726").getPath(), | ||
| 107 | + "/Users/tmyam/Downloads/out_16.pcm",80); | ||
| 108 | + } | ||
| 109 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/MP3Encoder.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | +import de.sciss.jump3r.lowlevel.LameEncoder; | ||
| 4 | +import de.sciss.jump3r.mp3.Lame; | ||
| 5 | + | ||
| 6 | +import javax.sound.sampled.AudioFormat; | ||
| 7 | +import java.io.ByteArrayOutputStream; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * Created by matrixy on 2020/4/27. | ||
| 11 | + */ | ||
| 12 | +public class MP3Encoder | ||
| 13 | +{ | ||
| 14 | + static final AudioFormat PCM_FORMAT = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 1, 1 * 2, -1, false); | ||
| 15 | + | ||
| 16 | + byte[] buffer = null; | ||
| 17 | + ByteArrayOutputStream mp3Data; | ||
| 18 | + LameEncoder encoder = null; | ||
| 19 | + | ||
| 20 | + public MP3Encoder() | ||
| 21 | + { | ||
| 22 | + encoder = new LameEncoder(PCM_FORMAT, 256, 3, Lame.MEDIUM, false); | ||
| 23 | + buffer = new byte[encoder.getPCMBufferSize()]; | ||
| 24 | + mp3Data = new ByteArrayOutputStream(encoder.getOutputBufferSize()); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public byte[] encode(byte[] pcm) | ||
| 28 | + { | ||
| 29 | + if (pcm == null) return null; | ||
| 30 | + int bytesToTransfer = Math.min(encoder.getPCMBufferSize(), pcm.length); | ||
| 31 | + int bytesWritten; | ||
| 32 | + int currentPcmPosition = 0; | ||
| 33 | + | ||
| 34 | + mp3Data.reset(); | ||
| 35 | + | ||
| 36 | + while (0 < (bytesWritten = encoder.encodeBuffer(pcm, currentPcmPosition, bytesToTransfer, buffer))) | ||
| 37 | + { | ||
| 38 | + currentPcmPosition += bytesToTransfer; | ||
| 39 | + bytesToTransfer = Math.min(buffer.length, pcm.length - currentPcmPosition); | ||
| 40 | + | ||
| 41 | + mp3Data.write(buffer, 0, bytesWritten); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + return mp3Data.toByteArray(); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public void close() | ||
| 48 | + { | ||
| 49 | + encoder.close(); | ||
| 50 | + } | ||
| 51 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/SilenceCodec.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec; | ||
| 2 | + | ||
| 3 | +import java.util.Arrays; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * Created by houcheng on 2019-12-11. | ||
| 7 | + */ | ||
| 8 | +public class SilenceCodec extends AudioCodec | ||
| 9 | +{ | ||
| 10 | + static final byte[] BLANK = new byte[0]; | ||
| 11 | + | ||
| 12 | + @Override | ||
| 13 | + public byte[] toPCM(byte[] data) | ||
| 14 | + { | ||
| 15 | + return BLANK; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public byte[] fromPCM(byte[] data) | ||
| 20 | + { | ||
| 21 | + return BLANK; | ||
| 22 | + } | ||
| 23 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/g726/G726.java
0 → 100644
| 1 | + | ||
| 2 | +package com.genersoft.iot.vmp.jt1078.codec.g726; | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | ||
| 6 | +import com.genersoft.iot.vmp.jt1078.codec.G711UCodec; | ||
| 7 | + | ||
| 8 | +/** Common routines for G.721 and G.723 conversions. | ||
| 9 | + * <p> | ||
| 10 | + * This implementation is based on the ANSI-C language reference implementations | ||
| 11 | + * of the CCITT (International Telegraph and Telephone Consultative Committee) | ||
| 12 | + * G.711, G.721 and G.723 voice compressions, provided by Sun Microsystems, Inc. | ||
| 13 | + * <p> | ||
| 14 | + * Acknowledgement to Sun Microsystems, Inc. for having released the original | ||
| 15 | + * ANSI-C source code to the public domain. | ||
| 16 | + */ | ||
| 17 | +public abstract class G726 { | ||
| 18 | + | ||
| 19 | + // ##### C-to-Java conversion: ##### | ||
| 20 | + // short becomes int | ||
| 21 | + // char becomes int | ||
| 22 | + // unsigned char becomes int | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + // *************************** STATIC *************************** | ||
| 26 | + | ||
| 27 | + /** ISDN u-law */ | ||
| 28 | + public static final int AUDIO_ENCODING_ULAW=1; | ||
| 29 | + | ||
| 30 | + /** ISDN A-law */ | ||
| 31 | + public static final int AUDIO_ENCODING_ALAW=2; | ||
| 32 | + | ||
| 33 | + /** PCM 2's-complement (0-center) */ | ||
| 34 | + public static final int AUDIO_ENCODING_LINEAR=3; | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + /** The first 15 values, powers of 2. */ | ||
| 39 | + private static final /*short*/int[] power2 = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000 }; | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + /** Quantizes the input val against the table of size short integers. | ||
| 43 | + * It returns i if table[i-1]<=val<table[i]. | ||
| 44 | + * <p> | ||
| 45 | + * Using linear search for simple coding. */ | ||
| 46 | + private static int quan(int val, /*short*/int[] table, int size) { | ||
| 47 | + | ||
| 48 | + int i; | ||
| 49 | + for (i=0; i<size; i++) if (val<table[i]) break; | ||
| 50 | + return i; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + /** Given a raw sample, 'd', of the difference signal and a | ||
| 55 | + * quantization step size scale factor, 'y', this routine returns the | ||
| 56 | + * ADPCM codeword to which that sample gets quantized. The step | ||
| 57 | + * size scale factor division operation is done in the log base 2 domain | ||
| 58 | + * as a subtraction. | ||
| 59 | + * <br> | ||
| 60 | + * @param d - Raw difference signal sample | ||
| 61 | + * @param y - Step size multiplier | ||
| 62 | + * @param table - Quantization table | ||
| 63 | + * @param size - Table size of short integers | ||
| 64 | + */ | ||
| 65 | + protected static int quantize(int d, int y, /*short*/int[] table, int size) { | ||
| 66 | + | ||
| 67 | + /* LOG | ||
| 68 | + * Compute base 2 log of 'd', and store in 'dl'. | ||
| 69 | + */ | ||
| 70 | + /*short*/int dqm=Math.abs(d); /* Magnitude of 'd' */ | ||
| 71 | + | ||
| 72 | + /*short*/int exp=quan(dqm>>1, power2, 15); /* Integer part of base 2 log of 'd' */ | ||
| 73 | + | ||
| 74 | + /*short*/int mant=((dqm<<7)>>exp)&0x7F; /* Fractional part of base 2 log */ | ||
| 75 | + | ||
| 76 | + /*short*/int dl=(exp<<7)+mant; /* Log of magnitude of 'd' */ | ||
| 77 | + | ||
| 78 | + /* SUBTB | ||
| 79 | + * "Divide" by step size multiplier. | ||
| 80 | + */ | ||
| 81 | + /* Step size scale factor normalized log */ | ||
| 82 | + /*short*/int dln=dl-(y>>2); | ||
| 83 | + | ||
| 84 | + /* QUAN | ||
| 85 | + * Obtain codword i for 'd'. | ||
| 86 | + */ | ||
| 87 | + int i=quan(dln, table, size); | ||
| 88 | + if (d<0) /* take 1's complement of i */ | ||
| 89 | + return ((size<<1)+1-i); | ||
| 90 | + else if (i==0) /* take 1's complement of 0 */ | ||
| 91 | + return ((size<<1)+1); /* new in 1988 */ | ||
| 92 | + else | ||
| 93 | + return (i); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + | ||
| 97 | + /** Returns reconstructed difference signal 'dq' obtained from | ||
| 98 | + * codeword 'i' and quantization step size scale factor 'y'. | ||
| 99 | + * Multiplication is performed in log base 2 domain as addition. | ||
| 100 | + * @param sign - 0 for non-negative value | ||
| 101 | + * @param dqln - G.72x codeword | ||
| 102 | + * @param y - Step size multiplier | ||
| 103 | + */ | ||
| 104 | + protected static int reconstruct(int sign, int dqln, int y) { | ||
| 105 | + | ||
| 106 | + /* Log of 'dq' magnitude */ | ||
| 107 | + /*short*/int dql=dqln+(y>>2); /* ADDA */ | ||
| 108 | + | ||
| 109 | + if (dql<0) { | ||
| 110 | + return ((sign!=0)? -0x8000 : 0); | ||
| 111 | + } | ||
| 112 | + else { | ||
| 113 | + /* ANTILOG */ | ||
| 114 | + /* Integer part of log */ | ||
| 115 | + /*short*/int dex=(dql>>7)&15; | ||
| 116 | + /*short*/int dqt=128+(dql&127); | ||
| 117 | + /* Reconstructed difference signal sample */ | ||
| 118 | + /*short*/int dq=(dqt<<7)>>(14-dex); | ||
| 119 | + return ((sign!=0)? (dq-0x8000) : dq); | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + | ||
| 124 | + /** updates the state variables for each output code | ||
| 125 | + * @param code_size - distinguish 723_40 with others | ||
| 126 | + * @param y - quantizer step size | ||
| 127 | + * @param wi - scale factor multiplier | ||
| 128 | + * @param fi - for long/short term energies | ||
| 129 | + * @param dq - quantized prediction difference | ||
| 130 | + * @param sr - reconstructed signal | ||
| 131 | + * @param dqsez - difference from 2-pole predictor | ||
| 132 | + * @param state - coder state | ||
| 133 | + */ | ||
| 134 | + protected static void update(int code_size, int y, int wi, int fi, int dq, int sr, int dqsez, G726State state) { | ||
| 135 | + | ||
| 136 | + int cnt; | ||
| 137 | + /*short*/int mag, exp, mant; /* Adaptive predictor, FLOAT A */ | ||
| 138 | + /*short*/int a2p; /* LIMC */ | ||
| 139 | + /*short*/int a1ul; /* UPA1 */ | ||
| 140 | + /*short*/int ua2, pks1; /* UPA2 */ | ||
| 141 | + /*short*/int uga2a, fa1; | ||
| 142 | + /*short*/int uga2b; | ||
| 143 | + /*char*/int tr; /* tone/transition detector */ | ||
| 144 | + /*short*/int ylint, thr2, dqthr; | ||
| 145 | + /*short*/int ylfrac, thr1; | ||
| 146 | + /*short*/int pk0; | ||
| 147 | + | ||
| 148 | + // ##### C-to-Java conversion: ##### | ||
| 149 | + // init a2p | ||
| 150 | + a2p=0; | ||
| 151 | + | ||
| 152 | + pk0=(dqsez<0)? 1 : 0; /* needed in updating predictor poles */ | ||
| 153 | + | ||
| 154 | + mag=dq&0x7FFF; /* prediction difference magnitude */ | ||
| 155 | + /* TRANS */ | ||
| 156 | + ylint=state.yl>>15; /* exponent part of yl */ | ||
| 157 | + ylfrac=(state.yl>>10)&0x1F; /* fractional part of yl */ | ||
| 158 | + thr1=(32+ylfrac)<<ylint; /* threshold */ | ||
| 159 | + thr2=(ylint>9)? 31<<10 : thr1; /* limit thr2 to 31<<10 */ | ||
| 160 | + dqthr=(thr2+(thr2>>1))>>1; /* dqthr=0.75 * thr2 */ | ||
| 161 | + if (state.td==0) /* signal supposed voice */ | ||
| 162 | + tr=0; | ||
| 163 | + else | ||
| 164 | + if (mag<=dqthr) /* supposed data, but small mag */ | ||
| 165 | + tr=0; /* treated as voice */ | ||
| 166 | + else /* signal is data (modem) */ | ||
| 167 | + tr=1; | ||
| 168 | + | ||
| 169 | + /* Quantizer scale factor adaptation. */ | ||
| 170 | + | ||
| 171 | + /* FUNCTW&FILTD&DELAY */ | ||
| 172 | + /* update non-steady state step size multiplier */ | ||
| 173 | + state.yu=y+((wi-y)>>5); | ||
| 174 | + | ||
| 175 | + /* LIMB */ | ||
| 176 | + if (state.yu<544) /* 544<=yu<=5120 */ | ||
| 177 | + state.yu=544; | ||
| 178 | + else | ||
| 179 | + if (state.yu>5120) | ||
| 180 | + state.yu=5120; | ||
| 181 | + | ||
| 182 | + /* FILTE&DELAY */ | ||
| 183 | + /* update steady state step size multiplier */ | ||
| 184 | + state.yl+=state.yu+((-state.yl)>>6); | ||
| 185 | + | ||
| 186 | + /* | ||
| 187 | + * Adaptive predictor coefficients. | ||
| 188 | + */ | ||
| 189 | + if (tr==1) { | ||
| 190 | + /* reset a's and b's for modem signal */ | ||
| 191 | + state.a[0]=0; | ||
| 192 | + state.a[1]=0; | ||
| 193 | + state.b[0]=0; | ||
| 194 | + state.b[1]=0; | ||
| 195 | + state.b[2]=0; | ||
| 196 | + state.b[3]=0; | ||
| 197 | + state.b[4]=0; | ||
| 198 | + state.b[5]=0; | ||
| 199 | + } | ||
| 200 | + else { | ||
| 201 | + /* update a's and b's */ | ||
| 202 | + pks1=pk0^state.pk[0]; /* UPA2 */ | ||
| 203 | + | ||
| 204 | + /* update predictor pole a[1] */ | ||
| 205 | + a2p=state.a[1]-(state.a[1]>>7); | ||
| 206 | + if (dqsez != 0) { | ||
| 207 | + fa1=(pks1!=0)? state.a[0] : -state.a[0]; | ||
| 208 | + if (fa1<-8191) /* a2p=function of fa1 */ | ||
| 209 | + a2p-=0x100; | ||
| 210 | + else | ||
| 211 | + if (fa1>8191) | ||
| 212 | + a2p+=0xFF; | ||
| 213 | + else | ||
| 214 | + a2p+=fa1>>5; | ||
| 215 | + | ||
| 216 | + if ((pk0^state.pk[1])!=0) { | ||
| 217 | + /* LIMC */ | ||
| 218 | + if (a2p<=-12160) | ||
| 219 | + a2p=-12288; | ||
| 220 | + else | ||
| 221 | + if (a2p>=12416) | ||
| 222 | + a2p=12288; | ||
| 223 | + else | ||
| 224 | + a2p-=0x80; | ||
| 225 | + } | ||
| 226 | + else | ||
| 227 | + if (a2p<=-12416) | ||
| 228 | + a2p=-12288; | ||
| 229 | + else | ||
| 230 | + if (a2p>=12160) | ||
| 231 | + a2p=12288; | ||
| 232 | + else | ||
| 233 | + a2p+=0x80; | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + /* TRIGB&DELAY */ | ||
| 237 | + state.a[1]=a2p; | ||
| 238 | + | ||
| 239 | + /* UPA1 */ | ||
| 240 | + /* update predictor pole a[0] */ | ||
| 241 | + state.a[0] -= state.a[0]>>8; | ||
| 242 | + if (dqsez != 0) | ||
| 243 | + if (pks1==0) | ||
| 244 | + state.a[0]+=192; | ||
| 245 | + else | ||
| 246 | + state.a[0] -= 192; | ||
| 247 | + | ||
| 248 | + /* LIMD */ | ||
| 249 | + a1ul=15360-a2p; | ||
| 250 | + if (state.a[0]<-a1ul) | ||
| 251 | + state.a[0]=-a1ul; | ||
| 252 | + else if (state.a[0]>a1ul) | ||
| 253 | + state.a[0]=a1ul; | ||
| 254 | + | ||
| 255 | + /* UPB : update predictor zeros b[6] */ | ||
| 256 | + for (cnt=0; cnt<6; cnt++) { | ||
| 257 | + | ||
| 258 | + if (code_size==5) /* for 40Kbps G.723 */ | ||
| 259 | + state.b[cnt]-=state.b[cnt]>>9; | ||
| 260 | + else /* for G.721 and 24Kbps G.723 */ | ||
| 261 | + state.b[cnt]-=state.b[cnt]>>8; | ||
| 262 | + if ((dq&0x7FFF)!=0) { | ||
| 263 | + /* XOR */ | ||
| 264 | + if ((dq^state.dq[cnt])>=0) | ||
| 265 | + state.b[cnt]+=128; | ||
| 266 | + else | ||
| 267 | + state.b[cnt]-=128; | ||
| 268 | + } | ||
| 269 | + } | ||
| 270 | + } | ||
| 271 | + | ||
| 272 | + for (cnt=5; cnt>0; cnt--) state.dq[cnt]=state.dq[cnt-1]; | ||
| 273 | + /* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */ | ||
| 274 | + if (mag==0) { | ||
| 275 | + state.dq[0]=(dq>=0)? 0x20 : 0xFC20; | ||
| 276 | + } | ||
| 277 | + else { | ||
| 278 | + exp=quan(mag, power2, 15); | ||
| 279 | + state.dq[0]=(dq>=0) ? (exp<<6)+((mag<<6)>>exp) : (exp<<6)+((mag<<6)>>exp)-0x400; | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + state.sr[1]=state.sr[0]; | ||
| 283 | + /* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */ | ||
| 284 | + if (sr==0) { | ||
| 285 | + state.sr[0]=0x20; | ||
| 286 | + } | ||
| 287 | + else | ||
| 288 | + if (sr>0) { | ||
| 289 | + exp=quan(sr, power2, 15); | ||
| 290 | + state.sr[0]=(exp<<6)+((sr<<6)>>exp); | ||
| 291 | + } | ||
| 292 | + else | ||
| 293 | + if (sr>-32768) { | ||
| 294 | + mag=-sr; | ||
| 295 | + exp=quan(mag, power2, 15); | ||
| 296 | + state.sr[0]=(exp<<6)+((mag<<6)>>exp)-0x400; | ||
| 297 | + } | ||
| 298 | + else | ||
| 299 | + state.sr[0]=0xFC20; | ||
| 300 | + | ||
| 301 | + /* DELAY A */ | ||
| 302 | + state.pk[1]=state.pk[0]; | ||
| 303 | + state.pk[0]=pk0; | ||
| 304 | + | ||
| 305 | + /* TONE */ | ||
| 306 | + if (tr==1) /* this sample has been treated as data */ | ||
| 307 | + state.td=0; /* next one will be treated as voice */ | ||
| 308 | + else | ||
| 309 | + if (a2p<-11776) /* small sample-to-sample correlation */ | ||
| 310 | + state.td=1; /* signal may be data */ | ||
| 311 | + else /* signal is voice */ | ||
| 312 | + state.td=0; | ||
| 313 | + | ||
| 314 | + /* | ||
| 315 | + * Adaptation speed control. | ||
| 316 | + */ | ||
| 317 | + state.dms+=(fi-state.dms)>>5; /* FILTA */ | ||
| 318 | + state.dml+=(((fi<<2)-state.dml)>>7); /* FILTB */ | ||
| 319 | + | ||
| 320 | + if (tr==1) | ||
| 321 | + state.ap=256; | ||
| 322 | + else | ||
| 323 | + if (y<1536) /* SUBTC */ | ||
| 324 | + state.ap+=(0x200-state.ap)>>4; | ||
| 325 | + else | ||
| 326 | + if (state.td==1) | ||
| 327 | + state.ap+=(0x200-state.ap)>>4; | ||
| 328 | + else | ||
| 329 | + if (Math.abs((state.dms<<2)-state.dml)>=(state.dml>>3)) | ||
| 330 | + state.ap+=(0x200-state.ap)>>4; | ||
| 331 | + else | ||
| 332 | + state.ap+=(-state.ap)>>4; | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + /** At the end of ADPCM decoding, it simulates an encoder which may be receiving | ||
| 336 | + * the output of this decoder as a tandem process. If the output of the | ||
| 337 | + * simulated encoder differs from the input to this decoder, the decoder output | ||
| 338 | + * is adjusted by one level of A-law or u-law codes. | ||
| 339 | + * | ||
| 340 | + * @param sr - decoder output linear PCM sample, | ||
| 341 | + * @param se - predictor estimate sample, | ||
| 342 | + * @param y - quantizer step size, | ||
| 343 | + * @param i - decoder input code, | ||
| 344 | + * @param sign - sign bit of code i | ||
| 345 | + * | ||
| 346 | + * @return adjusted A-law or u-law compressed sample. | ||
| 347 | + */ | ||
| 348 | + protected static int tandem_adjust_alaw(int sr, int se, int y, int i, int sign, /*short*/int[] qtab) { | ||
| 349 | + | ||
| 350 | + /*unsigned char*/int sp; /* A-law compressed 8-bit code */ | ||
| 351 | + /*short*/int dx; /* prediction error */ | ||
| 352 | + /*char*/int id; /* quantized prediction error */ | ||
| 353 | + int sd; /* adjusted A-law decoded sample value */ | ||
| 354 | + int im; /* biased magnitude of i */ | ||
| 355 | + int imx; /* biased magnitude of id */ | ||
| 356 | + | ||
| 357 | + if (sr<=-32768) sr=-1; | ||
| 358 | + sp= G711Codec.linear2alaw((short)((sr>>1)<<3)); /* short to A-law compression */ | ||
| 359 | + dx=(G711Codec.alaw2linear((byte)sp)>>2)-se; /* 16-bit prediction error */ | ||
| 360 | + id=quantize(dx, y, qtab, sign-1); | ||
| 361 | + | ||
| 362 | + if (id==i) { | ||
| 363 | + /* no adjustment on sp */ | ||
| 364 | + return (sp); | ||
| 365 | + } | ||
| 366 | + else { | ||
| 367 | + /* sp adjustment needed */ | ||
| 368 | + /* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */ | ||
| 369 | + im=i^sign; /* 2's complement to biased unsigned */ | ||
| 370 | + imx=id^sign; | ||
| 371 | + | ||
| 372 | + if (imx>im) { | ||
| 373 | + /* sp adjusted to next lower value */ | ||
| 374 | + if ((sp&0x80)!=0) { | ||
| 375 | + sd=(sp==0xD5)? 0x55 : ((sp^0x55)-1)^0x55; | ||
| 376 | + } | ||
| 377 | + else { | ||
| 378 | + sd=(sp==0x2A)? 0x2A : ((sp^0x55)+1)^0x55; | ||
| 379 | + } | ||
| 380 | + } | ||
| 381 | + else { | ||
| 382 | + /* sp adjusted to next higher value */ | ||
| 383 | + if ((sp&0x80)!=0) | ||
| 384 | + sd=(sp==0xAA)? 0xAA : ((sp^0x55)+1)^0x55; | ||
| 385 | + else | ||
| 386 | + sd=(sp==0x55)? 0xD5 : ((sp^0x55)-1)^0x55; | ||
| 387 | + } | ||
| 388 | + return (sd); | ||
| 389 | + } | ||
| 390 | + } | ||
| 391 | + | ||
| 392 | + /** @param sr - decoder output linear PCM sample | ||
| 393 | + * @param se - predictor estimate sample | ||
| 394 | + * @param y - quantizer step size | ||
| 395 | + * @param i - decoder input code | ||
| 396 | + * @param sign | ||
| 397 | + * @param qtab | ||
| 398 | + */ | ||
| 399 | + protected static int tandem_adjust_ulaw(int sr, int se, int y, int i, int sign, /*short*/int[] qtab) { | ||
| 400 | + | ||
| 401 | + /*unsigned char*/int sp; /* u-law compressed 8-bit code */ | ||
| 402 | + /*short*/int dx; /* prediction error */ | ||
| 403 | + /*char*/int id; /* quantized prediction error */ | ||
| 404 | + int sd; /* adjusted u-law decoded sample value */ | ||
| 405 | + int im; /* biased magnitude of i */ | ||
| 406 | + int imx; /* biased magnitude of id */ | ||
| 407 | + | ||
| 408 | + if (sr<=-32768) sr=0; | ||
| 409 | + sp= G711UCodec.linear2ulaw((short)(sr<<2)); /* short to u-law compression */ | ||
| 410 | + dx=(G711UCodec.ulaw2linear((byte)sp)>>2)-se; /* 16-bit prediction error */ | ||
| 411 | + id=quantize(dx, y, qtab, sign-1); | ||
| 412 | + if (id==i) { | ||
| 413 | + return (sp); | ||
| 414 | + } | ||
| 415 | + else { | ||
| 416 | + /* ADPCM codes : 8, 9, ... F, 0, 1, ... , 6, 7 */ | ||
| 417 | + im=i^sign; /* 2's complement to biased unsigned */ | ||
| 418 | + imx=id^sign; | ||
| 419 | + if (imx>im) { | ||
| 420 | + /* sp adjusted to next lower value */ | ||
| 421 | + if ((sp&0x80)!=0) | ||
| 422 | + sd=(sp==0xFF)? 0x7E : sp+1; | ||
| 423 | + else | ||
| 424 | + sd=(sp==0)? 0 : sp-1; | ||
| 425 | + | ||
| 426 | + } | ||
| 427 | + else { | ||
| 428 | + /* sp adjusted to next higher value */ | ||
| 429 | + if ((sp&0x80)!=0) | ||
| 430 | + sd=(sp==0x80)? 0x80 : sp-1; | ||
| 431 | + else | ||
| 432 | + sd=(sp==0x7F)? 0xFE : sp+1; | ||
| 433 | + } | ||
| 434 | + return (sd); | ||
| 435 | + } | ||
| 436 | + } | ||
| 437 | + | ||
| 438 | + | ||
| 439 | + // ##### C-to-Java conversion: ##### | ||
| 440 | + | ||
| 441 | + /** Converts a byte into an unsigned int. */ | ||
| 442 | + protected static int unsignedInt(byte b) { | ||
| 443 | + return ((int)b+0x100)&0xFF; | ||
| 444 | + } | ||
| 445 | + | ||
| 446 | + // ##### 2 bytes to int conversion: ##### | ||
| 447 | + | ||
| 448 | + /** Converts 2 little-endian-bytes into an unsigned int. */ | ||
| 449 | + public static int unsignedIntLittleEndian(byte hi_b, byte lo_b) { | ||
| 450 | + return (unsignedInt(hi_b)<<8) + unsignedInt(lo_b); | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + /** Converts 2 little-endian-bytes into a signed int. */ | ||
| 454 | + public static int signedIntLittleEndian(byte hi_b, byte lo_b) { | ||
| 455 | + int sign_bit=hi_b>>7; | ||
| 456 | + return (sign_bit==0)? (unsignedInt(hi_b)<<8) + unsignedInt(lo_b) : (-1^0x7FFF)^(((unsignedInt(hi_b)&0x7F)<<8) + unsignedInt(lo_b)); | ||
| 457 | + } | ||
| 458 | + | ||
| 459 | + | ||
| 460 | + // ************************* NON-STATIC ************************* | ||
| 461 | + | ||
| 462 | + /** Encoding state */ | ||
| 463 | + G726State state; | ||
| 464 | + | ||
| 465 | + int type; | ||
| 466 | + | ||
| 467 | + /** Creates a new G726 processor, that can be used to encode from or decode do PCM audio data. */ | ||
| 468 | + public G726(int type) { | ||
| 469 | + this.type = type; | ||
| 470 | + state=new G726State(); | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + public int getType() { | ||
| 474 | + return type; | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + /** Encodes the input vale of linear PCM, A-law or u-law data sl and returns | ||
| 478 | + * the resulting code. -1 is returned for unknown input coding value. */ | ||
| 479 | + public abstract int encode(int sl, int in_coding); | ||
| 480 | + | ||
| 481 | + | ||
| 482 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 483 | + * the G726 encoded chuck into out_buff. <br> | ||
| 484 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 485 | + * in_coding value. */ | ||
| 486 | + public abstract int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset); | ||
| 487 | + | ||
| 488 | + | ||
| 489 | + /** Decodes a 4-bit code of G.72x encoded data of i and | ||
| 490 | + * returns the resulting linear PCM, A-law or u-law value. | ||
| 491 | + * return -1 for unknown out_coding value. */ | ||
| 492 | + public abstract int decode(int i, int out_coding); | ||
| 493 | + | ||
| 494 | + | ||
| 495 | + /** Decodes the input chunk in_buff of G726 encoded data and returns | ||
| 496 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 497 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 498 | + * out_coding value. */ | ||
| 499 | + public abstract int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset); | ||
| 500 | + | ||
| 501 | +} | ||
| 0 | \ No newline at end of file | 502 | \ No newline at end of file |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/g726/G726State.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec.g726; | ||
| 2 | + | ||
| 3 | +public class G726State { | ||
| 4 | + | ||
| 5 | + /** Locked or steady state step size multiplier. */ | ||
| 6 | + /*long*/int yl; | ||
| 7 | + /** Unlocked or non-steady state step size multiplier. */ | ||
| 8 | + /*short*/int yu; | ||
| 9 | + /** Short term energy estimate. */ | ||
| 10 | + /*short*/int dms; | ||
| 11 | + /** Long term energy estimate. */ | ||
| 12 | + /*short*/int dml; | ||
| 13 | + /** Linear weighting coefficient of 'yl' and 'yu'. */ | ||
| 14 | + /*short*/int ap; | ||
| 15 | + | ||
| 16 | + /** Coefficients of pole portion of prediction filter. */ | ||
| 17 | + /*short*/int[] a; | ||
| 18 | + /** Coefficients of zero portion of prediction filter. */ | ||
| 19 | + /*short*/int[] b; | ||
| 20 | + /** Signs of previous two samples of a partially | ||
| 21 | + * reconstructed signal. */ | ||
| 22 | + /*short*/int[] pk; | ||
| 23 | + /** Previous 6 samples of the quantized difference | ||
| 24 | + * signal represented in an internal floating point | ||
| 25 | + * format. */ | ||
| 26 | + /*short*/int[] dq; | ||
| 27 | + /** Previous 2 samples of the quantized difference | ||
| 28 | + * signal represented in an internal floating point | ||
| 29 | + * format. */ | ||
| 30 | + /*short*/int[] sr; | ||
| 31 | + /* delayed tone detect, new in 1988 version */ | ||
| 32 | + /*char*/int td; | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + /** The first 15 values, powers of 2. */ | ||
| 36 | + private static final /*short*/int[] power2 = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000 }; | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + /** Quantizes the input val against the table of size short integers. | ||
| 40 | + * It returns i if table[i-1]<=val<table[i]. | ||
| 41 | + * <p> | ||
| 42 | + * Using linear search for simple coding. */ | ||
| 43 | + private static int quan(int val, /*short*/int[] table, int size) { | ||
| 44 | + | ||
| 45 | + int i; | ||
| 46 | + for (i=0; i<size; i++) if (val<table[i]) break; | ||
| 47 | + return i; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + /** returns the integer product of the 14-bit integer "an" and | ||
| 52 | + * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn". */ | ||
| 53 | + private static int fmult(int an, int srn) { | ||
| 54 | + | ||
| 55 | + /*short*/int anmag=(an>0)? an : ((-an)&0x1FFF); | ||
| 56 | + /*short*/int anexp=quan(anmag, power2, 15)-6; | ||
| 57 | + /*short*/int anmant=(anmag==0)? 32 : | ||
| 58 | + (anexp >= 0)? anmag>>anexp : anmag<<-anexp; | ||
| 59 | + /*short*/int wanexp=anexp + ((srn>>6)&0xF) - 13; | ||
| 60 | + | ||
| 61 | + /*short*/int wanmant=(anmant*(srn&077) + 0x30)>>4; | ||
| 62 | + /*short*/int retval=(wanexp>=0)? ((wanmant<<wanexp)&0x7FFF) : (wanmant>>-wanexp); | ||
| 63 | + | ||
| 64 | + return (((an^srn)<0)? -retval : retval); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + | ||
| 68 | + /** Creates a new G726State. */ | ||
| 69 | + public G726State() { | ||
| 70 | + a=new /*short*/int[2]; | ||
| 71 | + b=new /*short*/int[6]; | ||
| 72 | + pk=new /*short*/int[2]; | ||
| 73 | + dq=new /*short*/int[6]; | ||
| 74 | + sr=new /*short*/int[2]; | ||
| 75 | + init(); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + /** This routine initializes and/or resets the G726State 'state'. <br> | ||
| 79 | + * All the initial state values are specified in the CCITT G.721 document. */ | ||
| 80 | + private void init() { | ||
| 81 | + yl=34816; | ||
| 82 | + yu=544; | ||
| 83 | + dms=0; | ||
| 84 | + dml=0; | ||
| 85 | + ap=0; | ||
| 86 | + for (int cnta=0; cnta<2; cnta++) { | ||
| 87 | + a[cnta]=0; | ||
| 88 | + pk[cnta]=0; | ||
| 89 | + sr[cnta]=32; | ||
| 90 | + } | ||
| 91 | + for (int cnta=0; cnta<6; cnta++) { | ||
| 92 | + b[cnta]=0; | ||
| 93 | + dq[cnta]=32; | ||
| 94 | + } | ||
| 95 | + td=0; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + /** computes the estimated signal from 6-zero predictor. */ | ||
| 99 | + public int predictor_zero() { | ||
| 100 | + | ||
| 101 | + int sezi=fmult(b[0]>>2, dq[0]); | ||
| 102 | + /* ACCUM */ | ||
| 103 | + for (int i=1; i<6; i++) sezi+=fmult(b[i]>>2,dq[i]); | ||
| 104 | + return sezi; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + | ||
| 108 | + /** computes the estimated signal from 2-pole predictor. */ | ||
| 109 | + public int predictor_pole() { | ||
| 110 | + | ||
| 111 | + return (fmult(a[1]>>2,sr[1]) + fmult(a[0]>>2,sr[0])); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + | ||
| 115 | + /** computes the quantization step size of the adaptive quantizer. */ | ||
| 116 | + public int step_size() { | ||
| 117 | + | ||
| 118 | + if (ap>=256) return (yu); | ||
| 119 | + else { | ||
| 120 | + int y=yl>>6; | ||
| 121 | + int dif=yu-y; | ||
| 122 | + int al=ap>>2; | ||
| 123 | + if (dif>0) y+=(dif * al)>>6; | ||
| 124 | + else | ||
| 125 | + if (dif<0) y+=(dif * al+0x3F)>>6; | ||
| 126 | + return y; | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/g726/G726_16.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec.g726; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.codec.G711UCodec; | ||
| 6 | + | ||
| 7 | +/** G726_16 encoder and decoder. | ||
| 8 | + * <p> | ||
| 9 | + * These routines comprise an implementation of the CCITT G.726 16kbps | ||
| 10 | + * ADPCM coding algorithm. Essentially, this implementation is identical to | ||
| 11 | + * the bit level description except for a few deviations which | ||
| 12 | + * take advantage of workstation attributes, such as hardware 2's | ||
| 13 | + * complement arithmetic. | ||
| 14 | + * <p> | ||
| 15 | + * The deviation from the bit level specification (lookup tables), | ||
| 16 | + * preserves the bit level performance specifications. | ||
| 17 | + * <p> | ||
| 18 | + * As outlined in the G.723 Recommendation, the algorithm is broken | ||
| 19 | + * down into modules. Each section of code below is preceded by | ||
| 20 | + * the name of the module which it is implementing. | ||
| 21 | + * <p> | ||
| 22 | + * This implementation is based on the ANSI-C language reference implementations | ||
| 23 | + * of the CCITT (International Telegraph and Telephone Consultative Committee) | ||
| 24 | + * G.711, G.721 and G.723 voice compressions, provided by Sun Microsystems, Inc. | ||
| 25 | + * <p> | ||
| 26 | + * Acknowledgement to Sun Microsystems, Inc. for having released the original | ||
| 27 | + * ANSI-C source code to the public domain. | ||
| 28 | + */ | ||
| 29 | +public class G726_16 extends G726 { | ||
| 30 | + | ||
| 31 | + // ##### C-to-Java conversion: ##### | ||
| 32 | + // short becomes int | ||
| 33 | + // char becomes int | ||
| 34 | + // unsigned char becomes int | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + // *************************** STATIC *************************** | ||
| 38 | + | ||
| 39 | + /* | ||
| 40 | + * Maps G723_16 code word to ructeconstructed scale factor normalized log | ||
| 41 | + * magnitude values. | ||
| 42 | + */ | ||
| 43 | + static /*short*/int[] _dqlntab={116, 365, 365, 116}; | ||
| 44 | + | ||
| 45 | + /* Maps G723_16 code word to log of scale factor multiplier. */ | ||
| 46 | + static /*short*/int[] _witab={-704, 14048, 14048, -704}; | ||
| 47 | + | ||
| 48 | + /* | ||
| 49 | + * Maps G723_16 code words to a set of values whose long and short | ||
| 50 | + * term averages are computed and then compared to give an indication | ||
| 51 | + * how stationary (steady state) the signal is. | ||
| 52 | + */ | ||
| 53 | + static /*short*/int[] _fitab={ 0x000, 0xE00, 0xE00, 0x000}; | ||
| 54 | + | ||
| 55 | + static /*short*/int[] qtab_723_16={261}; | ||
| 56 | + | ||
| 57 | + /** Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens | ||
| 58 | + * the resulting 5-bit CCITT G726 16kbps code. | ||
| 59 | + * Returns -1 if the input coding value is invalid. */ | ||
| 60 | + public static int encode(int sl, int in_coding, G726State state) { | ||
| 61 | + | ||
| 62 | + /*short*/int sei, sezi, se, sez; /* ACCUM */ | ||
| 63 | + /*short*/int d; /* SUBTA */ | ||
| 64 | + /*short*/int y; /* MIX */ | ||
| 65 | + /*short*/int sr; /* ADDB */ | ||
| 66 | + /*short*/int dqsez; /* ADDC */ | ||
| 67 | + /*short*/int dq, i; | ||
| 68 | + | ||
| 69 | + switch (in_coding) { | ||
| 70 | + /* linearize input sample to 14-bit PCM */ | ||
| 71 | + case AUDIO_ENCODING_ALAW: | ||
| 72 | + sl= G711Codec.alaw2linear((byte) sl) >> 2; | ||
| 73 | + break; | ||
| 74 | + case AUDIO_ENCODING_ULAW: | ||
| 75 | + sl= G711UCodec.ulaw2linear((byte)sl) >> 2; | ||
| 76 | + break; | ||
| 77 | + case AUDIO_ENCODING_LINEAR: | ||
| 78 | + sl >>= 2; /* sl of 14-bit dynamic range */ | ||
| 79 | + break; | ||
| 80 | + default: | ||
| 81 | + return (-1); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + sezi=state.predictor_zero(); | ||
| 85 | + sez=sezi >> 1; | ||
| 86 | + sei=sezi+state.predictor_pole(); | ||
| 87 | + se=sei >> 1; /* se=estimated signal */ | ||
| 88 | + | ||
| 89 | + d=sl-se; /* d=estimation difference */ | ||
| 90 | + | ||
| 91 | + /* quantize prediction difference */ | ||
| 92 | + y=state.step_size(); /* adaptive quantizer step size */ | ||
| 93 | + i=quantize(d, y, qtab_723_16, 1); /* i=ADPCM code */ | ||
| 94 | + | ||
| 95 | + dq=reconstruct(i & 0x02, _dqlntab[i], y); /* quantized diff */ | ||
| 96 | + | ||
| 97 | + sr=(dq<0)? se-(dq & 0x3FFF) : se+dq; /* reconstructed signal */ | ||
| 98 | + | ||
| 99 | + dqsez=sr+sez-se; /* dqsez=pole prediction diff. */ | ||
| 100 | + | ||
| 101 | + update(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state); | ||
| 102 | + | ||
| 103 | + return (i); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + | ||
| 107 | + /** Decodes a 5-bit CCITT G.726 40kbps code and returns | ||
| 108 | + * the resulting 16-bit linear PCM, A-law or u-law sample value. | ||
| 109 | + * -1 is returned if the output coding is unknown. */ | ||
| 110 | + public static int decode(int i, int out_coding, G726State state) { | ||
| 111 | + | ||
| 112 | + /*short*/int sezi, sei, sez, se; /* ACCUM */ | ||
| 113 | + /*short*/int y, dif; /* MIX */ | ||
| 114 | + /*short*/int sr; /* ADDB */ | ||
| 115 | + /*short*/int dq; | ||
| 116 | + /*short*/int dqsez; | ||
| 117 | + | ||
| 118 | + i &= 0x03; /* mask to get proper bits */ | ||
| 119 | + sezi=state.predictor_zero(); | ||
| 120 | + sez=sezi >> 1; | ||
| 121 | + sei=sezi+state.predictor_pole(); | ||
| 122 | + se=sei >> 1; /* se=estimated signal */ | ||
| 123 | + | ||
| 124 | + y=state.step_size(); /* adaptive quantizer step size */ | ||
| 125 | + dq=reconstruct(i & 0x02, _dqlntab[i], y); /* estimation diff. */ | ||
| 126 | + | ||
| 127 | + sr=(dq<0)? (se-(dq & 0x3FFF)) : (se+dq); /* reconst. signal */ | ||
| 128 | + | ||
| 129 | + dqsez=sr-se+sez; /* pole prediction diff. */ | ||
| 130 | + | ||
| 131 | + update(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state); | ||
| 132 | + | ||
| 133 | + switch (out_coding) { | ||
| 134 | + | ||
| 135 | + case AUDIO_ENCODING_ALAW: | ||
| 136 | + return (tandem_adjust_alaw(sr, se, y, i, 0x02, qtab_723_16)); | ||
| 137 | + case AUDIO_ENCODING_ULAW: | ||
| 138 | + return (tandem_adjust_ulaw(sr, se, y, i, 0x02, qtab_723_16)); | ||
| 139 | + case AUDIO_ENCODING_LINEAR: | ||
| 140 | + return (sr << 2); /* sr was of 14-bit dynamic range */ | ||
| 141 | + default: | ||
| 142 | + return (-1); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + | ||
| 147 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 148 | + * the G726_16 encoded chuck into out_buff. <br> | ||
| 149 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 150 | + * in_coding value. */ | ||
| 151 | + public static int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 152 | + | ||
| 153 | + if (in_coding==AUDIO_ENCODING_ALAW || in_coding==AUDIO_ENCODING_ULAW) { | ||
| 154 | + | ||
| 155 | + int len_div_8=in_len/8; | ||
| 156 | + for (int i=0; i<len_div_8; i++) { | ||
| 157 | + long value8=0; | ||
| 158 | + int in_index=in_offset+i*8; | ||
| 159 | + for (int j=0; j<8; j++) { | ||
| 160 | + int in_value=unsignedInt(in_buff[in_index+j]); | ||
| 161 | + int out_value=encode(in_value,in_coding,state); | ||
| 162 | + value8+=((long)out_value)<<(2*(7-j)); | ||
| 163 | + } | ||
| 164 | + int out_index=out_offset+i*2; | ||
| 165 | + for (int k=0; k<2; k++) { | ||
| 166 | + out_buff[out_index+k]=(byte)(value8>>(8*(1-k))); | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + return len_div_8*2; | ||
| 170 | + } | ||
| 171 | + else | ||
| 172 | + if (in_coding==AUDIO_ENCODING_LINEAR) { | ||
| 173 | + | ||
| 174 | + int len_div_16=in_len/16; | ||
| 175 | + for (int i=0; i<len_div_16; i++) { | ||
| 176 | + long value16=0; | ||
| 177 | + int in_index=in_offset+i*16; | ||
| 178 | + for (int j=0; j<8; j++) { | ||
| 179 | + int j2=j*2; | ||
| 180 | + int in_value=signedIntLittleEndian(in_buff[in_index+j2+1],in_buff[in_index+j2]); | ||
| 181 | + int out_value=encode(in_value,in_coding,state); | ||
| 182 | + value16+=((long)out_value)<<(2*(7-j)); | ||
| 183 | + } | ||
| 184 | + int out_index=out_offset+i*2; | ||
| 185 | + for (int k=0; k<2; k++) { | ||
| 186 | + out_buff[out_index+k]=(byte)(value16>>(8*(1-k))); | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + return len_div_16*2; | ||
| 190 | + } | ||
| 191 | + else return -1; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + | ||
| 195 | + /** Decodes the input chunk in_buff of G726_16 encoded data and returns | ||
| 196 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 197 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 198 | + * out_coding value. */ | ||
| 199 | + public static int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 200 | + | ||
| 201 | + if (out_coding==AUDIO_ENCODING_ALAW || out_coding==AUDIO_ENCODING_ULAW) { | ||
| 202 | + | ||
| 203 | + int len_div_2=in_len/2; | ||
| 204 | + for (int i=0; i<len_div_2; i++) { | ||
| 205 | + int value8=0; | ||
| 206 | + int in_index=in_offset+i*2; | ||
| 207 | + for (int j=0; j<2; j++) { | ||
| 208 | + value8+=unsignedInt(in_buff[in_index+j])<<(8*(1-j)); | ||
| 209 | + } | ||
| 210 | + int out_index=out_offset+i*8; | ||
| 211 | + for (int k=0; k<8; k++) { | ||
| 212 | + int in_value=(value8>>(2*(7-k)))&0x3; | ||
| 213 | + int out_value=decode(in_value,out_coding,state); | ||
| 214 | + out_buff[out_index+k]=(byte)out_value; | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + return len_div_2*8; | ||
| 218 | + } | ||
| 219 | + else | ||
| 220 | + if (out_coding==AUDIO_ENCODING_LINEAR) { | ||
| 221 | + | ||
| 222 | + int len_div_2=in_len/2; | ||
| 223 | + for (int i=0; i<len_div_2; i++) { | ||
| 224 | + int value16=0; | ||
| 225 | + int in_index=in_offset+i*2; | ||
| 226 | + for (int j=0; j<2; j++) { | ||
| 227 | + value16+=unsignedInt(in_buff[in_index+j])<<(8*(1-j)); | ||
| 228 | + } | ||
| 229 | + int out_index=out_offset+i*16; | ||
| 230 | + for (int k=0; k<8; k++) { | ||
| 231 | + int k2=k*2; | ||
| 232 | + int in_value=(value16>>(2*(7-k)))&0x3; | ||
| 233 | + //int out_value=G711.ulaw2linear(decode(in_value,AUDIO_ENCODING_ULAW,state)); | ||
| 234 | + int out_value=decode(in_value,out_coding,state); | ||
| 235 | + out_buff[out_index+k2]=(byte)(out_value&0xFF); | ||
| 236 | + out_buff[out_index+k2+1]=(byte)(out_value>>8); | ||
| 237 | + } | ||
| 238 | + } | ||
| 239 | + return len_div_2*16; | ||
| 240 | + } | ||
| 241 | + else return -1; | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + | ||
| 245 | + // ************************* NON-STATIC ************************* | ||
| 246 | + | ||
| 247 | + /** Creates a new G726_16 processor, that can be used to encode from or decode do PCM audio data. */ | ||
| 248 | + public G726_16() { | ||
| 249 | + super(16000); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + | ||
| 253 | + /** Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens | ||
| 254 | + * the resulting 5-bit CCITT G.726 40kbps code. | ||
| 255 | + * Returns -1 if the input coding value is invalid. */ | ||
| 256 | + public int encode(int sl, int in_coding) { | ||
| 257 | + return encode(sl,in_coding,state); | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + | ||
| 261 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 262 | + * the G726_16 encoded chuck into out_buff. <br> | ||
| 263 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 264 | + * in_coding value. */ | ||
| 265 | + public int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset) { | ||
| 266 | + return encode(in_buff,in_offset,in_len,in_coding,out_buff,out_offset,state); | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + | ||
| 270 | + /** Decodes a 5-bit CCITT G.726 40kbps code and returns | ||
| 271 | + * the resulting 16-bit linear PCM, A-law or u-law sample value. | ||
| 272 | + * -1 is returned if the output coding is unknown. */ | ||
| 273 | + public int decode(int i, int out_coding) { | ||
| 274 | + return decode(i,out_coding,state); | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + | ||
| 278 | + /** Decodes the input chunk in_buff of G726_16 encoded data and returns | ||
| 279 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 280 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 281 | + * out_coding value. */ | ||
| 282 | + public int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset) { | ||
| 283 | + return decode(in_buff,in_offset,in_len,out_coding,out_buff,out_offset,state); | ||
| 284 | + } | ||
| 285 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/g726/G726_24.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec.g726; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.codec.G711UCodec; | ||
| 6 | + | ||
| 7 | +/** G726_24 encoder and decoder. | ||
| 8 | + * <p> | ||
| 9 | + * These routines comprise an implementation of the CCITT G.726 24kbps | ||
| 10 | + * ADPCM coding algorithm. Essentially, this implementation is identical to | ||
| 11 | + * the bit level description except for a few deviations which take advantage | ||
| 12 | + * of workstation attributes, such as hardware 2's complement arithmetic. | ||
| 13 | + * <p> | ||
| 14 | + * This implementation is based on the ANSI-C language reference implementations | ||
| 15 | + * of the CCITT (International Telegraph and Telephone Consultative Committee) | ||
| 16 | + * G.711, G.721 and G.723 voice compressions, provided by Sun Microsystems, Inc. | ||
| 17 | + * <p> | ||
| 18 | + * Acknowledgement to Sun Microsystems, Inc. for having released the original | ||
| 19 | + * ANSI-C source code to the public domain. | ||
| 20 | + */ | ||
| 21 | +public class G726_24 extends G726 { | ||
| 22 | + | ||
| 23 | + // ##### C-to-Java conversion: ##### | ||
| 24 | + // short becomes int | ||
| 25 | + // char becomes int | ||
| 26 | + // unsigned char becomes int | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + // *************************** STATIC *************************** | ||
| 30 | + | ||
| 31 | + /* | ||
| 32 | + * Maps G726_24 code word to reconstructed scale factor normalized log | ||
| 33 | + * magnitude values. | ||
| 34 | + */ | ||
| 35 | + static /*short*/int[] _dqlntab={-2048, 135, 273, 373, 373, 273, 135, -2048}; | ||
| 36 | + | ||
| 37 | + /* Maps G726_24 code word to log of scale factor multiplier. */ | ||
| 38 | + static /*short*/int[] _witab={-128, 960, 4384, 18624, 18624, 4384, 960, -128}; | ||
| 39 | + | ||
| 40 | + /* | ||
| 41 | + * Maps G726_24 code words to a set of values whose long and short | ||
| 42 | + * term averages are computed and then compared to give an indication | ||
| 43 | + * how stationary (steady state) the signal is. | ||
| 44 | + */ | ||
| 45 | + static /*short*/int[] _fitab={0, 0x200, 0x400, 0xE00, 0xE00, 0x400, 0x200, 0}; | ||
| 46 | + | ||
| 47 | + static /*short*/int[] qtab_723_24={8, 218, 331}; | ||
| 48 | + | ||
| 49 | + /** Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code. | ||
| 50 | + * Returns -1 if invalid input coding value. */ | ||
| 51 | + public static int encode(int sl, int in_coding, G726State state) { | ||
| 52 | + | ||
| 53 | + /*short*/int sei, sezi, se, sez; /* ACCUM */ | ||
| 54 | + /*short*/int d; /* SUBTA */ | ||
| 55 | + /*short*/int y; /* MIX */ | ||
| 56 | + /*short*/int sr; /* ADDB */ | ||
| 57 | + /*short*/int dqsez; /* ADDC */ | ||
| 58 | + /*short*/int dq, i; | ||
| 59 | + | ||
| 60 | + switch (in_coding) { | ||
| 61 | + /* linearize input sample to 14-bit PCM */ | ||
| 62 | + case AUDIO_ENCODING_ALAW: | ||
| 63 | + sl= G711Codec.alaw2linear((byte)sl) >> 2; | ||
| 64 | + break; | ||
| 65 | + case AUDIO_ENCODING_ULAW: | ||
| 66 | + sl= G711UCodec.ulaw2linear((byte)sl) >> 2; | ||
| 67 | + break; | ||
| 68 | + case AUDIO_ENCODING_LINEAR: | ||
| 69 | + sl >>= 2; /* sl of 14-bit dynamic range */ | ||
| 70 | + break; | ||
| 71 | + default: | ||
| 72 | + return (-1); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + sezi=state.predictor_zero(); | ||
| 76 | + sez=sezi >> 1; | ||
| 77 | + sei=sezi+state.predictor_pole(); | ||
| 78 | + se=sei >> 1; /* se=estimated signal */ | ||
| 79 | + | ||
| 80 | + d=sl-se; /* d=estimation diff. */ | ||
| 81 | + | ||
| 82 | + /* quantize prediction difference d */ | ||
| 83 | + y=state.step_size(); /* quantizer step size */ | ||
| 84 | + i=quantize(d, y, qtab_723_24, 3); /* i=ADPCM code */ | ||
| 85 | + dq=reconstruct(i & 4, _dqlntab[i], y); /* quantized diff. */ | ||
| 86 | + | ||
| 87 | + sr=(dq<0)? se-(dq & 0x3FFF) : se+dq; /* reconstructed signal */ | ||
| 88 | + | ||
| 89 | + dqsez=sr+sez-se; /* pole prediction diff. */ | ||
| 90 | + | ||
| 91 | + update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state); | ||
| 92 | + | ||
| 93 | + return (i); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + | ||
| 97 | + /** Decodes a 3-bit CCITT G.726 24kbps ADPCM code and returns | ||
| 98 | + * the resulting 16-bit linear PCM, A-law or u-law sample value. | ||
| 99 | + * -1 is returned if the output coding is unknown. */ | ||
| 100 | + public static int decode(int i, int out_coding, G726State state) { | ||
| 101 | + | ||
| 102 | + /*short*/int sezi, sei, sez, se; /* ACCUM */ | ||
| 103 | + /*short*/int y; /* MIX */ | ||
| 104 | + /*short*/int sr; /* ADDB */ | ||
| 105 | + /*short*/int dq; | ||
| 106 | + /*short*/int dqsez; | ||
| 107 | + | ||
| 108 | + i &= 0x07; /* mask to get proper bits */ | ||
| 109 | + sezi=state.predictor_zero(); | ||
| 110 | + sez=sezi >> 1; | ||
| 111 | + sei=sezi+state.predictor_pole(); | ||
| 112 | + se=sei >> 1; /* se=estimated signal */ | ||
| 113 | + | ||
| 114 | + y=state.step_size(); /* adaptive quantizer step size */ | ||
| 115 | + dq=reconstruct(i & 0x04, _dqlntab[i], y); /* unquantize pred diff */ | ||
| 116 | + | ||
| 117 | + sr=(dq<0)? (se-(dq & 0x3FFF)) : (se+dq); /* reconst. signal */ | ||
| 118 | + | ||
| 119 | + dqsez=sr-se+sez; /* pole prediction diff. */ | ||
| 120 | + | ||
| 121 | + update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state); | ||
| 122 | + | ||
| 123 | + switch (out_coding) { | ||
| 124 | + | ||
| 125 | + case AUDIO_ENCODING_ALAW: | ||
| 126 | + return (tandem_adjust_alaw(sr, se, y, i, 4, qtab_723_24)); | ||
| 127 | + case AUDIO_ENCODING_ULAW: | ||
| 128 | + return (tandem_adjust_ulaw(sr, se, y, i, 4, qtab_723_24)); | ||
| 129 | + case AUDIO_ENCODING_LINEAR: | ||
| 130 | + return (sr << 2); /* sr was of 14-bit dynamic range */ | ||
| 131 | + default: | ||
| 132 | + return (-1); | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + | ||
| 137 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 138 | + * the G726_24 encoded chuck into out_buff. <br> | ||
| 139 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 140 | + * in_coding value. */ | ||
| 141 | + public static int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 142 | + | ||
| 143 | + if (in_coding==AUDIO_ENCODING_ALAW || in_coding==AUDIO_ENCODING_ULAW) { | ||
| 144 | + | ||
| 145 | + int len_div_8=in_len/8; | ||
| 146 | + for (int i=0; i<len_div_8; i++) { | ||
| 147 | + int value8=0; | ||
| 148 | + int i8=i*8; | ||
| 149 | + for (int j=0; j<8; j++) { | ||
| 150 | + int in_value=unsignedInt(in_buff[in_offset+i8+j]); | ||
| 151 | + int out_value=encode(in_value,in_coding,state); | ||
| 152 | + value8+=out_value<<(3*(7-j)); | ||
| 153 | + } | ||
| 154 | + int index=out_offset+i*3; | ||
| 155 | + for (int k=0; k<3; k++) { | ||
| 156 | + out_buff[index+k]=(byte)(value8>>(8*(2-k))); | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + return len_div_8*3; | ||
| 160 | + } | ||
| 161 | + else | ||
| 162 | + if (in_coding==AUDIO_ENCODING_LINEAR) { | ||
| 163 | + | ||
| 164 | + int len_div_16=in_len/16; | ||
| 165 | + for (int i=0; i<len_div_16; i++) { | ||
| 166 | + int value16=0; | ||
| 167 | + int in_index=in_offset+i*16; | ||
| 168 | + for (int j=0; j<8; j++) { | ||
| 169 | + int j2=j*2; | ||
| 170 | + int in_value=signedIntLittleEndian(in_buff[in_index+j2+1],in_buff[in_index+j2]); | ||
| 171 | + //int out_value=encode(G711.linear2ulaw(in_value),AUDIO_ENCODING_ULAW,state); | ||
| 172 | + int out_value=encode(in_value,in_coding,state); | ||
| 173 | + value16+=out_value<<(3*(7-j)); | ||
| 174 | + } | ||
| 175 | + int out_index=out_offset+i*3; | ||
| 176 | + for (int k=0; k<3; k++) { | ||
| 177 | + out_buff[out_index+k]=(byte)(value16>>(8*(2-k))); | ||
| 178 | + } | ||
| 179 | + } | ||
| 180 | + return len_div_16*3; | ||
| 181 | + } | ||
| 182 | + else return -1; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + | ||
| 186 | + /** Decodes the input chunk in_buff of G726_24 encoded data and returns | ||
| 187 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 188 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 189 | + * out_coding value. */ | ||
| 190 | + public static int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 191 | + | ||
| 192 | + if (out_coding==AUDIO_ENCODING_ALAW || out_coding==AUDIO_ENCODING_ULAW) { | ||
| 193 | + | ||
| 194 | + int len_div_3=in_len/3; | ||
| 195 | + for (int i=0; i<len_div_3; i++) { | ||
| 196 | + int value8=0; | ||
| 197 | + int in_index=in_offset+i*3; | ||
| 198 | + for (int j=0; j<3; j++) { | ||
| 199 | + value8+=unsignedInt(in_buff[in_index+j])<<(8*(2-j)); | ||
| 200 | + } | ||
| 201 | + int out_index=out_offset+i*8; | ||
| 202 | + for (int k=0; k<8; k++) { | ||
| 203 | + int in_value=(value8>>(3*(7-k)))&0x7; | ||
| 204 | + int out_value=decode(in_value,out_coding,state); | ||
| 205 | + out_buff[out_index+k]=(byte)out_value; | ||
| 206 | + } | ||
| 207 | + } | ||
| 208 | + return len_div_3*8; | ||
| 209 | + } | ||
| 210 | + else | ||
| 211 | + if (out_coding==AUDIO_ENCODING_LINEAR) { | ||
| 212 | + | ||
| 213 | + int len_div_3=in_len/3; | ||
| 214 | + for (int i=0; i<len_div_3; i++) { | ||
| 215 | + int value16=0; | ||
| 216 | + int in_index=in_offset+i*3; | ||
| 217 | + for (int j=0; j<3; j++) { | ||
| 218 | + value16+=unsignedInt(in_buff[in_index+j])<<(8*(2-j)); | ||
| 219 | + } | ||
| 220 | + int out_index=out_offset+i*16; | ||
| 221 | + for (int k=0; k<8; k++) { | ||
| 222 | + int k2=k*2; | ||
| 223 | + int in_value=(value16>>(3*(7-k)))&0x7; | ||
| 224 | + //int out_value=G711.ulaw2linear(decode(in_value,AUDIO_ENCODING_ULAW,state)); | ||
| 225 | + int out_value=decode(in_value,out_coding,state); | ||
| 226 | + out_buff[out_index+k2]=(byte)(out_value&0xFF); | ||
| 227 | + out_buff[out_index+k2+1]=(byte)(out_value>>8); | ||
| 228 | + } | ||
| 229 | + } | ||
| 230 | + return len_div_3*16; | ||
| 231 | + } | ||
| 232 | + else return -1; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + | ||
| 236 | + // ************************* NON-STATIC ************************* | ||
| 237 | + | ||
| 238 | + /** Creates a new G726_24 processor, that can be used to encode from or decode do PCM audio data. */ | ||
| 239 | + public G726_24() { | ||
| 240 | + super(24000); | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + | ||
| 244 | + /** Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code. | ||
| 245 | + * Returns -1 if invalid input coding value. */ | ||
| 246 | + public int encode(int sl, int in_coding) { | ||
| 247 | + return encode(sl,in_coding,state); | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + | ||
| 251 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 252 | + * the G726_24 encoded chuck into out_buff. <br> | ||
| 253 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 254 | + * in_coding value. */ | ||
| 255 | + public int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset) { | ||
| 256 | + return encode(in_buff,in_offset,in_len,in_coding,out_buff,out_offset,state); | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + | ||
| 260 | + /** Decodes a 3-bit CCITT G.726 24kbps ADPCM code and returns | ||
| 261 | + * the resulting 16-bit linear PCM, A-law or u-law sample value. | ||
| 262 | + * -1 is returned if the output coding is unknown. */ | ||
| 263 | + public int decode(int i, int out_coding) { | ||
| 264 | + return decode(i,out_coding,state); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + | ||
| 268 | + /** Decodes the input chunk in_buff of G726_24 encoded data and returns | ||
| 269 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 270 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 271 | + * out_coding value. */ | ||
| 272 | + public int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset) { | ||
| 273 | + return decode(in_buff,in_offset,in_len,out_coding,out_buff,out_offset,state); | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/g726/G726_32.java
0 → 100644
| 1 | + | ||
| 2 | +package com.genersoft.iot.vmp.jt1078.codec.g726; | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | ||
| 6 | +import com.genersoft.iot.vmp.jt1078.codec.G711UCodec; | ||
| 7 | + | ||
| 8 | +/** G726_32 encoder and decoder. | ||
| 9 | + * <p> | ||
| 10 | + * These routines comprise an implementation of the CCITT G.726 32kbps ADPCM | ||
| 11 | + * coding algorithm. Essentially, this implementation is identical to | ||
| 12 | + * the bit level description except for a few deviations which | ||
| 13 | + * take advantage of work station attributes, such as hardware 2's | ||
| 14 | + * complement arithmetic and large memory. Specifically, certain time | ||
| 15 | + * consuming operations such as multiplications are replaced | ||
| 16 | + * with lookup tables and software 2's complement operations are | ||
| 17 | + * replaced with hardware 2's complement. | ||
| 18 | + * <p> | ||
| 19 | + * The deviation from the bit level specification (lookup tables) | ||
| 20 | + * preserves the bit level performance specifications. | ||
| 21 | + * <p> | ||
| 22 | + * As outlined in the G.726 Recommendation, the algorithm is broken | ||
| 23 | + * down into modules. Each section of code below is preceded by | ||
| 24 | + * the name of the module which it is implementing. | ||
| 25 | + * <p> | ||
| 26 | + * This implementation is based on the ANSI-C language reference implementations | ||
| 27 | + * of the CCITT (International Telegraph and Telephone Consultative Committee) | ||
| 28 | + * G.711, G.721 and G.723 voice compressions, provided by Sun Microsystems, Inc. | ||
| 29 | + * <p> | ||
| 30 | + * Acknowledgement to Sun Microsystems, Inc. for having released the original | ||
| 31 | + * ANSI-C source code to the public domain. | ||
| 32 | + */ | ||
| 33 | +public class G726_32 extends G726 { | ||
| 34 | + | ||
| 35 | + // ##### C-to-Java conversion: ##### | ||
| 36 | + // short becomes int | ||
| 37 | + // char becomes int | ||
| 38 | + // unsigned char becomes int | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + // *************************** STATIC *************************** | ||
| 42 | + | ||
| 43 | + static /*short*/int[] qtab_721={-124, 80, 178, 246, 300, 349, 400}; | ||
| 44 | + /* | ||
| 45 | + * Maps G726_32 code word to reconstructed scale factor normalized log | ||
| 46 | + * magnitude values. | ||
| 47 | + */ | ||
| 48 | + static /*short*/int[] _dqlntab={-2048, 4, 135, 213, 273, 323, 373, 425, 425, 373, 323, 273, 213, 135, 4, -2048}; | ||
| 49 | + | ||
| 50 | + /* Maps G726_32 code word to log of scale factor multiplier. */ | ||
| 51 | + static /*short*/int[] _witab={-12, 18, 41, 64, 112, 198, 355, 1122, 1122, 355, 198, 112, 64, 41, 18, -12}; | ||
| 52 | + | ||
| 53 | + /* | ||
| 54 | + * Maps G726_32 code words to a set of values whose long and short | ||
| 55 | + * term averages are computed and then compared to give an indication | ||
| 56 | + * how stationary (steady state) the signal is. | ||
| 57 | + */ | ||
| 58 | + static /*short*/int[] _fitab={0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00, 0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0}; | ||
| 59 | + | ||
| 60 | + /** Encodes the input vale of linear PCM, A-law or u-law data sl and returns | ||
| 61 | + * the resulting code. -1 is returned for unknown input coding value. */ | ||
| 62 | + public static int encode(int sl, int in_coding, G726State state) { | ||
| 63 | + | ||
| 64 | + /*short*/int sezi, se, sez; /* ACCUM */ | ||
| 65 | + /*short*/int d; /* SUBTA */ | ||
| 66 | + /*short*/int sr; /* ADDB */ | ||
| 67 | + /*short*/int y; /* MIX */ | ||
| 68 | + /*short*/int dqsez; /* ADDC */ | ||
| 69 | + /*short*/int dq, i; | ||
| 70 | + | ||
| 71 | + switch (in_coding) { | ||
| 72 | + /* linearize input sample to 14-bit PCM */ | ||
| 73 | + case AUDIO_ENCODING_ALAW: | ||
| 74 | + sl= G711Codec.alaw2linear((byte)sl) >> 2; | ||
| 75 | + break; | ||
| 76 | + case AUDIO_ENCODING_ULAW: | ||
| 77 | + sl= G711UCodec.ulaw2linear((byte)sl) >> 2; | ||
| 78 | + break; | ||
| 79 | + case AUDIO_ENCODING_LINEAR: | ||
| 80 | + sl >>= 2; /* 14-bit dynamic range */ | ||
| 81 | + break; | ||
| 82 | + default: | ||
| 83 | + return -1; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + sezi=state.predictor_zero(); | ||
| 87 | + sez=sezi >> 1; | ||
| 88 | + se=(sezi+state.predictor_pole()) >> 1; /* estimated signal */ | ||
| 89 | + | ||
| 90 | + d=sl-se; /* estimation difference */ | ||
| 91 | + | ||
| 92 | + /* quantize the prediction difference */ | ||
| 93 | + y=state.step_size(); /* quantizer step size */ | ||
| 94 | + i=quantize(d, y, qtab_721, 7); /* i=ADPCM code */ | ||
| 95 | + | ||
| 96 | + dq=reconstruct(i & 8, _dqlntab[i], y); /* quantized est diff */ | ||
| 97 | + | ||
| 98 | + sr=(dq<0)? se-(dq & 0x3FFF) : se+dq; /* reconst. signal */ | ||
| 99 | + | ||
| 100 | + dqsez=sr+sez-se; /* pole prediction diff. */ | ||
| 101 | + | ||
| 102 | + update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state); | ||
| 103 | + | ||
| 104 | + return i; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + /** Decodes a 4-bit code of G726_32 encoded data of i and | ||
| 108 | + * returns the resulting linear PCM, A-law or u-law value. | ||
| 109 | + * return -1 for unknown out_coding value. */ | ||
| 110 | + public static int decode(int i, int out_coding, G726State state) { | ||
| 111 | + | ||
| 112 | + /*short*/int sezi, sei, sez, se; /* ACCUM */ | ||
| 113 | + /*short*/int y; /* MIX */ | ||
| 114 | + /*short*/int sr; /* ADDB */ | ||
| 115 | + /*short*/int dq; | ||
| 116 | + /*short*/int dqsez; | ||
| 117 | + | ||
| 118 | + i &= 0x0f; /* mask to get proper bits */ | ||
| 119 | + sezi=state.predictor_zero(); | ||
| 120 | + sez=sezi >> 1; | ||
| 121 | + sei=sezi+state.predictor_pole(); | ||
| 122 | + se=sei >> 1; /* se=estimated signal */ | ||
| 123 | + | ||
| 124 | + y=state.step_size(); /* dynamic quantizer step size */ | ||
| 125 | + | ||
| 126 | + dq=reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */ | ||
| 127 | + | ||
| 128 | + sr=(dq<0)? (se-(dq & 0x3FFF)) : se+dq; /* reconst. signal */ | ||
| 129 | + | ||
| 130 | + dqsez=sr-se+sez; /* pole prediction diff. */ | ||
| 131 | + | ||
| 132 | + update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state); | ||
| 133 | + | ||
| 134 | + switch (out_coding) { | ||
| 135 | + | ||
| 136 | + case AUDIO_ENCODING_ALAW: | ||
| 137 | + return (tandem_adjust_alaw(sr, se, y, i, 8, qtab_721)); | ||
| 138 | + case AUDIO_ENCODING_ULAW: | ||
| 139 | + return (tandem_adjust_ulaw(sr, se, y, i, 8, qtab_721)); | ||
| 140 | + case AUDIO_ENCODING_LINEAR: | ||
| 141 | + return (sr << 2); /* sr was 14-bit dynamic range */ | ||
| 142 | + default: | ||
| 143 | + return -1; | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + | ||
| 148 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 149 | + * the G726_32 encoded chuck into out_buff. <br> | ||
| 150 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 151 | + * in_coding value. */ | ||
| 152 | + public static int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 153 | + | ||
| 154 | + if (in_coding==AUDIO_ENCODING_ALAW || in_coding==AUDIO_ENCODING_ULAW) { | ||
| 155 | + | ||
| 156 | + /* | ||
| 157 | + for (int i=0; i<in_len; i++) { | ||
| 158 | + int in_value=in_buff[in_offset+i]; | ||
| 159 | + int out_value=encode(in_value,in_coding,state); | ||
| 160 | + int i_div_2=i/2; | ||
| 161 | + if (i_div_2*2==i) | ||
| 162 | + out_buff[out_offset+i_div_2]=(byte)(out_value<<4); | ||
| 163 | + else | ||
| 164 | + out_buff[out_offset+i_div_2]=(byte)(out_value+unsignedInt(out_buff[out_offset+i_div_2])); | ||
| 165 | + } | ||
| 166 | + return in_len/2; | ||
| 167 | + */ | ||
| 168 | + int len_div_2=in_len/2; | ||
| 169 | + for (int i=0; i<len_div_2; i++) { | ||
| 170 | + int in_index=in_offset+i*2; | ||
| 171 | + int in_value1=in_buff[in_index]; | ||
| 172 | + int in_value2=in_buff[in_index+1]; | ||
| 173 | + int out_value1=encode(in_value1,in_coding,state); | ||
| 174 | + int out_value2=encode(in_value2,in_coding,state); | ||
| 175 | + out_buff[out_offset+i]=(byte)((out_value1<<4) + out_value2); | ||
| 176 | + } | ||
| 177 | + return len_div_2; | ||
| 178 | + } | ||
| 179 | + else | ||
| 180 | + if (in_coding==AUDIO_ENCODING_LINEAR) { | ||
| 181 | + | ||
| 182 | + int len_div_4=in_len/4; | ||
| 183 | + for (int i=0; i<len_div_4; i++) { | ||
| 184 | + int in_index=in_offset+i*4; | ||
| 185 | + int in_value1=signedIntLittleEndian(in_buff[in_index+1],in_buff[in_index+0]); | ||
| 186 | + int in_value2=signedIntLittleEndian(in_buff[in_index+3],in_buff[in_index+2]); | ||
| 187 | + | ||
| 188 | + //int out_value1=encode(G711.linear2ulaw(in_value1),AUDIO_ENCODING_ULAW,state); | ||
| 189 | + //int out_value2=encode(G711.linear2ulaw(in_value2),AUDIO_ENCODING_ULAW,state); | ||
| 190 | + int out_value1=encode(in_value1,in_coding,state); | ||
| 191 | + int out_value2=encode(in_value2,in_coding,state); | ||
| 192 | + out_buff[out_offset+i]=(byte)((out_value1<<4) + out_value2); | ||
| 193 | + } | ||
| 194 | + return len_div_4; | ||
| 195 | + } | ||
| 196 | + else return -1; | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + | ||
| 200 | + /** Decodes the input chunk in_buff of G726_32 encoded data and returns | ||
| 201 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 202 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 203 | + * out_coding value. */ | ||
| 204 | + public static int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 205 | + | ||
| 206 | + if (out_coding==AUDIO_ENCODING_ALAW || out_coding==AUDIO_ENCODING_ULAW) { | ||
| 207 | + | ||
| 208 | + /* | ||
| 209 | + for (int i=0; i<in_len*2; i++) { | ||
| 210 | + int in_value=unsignedInt(in_buff[in_offset+i/2]); | ||
| 211 | + if ((i/2)*2==i) | ||
| 212 | + in_value>>=4; | ||
| 213 | + else | ||
| 214 | + in_value%=0x10; | ||
| 215 | + int out_value=decode(in_value,out_coding,state); | ||
| 216 | + out_buff[out_offset+i]=(byte)out_value; | ||
| 217 | + } | ||
| 218 | + return in_len*2; | ||
| 219 | + */ | ||
| 220 | + for (int i=0; i<in_len; i++) { | ||
| 221 | + int in_value=unsignedInt(in_buff[in_offset+i]); | ||
| 222 | + int out_value1=decode(in_value>>4,out_coding,state); | ||
| 223 | + int out_value2=decode(in_value&0xF,out_coding,state); | ||
| 224 | + int out_index=out_offset+i*2; | ||
| 225 | + out_buff[out_index]=(byte)out_value1; | ||
| 226 | + out_buff[out_index+1]=(byte)out_value2; | ||
| 227 | + } | ||
| 228 | + return in_len*2; | ||
| 229 | + } | ||
| 230 | + else | ||
| 231 | + if (out_coding==AUDIO_ENCODING_LINEAR) { | ||
| 232 | + | ||
| 233 | + for (int i=0; i<in_len; i++) { | ||
| 234 | + int in_value=unsignedInt(in_buff[in_offset+i]); | ||
| 235 | + //int out_value1=G711.ulaw2linear(decode(in_value>>4,AUDIO_ENCODING_ULAW,state)); | ||
| 236 | + //int out_value2=G711.ulaw2linear(decode(in_value&0xF,AUDIO_ENCODING_ULAW,state)); | ||
| 237 | + int out_value1=decode(in_value>>4,out_coding,state); | ||
| 238 | + int out_value2=decode(in_value&0xF,out_coding,state); | ||
| 239 | + int out_index=out_offset+i*4; | ||
| 240 | + out_buff[out_index]=(byte)(out_value1&0xFF); | ||
| 241 | + out_buff[out_index+1]=(byte)(out_value1>>8); | ||
| 242 | + out_buff[out_index+2]=(byte)(out_value2&0xFF); | ||
| 243 | + out_buff[out_index+3]=(byte)(out_value2>>8); | ||
| 244 | + } | ||
| 245 | + return in_len*4; | ||
| 246 | + } | ||
| 247 | + else return -1; | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + | ||
| 251 | + // ************************* NON-STATIC ************************* | ||
| 252 | + | ||
| 253 | + /** Creates a new G726_32 processor, that can be used to encode from or decode do PCM audio data. */ | ||
| 254 | + public G726_32() { | ||
| 255 | + super(32000); | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + | ||
| 259 | + /** Encodes the input vale of linear PCM, A-law or u-law data sl and returns | ||
| 260 | + * the resulting code. -1 is returned for unknown input coding value. */ | ||
| 261 | + public int encode(int sl, int in_coding) { | ||
| 262 | + return encode(sl,in_coding,state); | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 266 | + * the G726_32 encoded chuck into out_buff. <br> | ||
| 267 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 268 | + * in_coding value. */ | ||
| 269 | + public int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset) { | ||
| 270 | + return encode(in_buff,in_offset,in_len,in_coding,out_buff,out_offset,state); | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + | ||
| 274 | + /** Decodes a 4-bit code of G726_32 encoded data of i and | ||
| 275 | + * returns the resulting linear PCM, A-law or u-law value. | ||
| 276 | + * return -1 for unknown out_coding value. */ | ||
| 277 | + public int decode(int i, int out_coding) { | ||
| 278 | + return decode(i,out_coding,state); | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + | ||
| 282 | + /** Decodes the input chunk in_buff of G726_32 encoded data and returns | ||
| 283 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 284 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 285 | + * out_coding value. */ | ||
| 286 | + public int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset) { | ||
| 287 | + return decode(in_buff,in_offset,in_len,out_coding,out_buff,out_offset,state); | ||
| 288 | + } | ||
| 289 | + | ||
| 290 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/codec/g726/G726_40.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.codec.g726; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.codec.G711UCodec; | ||
| 6 | + | ||
| 7 | +/** G726_40 encoder and decoder. | ||
| 8 | + * <p> | ||
| 9 | + * These routines comprise an implementation of the CCITT G.726 40kbps | ||
| 10 | + * ADPCM coding algorithm. Essentially, this implementation is identical to | ||
| 11 | + * the bit level description except for a few deviations which | ||
| 12 | + * take advantage of workstation attributes, such as hardware 2's | ||
| 13 | + * complement arithmetic. | ||
| 14 | + * <p> | ||
| 15 | + * The deviation from the bit level specification (lookup tables), | ||
| 16 | + * preserves the bit level performance specifications. | ||
| 17 | + * <p> | ||
| 18 | + * As outlined in the G.723 Recommendation, the algorithm is broken | ||
| 19 | + * down into modules. Each section of code below is preceded by | ||
| 20 | + * the name of the module which it is implementing. | ||
| 21 | + * <p> | ||
| 22 | + * This implementation is based on the ANSI-C language reference implementations | ||
| 23 | + * of the CCITT (International Telegraph and Telephone Consultative Committee) | ||
| 24 | + * G.711, G.721 and G.723 voice compressions, provided by Sun Microsystems, Inc. | ||
| 25 | + * <p> | ||
| 26 | + * Acknowledgement to Sun Microsystems, Inc. for having released the original | ||
| 27 | + * ANSI-C source code to the public domain. | ||
| 28 | + */ | ||
| 29 | +public class G726_40 extends G726 { | ||
| 30 | + | ||
| 31 | + // ##### C-to-Java conversion: ##### | ||
| 32 | + // short becomes int | ||
| 33 | + // char becomes int | ||
| 34 | + // unsigned char becomes int | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + // *************************** STATIC *************************** | ||
| 38 | + | ||
| 39 | + /* | ||
| 40 | + * Maps G723_40 code word to ructeconstructed scale factor normalized log | ||
| 41 | + * magnitude values. | ||
| 42 | + */ | ||
| 43 | + static /*short*/int[] _dqlntab={-2048, -66, 28, 104, 169, 224, 274, 318, 358, 395, 429, 459, 488, 514, 539, 566, 566, 539, 514, 488, 459, 429, 395, 358, 318, 274, 224, 169, 104, 28, -66, -2048}; | ||
| 44 | + | ||
| 45 | + /* Maps G723_40 code word to log of scale factor multiplier. */ | ||
| 46 | + static /*short*/int[] _witab={448, 448, 768, 1248, 1280, 1312, 1856, 3200, 4512, 5728, 7008, 8960, 11456, 14080, 16928, 22272, 22272, 16928, 14080, 11456, 8960, 7008, 5728, 4512, 3200, 1856, 1312, 1280, 1248, 768, 448, 448}; | ||
| 47 | + | ||
| 48 | + /* | ||
| 49 | + * Maps G723_40 code words to a set of values whose long and short | ||
| 50 | + * term averages are computed and then compared to give an indication | ||
| 51 | + * how stationary (steady state) the signal is. | ||
| 52 | + */ | ||
| 53 | + static /*short*/int[] _fitab={0, 0, 0, 0, 0, 0x200, 0x200, 0x200, 0x200, 0x200, 0x400, 0x600, 0x800, 0xA00, 0xC00, 0xC00, 0xC00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200, 0x200, 0x200, 0x200, 0x200, 0, 0, 0, 0, 0}; | ||
| 54 | + | ||
| 55 | + static /*short*/int[] qtab_723_40={-122, -16, 68, 139, 198, 250, 298, 339, 378, 413, 445, 475, 502, 528, 553}; | ||
| 56 | + | ||
| 57 | + /** Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens | ||
| 58 | + * the resulting 5-bit CCITT G726 40kbps code. | ||
| 59 | + * Returns -1 if the input coding value is invalid. */ | ||
| 60 | + public static int encode(int sl, int in_coding, G726State state) { | ||
| 61 | + | ||
| 62 | + /*short*/int sei, sezi, se, sez; /* ACCUM */ | ||
| 63 | + /*short*/int d; /* SUBTA */ | ||
| 64 | + /*short*/int y; /* MIX */ | ||
| 65 | + /*short*/int sr; /* ADDB */ | ||
| 66 | + /*short*/int dqsez; /* ADDC */ | ||
| 67 | + /*short*/int dq, i; | ||
| 68 | + | ||
| 69 | + switch (in_coding) { | ||
| 70 | + /* linearize input sample to 14-bit PCM */ | ||
| 71 | + case AUDIO_ENCODING_ALAW: | ||
| 72 | + sl= G711Codec.alaw2linear((byte) sl) >> 2; | ||
| 73 | + break; | ||
| 74 | + case AUDIO_ENCODING_ULAW: | ||
| 75 | + sl= G711UCodec.ulaw2linear((byte)sl) >> 2; | ||
| 76 | + break; | ||
| 77 | + case AUDIO_ENCODING_LINEAR: | ||
| 78 | + sl >>= 2; /* sl of 14-bit dynamic range */ | ||
| 79 | + break; | ||
| 80 | + default: | ||
| 81 | + return (-1); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + sezi=state.predictor_zero(); | ||
| 85 | + sez=sezi >> 1; | ||
| 86 | + sei=sezi+state.predictor_pole(); | ||
| 87 | + se=sei >> 1; /* se=estimated signal */ | ||
| 88 | + | ||
| 89 | + d=sl-se; /* d=estimation difference */ | ||
| 90 | + | ||
| 91 | + /* quantize prediction difference */ | ||
| 92 | + y=state.step_size(); /* adaptive quantizer step size */ | ||
| 93 | + i=quantize(d, y, qtab_723_40, 15); /* i=ADPCM code */ | ||
| 94 | + | ||
| 95 | + dq=reconstruct(i & 0x10, _dqlntab[i], y); /* quantized diff */ | ||
| 96 | + | ||
| 97 | + sr=(dq<0)? se-(dq & 0x7FFF) : se+dq; /* reconstructed signal */ | ||
| 98 | + | ||
| 99 | + dqsez=sr+sez-se; /* dqsez=pole prediction diff. */ | ||
| 100 | + | ||
| 101 | + update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state); | ||
| 102 | + | ||
| 103 | + return (i); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + | ||
| 107 | + /** Decodes a 5-bit CCITT G.726 40kbps code and returns | ||
| 108 | + * the resulting 16-bit linear PCM, A-law or u-law sample value. | ||
| 109 | + * -1 is returned if the output coding is unknown. */ | ||
| 110 | + public static int decode(int i, int out_coding, G726State state) { | ||
| 111 | + | ||
| 112 | + /*short*/int sezi, sei, sez, se; /* ACCUM */ | ||
| 113 | + /*short*/int y, dif; /* MIX */ | ||
| 114 | + /*short*/int sr; /* ADDB */ | ||
| 115 | + /*short*/int dq; | ||
| 116 | + /*short*/int dqsez; | ||
| 117 | + | ||
| 118 | + i &= 0x1f; /* mask to get proper bits */ | ||
| 119 | + sezi=state.predictor_zero(); | ||
| 120 | + sez=sezi >> 1; | ||
| 121 | + sei=sezi+state.predictor_pole(); | ||
| 122 | + se=sei >> 1; /* se=estimated signal */ | ||
| 123 | + | ||
| 124 | + y=state.step_size(); /* adaptive quantizer step size */ | ||
| 125 | + dq=reconstruct(i & 0x10, _dqlntab[i], y); /* estimation diff. */ | ||
| 126 | + | ||
| 127 | + sr=(dq<0)? (se-(dq & 0x7FFF)) : (se+dq); /* reconst. signal */ | ||
| 128 | + | ||
| 129 | + dqsez=sr-se+sez; /* pole prediction diff. */ | ||
| 130 | + | ||
| 131 | + update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state); | ||
| 132 | + | ||
| 133 | + switch (out_coding) { | ||
| 134 | + | ||
| 135 | + case AUDIO_ENCODING_ALAW: | ||
| 136 | + return (tandem_adjust_alaw(sr, se, y, i, 0x10, qtab_723_40)); | ||
| 137 | + case AUDIO_ENCODING_ULAW: | ||
| 138 | + return (tandem_adjust_ulaw(sr, se, y, i, 0x10, qtab_723_40)); | ||
| 139 | + case AUDIO_ENCODING_LINEAR: | ||
| 140 | + return (sr << 2); /* sr was of 14-bit dynamic range */ | ||
| 141 | + default: | ||
| 142 | + return (-1); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + | ||
| 147 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 148 | + * the G726_40 encoded chuck into out_buff. <br> | ||
| 149 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 150 | + * in_coding value. */ | ||
| 151 | + public static int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 152 | + | ||
| 153 | + if (in_coding==AUDIO_ENCODING_ALAW || in_coding==AUDIO_ENCODING_ULAW) { | ||
| 154 | + | ||
| 155 | + int len_div_8=in_len/8; | ||
| 156 | + for (int i=0; i<len_div_8; i++) { | ||
| 157 | + long value8=0; | ||
| 158 | + int in_index=in_offset+i*8; | ||
| 159 | + for (int j=0; j<8; j++) { | ||
| 160 | + int in_value=unsignedInt(in_buff[in_index+j]); | ||
| 161 | + int out_value=encode(in_value,in_coding,state); | ||
| 162 | + value8+=((long)out_value)<<(5*(7-j)); | ||
| 163 | + } | ||
| 164 | + int out_index=out_offset+i*5; | ||
| 165 | + for (int k=0; k<5; k++) { | ||
| 166 | + out_buff[out_index+k]=(byte)(value8>>(8*(4-k))); | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + return len_div_8*5; | ||
| 170 | + } | ||
| 171 | + else | ||
| 172 | + if (in_coding==AUDIO_ENCODING_LINEAR) { | ||
| 173 | + | ||
| 174 | + int len_div_16=in_len/16; | ||
| 175 | + for (int i=0; i<len_div_16; i++) { | ||
| 176 | + long value16=0; | ||
| 177 | + int in_index=in_offset+i*16; | ||
| 178 | + for (int j=0; j<8; j++) { | ||
| 179 | + int j2=j*2; | ||
| 180 | + int in_value=signedIntLittleEndian(in_buff[in_index+j2+1],in_buff[in_index+j2]); | ||
| 181 | + int out_value=encode(in_value,in_coding,state); | ||
| 182 | + value16+=((long)out_value)<<(5*(7-j)); | ||
| 183 | + } | ||
| 184 | + int out_index=out_offset+i*5; | ||
| 185 | + for (int k=0; k<5; k++) { | ||
| 186 | + out_buff[out_index+k]=(byte)(value16>>(8*(4-k))); | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + return len_div_16*5; | ||
| 190 | + } | ||
| 191 | + else return -1; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + | ||
| 195 | + /** Decodes the input chunk in_buff of G726_40 encoded data and returns | ||
| 196 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 197 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 198 | + * out_coding value. */ | ||
| 199 | + public static int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset, G726State state) { | ||
| 200 | + | ||
| 201 | + if (out_coding==AUDIO_ENCODING_ALAW || out_coding==AUDIO_ENCODING_ULAW) { | ||
| 202 | + | ||
| 203 | + int len_div_5=in_len/5; | ||
| 204 | + for (int i=0; i<len_div_5; i++) { | ||
| 205 | + long value8=0; | ||
| 206 | + int in_index=in_offset+i*5; | ||
| 207 | + for (int j=0; j<5; j++) { | ||
| 208 | + value8+=(long)unsignedInt(in_buff[in_index+j])<<(8*(4-j)); | ||
| 209 | + } | ||
| 210 | + int out_index=out_offset+i*8; | ||
| 211 | + for (int k=0; k<8; k++) { | ||
| 212 | + int in_value=(int)((value8>>(5*(7-k)))&0x1F); | ||
| 213 | + int out_value=decode(in_value,out_coding,state); | ||
| 214 | + out_buff[out_index+k]=(byte)out_value; | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + return len_div_5*8; | ||
| 218 | + } | ||
| 219 | + else | ||
| 220 | + if (out_coding==AUDIO_ENCODING_LINEAR) { | ||
| 221 | + | ||
| 222 | + int len_div_5=in_len/5; | ||
| 223 | + for (int i=0; i<len_div_5; i++) { | ||
| 224 | + long value16=0; | ||
| 225 | + int in_index=in_offset+i*5; | ||
| 226 | + for (int j=0; j<5; j++) { | ||
| 227 | + value16+=(long)unsignedInt(in_buff[in_index+j])<<(8*(4-j)); | ||
| 228 | + } | ||
| 229 | + int out_index=out_offset+i*16; | ||
| 230 | + for (int k=0; k<8; k++) { | ||
| 231 | + int k2=k*2; | ||
| 232 | + int in_value=(int)((value16>>(5*(7-k)))&0x1F); | ||
| 233 | + int out_value=decode(in_value,out_coding,state); | ||
| 234 | + out_buff[out_index+k2]=(byte)(out_value&0xFF); | ||
| 235 | + out_buff[out_index+k2+1]=(byte)(out_value>>8); | ||
| 236 | + } | ||
| 237 | + } | ||
| 238 | + return len_div_5*16; | ||
| 239 | + } | ||
| 240 | + else return -1; | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + | ||
| 244 | + // ************************* NON-STATIC ************************* | ||
| 245 | + | ||
| 246 | + /** Creates a new G726_40 processor, that can be used to encode from or decode do PCM audio data. */ | ||
| 247 | + public G726_40() { | ||
| 248 | + super(40000); | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + | ||
| 252 | + /** Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens | ||
| 253 | + * the resulting 5-bit CCITT G.726 40kbps code. | ||
| 254 | + * Returns -1 if the input coding value is invalid. */ | ||
| 255 | + public int encode(int sl, int in_coding) { | ||
| 256 | + return encode(sl,in_coding,state); | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + | ||
| 260 | + /** Encodes the input chunk in_buff of linear PCM, A-law or u-law data and returns | ||
| 261 | + * the G726_40 encoded chuck into out_buff. <br> | ||
| 262 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 263 | + * in_coding value. */ | ||
| 264 | + public int encode(byte[] in_buff, int in_offset, int in_len, int in_coding, byte[] out_buff, int out_offset) { | ||
| 265 | + return encode(in_buff,in_offset,in_len,in_coding,out_buff,out_offset,state); | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + | ||
| 269 | + /** Decodes a 5-bit CCITT G.726 40kbps code and returns | ||
| 270 | + * the resulting 16-bit linear PCM, A-law or u-law sample value. | ||
| 271 | + * -1 is returned if the output coding is unknown. */ | ||
| 272 | + public int decode(int i, int out_coding) { | ||
| 273 | + return decode(i,out_coding,state); | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + | ||
| 277 | + /** Decodes the input chunk in_buff of G726_40 encoded data and returns | ||
| 278 | + * the linear PCM, A-law or u-law chunk into out_buff. <br> | ||
| 279 | + * It returns the actual size of the output data, or -1 in case of unknown | ||
| 280 | + * out_coding value. */ | ||
| 281 | + public int decode(byte[] in_buff, int in_offset, int in_len, int out_coding, byte[] out_buff, int out_offset) { | ||
| 282 | + return decode(in_buff,in_offset,in_len,out_coding,out_buff,out_offset,state); | ||
| 283 | + } | ||
| 284 | + | ||
| 285 | + | ||
| 286 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/config/JT1078AutoConfiguration.java
| 1 | package com.genersoft.iot.vmp.jt1078.config; | 1 | package com.genersoft.iot.vmp.jt1078.config; |
| 2 | 2 | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.app.VideoServerApp; | ||
| 3 | import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template; | 4 | import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template; |
| 4 | import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer; | 5 | import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer; |
| 5 | import org.springframework.beans.factory.annotation.Value; | 6 | import org.springframework.beans.factory.annotation.Value; |
| @@ -18,11 +19,28 @@ import org.springframework.core.annotation.Order; | @@ -18,11 +19,28 @@ import org.springframework.core.annotation.Order; | ||
| 18 | @ConditionalOnProperty(value = "jt1078.enable", havingValue = "true") | 19 | @ConditionalOnProperty(value = "jt1078.enable", havingValue = "true") |
| 19 | public class JT1078AutoConfiguration { | 20 | public class JT1078AutoConfiguration { |
| 20 | 21 | ||
| 21 | - @Bean(initMethod = "start", destroyMethod = "stop") | ||
| 22 | - public TcpServer jt1078Server(@Value("${jt1078.port}") Integer port) { | ||
| 23 | - return new TcpServer(port); | ||
| 24 | - } | 22 | +// @Bean(initMethod = "start", destroyMethod = "stop") |
| 23 | +// public TcpServer jt1078Server(@Value("${jt1078.port}") Integer port) { | ||
| 24 | +// return new TcpServer(port); | ||
| 25 | +// } | ||
| 26 | + | ||
| 27 | +// @Bean(initMethod = "start", destroyMethod = "stop") | ||
| 28 | + @Bean | ||
| 29 | + public VideoServerApp jt1078VideoServerApp(@Value("${jt1078.port}") Integer port) { | ||
| 30 | + VideoServerApp videoServerApp = new VideoServerApp(); | ||
| 31 | + new Thread(new Runnable() { | ||
| 32 | + @Override | ||
| 33 | + public void run() { | ||
| 34 | + try { | ||
| 35 | + videoServerApp.createListenter(); | ||
| 36 | + } catch (Exception e) { | ||
| 37 | + throw new RuntimeException(e); | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + }).start(); | ||
| 25 | 41 | ||
| 42 | + return videoServerApp; | ||
| 43 | + } | ||
| 26 | @Bean | 44 | @Bean |
| 27 | public JT1078Template jt1078Template() { | 45 | public JT1078Template jt1078Template() { |
| 28 | return new JT1078Template(); | 46 | return new JT1078Template(); |
src/main/java/com/genersoft/iot/vmp/jt1078/entity/Audio.java
0 → 100644
src/main/java/com/genersoft/iot/vmp/jt1078/entity/Media.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.entity; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * Created by houcheng on 2019-12-11. | ||
| 5 | + * 数据流,可能是视频或是音频,视频为FLV封装,音频为PCM编码的片断 | ||
| 6 | + */ | ||
| 7 | +public class Media | ||
| 8 | +{ | ||
| 9 | + public enum Type { Video, Audio }; | ||
| 10 | + | ||
| 11 | + public Type type; | ||
| 12 | + public MediaEncoding.Encoding encoding; | ||
| 13 | + public long sequence; | ||
| 14 | + public byte[] data; | ||
| 15 | + | ||
| 16 | + public Media(long seq, MediaEncoding.Encoding encoding, byte[] data) | ||
| 17 | + { | ||
| 18 | + this.type = type; | ||
| 19 | + this.data = data; | ||
| 20 | + this.encoding = encoding; | ||
| 21 | + this.sequence = seq; | ||
| 22 | + } | ||
| 23 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/entity/MediaEncoding.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.entity; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * Created by matrixy on 2019/12/20. | ||
| 5 | + */ | ||
| 6 | +public final class MediaEncoding | ||
| 7 | +{ | ||
| 8 | + public enum Encoding | ||
| 9 | + { | ||
| 10 | + RESERVED, | ||
| 11 | + G721, | ||
| 12 | + G722, | ||
| 13 | + G723, | ||
| 14 | + G728, | ||
| 15 | + G729, | ||
| 16 | + G711A, | ||
| 17 | + G711U, | ||
| 18 | + G726, | ||
| 19 | + G729A, | ||
| 20 | + DVI4_3, | ||
| 21 | + DVI4_4, | ||
| 22 | + DVI4_8K, | ||
| 23 | + DVI4_16K, | ||
| 24 | + LPC, | ||
| 25 | + S16BE_STEREO, | ||
| 26 | + S16BE_MONO, | ||
| 27 | + MPEGAUDIO, | ||
| 28 | + LPCM, | ||
| 29 | + AAC, | ||
| 30 | + WMA9STD, | ||
| 31 | + HEAAC, | ||
| 32 | + PCM_VOICE, | ||
| 33 | + PCM_AUDIO, | ||
| 34 | + AACLC, | ||
| 35 | + MP3, | ||
| 36 | + ADPCMA, | ||
| 37 | + MP4AUDIO, | ||
| 38 | + AMR, // 28 | ||
| 39 | + | ||
| 40 | + H264, // 98 | ||
| 41 | + H265, | ||
| 42 | + AVS, | ||
| 43 | + SVAC, | ||
| 44 | + UNKNOWN | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public static Encoding getEncoding(Media.Type type, int pt) | ||
| 48 | + { | ||
| 49 | + if (type.equals(Media.Type.Audio)) | ||
| 50 | + { | ||
| 51 | + if (pt >= 0 && pt <= 28) return Encoding.values()[pt]; | ||
| 52 | + else return Encoding.UNKNOWN; | ||
| 53 | + } | ||
| 54 | + else | ||
| 55 | + { | ||
| 56 | + if (pt >= 98 && pt <= 101) return Encoding.values()[pt - 98 + 29]; | ||
| 57 | + else return Encoding.UNKNOWN; | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/entity/Video.java
0 → 100644
src/main/java/com/genersoft/iot/vmp/jt1078/flv/AudioTag.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.flv; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * flv 音频封装 | ||
| 5 | + * author:zhouyili (11861744@qq.com) | ||
| 6 | + */ | ||
| 7 | +public class AudioTag extends FlvTag{ | ||
| 8 | + public static final byte MP3 = 2; | ||
| 9 | + //UB[4] | ||
| 10 | + //0 = Linear PCM, platform endian 根据编码的平台,一般用3而不用这个 | ||
| 11 | + //1 = ADPCM | ||
| 12 | + //2 = MP3 | ||
| 13 | + //3 = Linear PCM, little endian | ||
| 14 | + //4 = Nellymoser 16-kHz mono | ||
| 15 | + //5 = Nellymoser 8-kHz mono | ||
| 16 | + //6 = Nellymoser | ||
| 17 | + //7 = G.711 A-law logarithmic PCM 8 = G.711 mu-law logarithmic PCM 9 = reserved | ||
| 18 | + //10 = AAC | ||
| 19 | + //11 = Speex | ||
| 20 | + //14 = MP3 8-Khz | ||
| 21 | + //15 = Device-specific sound | ||
| 22 | + private byte format; | ||
| 23 | + //0:5.5kHz,1:11kHz,2:22kHz,3:44kHz,format==10的话为3 | ||
| 24 | + private byte rate; | ||
| 25 | + //0:8bit,1:16bit | ||
| 26 | + private byte size; | ||
| 27 | + //0:mono,1:stereo,format==10的话为1 | ||
| 28 | + private byte type; | ||
| 29 | + //format==10是aac data,否则是对应编码后的数据 | ||
| 30 | + private byte[] data; | ||
| 31 | + | ||
| 32 | + public AudioTag() { | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public AudioTag(int offSetTimestamp, int tagDataSize, byte format, byte rate, byte size, byte type, byte[] data) { | ||
| 36 | + super(offSetTimestamp, tagDataSize); | ||
| 37 | + this.format = format; | ||
| 38 | + this.rate = rate; | ||
| 39 | + this.size = size; | ||
| 40 | + this.type = type; | ||
| 41 | + this.data = data; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + public byte getFormat() { | ||
| 45 | + return format; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + public void setFormat(byte format) { | ||
| 49 | + this.format = format; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + public byte getRate() { | ||
| 53 | + return rate; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public void setRate(byte rate) { | ||
| 57 | + this.rate = rate; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + public byte getSize() { | ||
| 61 | + return size; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + public void setSize(byte size) { | ||
| 65 | + this.size = size; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + public byte getType() { | ||
| 69 | + return type; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + public void setType(byte type) { | ||
| 73 | + this.type = type; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public byte[] getData() { | ||
| 77 | + return data; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + public void setData(byte[] data) { | ||
| 81 | + this.data = data; | ||
| 82 | + } | ||
| 83 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/flv/FlvAudioTagEncoder.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.flv; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.buffer.Unpooled; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * Flv 音频封装编码 | ||
| 10 | + * author:zhouyili (11861744@qq.com) | ||
| 11 | + */ | ||
| 12 | +public class FlvAudioTagEncoder { | ||
| 13 | + public static final Logger log = LoggerFactory.getLogger(FlvAudioTagEncoder.class); | ||
| 14 | + | ||
| 15 | + public ByteBuf encode(AudioTag audioTag) throws Exception { | ||
| 16 | + ByteBuf buffer = Unpooled.buffer(); | ||
| 17 | + if (audioTag == null) return buffer; | ||
| 18 | +// buffer.writeInt(audioTag.getPreTagSize()); | ||
| 19 | + //----------------------tag header begin------- | ||
| 20 | + buffer.writeByte(8); | ||
| 21 | + buffer.writeMedium(audioTag.getTagDataSize()); | ||
| 22 | + buffer.writeMedium(audioTag.getOffSetTimestamp() & 0xFFFFFF); | ||
| 23 | + buffer.writeByte(audioTag.getOffSetTimestamp() >> 24); | ||
| 24 | + buffer.writeMedium(audioTag.getStreamId()); | ||
| 25 | + //---------------------tag header length 11--------- | ||
| 26 | + //---------------------tag header end---------------- | ||
| 27 | + byte formatAndRateAndSize = (byte) (audioTag.getFormat() << 4 | audioTag.getRate() << 2 | audioTag.getSize() << 1 | audioTag.getType()); | ||
| 28 | + //-------------data begin------- | ||
| 29 | + buffer.writeByte(formatAndRateAndSize); | ||
| 30 | + buffer.writeBytes(audioTag.getData()); | ||
| 31 | + //-------------data end ------- | ||
| 32 | + buffer.writeInt(buffer.writerIndex());//应该等于11+tagDataSize | ||
| 33 | + return buffer; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/flv/FlvEncoder.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.flv; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | ||
| 4 | + | ||
| 5 | +import java.io.ByteArrayOutputStream; | ||
| 6 | +import java.io.IOException; | ||
| 7 | +import java.io.OutputStream; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * Created by matrixy on 2020/1/3. | ||
| 11 | + */ | ||
| 12 | +public final class FlvEncoder | ||
| 13 | +{ | ||
| 14 | + Packet flvHeader; | ||
| 15 | + Packet videoHeader; | ||
| 16 | + Packet SPS, PPS; | ||
| 17 | + int SPSSize, PPSSize; | ||
| 18 | + boolean writeAVCSeqHeader; | ||
| 19 | + int prevTagSize; | ||
| 20 | + int streamID; | ||
| 21 | + int videoTimeStamp; | ||
| 22 | + byte[] lastIFrame; | ||
| 23 | + | ||
| 24 | + Packet _pAudioSpecificConfig; | ||
| 25 | + int _nAudioConfigSize; | ||
| 26 | + int _aacProfile; | ||
| 27 | + int _sampleRateIndex; | ||
| 28 | + int _channelConfig; | ||
| 29 | + int _bWriteAACSeqHeader; | ||
| 30 | + | ||
| 31 | + boolean haveAudio, haveVideo; | ||
| 32 | + | ||
| 33 | + ByteArrayOutputStream videoFrame; | ||
| 34 | + | ||
| 35 | + public FlvEncoder(boolean haveVideo, boolean haveAudio) | ||
| 36 | + { | ||
| 37 | + this.haveVideo = haveVideo; | ||
| 38 | + // this.haveAudio = haveAudio; | ||
| 39 | + flvHeader = Packet.create(16); | ||
| 40 | + videoFrame = new ByteArrayOutputStream(2048 * 100); | ||
| 41 | + makeFlvHeader(); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + public Packet getHeader() | ||
| 45 | + { | ||
| 46 | + return flvHeader; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public Packet getVideoHeader() | ||
| 50 | + { | ||
| 51 | + return videoHeader; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + public boolean videoReady() | ||
| 55 | + { | ||
| 56 | + return writeAVCSeqHeader; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public byte[] getLastIFrame() | ||
| 60 | + { | ||
| 61 | + return this.lastIFrame; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + public byte[] write(byte[] nalu, int nTimeStamp) | ||
| 65 | + { | ||
| 66 | + this.videoTimeStamp = nTimeStamp; | ||
| 67 | + | ||
| 68 | + if (nalu == null || nalu.length <= 4) return null; | ||
| 69 | + | ||
| 70 | + int naluType = nalu[4] & 0x1f; | ||
| 71 | + // skip SEI | ||
| 72 | + if (naluType == 0x06) return null; | ||
| 73 | + if (naluType == 0x01 | ||
| 74 | + || naluType == 0x02 | ||
| 75 | + || naluType == 0x03 | ||
| 76 | + || naluType == 0x04 | ||
| 77 | + || naluType == 0x05 | ||
| 78 | + || naluType == 0x07 | ||
| 79 | + || naluType == 0x08) ; else return null; | ||
| 80 | + | ||
| 81 | + if (SPS == null && naluType == 0x07) | ||
| 82 | + { | ||
| 83 | + SPS = Packet.create(nalu); | ||
| 84 | + SPSSize = nalu.length; | ||
| 85 | + } | ||
| 86 | + if (PPS == null && naluType == 0x08) | ||
| 87 | + { | ||
| 88 | + PPS = Packet.create(nalu); | ||
| 89 | + PPSSize = nalu.length; | ||
| 90 | + } | ||
| 91 | + if (SPS != null && PPS != null && writeAVCSeqHeader == false) | ||
| 92 | + { | ||
| 93 | + writeH264Header(nTimeStamp); | ||
| 94 | + writeAVCSeqHeader = true; | ||
| 95 | + } | ||
| 96 | + if (writeAVCSeqHeader == false) return null; | ||
| 97 | + | ||
| 98 | + videoFrame.reset(); | ||
| 99 | + writeH264Frame(nalu, nTimeStamp); | ||
| 100 | + | ||
| 101 | + if (videoFrame.size() == 0) return null; | ||
| 102 | + | ||
| 103 | + // 如果当前NAL单元为I祯,则缓存一个 | ||
| 104 | + if (naluType == 0x05) | ||
| 105 | + { | ||
| 106 | + lastIFrame = videoFrame.toByteArray(); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + return videoFrame.toByteArray(); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + void makeFlvHeader() | ||
| 113 | + { | ||
| 114 | + flvHeader.addByte((byte)'F'); | ||
| 115 | + flvHeader.addByte((byte)'L'); | ||
| 116 | + flvHeader.addByte((byte)'V'); | ||
| 117 | + flvHeader.addByte((byte)0x01); // version | ||
| 118 | + flvHeader.addByte((byte)(0x00 | (haveVideo ? 0x01 : 0x00) | (haveAudio ? 0x04 : 0x00))); | ||
| 119 | + flvHeader.addInt(0x09); | ||
| 120 | + flvHeader.addInt(0x00); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + void writeH264Header(int nTimeStamp) | ||
| 124 | + { | ||
| 125 | + int nDataSize = 1 + 1 + 3 + 6 + 2 + (SPSSize - 4) + 1 + 2 + (PPSSize - 4); | ||
| 126 | + videoHeader = Packet.create(nDataSize + 32); | ||
| 127 | + | ||
| 128 | + byte cTagType = 0x09; | ||
| 129 | + videoHeader.addByte(cTagType); | ||
| 130 | + | ||
| 131 | + videoHeader.add3Bytes(nDataSize); | ||
| 132 | + | ||
| 133 | + videoHeader.add3Bytes(nTimeStamp); | ||
| 134 | + videoHeader.addByte((byte)(nTimeStamp >> 24)); | ||
| 135 | + | ||
| 136 | + videoHeader.add3Bytes(streamID); | ||
| 137 | + | ||
| 138 | + byte cVideoParam = 0x17; | ||
| 139 | + videoHeader.addByte(cVideoParam); | ||
| 140 | + | ||
| 141 | + byte cAVCPacketType = 0x00; | ||
| 142 | + videoHeader.addByte(cAVCPacketType); | ||
| 143 | + | ||
| 144 | + videoHeader.add3Bytes(0x00); | ||
| 145 | + | ||
| 146 | + videoHeader.addByte((byte)0x01); | ||
| 147 | + | ||
| 148 | + videoHeader.addByte(SPS.seek(5).nextByte()); | ||
| 149 | + videoHeader.addByte(SPS.seek(6).nextByte()); | ||
| 150 | + videoHeader.addByte(SPS.seek(7).nextByte()); | ||
| 151 | + videoHeader.addByte((byte)0xff); | ||
| 152 | + videoHeader.addByte((byte)0xe1); | ||
| 153 | + | ||
| 154 | + videoHeader.addShort((short)(SPSSize - 4)); | ||
| 155 | + videoHeader.addBytes(SPS.seek(4).nextBytes()); | ||
| 156 | + videoHeader.addByte((byte)0x01); | ||
| 157 | + | ||
| 158 | + videoHeader.addShort((short)(PPSSize - 4)); | ||
| 159 | + videoHeader.addBytes(PPS.seek(4).nextBytes()); | ||
| 160 | + | ||
| 161 | + prevTagSize = 11 + nDataSize; | ||
| 162 | + videoHeader.addInt(prevTagSize); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + void writeH264Frame(byte[] nalu, int nTimeStamp) | ||
| 166 | + { | ||
| 167 | + int nNaluType = nalu[4] & 0x1f; | ||
| 168 | + if (nNaluType == 7 || nNaluType == 8) return; | ||
| 169 | + | ||
| 170 | + writeByte(0x09); | ||
| 171 | + | ||
| 172 | + int nDataSize = 1 + 1 + 3 + 4 + (nalu.length - 4); | ||
| 173 | + writeU3(nDataSize); | ||
| 174 | + | ||
| 175 | + writeU3(nTimeStamp); | ||
| 176 | + writeByte(nTimeStamp >> 24); | ||
| 177 | + | ||
| 178 | + writeU3(streamID); | ||
| 179 | + | ||
| 180 | + if (nNaluType == 5) writeByte(0x17); | ||
| 181 | + else writeByte(0x27); | ||
| 182 | + | ||
| 183 | + writeByte(0x01); | ||
| 184 | + writeU3(0x00); | ||
| 185 | + writeU4(nalu.length - 4); | ||
| 186 | + writeBytes(nalu, 4, nalu.length - 4); | ||
| 187 | + | ||
| 188 | + prevTagSize = 11 + nDataSize; | ||
| 189 | + | ||
| 190 | + writeU4(prevTagSize); | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + void write(byte u) | ||
| 194 | + { | ||
| 195 | + videoFrame.write(u); | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + void writeBytes(byte[] data) | ||
| 199 | + { | ||
| 200 | + videoFrame.write(data, 0, data.length); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + void writeBytes(byte[] data, int offset, int len) | ||
| 204 | + { | ||
| 205 | + videoFrame.write(data, offset, len); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + void writeU4(int i) | ||
| 209 | + { | ||
| 210 | + write((byte)((i >> 24) & 0xff)); | ||
| 211 | + write((byte)((i >> 16) & 0xff)); | ||
| 212 | + write((byte)((i >> 8) & 0xff)); | ||
| 213 | + write((byte)((i >> 0) & 0xff)); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + void writeU3(int i) | ||
| 217 | + { | ||
| 218 | + write((byte)((i >> 16) & 0xff)); | ||
| 219 | + write((byte)((i >> 8) & 0xff)); | ||
| 220 | + write((byte)((i >> 0) & 0xff)); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + void writeU2(int i) | ||
| 224 | + { | ||
| 225 | + write((byte)((i >> 8) & 0xff)); | ||
| 226 | + write((byte)((i >> 0) & 0xff)); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + void writeByte(int i) | ||
| 230 | + { | ||
| 231 | + write((byte)(i & 0xff)); | ||
| 232 | + } | ||
| 233 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/flv/FlvTag.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.flv; | ||
| 2 | + | ||
| 3 | +//由于flv格式是header+body,而body是preTagSize+currentTagData.......循环形式,并且第一个的preTagSize始终为0, | ||
| 4 | +// 与其这样不如采用(header+preTag0Size)+(currentTagData+currentTagSize),这样就避免每次都要记录以下上一个tag的数据大小。 | ||
| 5 | +/** | ||
| 6 | + * author:zhouyili (11861744@qq.com) | ||
| 7 | + */ | ||
| 8 | +public class FlvTag { | ||
| 9 | + public static final int AUDIO = 8; | ||
| 10 | + public static final int VIDEO = 9; | ||
| 11 | + public static final int SCRIPT = 18;//0x12 | ||
| 12 | + private int preTagSize; | ||
| 13 | + private byte tagType; | ||
| 14 | + /**3个字节 streamId以后的数据长度*/ | ||
| 15 | + private int tagDataSize; | ||
| 16 | + //低3个字节写入后再写高位字节,相对于第一帧的时间偏移量,单位ms | ||
| 17 | + private int offSetTimestamp; | ||
| 18 | + //3个字节,一般总是0 | ||
| 19 | + private int streamId; | ||
| 20 | + | ||
| 21 | + public FlvTag() { | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public FlvTag(int offSetTimestamp, int tagDataSize) { | ||
| 25 | + this.tagDataSize = tagDataSize; | ||
| 26 | + this.offSetTimestamp = offSetTimestamp; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public int getPreTagSize() { | ||
| 30 | + return preTagSize; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public void setPreTagSize(int preTagSize) { | ||
| 34 | + this.preTagSize = preTagSize; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public byte getTagType() { | ||
| 38 | + return tagType; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public void setTagType(byte tagType) { | ||
| 42 | + this.tagType = tagType; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + public int getTagDataSize() { | ||
| 46 | + return tagDataSize; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public void setTagDataSize(int tagDataSize) { | ||
| 50 | + this.tagDataSize = tagDataSize; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + public int getOffSetTimestamp() { | ||
| 54 | + return offSetTimestamp; | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + public void setOffSetTimestamp(int offSetTimestamp) { | ||
| 58 | + this.offSetTimestamp = offSetTimestamp; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + public int getStreamId() { | ||
| 62 | + return streamId; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + public void setStreamId(int streamId) { | ||
| 66 | + this.streamId = streamId; | ||
| 67 | + } | ||
| 68 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/http/GeneralResponseWriter.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.http; | ||
| 2 | + | ||
| 3 | +import io.netty.buffer.ByteBuf; | ||
| 4 | +import io.netty.channel.ChannelHandlerContext; | ||
| 5 | +import io.netty.handler.codec.MessageToByteEncoder; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Created by matrixy on 2019/11/25. | ||
| 9 | + */ | ||
| 10 | +public class GeneralResponseWriter extends MessageToByteEncoder<byte[]> | ||
| 11 | +{ | ||
| 12 | + @Override | ||
| 13 | + protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception | ||
| 14 | + { | ||
| 15 | + out.writeBytes(msg); | ||
| 16 | + } | ||
| 17 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/http/NettyHttpServerHandler.java
0 → 100644
| 1 | +// | ||
| 2 | +// Source code recreated from a .class file by IntelliJ IDEA | ||
| 3 | +// (powered by FernFlower decompiler) | ||
| 4 | +// | ||
| 5 | + | ||
| 6 | +package com.genersoft.iot.vmp.jt1078.http; | ||
| 7 | + | ||
| 8 | +import com.genersoft.iot.vmp.jt1078.app.VideoServerApp; | ||
| 9 | +import com.genersoft.iot.vmp.jt1078.entity.Media.Type; | ||
| 10 | +import com.genersoft.iot.vmp.jt1078.publisher.PublishManager; | ||
| 11 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Handler; | ||
| 12 | +import com.genersoft.iot.vmp.jt1078.server.Session; | ||
| 13 | +import com.genersoft.iot.vmp.jt1078.util.FileUtils; | ||
| 14 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | ||
| 15 | +import io.netty.buffer.ByteBuf; | ||
| 16 | +import io.netty.buffer.Unpooled; | ||
| 17 | +import io.netty.channel.ChannelHandlerContext; | ||
| 18 | +import io.netty.channel.ChannelInboundHandlerAdapter; | ||
| 19 | +import io.netty.handler.codec.http.DefaultFullHttpResponse; | ||
| 20 | +import io.netty.handler.codec.http.FullHttpRequest; | ||
| 21 | +import io.netty.handler.codec.http.FullHttpResponse; | ||
| 22 | +import io.netty.handler.codec.http.HttpResponseStatus; | ||
| 23 | +import io.netty.handler.codec.http.HttpVersion; | ||
| 24 | +import io.netty.util.Attribute; | ||
| 25 | +import io.netty.util.AttributeKey; | ||
| 26 | +import org.apache.commons.lang3.StringUtils; | ||
| 27 | +import org.slf4j.Logger; | ||
| 28 | +import org.slf4j.LoggerFactory; | ||
| 29 | + | ||
| 30 | +import java.io.IOException; | ||
| 31 | +import java.net.URISyntaxException; | ||
| 32 | +import java.util.Objects; | ||
| 33 | + | ||
| 34 | +public class NettyHttpServerHandler extends ChannelInboundHandlerAdapter { | ||
| 35 | + private String tagMapping; | ||
| 36 | + private Integer httpPort; | ||
| 37 | + static Logger logger = LoggerFactory.getLogger(NettyHttpServerHandler.class); | ||
| 38 | + static final byte[] HTTP_403_DATA = "<h1>403 Forbidden</h1><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding--><!--padding-->".getBytes(); | ||
| 39 | + static final byte[] SUCCESS = "{code:0}".getBytes(); | ||
| 40 | + static final String HEADER_ENCODING = "ISO-8859-1"; | ||
| 41 | + private static final AttributeKey<Session> SESSION_KEY = AttributeKey.valueOf("session"); | ||
| 42 | + | ||
| 43 | + public NettyHttpServerHandler(String tagMapping, Integer httpPort) { | ||
| 44 | + this.tagMapping = tagMapping; | ||
| 45 | + this.httpPort = httpPort; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { | ||
| 49 | + FullHttpRequest fhr = (FullHttpRequest) msg; | ||
| 50 | + String uri = fhr.uri(); | ||
| 51 | + Packet resp = Packet.create(1024); | ||
| 52 | + String tagMapping; | ||
| 53 | + long wid; | ||
| 54 | + if (uri.startsWith("/video/")) { | ||
| 55 | + tagMapping = uri.substring("/video/".length()); | ||
| 56 | + resp.addBytes("HTTP/1.1 200 OK\r\n".getBytes("ISO-8859-1")); | ||
| 57 | + resp.addBytes("Connection: keep-alive\r\n".getBytes("ISO-8859-1")); | ||
| 58 | + resp.addBytes("Content-Type: video/x-flv\r\n".getBytes("ISO-8859-1")); | ||
| 59 | + resp.addBytes("Transfer-Encoding: chunked\r\n".getBytes("ISO-8859-1")); | ||
| 60 | + resp.addBytes("Cache-Control: no-cache\r\n".getBytes("ISO-8859-1")); | ||
| 61 | + resp.addBytes("Access-Control-Allow-Origin: *\r\n".getBytes("ISO-8859-1")); | ||
| 62 | + resp.addBytes("Access-Control-Allow-Credentials: true\r\n".getBytes("ISO-8859-1")); | ||
| 63 | + resp.addBytes("\r\n".getBytes("ISO-8859-1")); | ||
| 64 | + ctx.writeAndFlush(resp.getBytes()).await(); | ||
| 65 | + logger.info("Thread id:[{}]", Thread.currentThread().getId()); | ||
| 66 | + if (StringUtils.isEmpty(this.tagMapping)) { | ||
| 67 | + this.tagMapping = tagMapping; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + | ||
| 71 | + wid = PublishManager.getInstance().subscribe(this.tagMapping, Type.Video, ctx, this.httpPort).getId(); | ||
| 72 | + this.setSession(ctx, (new Session()).set("subscriber-id", wid).set("tag", this.tagMapping)); | ||
| 73 | +// if (wid == 0) { | ||
| 74 | + | ||
| 75 | + try { | ||
| 76 | + Jtt1078Handler.createStreamProxy(this.tagMapping, httpPort); | ||
| 77 | + } catch (URISyntaxException e) { | ||
| 78 | + throw new RuntimeException(e); | ||
| 79 | + } catch (IOException e) { | ||
| 80 | + throw new RuntimeException(e); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | +// } | ||
| 84 | + } else if (uri.equals("/test/multimedia")) { | ||
| 85 | + this.responseHTMLFile("/multimedia.html", ctx); | ||
| 86 | + } else { | ||
| 87 | + String httpPort; | ||
| 88 | + if (uri.startsWith("/stop/channel/")) { | ||
| 89 | + resp.addBytes("HTTP/1.1 200 OK\r\n".getBytes("ISO-8859-1")); | ||
| 90 | + resp.addBytes("Connection: keep-alive\r\n".getBytes("ISO-8859-1")); | ||
| 91 | + resp.addBytes("Content-Type: video/x-flv\r\n".getBytes("ISO-8859-1")); | ||
| 92 | + resp.addBytes("Transfer-Encoding: chunked\r\n".getBytes("ISO-8859-1")); | ||
| 93 | + resp.addBytes("Cache-Control: no-cache\r\n".getBytes("ISO-8859-1")); | ||
| 94 | + resp.addBytes("Access-Control-Allow-Origin: *\r\n".getBytes("ISO-8859-1")); | ||
| 95 | + resp.addBytes("Access-Control-Allow-Credentials: true\r\n".getBytes("ISO-8859-1")); | ||
| 96 | + tagMapping = uri.substring("/stop/channel/".length()); | ||
| 97 | + String str = uri.substring("/stop/channel/".length()); | ||
| 98 | + int endIndex = StringUtils.indexOf(str, "/"); | ||
| 99 | + StringUtils.substring(str, 0, endIndex); | ||
| 100 | + Integer startIndex = endIndex + 1; | ||
| 101 | + endIndex = StringUtils.indexOf(str, "/", startIndex); | ||
| 102 | + httpPort = StringUtils.substring(str, startIndex, endIndex); | ||
| 103 | + startIndex = endIndex + 1; | ||
| 104 | + httpPort = StringUtils.substring(str, startIndex, str.length()); | ||
| 105 | + PublishManager publishManager = PublishManager.getInstance(); | ||
| 106 | + publishManager.unsubscribeAndClose(tagMapping); | ||
| 107 | + VideoServerApp.stopServer(Integer.parseInt(httpPort), Integer.parseInt(httpPort)); | ||
| 108 | + logger.info("{}停流", tagMapping); | ||
| 109 | + ByteBuf body = Unpooled.buffer(SUCCESS.length); | ||
| 110 | + body.writeBytes(SUCCESS); | ||
| 111 | + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(200), body); | ||
| 112 | + response.headers().add("Content-Length", SUCCESS.length); | ||
| 113 | + ctx.writeAndFlush(response).await(); | ||
| 114 | + ctx.flush(); | ||
| 115 | + } else if (uri.startsWith("/new/server/")) { | ||
| 116 | + resp.addBytes("HTTP/1.1 200 OK\r\n".getBytes("ISO-8859-1")); | ||
| 117 | + resp.addBytes("Connection: keep-alive\r\n".getBytes("ISO-8859-1")); | ||
| 118 | + resp.addBytes("Content-Type: video/x-flv\r\n".getBytes("ISO-8859-1")); | ||
| 119 | + resp.addBytes("Transfer-Encoding: chunked\r\n".getBytes("ISO-8859-1")); | ||
| 120 | + resp.addBytes("Cache-Control: no-cache\r\n".getBytes("ISO-8859-1")); | ||
| 121 | + resp.addBytes("Access-Control-Allow-Origin: *\r\n".getBytes("ISO-8859-1")); | ||
| 122 | + resp.addBytes("Access-Control-Allow-Credentials: true\r\n".getBytes("ISO-8859-1")); | ||
| 123 | + tagMapping = uri.substring("/new/server/".length()); | ||
| 124 | + int endIndex = StringUtils.indexOf(tagMapping, "/"); | ||
| 125 | + String key = StringUtils.substring(tagMapping, 0, endIndex); | ||
| 126 | + Integer startIndex = endIndex + 1; | ||
| 127 | + endIndex = StringUtils.indexOf(tagMapping, "/", startIndex); | ||
| 128 | + String port = StringUtils.substring(tagMapping, startIndex, endIndex); | ||
| 129 | + startIndex = endIndex + 1; | ||
| 130 | + httpPort = StringUtils.substring(tagMapping, startIndex, tagMapping.length()); | ||
| 131 | + | ||
| 132 | + ByteBuf body = Unpooled.buffer(SUCCESS.length); | ||
| 133 | + body.writeBytes(SUCCESS); | ||
| 134 | + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(200), body); | ||
| 135 | + response.headers().add("Content-Length", SUCCESS.length); | ||
| 136 | + ctx.writeAndFlush(response).await(); | ||
| 137 | + ctx.flush(); | ||
| 138 | + } else if (uri.startsWith("/play/history/")) { | ||
| 139 | + tagMapping = uri.substring("/play/history/".length()); | ||
| 140 | + resp.addBytes("HTTP/1.1 200 OK\r\n".getBytes("ISO-8859-1")); | ||
| 141 | + resp.addBytes("Connection: keep-alive\r\n".getBytes("ISO-8859-1")); | ||
| 142 | + resp.addBytes("Content-Type: video/x-flv\r\n".getBytes("ISO-8859-1")); | ||
| 143 | + resp.addBytes("Transfer-Encoding: chunked\r\n".getBytes("ISO-8859-1")); | ||
| 144 | + resp.addBytes("Cache-Control: no-cache\r\n".getBytes("ISO-8859-1")); | ||
| 145 | + resp.addBytes("Access-Control-Allow-Origin: *\r\n".getBytes("ISO-8859-1")); | ||
| 146 | + resp.addBytes("Access-Control-Allow-Credentials: true\r\n".getBytes("ISO-8859-1")); | ||
| 147 | + resp.addBytes("\r\n".getBytes("ISO-8859-1")); | ||
| 148 | + ctx.writeAndFlush(resp.getBytes()).await(); | ||
| 149 | + logger.info("Thread id:[{}]", Thread.currentThread().getId()); | ||
| 150 | + wid = PublishManager.getInstance().subscribe(tagMapping, Type.Video, ctx, this.httpPort).getId(); | ||
| 151 | + this.setSession(ctx, (new Session()).set("subscriber-id", wid).set("tag", tagMapping)); | ||
| 152 | + } else { | ||
| 153 | + ByteBuf body = Unpooled.buffer(HTTP_403_DATA.length); | ||
| 154 | + body.writeBytes(HTTP_403_DATA); | ||
| 155 | + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(403), body); | ||
| 156 | + response.headers().add("Content-Length", HTTP_403_DATA.length); | ||
| 157 | + ctx.writeAndFlush(response).await(); | ||
| 158 | + ctx.flush(); | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { | ||
| 165 | + super.channelInactive(ctx); | ||
| 166 | + Session session = this.getSession(ctx); | ||
| 167 | + if (session != null && session.has("subscriber-id") && session.has("tag")) { | ||
| 168 | + String tag = (String) session.get("tag"); | ||
| 169 | + Long wid = (Long) session.get("subscriber-id"); | ||
| 170 | + PublishManager.getInstance().unsubscribe(tag, wid); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + private void responseHTMLFile(String htmlFilePath, ChannelHandlerContext ctx) { | ||
| 176 | + byte[] fileData = FileUtils.read(NettyHttpServerHandler.class.getResourceAsStream(htmlFilePath)); | ||
| 177 | + ByteBuf body = Unpooled.buffer(fileData.length); | ||
| 178 | + body.writeBytes(fileData); | ||
| 179 | + FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(200), body); | ||
| 180 | + response.headers().add("Content-Length", fileData.length); | ||
| 181 | + ctx.write(response); | ||
| 182 | + ctx.flush(); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { | ||
| 186 | + ctx.flush(); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | ||
| 190 | + ctx.close(); | ||
| 191 | + cause.printStackTrace(); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + public final void setSession(ChannelHandlerContext context, Session session) { | ||
| 195 | + context.channel().attr(SESSION_KEY).set(session); | ||
| 196 | + } | ||
| 197 | + | ||
| 198 | + public final Session getSession(ChannelHandlerContext context) { | ||
| 199 | + Attribute<Session> attr = context.channel().attr(SESSION_KEY); | ||
| 200 | + return null == attr ? null : (Session) attr.get(); | ||
| 201 | + } | ||
| 202 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/publisher/Channel.java
0 → 100644
| 1 | +// | ||
| 2 | +// Source code recreated from a .class file by IntelliJ IDEA | ||
| 3 | +// (powered by FernFlower decompiler) | ||
| 4 | +// | ||
| 5 | + | ||
| 6 | +package com.genersoft.iot.vmp.jt1078.publisher; | ||
| 7 | + | ||
| 8 | +import com.genersoft.iot.vmp.jt1078.codec.AudioCodec; | ||
| 9 | +import com.genersoft.iot.vmp.jt1078.entity.MediaEncoding; | ||
| 10 | +import com.genersoft.iot.vmp.jt1078.entity.Media.Type; | ||
| 11 | +import com.genersoft.iot.vmp.jt1078.flv.FlvEncoder; | ||
| 12 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Handler; | ||
| 13 | +import com.genersoft.iot.vmp.jt1078.subscriber.RTMPPublisher; | ||
| 14 | +import com.genersoft.iot.vmp.jt1078.subscriber.Subscriber; | ||
| 15 | +import com.genersoft.iot.vmp.jt1078.subscriber.VideoSubscriber; | ||
| 16 | +import com.genersoft.iot.vmp.jt1078.util.ByteHolder; | ||
| 17 | +import com.genersoft.iot.vmp.jt1078.util.Configs; | ||
| 18 | +import io.netty.channel.ChannelHandlerContext; | ||
| 19 | + | ||
| 20 | +import java.io.IOException; | ||
| 21 | +import java.net.URISyntaxException; | ||
| 22 | +import java.util.Iterator; | ||
| 23 | +import java.util.concurrent.ConcurrentLinkedQueue; | ||
| 24 | + | ||
| 25 | +import org.apache.commons.lang3.StringUtils; | ||
| 26 | +import org.slf4j.Logger; | ||
| 27 | +import org.slf4j.LoggerFactory; | ||
| 28 | + | ||
| 29 | +public class Channel { | ||
| 30 | + static Logger logger = LoggerFactory.getLogger(Channel.class); | ||
| 31 | + ConcurrentLinkedQueue<Subscriber> subscribers; | ||
| 32 | + RTMPPublisher rtmpPublisher; | ||
| 33 | + String tag; | ||
| 34 | + boolean publishing; | ||
| 35 | + ByteHolder buffer; | ||
| 36 | + AudioCodec audioCodec; | ||
| 37 | + FlvEncoder flvEncoder; | ||
| 38 | + private long firstTimestamp = -1L; | ||
| 39 | + private Integer httpPort; | ||
| 40 | + private boolean flag = true; | ||
| 41 | + | ||
| 42 | + public Channel(String tag, Integer httpPort) { | ||
| 43 | + | ||
| 44 | + this.tag = tag; | ||
| 45 | + this.subscribers = new ConcurrentLinkedQueue(); | ||
| 46 | + this.flvEncoder = new FlvEncoder(true, true); | ||
| 47 | + this.buffer = new ByteHolder(204800); | ||
| 48 | + this.httpPort = httpPort; | ||
| 49 | + if (!StringUtils.isEmpty(Configs.get("rtmp.url"))) { | ||
| 50 | + this.rtmpPublisher = new RTMPPublisher(tag, httpPort); | ||
| 51 | + this.rtmpPublisher.start(); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public boolean isPublishing() { | ||
| 57 | + return this.publishing; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + public Subscriber subscribe(ChannelHandlerContext ctx) { | ||
| 61 | + logger.info("channel: {} -> {}, subscriber: {}", new Object[]{Long.toHexString((long)this.hashCode() & 4294967295L), this.tag, ctx.channel().remoteAddress().toString()}); | ||
| 62 | + Subscriber subscriber = new VideoSubscriber(this.tag, ctx); | ||
| 63 | + this.subscribers.add(subscriber); | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + return subscriber; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + public void writeAudio(long timestamp, int pt, byte[] data) { | ||
| 70 | + if (this.audioCodec == null) { | ||
| 71 | + this.audioCodec = AudioCodec.getCodec(pt); | ||
| 72 | + logger.info("audio codec: {}", MediaEncoding.getEncoding(Type.Audio, pt)); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + this.broadcastAudio(timestamp, this.audioCodec.toPCM(data)); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + public void writeVideo(long sequence, long timeoffset, int payloadType, byte[] h264) { | ||
| 79 | + if (this.firstTimestamp == -1L) { | ||
| 80 | + this.firstTimestamp = timeoffset; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + this.publishing = true; | ||
| 84 | + this.buffer.write(h264); | ||
| 85 | + | ||
| 86 | + while(true) { | ||
| 87 | + byte[] nalu = this.readNalu(); | ||
| 88 | + if (nalu == null) { | ||
| 89 | + return; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + if (nalu.length >= 4) { | ||
| 93 | + byte[] flvTag = this.flvEncoder.write(nalu, (int)(timeoffset - this.firstTimestamp)); | ||
| 94 | + if (flvTag != null) { | ||
| 95 | + this.broadcastVideo(timeoffset, flvTag); | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + public void broadcastVideo(long timeoffset, byte[] flvTag) { | ||
| 102 | + Iterator var4 = this.subscribers.iterator(); | ||
| 103 | + | ||
| 104 | + while(var4.hasNext()) { | ||
| 105 | + Subscriber subscriber = (Subscriber)var4.next(); | ||
| 106 | + subscriber.onVideoData(timeoffset, flvTag, this.flvEncoder); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + public void broadcastAudio(long timeoffset, byte[] flvTag) { | ||
| 112 | + Iterator var4 = this.subscribers.iterator(); | ||
| 113 | + | ||
| 114 | + while(var4.hasNext()) { | ||
| 115 | + Subscriber subscriber = (Subscriber)var4.next(); | ||
| 116 | + subscriber.onAudioData(timeoffset, flvTag, this.flvEncoder); | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + public void unsubscribe(long watcherId) { | ||
| 122 | + Iterator<Subscriber> itr = this.subscribers.iterator(); | ||
| 123 | + | ||
| 124 | + Subscriber subscriber; | ||
| 125 | + do { | ||
| 126 | + if (!itr.hasNext()) { | ||
| 127 | + return; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + subscriber = (Subscriber)itr.next(); | ||
| 131 | + } while(subscriber.getId() != watcherId); | ||
| 132 | + | ||
| 133 | + itr.remove(); | ||
| 134 | + subscriber.close(); | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + public long getWatcherId(String tag) { | ||
| 138 | + Iterator<Subscriber> itr = this.subscribers.iterator(); | ||
| 139 | + | ||
| 140 | + Subscriber subscriber; | ||
| 141 | + do { | ||
| 142 | + if (!itr.hasNext()) { | ||
| 143 | + return -1100L; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + subscriber = (Subscriber)itr.next(); | ||
| 147 | + } while(!StringUtils.equals(tag, subscriber.getTag())); | ||
| 148 | + | ||
| 149 | + return subscriber.getId(); | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + public void close() { | ||
| 153 | + Iterator<Subscriber> itr = this.subscribers.iterator(); | ||
| 154 | + | ||
| 155 | + while(itr.hasNext()) { | ||
| 156 | + Subscriber subscriber = (Subscriber)itr.next(); | ||
| 157 | + subscriber.close(); | ||
| 158 | + itr.remove(); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + if (this.rtmpPublisher != null) { | ||
| 162 | + this.rtmpPublisher.close(); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + private byte[] readNalu() { | ||
| 168 | + for(int i = 0; i < this.buffer.size(); ++i) { | ||
| 169 | + int a = this.buffer.get(i + 0) & 255; | ||
| 170 | + int b = this.buffer.get(i + 1) & 255; | ||
| 171 | + int c = this.buffer.get(i + 2) & 255; | ||
| 172 | + int d = this.buffer.get(i + 3) & 255; | ||
| 173 | + if (a == 0 && b == 0 && c == 0 && d == 1 && i != 0) { | ||
| 174 | + byte[] nalu = new byte[i]; | ||
| 175 | + this.buffer.sliceInto(nalu, i); | ||
| 176 | + return nalu; | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + return null; | ||
| 181 | + } | ||
| 182 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/publisher/PublishManager.java
0 → 100644
| 1 | +// | ||
| 2 | +// Source code recreated from a .class file by IntelliJ IDEA | ||
| 3 | +// (powered by FernFlower decompiler) | ||
| 4 | +// | ||
| 5 | + | ||
| 6 | +package com.genersoft.iot.vmp.jt1078.publisher; | ||
| 7 | + | ||
| 8 | +import com.genersoft.iot.vmp.jt1078.entity.Media; | ||
| 9 | +import com.genersoft.iot.vmp.jt1078.entity.Media.Type; | ||
| 10 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Handler; | ||
| 11 | +import com.genersoft.iot.vmp.jt1078.subscriber.Subscriber; | ||
| 12 | +import io.netty.channel.ChannelHandlerContext; | ||
| 13 | + | ||
| 14 | +import java.io.IOException; | ||
| 15 | +import java.net.URISyntaxException; | ||
| 16 | +import java.util.Objects; | ||
| 17 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 18 | +import org.slf4j.Logger; | ||
| 19 | +import org.slf4j.LoggerFactory; | ||
| 20 | + | ||
| 21 | +public final class PublishManager { | ||
| 22 | + static Logger logger = LoggerFactory.getLogger(PublishManager.class); | ||
| 23 | + ConcurrentHashMap<String, Channel> channels = new ConcurrentHashMap(); | ||
| 24 | + static final PublishManager instance = new PublishManager(); | ||
| 25 | + private Integer httpPort; | ||
| 26 | + | ||
| 27 | + private PublishManager() { | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public Subscriber subscribe(String tag, Media.Type type, ChannelHandlerContext ctx, Integer httpPort) { | ||
| 31 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 32 | + if (chl == null) { | ||
| 33 | + chl = new Channel(tag, httpPort); | ||
| 34 | + this.channels.put(tag, chl); | ||
| 35 | + } | ||
| 36 | + this.httpPort = httpPort; | ||
| 37 | + | ||
| 38 | + Subscriber subscriber = null; | ||
| 39 | + if (type.equals(Type.Video)) { | ||
| 40 | + subscriber = chl.subscribe(ctx); | ||
| 41 | + subscriber.setName("subscriber-" + tag + "-" + subscriber.getId()); | ||
| 42 | + subscriber.start(); | ||
| 43 | + return subscriber; | ||
| 44 | + } else { | ||
| 45 | + throw new RuntimeException("unknown media type: " + type); | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public void publishAudio(String tag, int sequence, long timestamp, int payloadType, byte[] data) { | ||
| 50 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 51 | + if (chl != null) { | ||
| 52 | + chl.writeAudio(timestamp, payloadType, data); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + public void publishVideo(String tag, int sequence, long timestamp, int payloadType, byte[] data) { | ||
| 58 | + int length = data.length; | ||
| 59 | + StringBuilder builder = new StringBuilder(); | ||
| 60 | + | ||
| 61 | + for(int i = 0; i < length; ++i) { | ||
| 62 | + builder.append(this.valu(data, i)); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 66 | + if (chl != null) { | ||
| 67 | + chl.writeVideo((long)sequence, timestamp, payloadType, data); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + public String valu(byte[] data, int index) { | ||
| 73 | + byte val = data[index++]; | ||
| 74 | + int ch1 = val >> 4 & 15; | ||
| 75 | + int ch2 = val & 15; | ||
| 76 | + return ch1 + "" + ch2; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public Channel open(String tag, Integer httpPort) { | ||
| 80 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 81 | + if (chl == null) { | ||
| 82 | + chl = new Channel(tag, httpPort); | ||
| 83 | + this.channels.put(tag, chl); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + logger.info("Thread id:[{}]", Thread.currentThread().getId()); | ||
| 87 | + if (chl.isPublishing()) { | ||
| 88 | + throw new RuntimeException("channel already publishing"); | ||
| 89 | + } else { | ||
| 90 | + return chl; | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + public void close(String tag) { | ||
| 95 | + Channel chl = (Channel)this.channels.remove(tag); | ||
| 96 | + if (chl != null) { | ||
| 97 | + chl.close(); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + public void unsubscribe(String tag, long watcherId) { | ||
| 103 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 104 | + if (chl != null) { | ||
| 105 | + chl.unsubscribe(watcherId); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + | ||
| 109 | + logger.info("unsubscribe: {} - {}", tag, watcherId); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + public void unsubscribeAndClose(String tag) { | ||
| 113 | + try { | ||
| 114 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 115 | + if (chl != null) { | ||
| 116 | + long watcherId = chl.getWatcherId(tag); | ||
| 117 | + this.unsubscribe(tag, watcherId); | ||
| 118 | + } | ||
| 119 | + } catch (Exception var6) { | ||
| 120 | + logger.error("unsubscribeAndClose unsubscribe error;[{}]", tag); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + try { | ||
| 124 | + this.close(tag); | ||
| 125 | + } catch (Exception var5) { | ||
| 126 | + logger.error("unsubscribeAndClose close error;[{}]", tag); | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + public static void init() { | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + public static PublishManager getInstance() { | ||
| 135 | + return instance; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + private void createChannel(String tag, String tagMapping) { | ||
| 139 | + Channel chl = (Channel)this.channels.get(tag); | ||
| 140 | + } | ||
| 141 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/rtp/H264Packeter.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.rtp; | ||
| 2 | + | ||
| 3 | +import java.nio.ByteBuffer; | ||
| 4 | +import java.util.ArrayList; | ||
| 5 | +import java.util.Arrays; | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +public class H264Packeter { | ||
| 9 | + | ||
| 10 | + private final int MAX_PACKAGE_SIZE = 1400; | ||
| 11 | + | ||
| 12 | + private byte[] buffer; | ||
| 13 | + | ||
| 14 | + private long firstTimestamp = 0; | ||
| 15 | + | ||
| 16 | + private int seq = 0; | ||
| 17 | + | ||
| 18 | + private int lastPosition = 0; | ||
| 19 | + | ||
| 20 | + public List<byte[]> packet(byte[] h264, long timestamp) { | ||
| 21 | + List<byte[]> streams = new ArrayList<>(); | ||
| 22 | + if (buffer == null) { | ||
| 23 | + buffer = Arrays.copyOf(h264, h264.length); | ||
| 24 | + } else { | ||
| 25 | + byte[] nbuffer = new byte[buffer.length - lastPosition + h264.length]; | ||
| 26 | + System.arraycopy(buffer, lastPosition, nbuffer, 0, buffer.length - lastPosition); | ||
| 27 | + //System.out.println(toHex(nbuffer)); | ||
| 28 | + System.arraycopy(h264, 0, nbuffer, buffer.length - lastPosition, h264.length); | ||
| 29 | + //System.out.println(toHex(nbuffer)); | ||
| 30 | + buffer = nbuffer; | ||
| 31 | + } | ||
| 32 | + lastPosition = 0; | ||
| 33 | + //System.out.println(buffer.length); | ||
| 34 | + if (firstTimestamp == 0) { | ||
| 35 | + firstTimestamp = timestamp; | ||
| 36 | + } | ||
| 37 | + while (lastPosition < buffer.length - 4) { | ||
| 38 | + byte[] nalu = readNalu(); | ||
| 39 | + if (nalu == null) { | ||
| 40 | + break; | ||
| 41 | + } | ||
| 42 | + ByteBuffer buffer = null; | ||
| 43 | + byte[] header = new byte[14]; | ||
| 44 | + header[0] = (byte) (header[0] | 0x80); | ||
| 45 | + header[1] = (byte) (header[1] | 96); | ||
| 46 | + header[11] = 15; | ||
| 47 | + if (nalu.length <= MAX_PACKAGE_SIZE) { | ||
| 48 | + buffer = ByteBuffer.allocate(16 + nalu.length); | ||
| 49 | + header[1] = (byte) (header[1] | 0x80); | ||
| 50 | + buffer.put((byte) 0x24); | ||
| 51 | + buffer.put((byte) 0); | ||
| 52 | + buffer.putShort((short) (12 + nalu.length)); | ||
| 53 | + buffer.put(header, 0, 2); | ||
| 54 | + buffer.putShort((short) ++seq); | ||
| 55 | + buffer.putInt((int) (timestamp - firstTimestamp)); | ||
| 56 | + buffer.put(header, 8, 4); | ||
| 57 | + buffer.put(nalu); | ||
| 58 | + //System.out.println("完整: " + toHex(buffer.array())); | ||
| 59 | + streams.add(buffer.array()); | ||
| 60 | + } else { | ||
| 61 | + int tail = nalu.length % MAX_PACKAGE_SIZE, group = nalu.length / MAX_PACKAGE_SIZE + (tail > 0 ? 1 : 0); | ||
| 62 | + for (int i = 0; i < group; i++) { | ||
| 63 | + buffer = ByteBuffer.allocate(18 + MAX_PACKAGE_SIZE); | ||
| 64 | + if (i == 0) { | ||
| 65 | + buffer = ByteBuffer.allocate(17 + MAX_PACKAGE_SIZE); | ||
| 66 | + header[1] = (byte) (header[1] & 0x7F); | ||
| 67 | + header[12] = (byte) (header[12] | ((byte) (nalu[0] & 0x80)) << 7); | ||
| 68 | + header[12] = (byte) (header[12] | ((byte) ((nalu[0] & 0x60) >> 5)) << 5); | ||
| 69 | + header[12] = (byte) (header[12] | ((byte) 28)); | ||
| 70 | + header[13] = (byte) (header[13] & 0xBF); | ||
| 71 | + header[13] = (byte) (header[13] & 0xDF); | ||
| 72 | + header[13] = (byte) (header[13] | 0x80); | ||
| 73 | + header[13] = (byte) (header[13] | ((byte) (nalu[0] & 0x1F))); | ||
| 74 | + buffer.put((byte) 0x24); | ||
| 75 | + buffer.put((byte) 0); | ||
| 76 | + buffer.putShort((short) (13 + MAX_PACKAGE_SIZE)); | ||
| 77 | + buffer.put(header, 0, 2); | ||
| 78 | + buffer.putShort((short) ++seq); | ||
| 79 | + buffer.putInt((int) (timestamp - firstTimestamp)); | ||
| 80 | + buffer.put(header, 8, 6); | ||
| 81 | + buffer.put(nalu, i * MAX_PACKAGE_SIZE + 1, MAX_PACKAGE_SIZE - 1); | ||
| 82 | + //System.out.println(String.format("Nalu header:%02X", nalu[0])); | ||
| 83 | + //System.out.println("第一分片: " + toHex(buffer.array())); | ||
| 84 | + } else if (i == group - 1) { | ||
| 85 | + buffer = ByteBuffer.allocate(18 + tail); | ||
| 86 | + header[1] = (byte) (header[1] | 0x80); | ||
| 87 | + header[12] = (byte) (header[12] | ((byte) (nalu[0] & 0x80)) << 7); | ||
| 88 | + header[12] = (byte) (header[12] | ((byte) ((nalu[0] & 0x60) >> 5)) << 5); | ||
| 89 | + header[12] = (byte) (header[12] | ((byte) 28)); | ||
| 90 | + header[13] = (byte) (header[13] & 0xDF); | ||
| 91 | + header[13] = (byte) (header[13] & 0x7F); | ||
| 92 | + header[13] = (byte) (header[13] | 0x40); | ||
| 93 | + header[13] = (byte) (header[13] | ((byte) (nalu[0] & 0x1F))); | ||
| 94 | + buffer.put((byte) 0x24); | ||
| 95 | + buffer.put((byte) 0); | ||
| 96 | + buffer.putShort((short) (14 + tail)); | ||
| 97 | + buffer.put(header, 0, 2); | ||
| 98 | + buffer.putShort((short) ++seq); | ||
| 99 | + buffer.putInt((int) (timestamp - firstTimestamp)); | ||
| 100 | + buffer.put(header, 8, 6); | ||
| 101 | + buffer.put(nalu, i * MAX_PACKAGE_SIZE, tail); | ||
| 102 | + //System.out.println("最后分片: " + toHex(buffer.array())); | ||
| 103 | + } else { | ||
| 104 | + header[1] = (byte) (header[1] & 0x7F); | ||
| 105 | + header[12] = (byte) (header[12] | ((byte) (nalu[0] & 0x80)) << 7); | ||
| 106 | + header[12] = (byte) (header[12] | ((byte) ((nalu[0] & 0x60) >> 5)) << 5); | ||
| 107 | + header[12] = (byte) (header[12] | ((byte) 28)); | ||
| 108 | + header[13] = (byte) (header[13] & 0xDF); | ||
| 109 | + header[13] = (byte) (header[13] & 0x7F); | ||
| 110 | + header[13] = (byte) (header[13] & 0xBF); | ||
| 111 | + header[13] = (byte) (header[13] | ((byte) (nalu[0] & 0x1F))); | ||
| 112 | + buffer.put((byte) 0x24); | ||
| 113 | + buffer.put((byte) 0); | ||
| 114 | + buffer.putShort((short) (14 + MAX_PACKAGE_SIZE)); | ||
| 115 | + buffer.put(header, 0, 2); | ||
| 116 | + buffer.putShort((short) ++seq); | ||
| 117 | + buffer.putInt((int) (timestamp - firstTimestamp)); | ||
| 118 | + buffer.put(header, 8, 6); | ||
| 119 | + buffer.put(nalu, i * MAX_PACKAGE_SIZE, MAX_PACKAGE_SIZE); | ||
| 120 | + //System.out.println("中间分片: " + toHex(buffer.array())); | ||
| 121 | + } | ||
| 122 | + streams.add(buffer.array()); | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + return streams; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + public byte[] readNalu() { | ||
| 131 | + for (int i = (lastPosition == 0 ? 0 : lastPosition + 1); i < buffer.length - 3; i++) { | ||
| 132 | + if (buffer[i] == 0 && buffer[i + 1] == 0 && buffer[i + 2] == 0 && buffer[i + 3] == 1) { | ||
| 133 | + if (i != 0) { | ||
| 134 | + byte[] nalu = new byte[i - lastPosition - 4]; | ||
| 135 | + System.arraycopy(buffer, lastPosition + 4, nalu, 0, i - lastPosition - 4); | ||
| 136 | + lastPosition = i; | ||
| 137 | + //System.out.println(toHex(nalu)); | ||
| 138 | + | ||
| 139 | + return nalu; | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + return null; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + public String toHex(byte[] bytes) { | ||
| 148 | + StringBuilder sb = new StringBuilder(); | ||
| 149 | + for (byte b : bytes) { | ||
| 150 | + sb.append(String.format("%02X ", b)); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + return sb.toString(); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + public static void main(String[] args) { | ||
| 157 | + byte[] bytes = new byte[]{(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x67,(byte)0x4D,(byte)0x00,(byte)0x1F,(byte)0x96,(byte)0x35,(byte)0x41,(byte)0xE0,(byte)0x24,(byte)0xD3,(byte)0x70,(byte)0x50,(byte)0x10,(byte)0x50,(byte)0x20,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x68,(byte)0xEE,(byte)0x31,(byte)0xB2,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x06,(byte)0xE5,(byte)0x01,(byte)0x4A,(byte)0x80,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x65,(byte)0xB8,(byte)0x00,(byte)0x00,(byte)0x0C,(byte)0x16,(byte)0x90,(byte)0x00,(byte)0x00,(byte)0xBF,(byte)0xFE,(byte)0xD4,(byte)0xA7,(byte)0x99,(byte)0x63,(byte)0xE6,(byte)0xF9,(byte)0x5A,(byte)0x75,(byte)0xCE,(byte)0xDB,(byte)0x0C,(byte)0xD3,(byte)0xA6,(byte)0x31,(byte)0x05,(byte)0x66,(byte)0x6C,(byte)0x18,(byte)0x87,(byte)0xD0,(byte)0xF9,(byte)0xD0,(byte)0xCC,(byte)0xA3,(byte)0x57,(byte)0x07,(byte)0xDF,(byte)0x7C,(byte)0x6F,(byte)0x42,(byte)0xE9,(byte)0x8B,(byte)0x1B,(byte)0xA2,(byte)0x70,(byte)0x8C,(byte)0x80,(byte)0x00,(byte)0x00,(byte)0x1A,(byte)0xD6,(byte)0xEB,(byte)0x80,(byte)0xDE,(byte)0xE6,(byte)0xE2,(byte)0xF5,(byte)0xFF,(byte)0x33,(byte)0x98,(byte)0x97,(byte)0xCD,(byte)0xEB,(byte)0xEB,(byte)0xE5,(byte)0x60,(byte)0x00,(byte)0x00,(byte)0x0F,(byte)0xFB,(byte)0x49,(byte)0x08,(byte)0x9C,(byte)0x75,(byte)0xB4,(byte)0xDB,(byte)0xCE,(byte)0x58,(byte)0x08,(byte)0xB4,(byte)0x68,(byte)0x22,(byte)0x16,(byte)0x51,(byte)0x47,(byte)0xF3,(byte)0xD3,(byte)0x56,(byte)0xC2,(byte)0x4F,(byte)0x12,(byte)0xFD,(byte)0x2B,(byte)0xC9,(byte)0x45,(byte)0x80,(byte)0xDB,(byte)0xA4,(byte)0x62,(byte)0xEB,(byte)0xC3,(byte)0x6D,(byte)0xFE,(byte)0x36,(byte)0x20,(byte)0xAE,(byte)0xD9,(byte)0xD2,(byte)0x4C,(byte)0x9E,(byte)0x06,(byte)0xA0,(byte)0x8B,(byte)0x42,(byte)0x35,(byte)0xEC,(byte)0x64,(byte)0x03,(byte)0x22,(byte)0x29,(byte)0x26,(byte)0x19,(byte)0x70,(byte)0xCA,(byte)0x18,(byte)0xC0,(byte)0x7E,(byte)0x08,(byte)0x4F,(byte)0xEB,(byte)0xFD,(byte)0x5D,(byte)0x90,(byte)0x31,(byte)0x62,(byte)0x02,(byte)0x2E,(byte)0xBE,(byte)0x53,(byte)0xCF,(byte)0xC0,(byte)0xA8,(byte)0xAC,(byte)0xF3,(byte)0x92,(byte)0xC8,(byte)0x76,(byte)0x77,(byte)0x84,(byte)0x2F,(byte)0x76,(byte)0x45,(byte)0xF3,(byte)0xBF,(byte)0x07,(byte)0x1F,(byte)0x6D,(byte)0xC6,(byte)0x11,(byte)0xB9,(byte)0x83,(byte)0xF6,(byte)0xDF,(byte)0xA1,(byte)0x6D,(byte)0x56,(byte)0x6D,(byte)0xE0,(byte)0xFA,(byte)0xC1,(byte)0x7E,(byte)0xC5,(byte)0xC5,(byte)0x3C,(byte)0x69,(byte)0x57,(byte)0x61,(byte)0xCA,(byte)0x17,(byte)0x40,(byte)0x30,(byte)0xAE,(byte)0x4E,(byte)0x4C,(byte)0x61,(byte)0xC3,(byte)0xAF,(byte)0x6F,(byte)0xB4,(byte)0x48,(byte)0x33,(byte)0x4F,(byte)0x59,(byte)0x6D,(byte)0x88,(byte)0xA0,(byte)0x3B,(byte)0x9C,(byte)0x39,(byte)0x67,(byte)0xAD,(byte)0x0C,(byte)0xC0,(byte)0x64,(byte)0x8A,(byte)0xDB,(byte)0x95,(byte)0xB3,(byte)0xEF,(byte)0x6A,(byte)0xC0,(byte)0x9B,(byte)0xAF,(byte)0x44,(byte)0xBF,(byte)0x69,(byte)0x77,(byte)0x7D,(byte)0x2B,(byte)0xDB,(byte)0x47,(byte)0x78,(byte)0xD0,(byte)0x9C,(byte)0x79,(byte)0xA1,(byte)0xFE,(byte)0xC4,(byte)0xC4,(byte)0xAF,(byte)0x6A,(byte)0x2C,(byte)0x2D,(byte)0xF4,(byte)0xB6,(byte)0x27,(byte)0xC6,(byte)0x3C,(byte)0x71,(byte)0xC1,(byte)0x5E,(byte)0xB0,(byte)0x22,(byte)0x93,(byte)0x88,(byte)0x9C,(byte)0x98,(byte)0x3A,(byte)0x8D,(byte)0x7F,(byte)0x2E,(byte)0x48,(byte)0x53,(byte)0x2D,(byte)0xF5,(byte)0x7A,(byte)0xD0,(byte)0xC2,(byte)0x68,(byte)0xAF,(byte)0xB7,(byte)0x8C,(byte)0xF4,(byte)0xD4,(byte)0x99,(byte)0x96,(byte)0x24,(byte)0x47,(byte)0x2B,(byte)0x28,(byte)0x26,(byte)0xE4,(byte)0xBD,(byte)0xFA,(byte)0x65,(byte)0x7C,(byte)0xB3,(byte)0xA8,(byte)0x3E,(byte)0x43,(byte)0xF4,(byte)0x6D,(byte)0x50,(byte)0x7F,(byte)0xE3,(byte)0xF5,(byte)0x73,(byte)0xE6,(byte)0xF2,(byte)0x23,(byte)0x3A,(byte)0x22,(byte)0x74,(byte)0x7B,(byte)0x1E,(byte)0xDC,(byte)0xFB,(byte)0xF4,(byte)0xA8,(byte)0x97,(byte)0xB9,(byte)0x3A,(byte)0x73,(byte)0x8B,(byte)0x78,(byte)0x64,(byte)0x03,(byte)0x55,(byte)0x6E,(byte)0x52,(byte)0x7D,(byte)0x4C,(byte)0x28,(byte)0x00,(byte)0x43,(byte)0x72,(byte)0x84,(byte)0xF1,(byte)0x81,(byte)0x55,(byte)0x7B,(byte)0x8D,(byte)0x0F,(byte)0x7F,(byte)0xB4,(byte)0xEB,(byte)0xAB,(byte)0x69,(byte)0x65,(byte)0x7B,(byte)0x92,(byte)0xAC,(byte)0xB6,(byte)0xB4,(byte)0x33,(byte)0x5D,(byte)0x33,(byte)0x5D,(byte)0xC2,(byte)0xF8,(byte)0x25,(byte)0x7E,(byte)0x1D,(byte)0x1D,(byte)0xDB,(byte)0x1C,(byte)0xF8,(byte)0xBE,(byte)0x4B,(byte)0x25,(byte)0xA9,(byte)0xB5,(byte)0x8A,(byte)0x8D,(byte)0x67,(byte)0x61,(byte)0xFF,(byte)0xE3,(byte)0x18,(byte)0x1C,(byte)0x8F,(byte)0x7F,(byte)0xBA,(byte)0x50,(byte)0x47,(byte)0x10,(byte)0x5D,(byte)0xD5,(byte)0x97,(byte)0x62,(byte)0x06,(byte)0x09,(byte)0x52,(byte)0xC3,(byte)0x81,(byte)0x1A,(byte)0x58,(byte)0x87,(byte)0xFC,(byte)0x30,(byte)0x61,(byte)0x89,(byte)0xF5,(byte)0x2C,(byte)0x58,(byte)0x04,(byte)0x32,(byte)0x8B,(byte)0x3E,(byte)0x79,(byte)0xA3,(byte)0x10,(byte)0xFD,(byte)0x11,(byte)0x59,(byte)0xCA,(byte)0x08,(byte)0x48,(byte)0x24,(byte)0xDF,(byte)0x5F,(byte)0x02,(byte)0x12,(byte)0x2F,(byte)0x4C,(byte)0xDC,(byte)0xE9,(byte)0xFE,(byte)0xF0,(byte)0x21,(byte)0x5B,(byte)0xD3,(byte)0x0C,(byte)0xA7,(byte)0xF6,(byte)0xEF,(byte)0xFD,(byte)0xA4,(byte)0xB0,(byte)0xEF,(byte)0x47,(byte)0xC6,(byte)0x8F,(byte)0xB7,(byte)0x90,(byte)0xE3,(byte)0x03,(byte)0xBE,(byte)0x85,(byte)0x51,(byte)0x56,(byte)0x65,(byte)0xD3,(byte)0x6B,(byte)0xC4,(byte)0x8F,(byte)0x00,(byte)0x09,(byte)0xCC,(byte)0x0C,(byte)0x7C,(byte)0x69,(byte)0x42,(byte)0x68,(byte)0x05,(byte)0x97,(byte)0x5D,(byte)0xD8,(byte)0x66,(byte)0x8E,(byte)0x1D,(byte)0x2E,(byte)0x65,(byte)0x0B,(byte)0xCC,(byte)0x24,(byte)0x15,(byte)0xE4,(byte)0x10,(byte)0x23,(byte)0x4D,(byte)0xAE,(byte)0x01,(byte)0xCB,(byte)0xEB,(byte)0x16,(byte)0xAE,(byte)0x5A,(byte)0xA9,(byte)0xA0,(byte)0xFD,(byte)0xE8,(byte)0x62,(byte)0x57,(byte)0x8E,(byte)0x8F,(byte)0x57,(byte)0xA7,(byte)0xCC,(byte)0x6B,(byte)0xEB,(byte)0xDF,(byte)0xC1,(byte)0xBD,(byte)0xA6,(byte)0x40,(byte)0x40,(byte)0x07,(byte)0xAC,(byte)0x0A,(byte)0x40,(byte)0xD1,(byte)0xA7,(byte)0x9F,(byte)0x8D,(byte)0xE8,(byte)0x36,(byte)0xD8,(byte)0x53,(byte)0x54,(byte)0x66,(byte)0x14,(byte)0x5B,(byte)0x38,(byte)0x23,(byte)0xC5,(byte)0x72,(byte)0xA1,(byte)0x9D,(byte)0x3B,(byte)0xDD,(byte)0xD3,(byte)0xD6,(byte)0x46,(byte)0xE9,(byte)0x7D,(byte)0x0D,(byte)0xA7,(byte)0x22,(byte)0x00,(byte)0x87,(byte)0x7C,(byte)0x4E,(byte)0x4E,(byte)0x56,(byte)0xE1,(byte)0x03,(byte)0x99,(byte)0x4A,(byte)0xB5,(byte)0x09,(byte)0xD7,(byte)0xC1,(byte)0x0F,(byte)0xDD,(byte)0xB5,(byte)0x91,(byte)0xF8,(byte)0x3D,(byte)0x19,(byte)0x63,(byte)0xAD,(byte)0xC1,(byte)0x21,(byte)0x46,(byte)0x2F,(byte)0x2A,(byte)0xE8,(byte)0x11,(byte)0xFA,(byte)0x56,(byte)0xCD,(byte)0x16,(byte)0xB2,(byte)0x1C,(byte)0xA0,(byte)0xB1,(byte)0xBC,(byte)0xB4,(byte)0x99,(byte)0xBC,(byte)0xFB,(byte)0x60,(byte)0x48,(byte)0x45,(byte)0xFB,(byte)0x52,(byte)0x5A,(byte)0xE5,(byte)0x1A,(byte)0x43,(byte)0x6B,(byte)0x26,(byte)0xC3,(byte)0xD8,(byte)0xE6,(byte)0x1F,(byte)0x0F,(byte)0x1D,(byte)0x77,(byte)0x92,(byte)0xB7,(byte)0x05,(byte)0x15,(byte)0x8A,(byte)0xEE,(byte)0xB8,(byte)0x62,(byte)0x82,(byte)0x9D,(byte)0x98,(byte)0x94,(byte)0xA7,(byte)0xBA,(byte)0x7B,(byte)0x19,(byte)0x8B,(byte)0x8E,(byte)0x3F,(byte)0xB4,(byte)0x1B,(byte)0x9B,(byte)0x4D,(byte)0xD3,(byte)0xA2,(byte)0x28,(byte)0x05,(byte)0x99,(byte)0xC8,(byte)0xF7,(byte)0x2A,(byte)0x6F,(byte)0xB9,(byte)0xC9,(byte)0x96,(byte)0xF6,(byte)0x03,(byte)0xC6,(byte)0x10,(byte)0xBF,(byte)0xF2,(byte)0xD5,(byte)0xAE,(byte)0x7F,(byte)0x93,(byte)0xE4,(byte)0xB6,(byte)0x4D,(byte)0xE0,(byte)0xE5,(byte)0x06,(byte)0x4E,(byte)0x4C,(byte)0xC5,(byte)0xD5,(byte)0xD9,(byte)0xF8,(byte)0x1E,(byte)0x36,(byte)0x38,(byte)0x01,(byte)0x7C,(byte)0xBC,(byte)0x1C,(byte)0x71,(byte)0x46,(byte)0x2C,(byte)0xCE,(byte)0xBD,(byte)0x23,(byte)0x14,(byte)0x37,(byte)0xBB,(byte)0x70,(byte)0xC6,(byte)0x7A,(byte)0xF7,(byte)0x73,(byte)0xA8,(byte)0xA9,(byte)0xDC,(byte)0xC2,(byte)0xC0,(byte)0x7A,(byte)0xDA,(byte)0x74,(byte)0xFF,(byte)0x25,(byte)0x73,(byte)0x31,(byte)0xD8,(byte)0xF9,(byte)0x4D,(byte)0x66,(byte)0xD3,(byte)0x5E,(byte)0x98,(byte)0xC6,(byte)0xC4,(byte)0x55,(byte)0x0B,(byte)0xC4,(byte)0xB1,(byte)0xED,(byte)0x0F,(byte)0x74,(byte)0x5D,(byte)0x1B,(byte)0x7A,(byte)0x05,(byte)0xDB,(byte)0x7C,(byte)0x0D,(byte)0xDF,(byte)0xE2,(byte)0x6B,(byte)0xAF,(byte)0x22,(byte)0x3B,(byte)0x11,(byte)0x35,(byte)0xE3,(byte)0x51,(byte)0x13,(byte)0x07,(byte)0xD3,(byte)0x6E,(byte)0xAE,(byte)0x91,(byte)0xE2,(byte)0x98,(byte)0x02,(byte)0x6D,(byte)0xD9,(byte)0xD9,(byte)0xBD,(byte)0x7C,(byte)0x8E,(byte)0xBF,(byte)0xBE,(byte)0xB7,(byte)0x79,(byte)0xCA,(byte)0xC1,(byte)0x66,(byte)0x89,(byte)0x17,(byte)0x9B,(byte)0x77,(byte)0xBE,(byte)0xA7,(byte)0xED,(byte)0x3E,(byte)0xCC,(byte)0x86,(byte)0x44,(byte)0x42,(byte)0x38,(byte)0x50,(byte)0x8D,(byte)0xC3,(byte)0x58,(byte)0x07,(byte)0x42,(byte)0xBF,(byte)0x7C,(byte)0xC3,(byte)0x72,(byte)0x81,(byte)0x6E,(byte)0xFC,(byte)0xC8,(byte)0x63,(byte)0x8B,(byte)0x2E,(byte)0x63,(byte)0xA6,(byte)0x17,(byte)0x62,(byte)0x3C,(byte)0xED,(byte)0x29,(byte)0xFE,(byte)0xBC,(byte)0x4E,(byte)0x8B,(byte)0x94,(byte)0x4B,(byte)0x46,(byte)0xE6,(byte)0xC7,(byte)0x1A,(byte)0x32,(byte)0xE7,(byte)0xC8,(byte)0x44,(byte)0x47,(byte)0x1C,(byte)0xE8,(byte)0xC7,(byte)0x8C,(byte)0x1F,(byte)0x9E,(byte)0x16,(byte)0xED,(byte)0x12,(byte)0x8D,(byte)0x66,(byte)0x71,(byte)0xF4,(byte)0x1E,(byte)0x22,(byte)0xAB,(byte)0xD9,(byte)0xF5,(byte)0x22,(byte)0xC3,(byte)0x31,(byte)0x0B,(byte)0xD6,(byte)0x12,(byte)0x46,(byte)0x99,(byte)0x13,(byte)0xD2,(byte)0x02,(byte)0x34,(byte)0x7E,(byte)0x01,(byte)0x25,(byte)0xAC,(byte)0xB6,(byte)0xF1,(byte)0xF1,(byte)0x46,(byte)0xBE,(byte)0x90,(byte)0x79,(byte)0xBA,(byte)0x5B,(byte)0x36,(byte)0xF7,(byte)0x81,(byte)0x70,(byte)0x4A,(byte)0xDC,(byte)0xF1,(byte)0x24,(byte)0x9A,(byte)0x87,(byte)0x1E,(byte)0x59,(byte)0xE1,(byte)0x46,(byte)0xDC,(byte)0x0E,(byte)0x71,(byte)0xB4,(byte)0xE5,(byte)0x48,(byte)0x0E,(byte)0x11,(byte)0x87,(byte)0x99,(byte)0x2A,(byte)0x5C,(byte)0x61,(byte)0x75,(byte)0x3C,(byte)0x5B,(byte)0xF8,(byte)0xE6,(byte)0xE4,(byte)0x01,(byte)0xA2,(byte)0x01,(byte)0xE5,(byte)0x79,(byte)0x52,(byte)0x0B,(byte)0xC7,(byte)0xF7,(byte)0xED,(byte)0x0B,(byte)0x52,(byte)0x47,(byte)0x77,(byte)0xAD,(byte)0x45,(byte)0x72,(byte)0x21,(byte)0x0E,(byte)0xBE,(byte)0xA5,(byte)0x3D,(byte)0xEA,(byte)0xBF,(byte)0x44,(byte)0x7E,(byte)0x75,(byte)0x8C,(byte)0xF0,(byte)0x05,(byte)0xBB,(byte)0xDD,(byte)0xE3,(byte)0x53,(byte)0x4E,(byte)0x1B,(byte)0xB4,(byte)0x2F,(byte)0x65,(byte)0xCE,(byte)0x44,(byte)0x95,(byte)0x4F,(byte)0x44,(byte)0x1D,(byte)0x0D,(byte)0x54,(byte)0xF9,(byte)0xCD,(byte)0x30,(byte)0x00,(byte)0x81,(byte)0x2C,(byte)0x5C,(byte)0xFF,(byte)0xBE}; | ||
| 158 | + H264Packeter packeter = new H264Packeter(); | ||
| 159 | + packeter.packet(bytes, 0); | ||
| 160 | + } | ||
| 161 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/rtsp/RtspRequest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.rtsp; | ||
| 2 | + | ||
| 3 | +import io.netty.handler.codec.http.DefaultFullHttpRequest; | ||
| 4 | +import io.netty.handler.codec.http.FullHttpRequest; | ||
| 5 | +import io.netty.handler.codec.rtsp.RtspHeaderNames; | ||
| 6 | +import io.netty.handler.codec.rtsp.RtspMethods; | ||
| 7 | +import io.netty.handler.codec.rtsp.RtspVersions; | ||
| 8 | +import io.netty.util.internal.StringUtil; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Rtsp请求 | ||
| 12 | + */ | ||
| 13 | +public class RtspRequest { | ||
| 14 | + | ||
| 15 | + private final String CRLF = "\r\n"; | ||
| 16 | + | ||
| 17 | + private final String VERSION = "RTSP/1.0"; | ||
| 18 | + | ||
| 19 | + private int seq = 0; | ||
| 20 | + | ||
| 21 | + private String host; | ||
| 22 | + | ||
| 23 | + private int port; | ||
| 24 | + | ||
| 25 | + private String path; | ||
| 26 | + | ||
| 27 | + private String sessionID; | ||
| 28 | + | ||
| 29 | + public RtspRequest(String host, int port, String path) { | ||
| 30 | + this.host = host; | ||
| 31 | + this.port = port; | ||
| 32 | + this.path = path; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public String getHost() { | ||
| 36 | + return host; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public void setHost(String host) { | ||
| 40 | + this.host = host; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public int getPort() { | ||
| 44 | + return port; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public void setPort(int port) { | ||
| 48 | + this.port = port; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public String getPath() { | ||
| 52 | + return path; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public void setPath(String path) { | ||
| 56 | + this.path = path; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public String getSessionID() { | ||
| 60 | + return sessionID; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + public void setSessionID(String sessionID) { | ||
| 64 | + this.sessionID = sessionID; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + public FullHttpRequest option() { | ||
| 68 | + FullHttpRequest request = new DefaultFullHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, String.format("rtsp://%s:%d%s", host, port, path)); | ||
| 69 | + request.headers().set(RtspHeaderNames.CSEQ, ++seq); | ||
| 70 | + | ||
| 71 | + return request; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + public FullHttpRequest announce() { | ||
| 75 | + StringBuilder body = new StringBuilder(); | ||
| 76 | + FullHttpRequest request = new DefaultFullHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.ANNOUNCE, String.format("rtsp://%s:%d%s", host, port, path)); | ||
| 77 | + request.headers().set(RtspHeaderNames.CSEQ, ++seq); | ||
| 78 | + request.headers().set(RtspHeaderNames.CONTENT_TYPE, "application/sdp"); | ||
| 79 | + | ||
| 80 | + body.append("v=0").append(CRLF) | ||
| 81 | + .append("o=- 0 0 IN IP4 127.0.0.1").append(CRLF) | ||
| 82 | + .append("s=No Name").append(CRLF) | ||
| 83 | + .append("c=IN IP4 ").append(this.host).append(CRLF) | ||
| 84 | + .append("t=0 0").append(CRLF) | ||
| 85 | + .append("a=tool:libavformat 61.9.100").append(CRLF) | ||
| 86 | + .append("m=video 0 RTP/AVP 96").append(CRLF) | ||
| 87 | + .append("b=AS:3943").append(CRLF) | ||
| 88 | + .append("a=rtpmap:96 H264/90000").append(CRLF) | ||
| 89 | + .append("a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAoAAADAAgAAAMBlCA=,aO48gA==; profile-level-id=4D002A").append(CRLF) | ||
| 90 | + .append("a=control:streamid=0").append(CRLF); | ||
| 91 | + request.content().writeBytes(body.toString().getBytes()); | ||
| 92 | + request.headers().set(RtspHeaderNames.CONTENT_LENGTH, body.toString().getBytes().length); | ||
| 93 | + | ||
| 94 | + return request; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + public FullHttpRequest setup() { | ||
| 98 | + FullHttpRequest request = new DefaultFullHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.SETUP, String.format("rtsp://%s:%d%s/streamid=0", host, port, path)); | ||
| 99 | + request.headers().set(RtspHeaderNames.CSEQ, ++seq); | ||
| 100 | + request.headers().set(RtspHeaderNames.TRANSPORT, "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record"); | ||
| 101 | + request.headers().set(RtspHeaderNames.SESSION, sessionID); | ||
| 102 | + | ||
| 103 | + return request; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + public FullHttpRequest record() { | ||
| 107 | + FullHttpRequest request = new DefaultFullHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.RECORD, String.format("rtsp://%s:%d%s/streamid=0", host, port, path)); | ||
| 108 | + request.headers().set(RtspHeaderNames.CSEQ, ++seq); | ||
| 109 | + request.headers().set(RtspHeaderNames.RANGE, "npt=0.000-"); | ||
| 110 | + request.headers().set(RtspHeaderNames.SESSION, sessionID); | ||
| 111 | + | ||
| 112 | + return request; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + public String teardown() { | ||
| 116 | + StringBuilder sb = new StringBuilder(); | ||
| 117 | + | ||
| 118 | + return sb.toString(); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + private String header(int length) { | ||
| 122 | + StringBuilder sb = new StringBuilder(); | ||
| 123 | + | ||
| 124 | + return sb.append("CSeq: ").append(++seq).append(CRLF) | ||
| 125 | + .append("User-Agent: jt1078").append(CRLF) | ||
| 126 | + .append("Content-Length: ").append(length).append(CRLF) | ||
| 127 | + .append(StringUtil.isNullOrEmpty(sessionID) ? "" : "Session: ").append(StringUtil.isNullOrEmpty(sessionID) ? "" : sessionID).append(StringUtil.isNullOrEmpty(sessionID) ? "" : CRLF).append(CRLF).toString(); | ||
| 128 | + } | ||
| 129 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/rtsp/RtspSessionManager.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.rtsp; | ||
| 2 | + | ||
| 3 | +import io.netty.channel.Channel; | ||
| 4 | + | ||
| 5 | +import java.util.Map; | ||
| 6 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * Rtsp会话 | ||
| 10 | + */ | ||
| 11 | +public class RtspSessionManager { | ||
| 12 | + | ||
| 13 | + private static Map<String, Object> channel2register = new ConcurrentHashMap<>(); | ||
| 14 | + | ||
| 15 | + private static Map<String, Channel> channel2push = new ConcurrentHashMap<>(); | ||
| 16 | + | ||
| 17 | + public static void register(String channel) { | ||
| 18 | + channel2register.put(channel, System.currentTimeMillis()); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public static void unregister(String channel) { | ||
| 22 | + channel2register.remove(channel); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public static boolean isRegistered(String channel) { | ||
| 26 | + return channel2register.containsKey(channel); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public static void setPush(String channel, Channel push) { | ||
| 30 | + channel2push.put(channel, push); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public static Channel getPush(String channel) { | ||
| 34 | + return channel2push.get(channel); | ||
| 35 | + } | ||
| 36 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/server/Jtt1078Decoder.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.server; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.util.ByteHolder; | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | ||
| 5 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Created by matrixy on 2019/4/9. | ||
| 9 | + */ | ||
| 10 | +public class Jtt1078Decoder | ||
| 11 | +{ | ||
| 12 | + ByteHolder buffer = new ByteHolder(4096); | ||
| 13 | + | ||
| 14 | + public void write(byte[] block) | ||
| 15 | + { | ||
| 16 | + buffer.write(block); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + public void write(byte[] block, int startIndex, int length) | ||
| 20 | + { | ||
| 21 | + byte[] buff = new byte[length]; | ||
| 22 | + System.arraycopy(block, startIndex, buff, 0, length); | ||
| 23 | + write(buff); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + public Packet decode() | ||
| 27 | + { | ||
| 28 | + if (this.buffer.size() < 30) return null; | ||
| 29 | + | ||
| 30 | + if ((buffer.getInt(0) & 0x7fffffff) != 0x30316364) | ||
| 31 | + { | ||
| 32 | + String header = ByteUtils.toString(buffer.array(30)); | ||
| 33 | + throw new RuntimeException("invalid protocol header: " + header); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + int lengthOffset = 28; | ||
| 37 | + int dataType = (this.buffer.get(15) >> 4) & 0x0f; | ||
| 38 | + // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段 | ||
| 39 | + if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2; | ||
| 40 | + else if (dataType == 0x03) lengthOffset = 28 - 4; | ||
| 41 | + int bodyLength = this.buffer.getShort(lengthOffset); | ||
| 42 | + | ||
| 43 | + int packetLength = bodyLength + lengthOffset + 2; | ||
| 44 | + | ||
| 45 | + if (this.buffer.size() < packetLength) return null; | ||
| 46 | + byte[] block = new byte[packetLength]; | ||
| 47 | + this.buffer.sliceInto(block, packetLength); | ||
| 48 | + return Packet.create(block); | ||
| 49 | + } | ||
| 50 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/server/Jtt1078Handler.java
0 → 100644
| 1 | +// | ||
| 2 | +// Source code recreated from a .class file by IntelliJ IDEA | ||
| 3 | +// (powered by FernFlower decompiler) | ||
| 4 | +// | ||
| 5 | + | ||
| 6 | +package com.genersoft.iot.vmp.jt1078.server; | ||
| 7 | + | ||
| 8 | +import com.genersoft.iot.vmp.VManageBootstrap; | ||
| 9 | +import com.genersoft.iot.vmp.conf.MediaConfig; | ||
| 10 | +import com.genersoft.iot.vmp.jt1078.app.VideoServerApp; | ||
| 11 | +import com.genersoft.iot.vmp.jt1078.publisher.Channel; | ||
| 12 | +import com.genersoft.iot.vmp.jt1078.publisher.PublishManager; | ||
| 13 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | ||
| 14 | +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; | ||
| 15 | +import com.genersoft.iot.vmp.service.IStreamProxyService; | ||
| 16 | +import com.genersoft.iot.vmp.utils.SpringBeanFactory; | ||
| 17 | +import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.Jt1078ConfigBean; | ||
| 18 | +import com.genersoft.iot.vmp.vmanager.streamProxy.StreamProxyController; | ||
| 19 | +import io.lettuce.core.support.caching.RedisCache; | ||
| 20 | +import io.netty.channel.ChannelHandlerContext; | ||
| 21 | +import io.netty.channel.SimpleChannelInboundHandler; | ||
| 22 | +import io.netty.util.Attribute; | ||
| 23 | +import io.netty.util.AttributeKey; | ||
| 24 | + | ||
| 25 | +import java.io.IOException; | ||
| 26 | +import java.net.URISyntaxException; | ||
| 27 | +import java.util.Date; | ||
| 28 | +import java.util.Iterator; | ||
| 29 | +import java.util.Map; | ||
| 30 | +import java.util.Objects; | ||
| 31 | +import java.util.concurrent.TimeUnit; | ||
| 32 | + | ||
| 33 | +import org.apache.commons.lang3.StringUtils; | ||
| 34 | +import org.slf4j.Logger; | ||
| 35 | +import org.slf4j.LoggerFactory; | ||
| 36 | +import org.springframework.data.redis.core.RedisTemplate; | ||
| 37 | +import org.springframework.data.redis.core.StringRedisTemplate; | ||
| 38 | + | ||
| 39 | +public class Jtt1078Handler extends SimpleChannelInboundHandler<Packet> { | ||
| 40 | + static Logger logger = LoggerFactory.getLogger(Jtt1078Handler.class); | ||
| 41 | + private static final AttributeKey<Session> SESSION_KEY = AttributeKey.valueOf("session-key"); | ||
| 42 | + private ChannelHandlerContext context; | ||
| 43 | + private String tagMapping; | ||
| 44 | + private Integer httpPort; | ||
| 45 | + | ||
| 46 | + public Jtt1078Handler(String tagMapping, Integer httpPort) { | ||
| 47 | + this.tagMapping = tagMapping; | ||
| 48 | + this.httpPort = httpPort; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception { | ||
| 52 | + this.context = ctx; | ||
| 53 | + packet.seek(8); | ||
| 54 | + String sim = packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD(); | ||
| 55 | + int channel = packet.nextByte() & 255; | ||
| 56 | + String tag = sim + "-" + channel; | ||
| 57 | + if (StringUtils.isEmpty(this.tagMapping)) { | ||
| 58 | + this.tagMapping = tag; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + if (!StringUtils.endsWith(this.tagMapping, ".flv")) { | ||
| 62 | + endWithMapping(this.tagMapping); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + Session session = this.getSession(); | ||
| 67 | + if (null == session) { | ||
| 68 | + this.setSession(session = new Session()); | ||
| 69 | + Channel chl = PublishManager.getInstance().open(this.tagMapping, this.httpPort); | ||
| 70 | + session.set("tag", this.tagMapping); | ||
| 71 | + logger.info("start publishing: {} -> {}-{}", new Object[]{Long.toHexString((long) chl.hashCode() & 4294967295L), sim, channel}); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + Integer sequence = (Integer) session.get("video-sequence"); | ||
| 75 | + if (sequence == null) { | ||
| 76 | + sequence = 0; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + int lengthOffset = 28; | ||
| 80 | + int dataType = packet.seek(15).nextByte() >> 4 & 15; | ||
| 81 | + int pkType = packet.seek(15).nextByte() & 15; | ||
| 82 | + if (dataType == 4) { | ||
| 83 | + lengthOffset = 16; | ||
| 84 | + } else if (dataType == 3) { | ||
| 85 | + lengthOffset = 24; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + int pt = packet.seek(5).nextByte() & 127; | ||
| 89 | + long timestamp; | ||
| 90 | + if (dataType != 0 && dataType != 1 && dataType != 2) { | ||
| 91 | + if (dataType == 3) { | ||
| 92 | + timestamp = packet.seek(16).nextLong(); | ||
| 93 | + byte[] data = packet.seek(lengthOffset + 2).nextBytes(); | ||
| 94 | + PublishManager.getInstance().publishVideo(this.tagMapping, sequence, timestamp, pt, data); | ||
| 95 | + } | ||
| 96 | + } else { | ||
| 97 | + if (pkType == 0 || pkType == 2) { | ||
| 98 | + sequence = sequence + 1; | ||
| 99 | + session.set("video-sequence", sequence); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + timestamp = packet.seek(16).nextLong(); | ||
| 103 | + PublishManager.getInstance().publishVideo(this.tagMapping, sequence, timestamp, pt, packet.seek(lengthOffset + 2).nextBytes()); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + private byte[] convert(String str) { | ||
| 109 | + byte[] bytes = str.getBytes(); | ||
| 110 | + new StringBuilder(); | ||
| 111 | + int length = bytes.length; | ||
| 112 | + byte[] bys = new byte[length]; | ||
| 113 | + | ||
| 114 | + for (int i = 0; i < length; ++i) { | ||
| 115 | + bys[i] = Byte.valueOf(Integer.toBinaryString(bytes[i]) + ""); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + return bys; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + public final Session getSession() { | ||
| 122 | + Attribute<Session> attr = this.context.channel().attr(SESSION_KEY); | ||
| 123 | + return null == attr ? null : (Session) attr.get(); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + public void channelInactive(ChannelHandlerContext ctx) throws Exception { | ||
| 127 | + super.channelInactive(ctx); | ||
| 128 | + this.release(); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + public final void setSession(Session session) { | ||
| 132 | + this.context.channel().attr(SESSION_KEY).set(session); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | ||
| 136 | + cause.printStackTrace(); | ||
| 137 | + this.release(); | ||
| 138 | + ctx.close(); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + private void release() { | ||
| 142 | + String tag = (String) this.getSession().get("tag"); | ||
| 143 | + if (tag != null) { | ||
| 144 | + logger.info("close netty channel: {}", tag); | ||
| 145 | + PublishManager.getInstance().close(tag); | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + public static void createStreamProxy(String stream, Integer port) throws URISyntaxException, IOException { | ||
| 151 | + Jt1078ConfigBean jt1078ConfigBean = VManageBootstrap.getBean(Jt1078ConfigBean.class); | ||
| 152 | + MediaConfig mediaConfig = VManageBootstrap.getBean(MediaConfig.class); | ||
| 153 | + StreamProxyController streamProxyController = VManageBootstrap.getBean(StreamProxyController.class); | ||
| 154 | + | ||
| 155 | + if (StringUtils.endsWith(stream, ".flv")) { | ||
| 156 | + stream = StringUtils.substringBeforeLast(stream, ".flv"); | ||
| 157 | + } | ||
| 158 | + if(Objects.isNull(jt1078ConfigBean)){ | ||
| 159 | + return; | ||
| 160 | + } | ||
| 161 | + String url = StringUtils.replace(jt1078ConfigBean.getGetURL(), "{stream}", stream); | ||
| 162 | + url = StringUtils.replace(url, "{port}", port + ""); | ||
| 163 | + if (!StringUtils.endsWith(url, ".flv")) { | ||
| 164 | + url = url + ".flv"; | ||
| 165 | + } | ||
| 166 | + StreamProxyItem item = new StreamProxyItem(); | ||
| 167 | + item.setApp("schedule"); | ||
| 168 | + item.setEnable(true); | ||
| 169 | + item.setEnableAudio(true); | ||
| 170 | + item.setRtpType("default"); | ||
| 171 | + item.setStream(stream); | ||
| 172 | + item.setMediaServerId(mediaConfig.getId()); | ||
| 173 | + item.setUrl(url); | ||
| 174 | + item.setFfmpegCmdKey("ffmpeg.cmd"); | ||
| 175 | + item.setEnable(true); | ||
| 176 | + item.setEnableAudio(true); | ||
| 177 | + item.setEnableMp4(false); | ||
| 178 | + item.setEnableRemoveNoneReader(false); | ||
| 179 | + item.setEnableDisableNoneReader(false); | ||
| 180 | + item.setName(stream); | ||
| 181 | + | ||
| 182 | + StringRedisTemplate redisTemplate = VManageBootstrap.getBean(StringRedisTemplate.class); | ||
| 183 | + String key = "jtt1078:" + stream; | ||
| 184 | + String closeKey = "jt1078:count:"+stream; | ||
| 185 | + String timeoutKey = "timeout:"+stream; | ||
| 186 | + Object timeOutVal = redisTemplate.opsForValue().get(timeoutKey); | ||
| 187 | + if(Objects.equals("2",timeOutVal)){ | ||
| 188 | + IStreamProxyService streamProxyService = VManageBootstrap.getBean(IStreamProxyService.class); | ||
| 189 | + streamProxyService.stop1("schedule",stream); | ||
| 190 | + redisTemplate.delete(timeoutKey); | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + if(redisTemplate.hasKey(closeKey)){ | ||
| 194 | + IStreamProxyService streamProxyService = VManageBootstrap.getBean(IStreamProxyService.class); | ||
| 195 | + streamProxyService.del("schedule",stream); | ||
| 196 | + }else if (redisTemplate.hasKey(key)) { | ||
| 197 | + redisTemplate.opsForValue().set(key, "1", 300, TimeUnit.SECONDS); | ||
| 198 | + } else { | ||
| 199 | + try { | ||
| 200 | + streamProxyController.save(item); | ||
| 201 | + }catch (Exception e){ | ||
| 202 | + logger.error(e.getMessage()); | ||
| 203 | + } | ||
| 204 | + timeOutVal = redisTemplate.opsForValue().get(timeoutKey); | ||
| 205 | + if(Objects.equals("2",timeOutVal)) { | ||
| 206 | + IStreamProxyService streamProxyService = VManageBootstrap.getBean(IStreamProxyService.class); | ||
| 207 | + streamProxyService.stop1("schedule", stream); | ||
| 208 | + redisTemplate.delete(timeoutKey); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + } | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + private synchronized String endWithMapping(String tagMapping) throws URISyntaxException, IOException { | ||
| 215 | + if (!StringUtils.endsWith(this.tagMapping, ".flv")) { | ||
| 216 | + this.tagMapping = this.tagMapping + ".flv"; | ||
| 217 | + } | ||
| 218 | + return this.tagMapping; | ||
| 219 | + } | ||
| 220 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/server/Jtt1078MessageDecoder.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.server; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | ||
| 5 | +import io.netty.buffer.ByteBuf; | ||
| 6 | +import io.netty.channel.ChannelHandlerContext; | ||
| 7 | +import io.netty.handler.codec.ByteToMessageDecoder; | ||
| 8 | +import org.slf4j.Logger; | ||
| 9 | +import org.slf4j.LoggerFactory; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * Created by matrixy on 2019/4/9. | ||
| 15 | + */ | ||
| 16 | +public class Jtt1078MessageDecoder extends ByteToMessageDecoder | ||
| 17 | +{ | ||
| 18 | + static Logger logger = LoggerFactory.getLogger(Jtt1078MessageDecoder.class); | ||
| 19 | + byte[] block = new byte[4096]; | ||
| 20 | + Jtt1078Decoder decoder = new Jtt1078Decoder(); | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception | ||
| 24 | + { | ||
| 25 | + int length = in.readableBytes(); | ||
| 26 | + for (int i = 0, k = (int)Math.ceil(length / 512f); i < k; i++) | ||
| 27 | + { | ||
| 28 | + int l = i < k - 1 ? 512 : length - (i * 512); | ||
| 29 | + in.readBytes(block, 0, l); | ||
| 30 | + | ||
| 31 | + decoder.write(block, 0, l); | ||
| 32 | + | ||
| 33 | + while (true) | ||
| 34 | + { | ||
| 35 | + Packet p = decoder.decode(); | ||
| 36 | + if (p == null) break; | ||
| 37 | + | ||
| 38 | + out.add(p); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | +} |
src/main/java/com/genersoft/iot/vmp/jt1078/server/RTSPHandler.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.server; | ||
| 2 | + | ||
| 3 | +import com.genersoft.iot.vmp.jt1078.rtsp.RtspRequest; | ||
| 4 | +import com.genersoft.iot.vmp.jt1078.rtsp.RtspSessionManager; | ||
| 5 | +import io.netty.bootstrap.Bootstrap; | ||
| 6 | +import io.netty.buffer.ByteBuf; | ||
| 7 | +import io.netty.channel.*; | ||
| 8 | +import io.netty.channel.nio.NioEventLoopGroup; | ||
| 9 | +import io.netty.channel.socket.SocketChannel; | ||
| 10 | +import io.netty.channel.socket.nio.NioSocketChannel; | ||
| 11 | +import io.netty.handler.codec.http.DefaultHttpContent; | ||
| 12 | +import io.netty.handler.codec.http.DefaultHttpResponse; | ||
| 13 | +import io.netty.handler.codec.http.HttpMethod; | ||
| 14 | +import io.netty.handler.codec.rtsp.*; | ||
| 15 | +import io.netty.util.internal.StringUtil; | ||
| 16 | +import org.slf4j.Logger; | ||
| 17 | +import org.slf4j.LoggerFactory; | ||
| 18 | + | ||
| 19 | +import java.net.InetSocketAddress; | ||
| 20 | +import java.util.*; | ||
| 21 | + | ||
| 22 | +public class RTSPHandler extends ChannelInboundHandlerAdapter { | ||
| 23 | + | ||
| 24 | + private final static Logger log = LoggerFactory.getLogger(RTSPHandler.class); | ||
| 25 | + | ||
| 26 | + private String channelId = "122223333444-1"; | ||
| 27 | + | ||
| 28 | + private HttpMethod currentMethod = RtspMethods.OPTIONS; | ||
| 29 | + | ||
| 30 | + private List<HttpMethod> methods = Arrays.asList(RtspMethods.OPTIONS, RtspMethods.ANNOUNCE, RtspMethods.SETUP, RtspMethods.RECORD, null); | ||
| 31 | + | ||
| 32 | + private RtspRequest rtspRequest = new RtspRequest("192.168.169.100", 9555, String.format("/schedule/%s?sign=41db35390ddad33f83944f44b8b75ded", channelId)); | ||
| 33 | + | ||
| 34 | + /* | ||
| 35 | + When notified that the channel is active, sends a message. A channel is active | ||
| 36 | + when a connection has been established, so the method is invoked when the connections | ||
| 37 | + is established. | ||
| 38 | + */ | ||
| 39 | + @Override | ||
| 40 | + public void channelActive(final ChannelHandlerContext ctx) { | ||
| 41 | + log.debug("channelActive, connection established: {}", ctx); | ||
| 42 | + log.debug("Sending request to the server"); | ||
| 43 | + ctx.writeAndFlush(rtspRequest.option()); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @Override | ||
| 47 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { | ||
| 48 | + log.info("exceptionCaught: {}", cause); | ||
| 49 | + ctx.close(); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Override | ||
| 53 | + public void channelRead(ChannelHandlerContext ctx, Object msg) { | ||
| 54 | + log.info("Received from RTSP Server: {}", ctx); | ||
| 55 | + log.info("Received from RTSP msg: {}", msg.toString()); | ||
| 56 | + log.debug("Received Class Type: {}", msg.getClass().getTypeName()); | ||
| 57 | + if (msg instanceof DefaultHttpResponse) { | ||
| 58 | + DefaultHttpResponse res = (DefaultHttpResponse) msg; | ||
| 59 | + if (RtspResponseStatuses.OK.equals(res.status())) { | ||
| 60 | + log.debug("{}: {}", currentMethod, res.status()); | ||
| 61 | + if (res.headers().contains(RtspHeaderNames.SESSION)) { | ||
| 62 | + rtspRequest.setSessionID(res.headers().get(RtspHeaderNames.SESSION)); | ||
| 63 | + } | ||
| 64 | + nextMethod(ctx); | ||
| 65 | + } else { | ||
| 66 | + ctx.close(); | ||
| 67 | + } | ||
| 68 | + } else if (msg instanceof DefaultHttpContent) { | ||
| 69 | + DefaultHttpContent content = (DefaultHttpContent) msg; | ||
| 70 | + log.info("Content: {}", content); | ||
| 71 | + | ||
| 72 | + ByteBuf byteBuf = content.content(); | ||
| 73 | + } else { | ||
| 74 | + log.debug("dataType error: {}", msg.getClass().getTypeName()); | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + private void nextMethod(ChannelHandlerContext ctx) { | ||
| 79 | + for (int i = 0;i < methods.size(); i++) { | ||
| 80 | + if (methods.get(i).equals(currentMethod) && i < methods.size() - 1) { | ||
| 81 | + currentMethod = methods.get(i + 1); | ||
| 82 | + break; | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + if (currentMethod == null) { | ||
| 86 | + RtspSessionManager.register(channelId); | ||
| 87 | + return; | ||
| 88 | + } | ||
| 89 | + if (currentMethod == RtspMethods.ANNOUNCE) { | ||
| 90 | + ctx.writeAndFlush(rtspRequest.announce()); | ||
| 91 | + } | ||
| 92 | + if (currentMethod == RtspMethods.SETUP) { | ||
| 93 | + ctx.writeAndFlush(rtspRequest.setup()); | ||
| 94 | + } | ||
| 95 | + if (currentMethod == RtspMethods.RECORD) { | ||
| 96 | + ctx.writeAndFlush(rtspRequest.record()); | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + private void parseSdp(String sdp) { | ||
| 101 | + log.debug("Parsing SDP: {}", sdp); | ||
| 102 | + Map<String, List<String>> mediaMap = new HashMap<>(10); | ||
| 103 | + String[] array = sdp.split("\\n"); | ||
| 104 | + String mediaName = ""; | ||
| 105 | + for (int i = 0; i < array.length; i++) { | ||
| 106 | + String line = array[i]; | ||
| 107 | + if (line.startsWith("m=")) { | ||
| 108 | + mediaName = line.substring(line.indexOf("=") + 1, line.indexOf(" ")); | ||
| 109 | + if (mediaName.equals("video") || mediaName.equals("audio")) { | ||
| 110 | + mediaMap.put(mediaName, new ArrayList<>()); | ||
| 111 | + } else { | ||
| 112 | + mediaName = ""; | ||
| 113 | + } | ||
| 114 | + } else if (!StringUtil.isNullOrEmpty(mediaName)) { | ||
| 115 | + if (line.startsWith("b=") || line.startsWith("a=")) { | ||
| 116 | + List<String> medialist = mediaMap.get(mediaName); | ||
| 117 | + medialist.add(line); | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + for (String mediaKey : mediaMap.keySet()) { | ||
| 122 | + StringBuilder stringBuilder = new StringBuilder(); | ||
| 123 | + List<String> mediaInfo = mediaMap.get(mediaKey); | ||
| 124 | + mediaInfo.forEach((s) -> { | ||
| 125 | + stringBuilder.append("\n"); | ||
| 126 | + stringBuilder.append(s); | ||
| 127 | + }); | ||
| 128 | + log.info("[>>>>> {} <<<<<] {}", mediaKey, stringBuilder.toString()); | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + public static void main(String[] args) { | ||
| 133 | + EventLoopGroup group = new NioEventLoopGroup(); | ||
| 134 | + try { | ||
| 135 | + Bootstrap clientBootstrap = new Bootstrap(); | ||
| 136 | + clientBootstrap.group(group) | ||
| 137 | + .option(ChannelOption.TCP_NODELAY, true) | ||
| 138 | + .channel(NioSocketChannel.class) | ||
| 139 | + .remoteAddress(new InetSocketAddress("192.168.169.100", 9555)) | ||
| 140 | + .handler(new ChannelInitializer<SocketChannel>() { | ||
| 141 | + @Override | ||
| 142 | + protected void initChannel(SocketChannel socketChannel) { | ||
| 143 | + socketChannel.pipeline() | ||
| 144 | + .addLast(new RtspEncoder()) | ||
| 145 | + .addLast(new RtspDecoder()) | ||
| 146 | + .addLast(new RTSPHandler()); | ||
| 147 | + } | ||
| 148 | + }); | ||
| 149 | + ChannelFuture f = null; | ||
| 150 | + while (true) { | ||
| 151 | + log.info("Waiting for server connection"); | ||
| 152 | + f = clientBootstrap.connect(); | ||
| 153 | + f.awaitUninterruptibly(); | ||
| 154 | + if (f.isSuccess()) { | ||
| 155 | + log.info("RTSP Connection success!"); | ||
| 156 | + break; | ||
| 157 | + } | ||
| 158 | + Thread.sleep(1000); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + // Wait for the server to close the connection. | ||
| 162 | + f.channel().closeFuture().sync(); | ||
| 163 | + } catch (Exception e) { | ||
| 164 | + log.error("Error ->", e); | ||
| 165 | + log.error("<- Error"); | ||
| 166 | + } finally { | ||
| 167 | + // Shut down executor threads to exit.Ahkk | ||
| 168 | + try { | ||
| 169 | + log.info("ShutdownGracefully the connection group"); | ||
| 170 | + group.shutdownGracefully().sync(); | ||
| 171 | + } catch (InterruptedException e) { | ||
| 172 | + log.error("", e); | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | +} | ||
| 0 | \ No newline at end of file | 177 | \ No newline at end of file |