Commit 4bfd97b73e4e3819a1884ff0218518b4a81f7597
1 parent
f82dd1bd
优化
Showing
74 changed files
with
7002 additions
and
96 deletions
pom.xml
| ... | ... | @@ -100,6 +100,19 @@ |
| 100 | 100 | <artifactId>spring-boot-starter-data-redis</artifactId> |
| 101 | 101 | </dependency> |
| 102 | 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 | 116 | <groupId>org.springframework.boot</groupId> |
| 104 | 117 | <artifactId>spring-boot-starter-web</artifactId> |
| 105 | 118 | </dependency> | ... | ... |
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
| ... | ... | @@ -17,6 +17,7 @@ import javax.servlet.ServletException; |
| 17 | 17 | import javax.servlet.SessionCookieConfig; |
| 18 | 18 | import javax.servlet.SessionTrackingMode; |
| 19 | 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 | 63 | } |
| 63 | 64 | |
| 64 | 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 | 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 | 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 | 1 | package com.genersoft.iot.vmp.jt1078.config; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.jt1078.app.VideoServerApp; | |
| 3 | 4 | import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template; |
| 4 | 5 | import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer; |
| 5 | 6 | import org.springframework.beans.factory.annotation.Value; |
| ... | ... | @@ -18,11 +19,28 @@ import org.springframework.core.annotation.Order; |
| 18 | 19 | @ConditionalOnProperty(value = "jt1078.enable", havingValue = "true") |
| 19 | 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 | 44 | @Bean |
| 27 | 45 | public JT1078Template jt1078Template() { |
| 28 | 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 | 177 | \ No newline at end of file | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/server/Session.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.server; | |
| 2 | + | |
| 3 | +import java.util.HashMap; | |
| 4 | +import java.util.Map; | |
| 5 | +import java.util.Set; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * Created by matrixy on 2019/4/10. | |
| 9 | + */ | |
| 10 | +public final class Session | |
| 11 | +{ | |
| 12 | + Map<String, Object> attributes; | |
| 13 | + public Session() | |
| 14 | + { | |
| 15 | + this.attributes = new HashMap<String, Object>(); | |
| 16 | + } | |
| 17 | + | |
| 18 | + public Session set(String key, Object value) | |
| 19 | + { | |
| 20 | + this.attributes.put(key, value); | |
| 21 | + return this; | |
| 22 | + } | |
| 23 | + | |
| 24 | + public boolean has(String key) | |
| 25 | + { | |
| 26 | + return this.attributes.containsKey(key); | |
| 27 | + } | |
| 28 | + | |
| 29 | + public Set<String> keys() | |
| 30 | + { | |
| 31 | + return this.attributes.keySet(); | |
| 32 | + } | |
| 33 | + | |
| 34 | + public <T> T get(String key) | |
| 35 | + { | |
| 36 | + return (T) this.attributes.get(key); | |
| 37 | + } | |
| 38 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/server/SessionManager.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.server; | |
| 2 | + | |
| 3 | +import io.netty.channel.Channel; | |
| 4 | + | |
| 5 | +import java.util.HashMap; | |
| 6 | +import java.util.Map; | |
| 7 | + | |
| 8 | +public final class SessionManager | |
| 9 | +{ | |
| 10 | + private static final Map<String, Object> mappings = new HashMap<>(); | |
| 11 | + | |
| 12 | + public static void init() | |
| 13 | + { | |
| 14 | + // ... | |
| 15 | + } | |
| 16 | + | |
| 17 | + public static <T> T get(Channel channel, String key) | |
| 18 | + { | |
| 19 | + return (T) mappings.get(channel.id().asLongText() + key); | |
| 20 | + } | |
| 21 | + | |
| 22 | + public static void set(Channel channel, String key, Object value) | |
| 23 | + { | |
| 24 | + mappings.put(channel.id().asLongText() + key, value); | |
| 25 | + } | |
| 26 | + | |
| 27 | + public static boolean contains(Channel channel, String key) | |
| 28 | + { | |
| 29 | + return mappings.containsKey(channel.id().asLongText() + key); | |
| 30 | + } | |
| 31 | +} | |
| 0 | 32 | \ No newline at end of file | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/subscriber/RTMPPublisher.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.subscriber; | |
| 7 | + | |
| 8 | +import com.genersoft.iot.vmp.jt1078.util.Configs; | |
| 9 | +import java.io.InputStream; | |
| 10 | +import org.slf4j.Logger; | |
| 11 | +import org.slf4j.LoggerFactory; | |
| 12 | + | |
| 13 | +public class RTMPPublisher extends Thread { | |
| 14 | + static Logger logger = LoggerFactory.getLogger(RTMPPublisher.class); | |
| 15 | + String tag = null; | |
| 16 | + private Integer port; | |
| 17 | + Process process = null; | |
| 18 | + | |
| 19 | + public RTMPPublisher(String tag, Integer port) { | |
| 20 | + this.tag = tag; | |
| 21 | + this.port = port; | |
| 22 | + } | |
| 23 | + | |
| 24 | + @Override | |
| 25 | + public void run() | |
| 26 | + { | |
| 27 | + InputStream stderr = null; | |
| 28 | + int len = -1; | |
| 29 | + byte[] buff = new byte[512]; | |
| 30 | + boolean debugMode = "on".equalsIgnoreCase(Configs.get("debug.mode")); | |
| 31 | + | |
| 32 | + try | |
| 33 | + { | |
| 34 | + String rtmpUrl = Configs.get("rtmp.url").replaceAll("\\{TAG\\}", tag); | |
| 35 | + String cmd = String.format("%s -i http://localhost:%d/video/%s -vcodec copy -acodec aac -f flv %s", | |
| 36 | + Configs.get("ffmpeg.path"), | |
| 37 | + Configs.getInt("server.http.port", 3333), | |
| 38 | + tag, | |
| 39 | + rtmpUrl | |
| 40 | + ); | |
| 41 | + logger.info("Execute: {}", cmd); | |
| 42 | + process = Runtime.getRuntime().exec(cmd); | |
| 43 | + stderr = process.getErrorStream(); | |
| 44 | + while ((len = stderr.read(buff)) > -1) | |
| 45 | + { | |
| 46 | + if (debugMode) System.out.print(new String(buff, 0, len)); | |
| 47 | + } | |
| 48 | + logger.info("Process FFMPEG exited..."); | |
| 49 | + } | |
| 50 | + catch(Exception ex) | |
| 51 | + { | |
| 52 | + logger.error("publish failed", ex); | |
| 53 | + } | |
| 54 | + } | |
| 55 | + | |
| 56 | + public void close() | |
| 57 | + { | |
| 58 | + try { if (process != null) process.destroyForcibly(); } catch(Exception e) { } | |
| 59 | + } | |
| 60 | +} | |
| 0 | 61 | \ No newline at end of file | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/subscriber/Subscriber.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.subscriber; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.flv.FlvEncoder; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 5 | +import io.netty.channel.ChannelFuture; | |
| 6 | +import io.netty.channel.ChannelHandlerContext; | |
| 7 | +import org.slf4j.Logger; | |
| 8 | +import org.slf4j.LoggerFactory; | |
| 9 | + | |
| 10 | +import java.util.LinkedList; | |
| 11 | +import java.util.concurrent.atomic.AtomicLong; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * Created by matrixy on 2020/1/11. | |
| 15 | + */ | |
| 16 | +public abstract class Subscriber extends Thread | |
| 17 | +{ | |
| 18 | + static Logger logger = LoggerFactory.getLogger(Subscriber.class); | |
| 19 | + static final AtomicLong SEQUENCE = new AtomicLong(0L); | |
| 20 | + | |
| 21 | + private long id; | |
| 22 | + private String tag; | |
| 23 | + private Object lock; | |
| 24 | + private ChannelHandlerContext context; | |
| 25 | + protected LinkedList<byte[]> messages; | |
| 26 | + | |
| 27 | + public Subscriber(String tag, ChannelHandlerContext ctx) | |
| 28 | + { | |
| 29 | + this.tag = tag; | |
| 30 | + this.context = ctx; | |
| 31 | + this.lock = new Object(); | |
| 32 | + this.messages = new LinkedList<byte[]>(); | |
| 33 | + | |
| 34 | + this.id = SEQUENCE.getAndAdd(1L); | |
| 35 | + } | |
| 36 | + | |
| 37 | + public long getId() | |
| 38 | + { | |
| 39 | + return this.id; | |
| 40 | + } | |
| 41 | + | |
| 42 | + public String getTag() | |
| 43 | + { | |
| 44 | + return this.tag; | |
| 45 | + } | |
| 46 | + | |
| 47 | + public abstract void onVideoData(long timeoffset, byte[] data, FlvEncoder flvEncoder); | |
| 48 | + | |
| 49 | + public abstract void onAudioData(long timeoffset, byte[] data, FlvEncoder flvEncoder); | |
| 50 | + | |
| 51 | + public void enqueue(byte[] data) | |
| 52 | + { | |
| 53 | + if (data == null) return; | |
| 54 | + synchronized (lock) | |
| 55 | + { | |
| 56 | + messages.addLast(data); | |
| 57 | + lock.notify(); | |
| 58 | + } | |
| 59 | + } | |
| 60 | + | |
| 61 | + public void run() | |
| 62 | + { | |
| 63 | + loop : while (!this.isInterrupted()) | |
| 64 | + { | |
| 65 | + try | |
| 66 | + { | |
| 67 | + byte[] data = take(); | |
| 68 | + if (data != null) send(data).await(); | |
| 69 | + } | |
| 70 | + catch(Exception ex) | |
| 71 | + { | |
| 72 | + //销毁线程时,如果有锁wait就不会销毁线程,抛出InterruptedException异常 | |
| 73 | + if (ex instanceof InterruptedException) | |
| 74 | + { | |
| 75 | + break loop; | |
| 76 | + } | |
| 77 | + logger.error("send failed", ex); | |
| 78 | + } | |
| 79 | + } | |
| 80 | + logger.info("subscriber closed"); | |
| 81 | + } | |
| 82 | + | |
| 83 | + protected byte[] take() | |
| 84 | + { | |
| 85 | + byte[] data = null; | |
| 86 | + try | |
| 87 | + { | |
| 88 | + synchronized (lock) | |
| 89 | + { | |
| 90 | + while (messages.isEmpty()) | |
| 91 | + { | |
| 92 | + lock.wait(100); | |
| 93 | + if (this.isInterrupted()) return null; | |
| 94 | + } | |
| 95 | + data = messages.removeFirst(); | |
| 96 | + } | |
| 97 | + return data; | |
| 98 | + } | |
| 99 | + catch(Exception ex) | |
| 100 | + { | |
| 101 | + this.interrupt(); | |
| 102 | + return null; | |
| 103 | + } | |
| 104 | + } | |
| 105 | + | |
| 106 | + public void close() | |
| 107 | + { | |
| 108 | + this.interrupt(); | |
| 109 | + } | |
| 110 | + | |
| 111 | + public ChannelFuture send(byte[] message) | |
| 112 | + { | |
| 113 | + return context.writeAndFlush(message); | |
| 114 | + } | |
| 115 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/subscriber/VideoSubscriber.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.subscriber; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.codec.MP3Encoder; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.flv.AudioTag; | |
| 5 | +import com.genersoft.iot.vmp.jt1078.flv.FlvAudioTagEncoder; | |
| 6 | +import com.genersoft.iot.vmp.jt1078.flv.FlvEncoder; | |
| 7 | +import com.genersoft.iot.vmp.jt1078.util.ByteBufUtils; | |
| 8 | +import com.genersoft.iot.vmp.jt1078.util.FLVUtils; | |
| 9 | +import com.genersoft.iot.vmp.jt1078.util.HttpChunk; | |
| 10 | +import io.netty.buffer.ByteBuf; | |
| 11 | +import io.netty.channel.ChannelHandlerContext; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * Created by matrixy on 2020/1/13. | |
| 15 | + */ | |
| 16 | +public class VideoSubscriber extends Subscriber | |
| 17 | +{ | |
| 18 | + private long videoTimestamp = 0; | |
| 19 | + private long audioTimestamp = 0; | |
| 20 | + private long lastVideoFrameTimeOffset = 0; | |
| 21 | + private long lastAudioFrameTimeOffset = 0; | |
| 22 | + private boolean videoHeaderSent = false; | |
| 23 | + | |
| 24 | + public VideoSubscriber(String tag, ChannelHandlerContext ctx) | |
| 25 | + { | |
| 26 | + super(tag, ctx); | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + public void onVideoData(long timeoffset, byte[] data, FlvEncoder flvEncoder) | |
| 31 | + { | |
| 32 | + if (lastVideoFrameTimeOffset == 0) lastVideoFrameTimeOffset = timeoffset; | |
| 33 | + | |
| 34 | + // 之前是不是已经发送过了?没有的话,需要补发FLV HEADER的。。。 | |
| 35 | + if (videoHeaderSent == false && flvEncoder.videoReady()) | |
| 36 | + { | |
| 37 | + enqueue(HttpChunk.make(flvEncoder.getHeader().getBytes())); | |
| 38 | + enqueue(HttpChunk.make(flvEncoder.getVideoHeader().getBytes())); | |
| 39 | + | |
| 40 | + // 直接下发第一个I帧 | |
| 41 | + byte[] iFrame = flvEncoder.getLastIFrame(); | |
| 42 | + if (iFrame != null) | |
| 43 | + { | |
| 44 | + FLVUtils.resetTimestamp(iFrame, (int) videoTimestamp); | |
| 45 | + enqueue(HttpChunk.make(iFrame)); | |
| 46 | + } | |
| 47 | + | |
| 48 | + videoHeaderSent = true; | |
| 49 | + } | |
| 50 | + | |
| 51 | + if (data == null) return; | |
| 52 | + | |
| 53 | + // 修改时间戳 | |
| 54 | + // System.out.println("Time: " + videoTimestamp + ", current: " + timeoffset); | |
| 55 | + FLVUtils.resetTimestamp(data, (int) videoTimestamp); | |
| 56 | + videoTimestamp += (int)(timeoffset - lastVideoFrameTimeOffset); | |
| 57 | + lastVideoFrameTimeOffset = timeoffset; | |
| 58 | + | |
| 59 | + enqueue(HttpChunk.make(data)); | |
| 60 | + } | |
| 61 | + | |
| 62 | + private FlvAudioTagEncoder audioEncoder = new FlvAudioTagEncoder(); | |
| 63 | + MP3Encoder mp3Encoder = new MP3Encoder(); | |
| 64 | + | |
| 65 | + @Override | |
| 66 | + public void onAudioData(long timeoffset, byte[] data, FlvEncoder flvEncoder) | |
| 67 | + { | |
| 68 | + if (!videoHeaderSent) return; | |
| 69 | + | |
| 70 | + byte[] mp3Data = mp3Encoder.encode(data); | |
| 71 | + if (mp3Data == null || mp3Data.length == 0) return; | |
| 72 | + AudioTag audioTag = new AudioTag(0, mp3Data.length + 1, AudioTag.MP3, (byte) 0, (byte)1, (byte) 0, mp3Data); | |
| 73 | + byte[] frameData = null; | |
| 74 | + try | |
| 75 | + { | |
| 76 | + ByteBuf audioBuf = audioEncoder.encode(audioTag); | |
| 77 | + frameData = ByteBufUtils.readReadableBytes(audioBuf); | |
| 78 | + } | |
| 79 | + catch (Exception e) | |
| 80 | + { | |
| 81 | + e.printStackTrace(); | |
| 82 | + } | |
| 83 | + | |
| 84 | + if (lastAudioFrameTimeOffset == 0) lastAudioFrameTimeOffset = timeoffset; | |
| 85 | + | |
| 86 | + if (data == null) return; | |
| 87 | + | |
| 88 | + FLVUtils.resetTimestamp(frameData, (int) audioTimestamp); | |
| 89 | + audioTimestamp += (int)(timeoffset - lastAudioFrameTimeOffset); | |
| 90 | + lastAudioFrameTimeOffset = timeoffset; | |
| 91 | + | |
| 92 | + enqueue(HttpChunk.make(frameData)); | |
| 93 | + } | |
| 94 | + | |
| 95 | + @Override | |
| 96 | + public void close() | |
| 97 | + { | |
| 98 | + super.close(); | |
| 99 | + mp3Encoder.close(); | |
| 100 | + } | |
| 101 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/AudioTest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Decoder; | |
| 5 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | |
| 6 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 7 | + | |
| 8 | +import java.io.FileInputStream; | |
| 9 | +import java.io.FileOutputStream; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * Created by matrixy on 2019/12/21. | |
| 13 | + */ | |
| 14 | +public class AudioTest | |
| 15 | +{ | |
| 16 | + public static void main(String[] args) throws Exception | |
| 17 | + { | |
| 18 | + int len = -1; | |
| 19 | + byte[] block = new byte[1024]; | |
| 20 | + FileInputStream fis = new FileInputStream("e:\\test\\streaming.hex"); | |
| 21 | + Jtt1078Decoder decoder = new Jtt1078Decoder(); | |
| 22 | + G711Codec codec = new G711Codec(); | |
| 23 | + FileOutputStream fos = new FileOutputStream("e:\\test\\fuckfuckfuck1111.pcm"); | |
| 24 | + while ((len = fis.read(block)) > -1) | |
| 25 | + { | |
| 26 | + decoder.write(block, 0, len); | |
| 27 | + while (true) | |
| 28 | + { | |
| 29 | + Packet p = decoder.decode(); | |
| 30 | + if (p == null) break; | |
| 31 | + | |
| 32 | + int lengthOffset = 28; | |
| 33 | + int dataType = (p.seek(15).nextByte() >> 4) & 0x0f; | |
| 34 | + // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段 | |
| 35 | + if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2; | |
| 36 | + else if (dataType == 0x03) lengthOffset = 28 - 4; | |
| 37 | + | |
| 38 | + if (dataType == 0x03) | |
| 39 | + { | |
| 40 | + byte[] pcmData = codec.toPCM(p.seek(lengthOffset + 2).nextBytes()); | |
| 41 | + fos.write(pcmData); | |
| 42 | + fos.flush(); | |
| 43 | + } | |
| 44 | + } | |
| 45 | + } | |
| 46 | + fos.close(); | |
| 47 | + fis.close(); | |
| 48 | + } | |
| 49 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/ChannelTest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.util.ByteHolder; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | |
| 5 | + | |
| 6 | +import java.io.ByteArrayOutputStream; | |
| 7 | +import java.io.IOException; | |
| 8 | +import java.nio.ByteBuffer; | |
| 9 | +import java.nio.channels.ByteChannel; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * Created by matrixy on 2020/1/9. | |
| 13 | + */ | |
| 14 | +public class ChannelTest implements ByteChannel | |
| 15 | +{ | |
| 16 | + byte[] temp = new byte[4]; | |
| 17 | + ByteHolder buffer = new ByteHolder(1024); | |
| 18 | + | |
| 19 | + // 读出,存入dst | |
| 20 | + @Override | |
| 21 | + public int read(ByteBuffer dst) throws IOException | |
| 22 | + { | |
| 23 | + dst.flip(); | |
| 24 | + int len = Math.min(4, buffer.size()); | |
| 25 | + if (dst.remaining() > len) | |
| 26 | + { | |
| 27 | + buffer.sliceInto(temp, len); | |
| 28 | + dst.put(temp, 0, len); | |
| 29 | + } | |
| 30 | + else | |
| 31 | + { | |
| 32 | + // 丢掉??? | |
| 33 | + } | |
| 34 | + dst.flip(); | |
| 35 | + return len; | |
| 36 | + } | |
| 37 | + | |
| 38 | + // 从src读出,写入进来 | |
| 39 | + @Override | |
| 40 | + public int write(ByteBuffer src) throws IOException | |
| 41 | + { | |
| 42 | + int len = -1; | |
| 43 | + // src.flip(); | |
| 44 | + len = Math.min(4, src.limit()); | |
| 45 | + src.get(temp, 0, len); | |
| 46 | + buffer.write(temp, 0, len); | |
| 47 | + // src.flip(); | |
| 48 | + System.out.println("write: " + len); | |
| 49 | + return len; | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Override | |
| 53 | + public boolean isOpen() | |
| 54 | + { | |
| 55 | + return true; | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Override | |
| 59 | + public void close() throws IOException | |
| 60 | + { | |
| 61 | + | |
| 62 | + } | |
| 63 | + | |
| 64 | + public byte[] array() | |
| 65 | + { | |
| 66 | + return buffer.array(); | |
| 67 | + } | |
| 68 | + | |
| 69 | + public static void main(String[] args) throws Exception | |
| 70 | + { | |
| 71 | + ChannelTest chl = new ChannelTest(); | |
| 72 | + ByteBuffer buffer = ByteBuffer.allocate(4); | |
| 73 | + java.nio.ByteBuffer xx; | |
| 74 | + System.out.println(buffer.getClass().getName()); | |
| 75 | + for (int i = 0; i < 4096; i++) | |
| 76 | + buffer.put((byte)'f'); | |
| 77 | + /* | |
| 78 | + buffer.putLong(0x1122334455667788L); | |
| 79 | + buffer.flip(); | |
| 80 | + // flip太迷惑了 | |
| 81 | + buffer.isReadOnly(); | |
| 82 | + int len = chl.write(buffer); | |
| 83 | + len = chl.write(buffer); | |
| 84 | + ByteUtils.dump(chl.array()); | |
| 85 | + */ | |
| 86 | + } | |
| 87 | + | |
| 88 | + static final class ByteBufferWrapper | |
| 89 | + { | |
| 90 | + boolean writeMode; | |
| 91 | + ByteBuffer buffer; | |
| 92 | + | |
| 93 | + private ByteBufferWrapper(int size) | |
| 94 | + { | |
| 95 | + this.buffer = ByteBuffer.allocate(size); | |
| 96 | + } | |
| 97 | + | |
| 98 | + // 控制写入,代理过来 | |
| 99 | + public void write() | |
| 100 | + { | |
| 101 | + | |
| 102 | + } | |
| 103 | + | |
| 104 | + // 写出就无所谓了 | |
| 105 | + | |
| 106 | + public static ByteBufferWrapper create(int size) | |
| 107 | + { | |
| 108 | + return new ByteBufferWrapper(size); | |
| 109 | + } | |
| 110 | + } | |
| 111 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/FuckTest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 5 | + | |
| 6 | +import java.io.DataInputStream; | |
| 7 | +import java.io.FileInputStream; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * Created by matrixy on 2020/3/19. | |
| 11 | + */ | |
| 12 | +public class FuckTest | |
| 13 | +{ | |
| 14 | + public static void main(String[] args) throws Exception | |
| 15 | + { | |
| 16 | + String line; | |
| 17 | + DataInputStream dis = new DataInputStream(new FileInputStream("d:\\temp\\rtp.txt")); | |
| 18 | + while ((line = dis.readLine()) != null) | |
| 19 | + { | |
| 20 | + byte[] rtp = ByteUtils.parse(line); | |
| 21 | + Packet packet = Packet.create(rtp); | |
| 22 | + | |
| 23 | + int lengthOffset = 28; | |
| 24 | + int dataType = (packet.seek(15).nextByte() >> 4) & 0x0f; | |
| 25 | + int pkType = packet.seek(15).nextByte() & 0x0f; | |
| 26 | + // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段 | |
| 27 | + if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2; | |
| 28 | + else if (dataType == 0x03) lengthOffset = 28 - 4; | |
| 29 | + | |
| 30 | + if (dataType <= 0x02) | |
| 31 | + { | |
| 32 | + if (dataType == 0x00) System.out.println("I Frame---------------------------------"); | |
| 33 | + if (dataType == 0x01) System.out.println("P Frame"); | |
| 34 | + if (dataType == 0x02) System.out.println("B Frame"); | |
| 35 | + } | |
| 36 | + } | |
| 37 | + } | |
| 38 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/G711ATest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.codec.G711Codec; | |
| 4 | + | |
| 5 | +import java.io.FileInputStream; | |
| 6 | +import java.io.FileOutputStream; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * Created by matrixy on 2019/12/21. | |
| 10 | + */ | |
| 11 | +public class G711ATest | |
| 12 | +{ | |
| 13 | + public static void main(String[] args) throws Exception | |
| 14 | + { | |
| 15 | + int len = -1; | |
| 16 | + byte[] block = new byte[1024]; | |
| 17 | + FileInputStream fis = new FileInputStream("E:\\workspace\\enc_dec_audio\\g711\\encode_out.g711a"); | |
| 18 | + FileOutputStream fos = new FileOutputStream("E:\\test\\fuckfuckfuck111.pcm"); | |
| 19 | + G711Codec codec = new G711Codec(); | |
| 20 | + while ((len = fis.read(block)) > -1) | |
| 21 | + { | |
| 22 | + byte[] pcmData = null; | |
| 23 | + if (len == 1024) | |
| 24 | + { | |
| 25 | + pcmData = codec.toPCM(block); | |
| 26 | + } | |
| 27 | + else | |
| 28 | + { | |
| 29 | + byte[] temp = new byte[len]; | |
| 30 | + System.arraycopy(block, 0, temp, 0, len); | |
| 31 | + pcmData = codec.toPCM(temp); | |
| 32 | + } | |
| 33 | + fos.write(pcmData); | |
| 34 | + fos.flush(); | |
| 35 | + } | |
| 36 | + fis.close(); | |
| 37 | + fos.close(); | |
| 38 | + } | |
| 39 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/MP3Test.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 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 | +import java.io.FileInputStream; | |
| 9 | +import java.io.FileOutputStream; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * Created by matrixy on 2020/4/27. | |
| 13 | + */ | |
| 14 | +public class MP3Test | |
| 15 | +{ | |
| 16 | + public static void main(String[] args) throws Exception | |
| 17 | + { | |
| 18 | + AudioFormat sourceFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 1, 1 * 2, -1, false); | |
| 19 | + LameEncoder encoder = new LameEncoder(sourceFormat, 256, 3, Lame.MEDIUM, false); | |
| 20 | + | |
| 21 | + byte[] block = new byte[320]; | |
| 22 | + int len = -1; | |
| 23 | + FileInputStream fis = new FileInputStream("d:\\temp\\hello.pcm"); | |
| 24 | + ByteArrayOutputStream mp3 = new ByteArrayOutputStream(encoder.getOutputBufferSize()); | |
| 25 | + byte[] buffer = new byte[encoder.getPCMBufferSize()]; | |
| 26 | + int bytesToTransfer = 0; | |
| 27 | + int bytesWritten; | |
| 28 | + int currentPcmPosition = 0; | |
| 29 | + | |
| 30 | + FileOutputStream fos = new FileOutputStream("d:\\temp\\fuck.mp3"); | |
| 31 | + | |
| 32 | + while ((len = fis.read(block)) > -1) | |
| 33 | + { | |
| 34 | + bytesToTransfer = len; | |
| 35 | + currentPcmPosition = 0; | |
| 36 | + while (0 < (bytesWritten = encoder.encodeBuffer(block, currentPcmPosition, bytesToTransfer, buffer))) | |
| 37 | + { | |
| 38 | + currentPcmPosition += bytesToTransfer; | |
| 39 | + bytesToTransfer = Math.min(buffer.length, len - currentPcmPosition); | |
| 40 | + | |
| 41 | + mp3.write(buffer, 0, bytesWritten); | |
| 42 | + fos.write(buffer, 0, bytesWritten); | |
| 43 | + | |
| 44 | + System.out.println(String.format("pcm data: %4d, written: %4d, pos: %4d", len, bytesWritten, currentPcmPosition)); | |
| 45 | + } | |
| 46 | + System.out.println(); | |
| 47 | + } | |
| 48 | + | |
| 49 | + fos.close(); | |
| 50 | + fis.close(); | |
| 51 | + | |
| 52 | + encoder.close(); | |
| 53 | + } | |
| 54 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/RTPGenerate.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.flv.FlvEncoder; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Decoder; | |
| 5 | +import com.genersoft.iot.vmp.jt1078.util.ByteHolder; | |
| 6 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | |
| 7 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 8 | + | |
| 9 | +import java.io.FileInputStream; | |
| 10 | +import java.io.FileOutputStream; | |
| 11 | +import java.io.InputStream; | |
| 12 | +import java.io.OutputStream; | |
| 13 | +import java.util.LinkedList; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * Created by matrixy on 2019/12/16. | |
| 17 | + */ | |
| 18 | +public class RTPGenerate | |
| 19 | +{ | |
| 20 | + public static void main(String[] args) throws Exception | |
| 21 | + { | |
| 22 | + InputStream input = new FileInputStream("d:\\test\\1078\\streamax-20191209.bin"); | |
| 23 | + LinkedList<Packet> packets = readPackets(input); | |
| 24 | + | |
| 25 | + for (int i = 0; i < 100; i++) | |
| 26 | + { | |
| 27 | + String sim = String.format("013800138%03d", i); | |
| 28 | + int channel = 1; | |
| 29 | + | |
| 30 | + try (OutputStream output = new FileOutputStream("d:\\test\\1078\\temp\\" + sim + "-" + channel + ".bin")) | |
| 31 | + { | |
| 32 | + for (Packet p : packets) | |
| 33 | + { | |
| 34 | + p.seek(8).putBytes(toBCD(sim)); | |
| 35 | + p.seek(14).putByte((byte)channel); | |
| 36 | + output.write(p.getBytes()); | |
| 37 | + } | |
| 38 | + System.out.println(String.format(" -> %s-%d generated...", sim, channel)); | |
| 39 | + } | |
| 40 | + catch(Exception ex) | |
| 41 | + { | |
| 42 | + ex.printStackTrace(); | |
| 43 | + System.out.println(ex); | |
| 44 | + } | |
| 45 | + } | |
| 46 | + | |
| 47 | + input.close(); | |
| 48 | + } | |
| 49 | + | |
| 50 | + public static LinkedList<Packet> readPackets(InputStream input) throws Exception | |
| 51 | + { | |
| 52 | + int len = -1; | |
| 53 | + byte[] block = new byte[1024]; | |
| 54 | + Jtt1078Decoder decoder = new Jtt1078Decoder(); | |
| 55 | + | |
| 56 | + LinkedList<Packet> packets = new LinkedList(); | |
| 57 | + | |
| 58 | + while ((len = input.read(block)) > -1) | |
| 59 | + { | |
| 60 | + decoder.write(block, 0, len); | |
| 61 | + while (true) | |
| 62 | + { | |
| 63 | + Packet p = decoder.decode(); | |
| 64 | + if (p == null) break; | |
| 65 | + | |
| 66 | + packets.add(p); | |
| 67 | + } | |
| 68 | + } | |
| 69 | + | |
| 70 | + return packets; | |
| 71 | + } | |
| 72 | + | |
| 73 | + public static byte[] toBCD(String sim) | |
| 74 | + { | |
| 75 | + byte[] bcd = new byte[sim.length() / 2]; | |
| 76 | + for (int i = 0, k = 0, l = sim.length(); i < l; i+=2) | |
| 77 | + { | |
| 78 | + char a = (char)(sim.charAt(i) - '0'); | |
| 79 | + char b = (char)(sim.charAt(i + 1) - '0'); | |
| 80 | + bcd[k++] = ((byte)(a << 4 | b)); | |
| 81 | + } | |
| 82 | + return bcd; | |
| 83 | + } | |
| 84 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/UnPack.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Decoder; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 5 | + | |
| 6 | +import java.io.FileInputStream; | |
| 7 | +import java.io.FileOutputStream; | |
| 8 | + | |
| 9 | +public class UnPack | |
| 10 | +{ | |
| 11 | + public static void main(String[] args) throws Exception | |
| 12 | + { | |
| 13 | + FileInputStream input = new FileInputStream("d:\\test\\1078\\d.bin"); | |
| 14 | + FileOutputStream output = new FileOutputStream("d:\\test\\1078\\fuck.1078.xxx"); | |
| 15 | + | |
| 16 | + int len = -1; | |
| 17 | + byte[] block = new byte[1024]; | |
| 18 | + Jtt1078Decoder decoder = new Jtt1078Decoder(); | |
| 19 | + while (true) | |
| 20 | + { | |
| 21 | + len = input.read(block); | |
| 22 | + if (len == -1) break; | |
| 23 | + decoder.write(block, 0, len); | |
| 24 | + | |
| 25 | + while (true) | |
| 26 | + { | |
| 27 | + Packet p = decoder.decode(); | |
| 28 | + if (p == null) break; | |
| 29 | + | |
| 30 | + int lengthOffset = 28; | |
| 31 | + int dataType = (p.seek(15).nextByte() >> 4) & 0x0f; | |
| 32 | + // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段 | |
| 33 | + if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2; | |
| 34 | + else if (dataType == 0x03) lengthOffset = 28 - 4; | |
| 35 | + | |
| 36 | + // FFMpegManager.getInstance().feed(publisherId, packet.seek(lengthOffset + 2).nextBytes()); | |
| 37 | + if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02) | |
| 38 | + { | |
| 39 | + // 视频 | |
| 40 | + // p.seek(lengthOffset + 2).nextBytes() | |
| 41 | + // output.write(p.seek(lengthOffset + 2).nextBytes()); | |
| 42 | + System.out.println(p.seek(lengthOffset).nextShort()); | |
| 43 | + } | |
| 44 | + else | |
| 45 | + { | |
| 46 | + // 音频 | |
| 47 | + // p.seek(lengthOffset + 2).nextBytes() | |
| 48 | + } | |
| 49 | + } | |
| 50 | + } | |
| 51 | + output.flush(); | |
| 52 | + output.close(); | |
| 53 | + input.close(); | |
| 54 | + } | |
| 55 | +} | |
| 0 | 56 | \ No newline at end of file | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/VideoPushTest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import java.io.FileInputStream; | |
| 4 | +import java.io.InputStream; | |
| 5 | +import java.io.OutputStream; | |
| 6 | +import java.net.Socket; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * Created by matrixy on 2019/4/10. | |
| 10 | + */ | |
| 11 | +public class VideoPushTest | |
| 12 | +{ | |
| 13 | + public static void main(String[] args) throws Exception | |
| 14 | + { | |
| 15 | + Socket conn = new Socket("localhost", 1078); | |
| 16 | + OutputStream os = conn.getOutputStream(); | |
| 17 | + | |
| 18 | + // InputStream fis = new FileInputStream("e:\\workspace\\enc_dec_audio\\streamax.bin"); | |
| 19 | + // InputStream fis = new FileInputStream("e:\\test\\streaming.hex"); | |
| 20 | + InputStream fis = VideoPushTest.class.getResourceAsStream("/tcpdump.bin"); | |
| 21 | + int len = -1; | |
| 22 | + byte[] block = new byte[512]; | |
| 23 | + while ((len = fis.read(block)) > -1) | |
| 24 | + { | |
| 25 | + os.write(block, 0, len); | |
| 26 | + os.flush(); | |
| 27 | + Thread.sleep(20); | |
| 28 | + System.out.println("sending..."); | |
| 29 | + } | |
| 30 | + os.close(); | |
| 31 | + fis.close(); | |
| 32 | + conn.close(); | |
| 33 | + } | |
| 34 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/VideoServer.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.server.Jtt1078Decoder; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 5 | + | |
| 6 | +import java.io.FileOutputStream; | |
| 7 | +import java.io.InputStream; | |
| 8 | +import java.net.ServerSocket; | |
| 9 | +import java.net.Socket; | |
| 10 | +import java.util.LinkedList; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * Created by houcheng on 2019-12-10. | |
| 14 | + */ | |
| 15 | +public class VideoServer | |
| 16 | +{ | |
| 17 | + public static void main(String[] args) throws Exception | |
| 18 | + { | |
| 19 | + System.out.println("started..."); | |
| 20 | + | |
| 21 | + String command = ("ffmpeg -f h264 -i /opt/test/xxoo.Video -f sln -ar 8000 -ac 1 -i /opt/test/xxoo.Audio -vcodec copy -acodec aac -map 0:v:0 -map 1:a:0 -probesize 512 -analyzeduration 100 -f flv rtmp://localhost/live/fuck"); | |
| 22 | + Process process = Runtime.getRuntime().exec(command); | |
| 23 | + new Reader(process.getErrorStream()).start(); | |
| 24 | + | |
| 25 | + Thread.sleep(2000); | |
| 26 | + | |
| 27 | + ServerSocket server = new ServerSocket(1078, 100); | |
| 28 | + Socket conn = server.accept(); | |
| 29 | + System.out.println("Connected from: " + conn.getRemoteSocketAddress()); | |
| 30 | + InputStream input = conn.getInputStream(); | |
| 31 | + | |
| 32 | + Publisher videoPublisher = new VideoPublisher(); | |
| 33 | + Publisher audioPublisher = new AudioPublisher(); | |
| 34 | + | |
| 35 | + videoPublisher.start(); | |
| 36 | + audioPublisher.start(); | |
| 37 | + | |
| 38 | + int len = -1; | |
| 39 | + byte[] block = new byte[1024]; | |
| 40 | + Jtt1078Decoder decoder = new Jtt1078Decoder(); | |
| 41 | + while (true) | |
| 42 | + { | |
| 43 | + len = input.read(block); | |
| 44 | + if (len == -1) break; | |
| 45 | + decoder.write(block, 0, len); | |
| 46 | + | |
| 47 | + while (true) | |
| 48 | + { | |
| 49 | + Packet p = decoder.decode(); | |
| 50 | + if (p == null) break; | |
| 51 | + | |
| 52 | + int lengthOffset = 28; | |
| 53 | + int dataType = (p.seek(15).nextByte() >> 4) & 0x0f; | |
| 54 | + // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段 | |
| 55 | + if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2; | |
| 56 | + else if (dataType == 0x03) lengthOffset = 28 - 4; | |
| 57 | + | |
| 58 | + // FFMpegManager.getInstance().feed(publisherId, packet.seek(lengthOffset + 2).nextBytes()); | |
| 59 | + if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02) | |
| 60 | + { | |
| 61 | + videoPublisher.publish(p.seek(lengthOffset + 2).nextBytes()); | |
| 62 | + } | |
| 63 | + else | |
| 64 | + { | |
| 65 | + audioPublisher.publish(p.seek(lengthOffset + 2).nextBytes()); | |
| 66 | + } | |
| 67 | + } | |
| 68 | + } | |
| 69 | + | |
| 70 | + System.in.read(); | |
| 71 | + } | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + static class Reader extends Thread | |
| 76 | + { | |
| 77 | + InputStream stdout = null; | |
| 78 | + public Reader(InputStream is) | |
| 79 | + { | |
| 80 | + this.stdout = is; | |
| 81 | + } | |
| 82 | + | |
| 83 | + public void run() | |
| 84 | + { | |
| 85 | + int len = -1; | |
| 86 | + byte[] block = new byte[512]; | |
| 87 | + try | |
| 88 | + { | |
| 89 | + while ((len = stdout.read(block)) > -1) | |
| 90 | + { | |
| 91 | + System.out.print(new String(block, 0, len)); | |
| 92 | + } | |
| 93 | + } | |
| 94 | + catch(Exception ex) | |
| 95 | + { | |
| 96 | + ex.printStackTrace(); | |
| 97 | + } | |
| 98 | + } | |
| 99 | + } | |
| 100 | + | |
| 101 | + static class Publisher extends Thread | |
| 102 | + { | |
| 103 | + Object lock = new Object(); | |
| 104 | + LinkedList<byte[]> messages = new LinkedList(); | |
| 105 | + | |
| 106 | + public void publish(byte[] msg) | |
| 107 | + { | |
| 108 | + synchronized (lock) | |
| 109 | + { | |
| 110 | + messages.add(msg); | |
| 111 | + lock.notify(); | |
| 112 | + } | |
| 113 | + } | |
| 114 | + | |
| 115 | + public byte[] peek() | |
| 116 | + { | |
| 117 | + byte[] msg = null; | |
| 118 | + synchronized (lock) | |
| 119 | + { | |
| 120 | + while (messages.size() == 0) try { lock.wait(); } catch(Exception e) { } | |
| 121 | + msg = messages.removeFirst(); | |
| 122 | + } | |
| 123 | + return msg; | |
| 124 | + } | |
| 125 | + | |
| 126 | + public FileOutputStream open(String fname) | |
| 127 | + { | |
| 128 | + try | |
| 129 | + { | |
| 130 | + return new FileOutputStream(fname); | |
| 131 | + } | |
| 132 | + catch(Exception ex) | |
| 133 | + { | |
| 134 | + throw new RuntimeException(ex); | |
| 135 | + } | |
| 136 | + } | |
| 137 | + } | |
| 138 | + | |
| 139 | + static class VideoPublisher extends Publisher | |
| 140 | + { | |
| 141 | + public void run() | |
| 142 | + { | |
| 143 | + FileOutputStream fos = open("/opt/test/xxoo.Video"); | |
| 144 | + while (!this.isInterrupted()) | |
| 145 | + { | |
| 146 | + try | |
| 147 | + { | |
| 148 | + byte[] msg = peek(); | |
| 149 | + fos.write(msg); | |
| 150 | + fos.flush(); | |
| 151 | + } | |
| 152 | + catch(Exception ex) | |
| 153 | + { | |
| 154 | + ex.printStackTrace(); | |
| 155 | + break; | |
| 156 | + } | |
| 157 | + } | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + static class AudioPublisher extends Publisher | |
| 162 | + { | |
| 163 | + public void run() | |
| 164 | + { | |
| 165 | + FileOutputStream fos = open("/opt/test/xxoo.Audio"); | |
| 166 | + while (!this.isInterrupted()) | |
| 167 | + { | |
| 168 | + try | |
| 169 | + { | |
| 170 | + byte[] msg = peek(); | |
| 171 | + fos.write(msg); | |
| 172 | + fos.flush(); | |
| 173 | + } | |
| 174 | + catch(Exception ex) | |
| 175 | + { | |
| 176 | + ex.printStackTrace(); | |
| 177 | + break; | |
| 178 | + } | |
| 179 | + } | |
| 180 | + } | |
| 181 | + } | |
| 182 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/test/WAVTest.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.test; | |
| 2 | + | |
| 3 | +import com.genersoft.iot.vmp.jt1078.util.ByteUtils; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.util.Packet; | |
| 5 | + | |
| 6 | +import java.io.FileInputStream; | |
| 7 | +import java.io.FileOutputStream; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * Created by matrixy on 2019/12/18. | |
| 11 | + */ | |
| 12 | +public class WAVTest | |
| 13 | +{ | |
| 14 | + public static void main(String[] args) throws Exception | |
| 15 | + { | |
| 16 | + int len; | |
| 17 | + byte[] block = new byte[1024 * 2048]; | |
| 18 | + FileInputStream fis = new FileInputStream("d:\\temp\\xxoo.pcm"); | |
| 19 | + Packet p = Packet.create(1024 * 2048); | |
| 20 | + while ((len = fis.read(block)) > -1) | |
| 21 | + { | |
| 22 | + p.reset(); | |
| 23 | + p.addBytes("RIFF".getBytes()) | |
| 24 | + .addBytes(ByteUtils.toLEBytes(len + 36)) | |
| 25 | + .addBytes("WAVE".getBytes()) // wave type | |
| 26 | + .addBytes("fmt ".getBytes()) // fmt id | |
| 27 | + .addInt(0x10000000) // fmt chunk size | |
| 28 | + .addShort((short)0x0100) // format: 1 -> PCM | |
| 29 | + .addShort((short)0x0100) // channels: 1 | |
| 30 | + .addBytes(ByteUtils.toLEBytes(8000)) // samples per second | |
| 31 | + .addBytes(ByteUtils.toLEBytes(1 * 8000 * 16 / 8)) // BPSecond | |
| 32 | + .addBytes(ByteUtils.toLEBytes((short)(1 * 16 / 8))) // BPSample | |
| 33 | + .addBytes(ByteUtils.toLEBytes((short)(1 * 16))) // bPSecond | |
| 34 | + .addBytes("data".getBytes()) // data id | |
| 35 | + .addBytes(ByteUtils.toLEBytes(len)); // data chunk size | |
| 36 | + | |
| 37 | + p.addBytes(block, len); | |
| 38 | + | |
| 39 | + FileOutputStream fos = new FileOutputStream("d:\\fuck.wav"); | |
| 40 | + fos.write(p.getBytes()); | |
| 41 | + fos.flush(); | |
| 42 | + fos.close(); | |
| 43 | + } | |
| 44 | + } | |
| 45 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/ByteBufUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +import io.netty.buffer.ByteBuf; | |
| 4 | + | |
| 5 | +import java.nio.charset.StandardCharsets; | |
| 6 | +/** | |
| 7 | + * author:zhouyili (11861744@qq.com) | |
| 8 | + */ | |
| 9 | +public class ByteBufUtils { | |
| 10 | + | |
| 11 | + public static byte[] readReadableBytes(ByteBuf msg) { | |
| 12 | + byte[] content = new byte[msg.readableBytes()]; | |
| 13 | + msg.readBytes(content); | |
| 14 | + return content; | |
| 15 | + } | |
| 16 | + | |
| 17 | + public static byte[] getReadableBytes(ByteBuf msg) { | |
| 18 | + byte[] content = new byte[msg.readableBytes()]; | |
| 19 | + int start = msg.readerIndex(); | |
| 20 | + msg.getBytes(start,content); | |
| 21 | + return content; | |
| 22 | + } | |
| 23 | + public static byte[] readBytes(ByteBuf buf, int length) { | |
| 24 | + validLength(buf, length); | |
| 25 | + byte[] content = new byte[length]; | |
| 26 | + buf.readBytes(content); | |
| 27 | + return content; | |
| 28 | + } | |
| 29 | + | |
| 30 | + private static void validLength(ByteBuf buf, int length) { | |
| 31 | + int readableLength = buf.readableBytes(); | |
| 32 | + if (readableLength<length){ | |
| 33 | + throw new RuntimeException("可读数据长度小于设定值"); | |
| 34 | + } | |
| 35 | + } | |
| 36 | + | |
| 37 | + public static String toString(ByteBuf buf, int length) { | |
| 38 | + validLength(buf,length); | |
| 39 | + return buf.readCharSequence(length, StandardCharsets.ISO_8859_1).toString(); | |
| 40 | + } | |
| 41 | + | |
| 42 | + public static String toString(ByteBuf buf) { | |
| 43 | + return toString(buf,buf.readableBytes()); | |
| 44 | + } | |
| 45 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/ByteHolder.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +import java.util.Arrays; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Created by matrixy on 2018-06-15. | |
| 7 | + */ | |
| 8 | +public class ByteHolder | |
| 9 | +{ | |
| 10 | + int offset = 0; | |
| 11 | + int size = 0; | |
| 12 | + byte[] buffer = null; | |
| 13 | + | |
| 14 | + public ByteHolder(int bufferSize) | |
| 15 | + { | |
| 16 | + this.buffer = new byte[bufferSize]; | |
| 17 | + } | |
| 18 | + | |
| 19 | + public int size() | |
| 20 | + { | |
| 21 | + return this.size; | |
| 22 | + } | |
| 23 | + | |
| 24 | + public void write(byte[] data) | |
| 25 | + { | |
| 26 | + write(data, 0, data.length); | |
| 27 | + } | |
| 28 | + | |
| 29 | + public void write(byte[] data, int offset, int length) | |
| 30 | + { | |
| 31 | + while (this.offset + length >= buffer.length) | |
| 32 | + throw new RuntimeException(String.format("exceed the max buffer size, max length: %d, data length: %d", buffer.length, length)); | |
| 33 | + | |
| 34 | + // 复制一下内容 | |
| 35 | + System.arraycopy(data, offset, buffer, this.offset, length); | |
| 36 | + | |
| 37 | + this.offset += length; | |
| 38 | + this.size += length; | |
| 39 | + } | |
| 40 | + | |
| 41 | + public byte[] array() | |
| 42 | + { | |
| 43 | + return array(this.size); | |
| 44 | + } | |
| 45 | + | |
| 46 | + public byte[] array(int length) | |
| 47 | + { | |
| 48 | + return Arrays.copyOf(this.buffer, length); | |
| 49 | + } | |
| 50 | + | |
| 51 | + public void write(byte b) | |
| 52 | + { | |
| 53 | + this.buffer[offset++] = b; | |
| 54 | + this.size += 1; | |
| 55 | + } | |
| 56 | + | |
| 57 | + public void sliceInto(byte[] dest, int length) | |
| 58 | + { | |
| 59 | + System.arraycopy(this.buffer, 0, dest, 0, length); | |
| 60 | + // 往前挪length个位 | |
| 61 | + System.arraycopy(this.buffer, length, this.buffer, 0, this.size - length); | |
| 62 | + this.offset -= length; | |
| 63 | + this.size -= length; | |
| 64 | + } | |
| 65 | + | |
| 66 | + public void slice(int length) | |
| 67 | + { | |
| 68 | + // 往前挪length个位 | |
| 69 | + System.arraycopy(this.buffer, length, this.buffer, 0, this.size - length); | |
| 70 | + this.offset -= length; | |
| 71 | + this.size -= length; | |
| 72 | + } | |
| 73 | + | |
| 74 | + public byte get(int position) | |
| 75 | + { | |
| 76 | + return this.buffer[position]; | |
| 77 | + } | |
| 78 | + | |
| 79 | + public void clear() | |
| 80 | + { | |
| 81 | + this.offset = 0; | |
| 82 | + this.size = 0; | |
| 83 | + } | |
| 84 | + | |
| 85 | + public int getInt(int offset) | |
| 86 | + { | |
| 87 | + return ByteUtils.getInt(this.buffer, offset, 4); | |
| 88 | + } | |
| 89 | + | |
| 90 | + public int getShort(int position) | |
| 91 | + { | |
| 92 | + int h = this.buffer[position] & 0xff; | |
| 93 | + int l = this.buffer[position + 1] & 0xff; | |
| 94 | + return ((h << 8) | l) & 0xffff; | |
| 95 | + } | |
| 96 | +} | |
| 0 | 97 | \ No newline at end of file | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/ByteUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Created by matrixy on 2017/8/22. | |
| 5 | + */ | |
| 6 | +public final class ByteUtils | |
| 7 | +{ | |
| 8 | + public static byte[] parse(String hexString) | |
| 9 | + { | |
| 10 | + String[] hexes = hexString.split(" "); | |
| 11 | + byte[] data = new byte[hexes.length]; | |
| 12 | + for (int i = 0; i < hexes.length; i++) data[i] = (byte)(Integer.parseInt(hexes[i], 16) & 0xff); | |
| 13 | + return data; | |
| 14 | + } | |
| 15 | + | |
| 16 | + public static synchronized void dump(byte[] data) | |
| 17 | + { | |
| 18 | + dump(data, data.length); | |
| 19 | + } | |
| 20 | + | |
| 21 | + public static synchronized void dump(byte[] data, int len) | |
| 22 | + { | |
| 23 | + for (int i = 0, l = len; i < l && i < data.length; ) | |
| 24 | + { | |
| 25 | + String ascii = ""; | |
| 26 | + int k = 0, f = 0; | |
| 27 | + for (; k < 16 && k < data.length; k++) | |
| 28 | + { | |
| 29 | + if (k + i < l) | |
| 30 | + { | |
| 31 | + f++; | |
| 32 | + byte d = data[i + k]; | |
| 33 | + String hex = Integer.toHexString(d & 0xff).toUpperCase(); | |
| 34 | + if (hex.length() == 1) hex = "0" + hex; | |
| 35 | + if (d >= 0x20 && d < 127) ascii += (char)d; | |
| 36 | + else ascii += '.'; | |
| 37 | + System.out.print(hex); | |
| 38 | + } | |
| 39 | + else | |
| 40 | + { | |
| 41 | + System.out.print(' '); | |
| 42 | + System.out.print(' '); | |
| 43 | + } | |
| 44 | + | |
| 45 | + if (k % 4 == 3) System.out.print(" "); | |
| 46 | + else System.out.print(' '); | |
| 47 | + } | |
| 48 | + i += f; | |
| 49 | + System.out.println(ascii); | |
| 50 | + } | |
| 51 | + } | |
| 52 | + | |
| 53 | + public static String toString(byte[] data) | |
| 54 | + { | |
| 55 | + if (null == data) return ""; | |
| 56 | + return toString(data, data.length); | |
| 57 | + } | |
| 58 | + | |
| 59 | + public static String toString(byte[] buff, int length) | |
| 60 | + { | |
| 61 | + StringBuffer sb = new StringBuffer(length * 2); | |
| 62 | + for (int i = 0; i < buff.length && i < length; i++) | |
| 63 | + { | |
| 64 | + if ((buff[i] & 0xff) < 0x10) sb.append('0'); | |
| 65 | + sb.append(Integer.toHexString(buff[i] & 0xff).toUpperCase()); | |
| 66 | + sb.append(' '); | |
| 67 | + } | |
| 68 | + return sb.toString(); | |
| 69 | + } | |
| 70 | + | |
| 71 | + public static boolean getBit(int val, int pos) | |
| 72 | + { | |
| 73 | + return getBit(new byte[] { | |
| 74 | + (byte)((val >> 0) & 0xff), | |
| 75 | + (byte)((val >> 8) & 0xff), | |
| 76 | + (byte)((val >> 16) & 0xff), | |
| 77 | + (byte)((val >> 24) & 0xff) | |
| 78 | + }, pos); | |
| 79 | + } | |
| 80 | + | |
| 81 | + public static int reverse(int val) | |
| 82 | + { | |
| 83 | + byte[] bytes = toBytes(val); | |
| 84 | + byte[] ret = new byte[4]; | |
| 85 | + for (int i = 0; i < 4; i++) ret[i] = bytes[3 - i]; | |
| 86 | + return toInt(ret); | |
| 87 | + } | |
| 88 | + | |
| 89 | + public static byte[] toLEBytes(int val) | |
| 90 | + { | |
| 91 | + byte[] bytes = new byte[4]; | |
| 92 | + for (int i = 0; i < 4; i++) | |
| 93 | + { | |
| 94 | + bytes[3 - i] = (byte)(val >> ((3 - i) * 8) & 0xff); | |
| 95 | + } | |
| 96 | + return bytes; | |
| 97 | + } | |
| 98 | + | |
| 99 | + public static byte[] toLEBytes(short s) | |
| 100 | + { | |
| 101 | + byte[] bytes = new byte[2]; | |
| 102 | + bytes[0] = (byte)(s & 0xff); | |
| 103 | + bytes[1] = (byte)((s >> 8) & 0xff); | |
| 104 | + return bytes; | |
| 105 | + } | |
| 106 | + | |
| 107 | + public static int toInt(byte[] bytes) | |
| 108 | + { | |
| 109 | + int val = 0; | |
| 110 | + for (int i = 0; i < 4; i++) val |= (bytes[i] & 0xff) << ((3 - i) * 8); | |
| 111 | + return val; | |
| 112 | + } | |
| 113 | + | |
| 114 | + public static byte[] toBytes(int val) | |
| 115 | + { | |
| 116 | + byte[] bytes = new byte[4]; | |
| 117 | + for (int i = 0; i < 4; i++) | |
| 118 | + { | |
| 119 | + bytes[i] = (byte)(val >> ((3 - i) * 8) & 0xff); | |
| 120 | + } | |
| 121 | + return bytes; | |
| 122 | + } | |
| 123 | + | |
| 124 | + public static byte[] toBytes(long val) | |
| 125 | + { | |
| 126 | + byte[] bytes = new byte[8]; | |
| 127 | + for (int i = 0; i < 8; i++) | |
| 128 | + { | |
| 129 | + bytes[i] = (byte)(val >> ((7 - i) * 8) & 0xff); | |
| 130 | + } | |
| 131 | + return bytes; | |
| 132 | + } | |
| 133 | + | |
| 134 | + public static int getInt(byte[] data, int offset, int length) | |
| 135 | + { | |
| 136 | + int val = 0; | |
| 137 | + for (int i = 0; i < length; i++) val |= (data[offset + i] & 0xff) << ((length - i - 1) * 8); | |
| 138 | + return val; | |
| 139 | + } | |
| 140 | + | |
| 141 | + public static long getLong(byte[] data, int offset, int length) | |
| 142 | + { | |
| 143 | + long val = 0; | |
| 144 | + for (int i = 0; i < length; i++) val |= ((long)data[offset + i] & 0xff) << ((length - i - 1) * 8); | |
| 145 | + return val; | |
| 146 | + } | |
| 147 | + | |
| 148 | + public static boolean getBit(byte[] data, int pos) | |
| 149 | + { | |
| 150 | + return ((data[pos / 8] >> (pos % 8)) & 0x01) == 0x01; | |
| 151 | + } | |
| 152 | + | |
| 153 | + public static byte[] concat(byte[]...byteArrays) | |
| 154 | + { | |
| 155 | + int len = 0, index = 0; | |
| 156 | + for (int i = 0; i < byteArrays.length; i++) len += byteArrays[i].length; | |
| 157 | + byte[] buff = new byte[len]; | |
| 158 | + for (int i = 0; i < byteArrays.length; i++) | |
| 159 | + { | |
| 160 | + System.arraycopy(byteArrays[i], 0, buff, index, byteArrays[i].length); | |
| 161 | + index += byteArrays[i].length; | |
| 162 | + } | |
| 163 | + return buff; | |
| 164 | + } | |
| 165 | + | |
| 166 | + public static boolean compare(byte[] data1, byte[] data2) | |
| 167 | + { | |
| 168 | + if (data1.length != data2.length) return false; | |
| 169 | + for (int i = 0; i < data1.length; i++) | |
| 170 | + if ((data1[i] & 0xff) != (data2[i] & 0xff)) return false; | |
| 171 | + return true; | |
| 172 | + } | |
| 173 | + | |
| 174 | + // 相当于(short *) byte_pointer的效果 | |
| 175 | + public static short[] toShortArray(byte[] src) | |
| 176 | + { | |
| 177 | + short[] dst = new short[src.length / 2]; | |
| 178 | + for (int i = 0, k = 0; i < src.length; ) | |
| 179 | + { | |
| 180 | + dst[k++] = (short)((src[i++] & 0xff) | ((src[i++] & 0xff) << 8)); | |
| 181 | + } | |
| 182 | + return dst; | |
| 183 | + } | |
| 184 | + | |
| 185 | + // 相当于(char *) short_pointer的效果 | |
| 186 | + public static byte[] toByteArray(short[] src) | |
| 187 | + { | |
| 188 | + byte[] dst = new byte[src.length * 2]; | |
| 189 | + for (int i = 0, k = 0; i < src.length; i++) | |
| 190 | + { | |
| 191 | + dst[k++] = (byte)(src[i] & 0xff); | |
| 192 | + dst[k++] = (byte)((src[i] >> 8) & 0xff); | |
| 193 | + } | |
| 194 | + return dst; | |
| 195 | + } | |
| 196 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/Configs.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +import java.io.File; | |
| 4 | +import java.io.FileInputStream; | |
| 5 | +import java.io.IOException; | |
| 6 | +import java.util.Properties; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * Created by matrixy on 2017/8/14. | |
| 10 | + */ | |
| 11 | +public final class Configs | |
| 12 | +{ | |
| 13 | + static Properties properties = new Properties(); | |
| 14 | + | |
| 15 | + public static void init(String configFilePath) | |
| 16 | + { | |
| 17 | + try | |
| 18 | + { | |
| 19 | + File file = new File((configFilePath.startsWith("/") ? "." : "") + configFilePath); | |
| 20 | + if (file.exists()) properties.load(new FileInputStream(file)); | |
| 21 | + else properties.load(Configs.class.getResourceAsStream(configFilePath)); | |
| 22 | + } | |
| 23 | + catch (IOException e) | |
| 24 | + { | |
| 25 | + e.printStackTrace(); | |
| 26 | + } | |
| 27 | + } | |
| 28 | + | |
| 29 | + public static String get(String key) | |
| 30 | + { | |
| 31 | + Object val = properties.get(key); | |
| 32 | + if (null == val) return null; | |
| 33 | + else return String.valueOf(val).trim(); | |
| 34 | + } | |
| 35 | + | |
| 36 | + public static int getInt(String key, int defaultVal) | |
| 37 | + { | |
| 38 | + String val = get(key); | |
| 39 | + if (null == val) return defaultVal; | |
| 40 | + else return Integer.parseInt(val); | |
| 41 | + } | |
| 42 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/FLVUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +import java.util.Arrays; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Created by matrixy on 2019/12/16. | |
| 7 | + */ | |
| 8 | +public final class FLVUtils | |
| 9 | +{ | |
| 10 | + // 重置FLV的时间戳 | |
| 11 | + public static void resetTimestamp(byte[] packet, int timestamp) | |
| 12 | + { | |
| 13 | + // 0 1 2 3 | |
| 14 | + // 4 5 6 7 | |
| 15 | + // 只对视频类的TAG进行修改 | |
| 16 | + if (packet[0] != 9 && packet[0] != 8) return; | |
| 17 | + | |
| 18 | + packet[4] = (byte)((timestamp >> 16) & 0xff); | |
| 19 | + packet[5] = (byte)((timestamp >> 8) & 0xff); | |
| 20 | + packet[6] = (byte)((timestamp >> 0) & 0xff); | |
| 21 | + packet[7] = (byte)((timestamp >> 24) & 0xff); | |
| 22 | + } | |
| 23 | +} | |
| 24 | + | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/FileUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +import java.io.*; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Created by matrixy on 2019/8/25. | |
| 7 | + */ | |
| 8 | +public final class FileUtils | |
| 9 | +{ | |
| 10 | + public static void writeFile(File file, byte[] data) | |
| 11 | + { | |
| 12 | + writeFile(file, data, false); | |
| 13 | + } | |
| 14 | + | |
| 15 | + public static void writeFile(File file, byte[] data, boolean append) | |
| 16 | + { | |
| 17 | + FileOutputStream fos = null; | |
| 18 | + try | |
| 19 | + { | |
| 20 | + fos = new FileOutputStream(file, append); | |
| 21 | + fos.write(data); | |
| 22 | + } | |
| 23 | + catch(Exception ex) | |
| 24 | + { | |
| 25 | + throw new RuntimeException(ex); | |
| 26 | + } | |
| 27 | + finally | |
| 28 | + { | |
| 29 | + try { fos.close(); } catch(Exception e) { } | |
| 30 | + } | |
| 31 | + } | |
| 32 | + | |
| 33 | + public static byte[] read(File file) | |
| 34 | + { | |
| 35 | + FileInputStream fis = null; | |
| 36 | + try | |
| 37 | + { | |
| 38 | + fis = new FileInputStream(file); | |
| 39 | + return read(fis); | |
| 40 | + } | |
| 41 | + catch(Exception ex) | |
| 42 | + { | |
| 43 | + throw new RuntimeException(ex); | |
| 44 | + } | |
| 45 | + finally | |
| 46 | + { | |
| 47 | + try { fis.close(); } catch(Exception e) { } | |
| 48 | + } | |
| 49 | + } | |
| 50 | + | |
| 51 | + public static byte[] read(InputStream fis) | |
| 52 | + { | |
| 53 | + ByteArrayOutputStream baos = null; | |
| 54 | + try | |
| 55 | + { | |
| 56 | + baos = new ByteArrayOutputStream(1024); | |
| 57 | + | |
| 58 | + int len = -1; | |
| 59 | + byte[] block = new byte[1024]; | |
| 60 | + while ((len = fis.read(block)) > -1) | |
| 61 | + { | |
| 62 | + baos.write(block, 0, len); | |
| 63 | + } | |
| 64 | + | |
| 65 | + return baos.toByteArray(); | |
| 66 | + } | |
| 67 | + catch(Exception ex) | |
| 68 | + { | |
| 69 | + throw new RuntimeException(ex); | |
| 70 | + } | |
| 71 | + } | |
| 72 | + | |
| 73 | + public static void readInto(File file, OutputStream os) | |
| 74 | + { | |
| 75 | + FileInputStream fis = null; | |
| 76 | + try | |
| 77 | + { | |
| 78 | + int len = -1; | |
| 79 | + byte[] block = new byte[1024]; | |
| 80 | + fis = new FileInputStream(file); | |
| 81 | + while ((len = fis.read(block)) > -1) | |
| 82 | + { | |
| 83 | + os.write(block, 0, len); | |
| 84 | + os.flush(); | |
| 85 | + } | |
| 86 | + } | |
| 87 | + catch(Exception ex) | |
| 88 | + { | |
| 89 | + throw new RuntimeException(ex); | |
| 90 | + } | |
| 91 | + finally | |
| 92 | + { | |
| 93 | + try { fis.close(); } catch(Exception e) { } | |
| 94 | + } | |
| 95 | + } | |
| 96 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/HttpChunk.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Created by matrixy on 2020/1/15. | |
| 5 | + */ | |
| 6 | +public final class HttpChunk | |
| 7 | +{ | |
| 8 | + public static byte[] make(byte[] data) | |
| 9 | + { | |
| 10 | + Packet p = Packet.create(data.length + 64); | |
| 11 | + p.addBytes(String.format("%x\r\n", data.length).getBytes()); | |
| 12 | + p.addBytes(data); | |
| 13 | + p.addByte((byte)'\r'); | |
| 14 | + p.addByte((byte)'\n'); | |
| 15 | + return p.getBytes(); | |
| 16 | + } | |
| 17 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/Packet.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +import java.util.Arrays; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Created by matrixy on 2018/4/14. | |
| 7 | + */ | |
| 8 | +public class Packet | |
| 9 | +{ | |
| 10 | + int size = 0; | |
| 11 | + int offset = 0; | |
| 12 | + int maxSize = 0; | |
| 13 | + public byte[] data; | |
| 14 | + | |
| 15 | + protected Packet() | |
| 16 | + { | |
| 17 | + // do nothing here.. | |
| 18 | + } | |
| 19 | + | |
| 20 | + public int size() | |
| 21 | + { | |
| 22 | + return size; | |
| 23 | + } | |
| 24 | + | |
| 25 | + public void resizeTo(int size) | |
| 26 | + { | |
| 27 | + if (this.maxSize >= size) return; | |
| 28 | + byte[] old = Arrays.copyOf(this.data, this.offset); | |
| 29 | + this.data = new byte[size]; | |
| 30 | + System.arraycopy(old, 0, this.data, 0, old.length); | |
| 31 | + } | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 按指定的大小初始化一个数据包,一般大小为2 + 2 + 8 + 4 + DATA_LENGTH | |
| 35 | + * | |
| 36 | + * @param packetLength 数据包最大字节数 | |
| 37 | + * @param bytes | |
| 38 | + * @return | |
| 39 | + */ | |
| 40 | + public static Packet create(int packetLength, byte[] bytes) | |
| 41 | + { | |
| 42 | + Packet p = new Packet(); | |
| 43 | + p.data = new byte[packetLength]; | |
| 44 | + p.maxSize = packetLength; | |
| 45 | + return p; | |
| 46 | + } | |
| 47 | + | |
| 48 | + public static Packet create(int length) | |
| 49 | + { | |
| 50 | + Packet p = new Packet(); | |
| 51 | + p.data = new byte[length]; | |
| 52 | + p.maxSize = length; | |
| 53 | + p.size = 0; | |
| 54 | + p.offset = 0; | |
| 55 | + return p; | |
| 56 | + } | |
| 57 | + | |
| 58 | + public static Packet create(byte[] data) | |
| 59 | + { | |
| 60 | + Packet p = new Packet(); | |
| 61 | + p.data = data; | |
| 62 | + p.maxSize = data.length; | |
| 63 | + p.size = data.length; | |
| 64 | + p.offset = 0; | |
| 65 | + return p; | |
| 66 | + } | |
| 67 | + | |
| 68 | + public Packet addByte(byte b) | |
| 69 | + { | |
| 70 | + this.data[size++] = b; | |
| 71 | + return this; | |
| 72 | + } | |
| 73 | + | |
| 74 | + public Packet add3Bytes(int v) | |
| 75 | + { | |
| 76 | + this.data[size++] = (byte)((v >> 16) & 0xff); | |
| 77 | + this.data[size++] = (byte)((v >> 8) & 0xff); | |
| 78 | + this.data[size++] = (byte)((v >> 0) & 0xff); | |
| 79 | + return this; | |
| 80 | + } | |
| 81 | + | |
| 82 | + // 如果b的值为0x7e,则自动转义 | |
| 83 | + public Packet addByteAutoQuote(byte b) | |
| 84 | + { | |
| 85 | + if (b == 0x7e) | |
| 86 | + { | |
| 87 | + addByte((byte)0x7d); | |
| 88 | + addByte((byte)0x02); | |
| 89 | + } | |
| 90 | + else if (b == 0x7d) | |
| 91 | + { | |
| 92 | + addByte((byte)0x7d); | |
| 93 | + addByte((byte)0x01); | |
| 94 | + } | |
| 95 | + else | |
| 96 | + { | |
| 97 | + addByte(b); | |
| 98 | + } | |
| 99 | + return this; | |
| 100 | + } | |
| 101 | + | |
| 102 | + public Packet putByte(byte b) | |
| 103 | + { | |
| 104 | + this.data[offset++] = b; | |
| 105 | + return this; | |
| 106 | + } | |
| 107 | + | |
| 108 | + public Packet addShort(short s) | |
| 109 | + { | |
| 110 | + this.data[size++] = (byte) ((s >> 8) & 0xff); | |
| 111 | + this.data[size++] = (byte) (s & 0xff); | |
| 112 | + return this; | |
| 113 | + } | |
| 114 | + | |
| 115 | + public Packet putShort(short s) | |
| 116 | + { | |
| 117 | + this.data[offset++] = (byte) ((s >> 8) & 0xff); | |
| 118 | + this.data[offset++] = (byte) (s & 0xff); | |
| 119 | + return this; | |
| 120 | + } | |
| 121 | + | |
| 122 | + public Packet addInt(int i) | |
| 123 | + { | |
| 124 | + this.data[size++] = (byte) ((i >> 24) & 0xff); | |
| 125 | + this.data[size++] = (byte) ((i >> 16) & 0xff); | |
| 126 | + this.data[size++] = (byte) ((i >> 8) & 0xff); | |
| 127 | + this.data[size++] = (byte) (i & 0xff); | |
| 128 | + return this; | |
| 129 | + } | |
| 130 | + | |
| 131 | + public Packet putInt(int i) | |
| 132 | + { | |
| 133 | + this.data[offset++] = (byte) ((i >> 24) & 0xff); | |
| 134 | + this.data[offset++] = (byte) ((i >> 16) & 0xff); | |
| 135 | + this.data[offset++] = (byte) ((i >> 8) & 0xff); | |
| 136 | + this.data[offset++] = (byte) (i & 0xff); | |
| 137 | + return this; | |
| 138 | + } | |
| 139 | + | |
| 140 | + public Packet addLong(long l) | |
| 141 | + { | |
| 142 | + this.data[size++] = (byte) ((l >> 56) & 0xff); | |
| 143 | + this.data[size++] = (byte) ((l >> 48) & 0xff); | |
| 144 | + this.data[size++] = (byte) ((l >> 40) & 0xff); | |
| 145 | + this.data[size++] = (byte) ((l >> 32) & 0xff); | |
| 146 | + this.data[size++] = (byte) ((l >> 24) & 0xff); | |
| 147 | + this.data[size++] = (byte) ((l >> 16) & 0xff); | |
| 148 | + this.data[size++] = (byte) ((l >> 8) & 0xff); | |
| 149 | + this.data[size++] = (byte) (l & 0xff); | |
| 150 | + return this; | |
| 151 | + } | |
| 152 | + | |
| 153 | + public Packet putLong(long l) | |
| 154 | + { | |
| 155 | + this.data[offset++] = (byte) ((l >> 56) & 0xff); | |
| 156 | + this.data[offset++] = (byte) ((l >> 48) & 0xff); | |
| 157 | + this.data[offset++] = (byte) ((l >> 40) & 0xff); | |
| 158 | + this.data[offset++] = (byte) ((l >> 32) & 0xff); | |
| 159 | + this.data[offset++] = (byte) ((l >> 24) & 0xff); | |
| 160 | + this.data[offset++] = (byte) ((l >> 16) & 0xff); | |
| 161 | + this.data[offset++] = (byte) ((l >> 8) & 0xff); | |
| 162 | + this.data[offset++] = (byte) (l & 0xff); | |
| 163 | + return this; | |
| 164 | + } | |
| 165 | + | |
| 166 | + public Packet addBytes(byte[] b) | |
| 167 | + { | |
| 168 | + return addBytes(b, b.length); | |
| 169 | + } | |
| 170 | + | |
| 171 | + public Packet addBytes(byte[] b, int len) | |
| 172 | + { | |
| 173 | + System.arraycopy(b, 0, this.data, size, len); | |
| 174 | + size += len; | |
| 175 | + return this; | |
| 176 | + } | |
| 177 | + | |
| 178 | + public Packet putBytes(byte[] b) | |
| 179 | + { | |
| 180 | + System.arraycopy(b, 0, this.data, offset, b.length); | |
| 181 | + offset += b.length; | |
| 182 | + return this; | |
| 183 | + } | |
| 184 | + | |
| 185 | + public Packet rewind() | |
| 186 | + { | |
| 187 | + this.offset = 0; | |
| 188 | + return this; | |
| 189 | + } | |
| 190 | + | |
| 191 | + public byte nextByte() | |
| 192 | + { | |
| 193 | + return this.data[offset++]; | |
| 194 | + } | |
| 195 | + | |
| 196 | + public short nextShort() | |
| 197 | + { | |
| 198 | + return (short) (((this.data[offset++] & 0xff) << 8) | (this.data[offset++] & 0xff)); | |
| 199 | + } | |
| 200 | + | |
| 201 | + public short nextWord() | |
| 202 | + { | |
| 203 | + return nextShort(); | |
| 204 | + } | |
| 205 | + | |
| 206 | + public int nextDWord() | |
| 207 | + { | |
| 208 | + return nextInt(); | |
| 209 | + } | |
| 210 | + | |
| 211 | + public int nextInt() | |
| 212 | + { | |
| 213 | + return (this.data[offset++] & 0xff) << 24 | (this.data[offset++] & 0xff) << 16 | (this.data[offset++] & 0xff) << 8 | (this.data[offset++] & 0xff); | |
| 214 | + } | |
| 215 | + | |
| 216 | + public String nextBCD() | |
| 217 | + { | |
| 218 | + byte val = this.data[offset++]; | |
| 219 | + int ch1 = (val >> 4) & 0x0f; | |
| 220 | + int ch2 = (val & 0x0f); | |
| 221 | + return ch1 + "" + ch2; | |
| 222 | + } | |
| 223 | + | |
| 224 | + public Packet addBCD(String num) | |
| 225 | + { | |
| 226 | + for (int i = 0, l = num.length(); i < l; i+=2) | |
| 227 | + { | |
| 228 | + char a = (char)(num.charAt(i) - '0'); | |
| 229 | + char b = (char)(num.charAt(i + 1) - '0'); | |
| 230 | + addByte((byte)(a << 4 | b)); | |
| 231 | + } | |
| 232 | + return this; | |
| 233 | + } | |
| 234 | + | |
| 235 | + public long nextLong() | |
| 236 | + { | |
| 237 | + return ((long) this.data[offset++] & 0xff) << 56 | ((long) this.data[offset++] & 0xff) << 48 | ((long) this.data[offset++] & 0xff) << 40 | ((long) this.data[offset++] & 0xff) << 32 | ((long) this.data[offset++] & 0xff) << 24 | ((long) this.data[offset++] & 0xff) << 16 | ((long) this.data[offset++] & 0xff) << 8 | ((long) this.data[offset++] & 0xff); | |
| 238 | + } | |
| 239 | + | |
| 240 | + public byte[] nextBytes(int length) | |
| 241 | + { | |
| 242 | + byte[] buf = new byte[length]; | |
| 243 | + System.arraycopy(this.data, offset, buf, 0, length); | |
| 244 | + offset += length; | |
| 245 | + return buf; | |
| 246 | + } | |
| 247 | + | |
| 248 | + public byte[] nextBytes() | |
| 249 | + { | |
| 250 | + return nextBytes(size - offset); | |
| 251 | + } | |
| 252 | + | |
| 253 | + public Packet skip(int offset) | |
| 254 | + { | |
| 255 | + this.offset += offset; | |
| 256 | + return this; | |
| 257 | + } | |
| 258 | + | |
| 259 | + public Packet seek(int index) | |
| 260 | + { | |
| 261 | + this.offset = index; | |
| 262 | + return this; | |
| 263 | + } | |
| 264 | + | |
| 265 | + public Packet reset() | |
| 266 | + { | |
| 267 | + this.offset = 0; | |
| 268 | + this.size = 0; | |
| 269 | + | |
| 270 | + return this; | |
| 271 | + } | |
| 272 | + | |
| 273 | + public byte[] getBytes() | |
| 274 | + { | |
| 275 | + if (size == maxSize) return this.data; | |
| 276 | + else | |
| 277 | + { | |
| 278 | + byte[] buff = new byte[size]; | |
| 279 | + System.arraycopy(this.data, 0, buff, 0, size); | |
| 280 | + return buff; | |
| 281 | + } | |
| 282 | + } | |
| 283 | + | |
| 284 | + public boolean hasMoreBytes() | |
| 285 | + { | |
| 286 | + return size - offset > 0; | |
| 287 | + } | |
| 288 | + | |
| 289 | + public static void main(String[] args) throws Exception | |
| 290 | + { | |
| 291 | + ByteUtils.dump(Packet.create(32).addBCD("013800138000").getBytes()); | |
| 292 | + } | |
| 293 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/jt1078/util/WAVUtils.java
0 → 100644
| 1 | +package com.genersoft.iot.vmp.jt1078.util; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Created by matrixy on 2019/12/18. | |
| 5 | + */ | |
| 6 | +public final class WAVUtils | |
| 7 | +{ | |
| 8 | + /** | |
| 9 | + * 创建WAV头,仅返回WAV头部字节数组信息 | |
| 10 | + * @param dataLength PCM数据字节总 | |
| 11 | + * @param channels 通道数,通常为1 | |
| 12 | + * @param sampleRate 采样率,通常为8000 | |
| 13 | + * @param sampleBits 样本比特位数,通常为16 | |
| 14 | + * @return | |
| 15 | + */ | |
| 16 | + public static byte[] createHeader(int dataLength, int channels, int sampleRate, int sampleBits) | |
| 17 | + { | |
| 18 | + Packet p = Packet.create(44); | |
| 19 | + p.addBytes("RIFF".getBytes()) | |
| 20 | + .addBytes(ByteUtils.toLEBytes(dataLength + 36)) | |
| 21 | + .addBytes("WAVE".getBytes()) // wave type | |
| 22 | + .addBytes("fmt ".getBytes()) // fmt id | |
| 23 | + .addInt(0x10000000) // fmt chunk size | |
| 24 | + .addShort((short)0x0100) // format: 1 -> PCM | |
| 25 | + .addBytes(ByteUtils.toLEBytes((short)channels)) // channels: 1 | |
| 26 | + .addBytes(ByteUtils.toLEBytes(sampleRate)) // samples per second | |
| 27 | + .addBytes(ByteUtils.toLEBytes(1 * sampleRate * sampleBits / 8)) // BPSecond | |
| 28 | + .addBytes(ByteUtils.toLEBytes((short)(1 * sampleBits / 8))) // BPSample | |
| 29 | + .addBytes(ByteUtils.toLEBytes((short)(1 * sampleBits))) // bPSecond | |
| 30 | + .addBytes("data".getBytes()) // data id | |
| 31 | + .addBytes(ByteUtils.toLEBytes(dataLength)); // data chunk size | |
| 32 | + | |
| 33 | + return p.getBytes(); | |
| 34 | + } | |
| 35 | +} | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
| ... | ... | @@ -74,6 +74,14 @@ public interface IStreamProxyService { |
| 74 | 74 | boolean stop(String app, String stream); |
| 75 | 75 | |
| 76 | 76 | /** |
| 77 | + * 停用用视频代理 | |
| 78 | + * @param app | |
| 79 | + * @param stream | |
| 80 | + * @return | |
| 81 | + */ | |
| 82 | + boolean stop1(String app, String stream); | |
| 83 | + | |
| 84 | + /** | |
| 77 | 85 | * 获取ffmpeg.cmd模板 |
| 78 | 86 | * @return |
| 79 | 87 | */ | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/StremProxyService1078.java
| ... | ... | @@ -6,4 +6,6 @@ public interface StremProxyService1078 { |
| 6 | 6 | Map<String, Object> sendIORequestStop(String sim, String channel, String stream, Integer port, Integer httpPort); |
| 7 | 7 | |
| 8 | 8 | Map<String, Object> sendIORequestStop( String stream); |
| 9 | + | |
| 10 | + Integer stopCount(String stream); | |
| 9 | 11 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
| ... | ... | @@ -422,9 +422,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { |
| 422 | 422 | streamProxy.setEnable(true); |
| 423 | 423 | updateStreamProxy(streamProxy); |
| 424 | 424 | } else if (jsonObject.getInteger("code") == -1) { |
| 425 | - redisTemplate.opsForValue().set("stream:status:" + stream, "1", 30, TimeUnit.SECONDS); | |
| 425 | + redisTemplate.opsForValue().set("jt1078:stream:status:" + stream, "1", 30, TimeUnit.SECONDS); | |
| 426 | 426 | if(StringUtils.equals(jsonObject.getString("msg"),"This stream already exists" )){ |
| 427 | - redisTemplate.opsForValue().set("stream:status:" + stream, "2", 30, TimeUnit.SECONDS); | |
| 427 | + redisTemplate.opsForValue().set("jt1078:stream:status:" + stream, "2", 30, TimeUnit.SECONDS); | |
| 428 | 428 | } |
| 429 | 429 | } else { |
| 430 | 430 | logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), |
| ... | ... | @@ -451,6 +451,21 @@ public class StreamProxyServiceImpl implements IStreamProxyService { |
| 451 | 451 | } |
| 452 | 452 | |
| 453 | 453 | @Override |
| 454 | + public boolean stop1(String app, String stream) { | |
| 455 | + boolean result = false; | |
| 456 | + StreamProxyItem streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream); | |
| 457 | + if (streamProxyDto != null) { | |
| 458 | + JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto); | |
| 459 | + if (jsonObject != null && jsonObject.getInteger("code") == 0) { | |
| 460 | + streamProxyDto.setEnable(false); | |
| 461 | + result = updateStreamProxy(streamProxyDto); | |
| 462 | + } | |
| 463 | + } | |
| 464 | + return result; | |
| 465 | + } | |
| 466 | + | |
| 467 | + | |
| 468 | + @Override | |
| 454 | 469 | public JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem) { |
| 455 | 470 | JSONObject result = new JSONObject(); |
| 456 | 471 | JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); | ... | ... |
src/main/java/com/genersoft/iot/vmp/service/impl/StremProxyService1078Impl.java
| 1 | 1 | package com.genersoft.iot.vmp.service.impl; |
| 2 | 2 | |
| 3 | +import com.genersoft.iot.vmp.VManageBootstrap; | |
| 4 | +import com.genersoft.iot.vmp.jt1078.app.VideoServerApp; | |
| 5 | +import com.genersoft.iot.vmp.service.IStreamProxyService; | |
| 3 | 6 | import com.genersoft.iot.vmp.service.StremProxyService1078; |
| 4 | 7 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.ben.HttpClientPostEntity; |
| 5 | 8 | import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.Jt1078ConfigBean; |
| ... | ... | @@ -61,8 +64,15 @@ public class StremProxyService1078Impl implements StremProxyService1078 { |
| 61 | 64 | url = StringUtils.replace(this.jt1078ConfigBean.getJt1078Url(), "{0}", this.jt1078ConfigBean.getStopSendPort()); |
| 62 | 65 | HttpClientPostEntity entity = httpClientUtil.doPost(url, msg, null); |
| 63 | 66 | |
| 64 | - url = jt1078ConfigBean.formatStopPushURL(stream, port, httpPort); | |
| 65 | - httpClientUtil.doGet(url, null); | |
| 67 | + IStreamProxyService streamProxyService = VManageBootstrap.getBean(IStreamProxyService.class); | |
| 68 | + if(Objects.nonNull(streamProxyService)){ | |
| 69 | + streamProxyService.stop1("schedule",stream); | |
| 70 | + } | |
| 71 | + log.info("port:[{}]",port); | |
| 72 | + if(Objects.nonNull(port)){ | |
| 73 | + VideoServerApp.stopServer(port,httpPort); | |
| 74 | + } | |
| 75 | + | |
| 66 | 76 | |
| 67 | 77 | |
| 68 | 78 | if (Objects.isNull(entity)) { |
| ... | ... | @@ -71,6 +81,8 @@ public class StremProxyService1078Impl implements StremProxyService1078 { |
| 71 | 81 | log.info(entity.getResultStr()); |
| 72 | 82 | } |
| 73 | 83 | |
| 84 | + redisTemplate.opsForValue().set("jt1078:count:"+stream,20000,300,TimeUnit.SECONDS); | |
| 85 | + | |
| 74 | 86 | // streamProxyService.del("schedule", stream); |
| 75 | 87 | resultMap.put("code", "1"); |
| 76 | 88 | resultMap.put("message", "OK"); |
| ... | ... | @@ -95,4 +107,10 @@ public class StremProxyService1078Impl implements StremProxyService1078 { |
| 95 | 107 | |
| 96 | 108 | return sendIORequestStop(sim, channel, stream, (Integer) port, (Integer) httpPort); |
| 97 | 109 | } |
| 110 | + | |
| 111 | + @Override | |
| 112 | + public Integer stopCount(String stream) { | |
| 113 | + Object countVal = redisTemplate.opsForValue().get("jt1078:count:" + stream); | |
| 114 | + return Objects.nonNull(countVal)?(Integer) countVal:null; | |
| 115 | + } | |
| 98 | 116 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/Jt1078OfCarController.java
| ... | ... | @@ -9,9 +9,11 @@ import com.alibaba.fastjson2.JSON; |
| 9 | 9 | import com.alibaba.fastjson2.JSONArray; |
| 10 | 10 | import com.alibaba.fastjson2.JSONException; |
| 11 | 11 | import com.alibaba.fastjson2.JSONObject; |
| 12 | +import com.genersoft.iot.vmp.VManageBootstrap; | |
| 12 | 13 | import com.genersoft.iot.vmp.conf.MediaConfig; |
| 13 | 14 | import com.genersoft.iot.vmp.conf.StreamProxyTask; |
| 14 | 15 | import com.genersoft.iot.vmp.conf.exception.ControllerException; |
| 16 | +import com.genersoft.iot.vmp.jt1078.app.VideoServerApp; | |
| 15 | 17 | import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; |
| 16 | 18 | import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; |
| 17 | 19 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; |
| ... | ... | @@ -201,15 +203,18 @@ public class Jt1078OfCarController { |
| 201 | 203 | this.redisTemplate.opsForValue().set("tag:history:port:" + stream, entity.getPort(), 2L, TimeUnit.DAYS); |
| 202 | 204 | this.redisTemplate.opsForValue().set("tag:history:httpPort:" + stream, entity.getHttpPort(), 2L, TimeUnit.DAYS); |
| 203 | 205 | this.redisTemplate.opsForValue().set("tag:history:httpPort:time:" + stream, (new Date()).getTime(), StreamProxyTask.TIME_OUT, TimeUnit.SECONDS); |
| 206 | + redisTemplate.delete("jt1078:count:" + stream); | |
| 207 | + | |
| 204 | 208 | msg = this.jt1078ConfigBean.formatMessageId(sim, channel, this.rtspConfigBean, entity.getPort()); |
| 205 | - HttpClientPostEntity entity1 = this.httpClientUtil.doPost(url, msg, (String) null); | |
| 206 | - Map<String, Object> resultMap1 = this.chooseEntity(entity, url, false); | |
| 209 | + VManageBootstrap.getBean(VideoServerApp.class).newVideoServer(stream, entity.getPort(), entity.getHttpPort()); | |
| 210 | + | |
| 211 | + HttpClientPostEntity entity1 = httpClientUtil.doPost(url, msg, null); | |
| 212 | + Map<String, Object> resultMap1 = this.chooseEntity(entity1, url, false); | |
| 207 | 213 | if (Objects.nonNull(resultMap1)) { |
| 208 | 214 | return resultMap1; |
| 209 | 215 | } |
| 210 | 216 | |
| 211 | - log.info(entity1.getResultStr()); | |
| 212 | - this.createStreamProxy(sim + "-" + channel, entity.getHttpPort()); | |
| 217 | + createStreamProxy(stream, entity.getPort()); | |
| 213 | 218 | Map<String, Object> resultMap2 = this.getStreamContent(stream, entity.getHttpPort()); |
| 214 | 219 | if (Objects.nonNull(resultMap2) && Objects.nonNull(resultMap2.get("code")) && !StringUtils.equals(resultMap2.get("code").toString(), "1")) { |
| 215 | 220 | return resultMap2; |
| ... | ... | @@ -233,6 +238,11 @@ public class Jt1078OfCarController { |
| 233 | 238 | resultMap.put("code", "-20"); |
| 234 | 239 | resultMap.put("msg", "发送推流指令异常"); |
| 235 | 240 | return resultMap; |
| 241 | + } catch (Exception e) { | |
| 242 | + log.error("发送推流指令异常;[{}],[{}]", new Object[]{url, msg, e}); | |
| 243 | + resultMap.put("code", "-20"); | |
| 244 | + resultMap.put("msg", "发送推流指令异常"); | |
| 245 | + return resultMap; | |
| 236 | 246 | } |
| 237 | 247 | } |
| 238 | 248 | } |
| ... | ... | @@ -433,6 +443,7 @@ public class Jt1078OfCarController { |
| 433 | 443 | resultMap.put("msg", "结束时间不能为空"); |
| 434 | 444 | return resultMap; |
| 435 | 445 | } else { |
| 446 | + | |
| 436 | 447 | StreamContent streamContent = this.getStreamContentPlayURL(StringUtils.join(new String[]{channelMapping})); |
| 437 | 448 | if (Objects.nonNull(streamContent) && StringUtils.isNotEmpty(streamContent.getWs_flv())) { |
| 438 | 449 | resultMap.put("code", "1"); |
| ... | ... | @@ -444,6 +455,8 @@ public class Jt1078OfCarController { |
| 444 | 455 | resultMap.put("stream", channelMapping); |
| 445 | 456 | return resultMap; |
| 446 | 457 | } |
| 458 | + redisTemplate.delete("jt1078:count:" + channelMapping); | |
| 459 | + | |
| 447 | 460 | startTime = this.formatTime(startTime); |
| 448 | 461 | endTime = this.formatTime(endTime); |
| 449 | 462 | |
| ... | ... | @@ -468,16 +481,16 @@ public class Jt1078OfCarController { |
| 468 | 481 | this.redisTemplate.opsForValue().set("tag:history:httpPort:" + channelMapping, entity.getHttpPort(), 2L, TimeUnit.DAYS); |
| 469 | 482 | this.redisTemplate.opsForValue().set("tag:history:httpPort:time:" + channelMapping, (new Date()).getTime(), StreamProxyTask.TIME_OUT, TimeUnit.SECONDS); |
| 470 | 483 | msg = this.jt1078ConfigBean.formatMessageHistoryPlayRTSP(sim, channel, startTime, endTime, this.rtspConfigBean, entity.getPort()); |
| 484 | + VManageBootstrap.getBean(VideoServerApp.class).newVideoServer(channelMapping, entity.getPort(), entity.getHttpPort()); | |
| 471 | 485 | |
| 472 | - log.info("获取推流"); | |
| 473 | 486 | this.createStreamProxy(sim + "-" + channel, entity.getHttpPort()); |
| 474 | - entity = this.httpClientUtil.doPost(url, msg, (String) null); | |
| 475 | - Map<String, Object> resultMap2 = this.chooseEntity(entity, url, true); | |
| 487 | + HttpClientPostEntity entity1 = this.httpClientUtil.doPost(url, msg, (String) null); | |
| 488 | + createStreamProxy(channelMapping, entity.getHttpPort()); | |
| 489 | + | |
| 490 | + Map<String, Object> resultMap2 = this.chooseEntity(entity1, url, true); | |
| 476 | 491 | if (Objects.nonNull(resultMap2)) { |
| 477 | 492 | return resultMap2; |
| 478 | 493 | } else { |
| 479 | - log.info(entity.getResultStr()); | |
| 480 | - this.createStreamProxy(channelMapping, entity.getHttpPort()); | |
| 481 | 494 | resultMap.put("code", "1"); |
| 482 | 495 | Map<String, Object> resultMap3 = this.getStreamContent(channelMapping, entity.getHttpPort()); |
| 483 | 496 | if (Objects.nonNull(resultMap3) && Objects.nonNull(resultMap3.get("code")) && !StringUtils.equals(resultMap3.get("code").toString(), "1")) { |
| ... | ... | @@ -496,15 +509,21 @@ public class Jt1078OfCarController { |
| 496 | 509 | } |
| 497 | 510 | } |
| 498 | 511 | } |
| 499 | - } catch (IOException | URISyntaxException var16) { | |
| 500 | - Exception e = var16; | |
| 512 | + } catch (IOException | URISyntaxException e) { | |
| 513 | + log.error("发送推流指令异常;[{}],[{}]", new Object[]{url, msg, e}); | |
| 514 | + resultMap.put("code", "-20"); | |
| 515 | + resultMap.put("msg", "发送推流指令异常"); | |
| 516 | + return resultMap; | |
| 517 | + } catch (InterruptedException e) { | |
| 518 | + log.error("发送推流指令异常;[{}],[{}]", new Object[]{url, msg, e}); | |
| 519 | + resultMap.put("code", "-20"); | |
| 520 | + resultMap.put("msg", "发送推流指令异常"); | |
| 521 | + return resultMap; | |
| 522 | + } catch (Exception e) { | |
| 501 | 523 | log.error("发送推流指令异常;[{}],[{}]", new Object[]{url, msg, e}); |
| 502 | 524 | resultMap.put("code", "-20"); |
| 503 | 525 | resultMap.put("msg", "发送推流指令异常"); |
| 504 | 526 | return resultMap; |
| 505 | - } catch (InterruptedException var17) { | |
| 506 | - InterruptedException e = var17; | |
| 507 | - throw new RuntimeException(e); | |
| 508 | 527 | } |
| 509 | 528 | } |
| 510 | 529 | } |
| ... | ... | @@ -531,6 +550,7 @@ public class Jt1078OfCarController { |
| 531 | 550 | streamContent = new StreamContent(); |
| 532 | 551 | streamContent.setWs_flv(StringUtils.replace(this.jt1078ConfigBean.getWs(), "{stream}", stream)); |
| 533 | 552 | streamContent.setWss_flv(StringUtils.replace(this.jt1078ConfigBean.getWss(), "{stream}", stream)); |
| 553 | + streamContent.setFlv(StringUtils.replace(this.jt1078ConfigBean.getDownloadFlv(), "{stream}", stream)); | |
| 534 | 554 | |
| 535 | 555 | |
| 536 | 556 | new Thread(new Runnable() { |
| ... | ... | @@ -558,7 +578,7 @@ public class Jt1078OfCarController { |
| 558 | 578 | break; |
| 559 | 579 | } |
| 560 | 580 | |
| 561 | - Object valObj = redisTemplate.opsForValue().get("stream:status:" + stream); | |
| 581 | + Object valObj = redisTemplate.opsForValue().get("jt1078:stream:status:" + stream); | |
| 562 | 582 | if (Objects.nonNull(valObj) && StringUtils.equals(valObj.toString(), "1")) { |
| 563 | 583 | StreamProxyItem streamProxyItem = streamProxyController.one("schedule", stream); |
| 564 | 584 | if (Objects.nonNull(streamProxyItem) && StringUtils.isNotEmpty(streamProxyItem.getApp())) { |
| ... | ... | @@ -574,8 +594,10 @@ public class Jt1078OfCarController { |
| 574 | 594 | |
| 575 | 595 | } else if (Objects.nonNull(valObj) && StringUtils.equals(valObj.toString(), "2")) { |
| 576 | 596 | // streamProxyService.del("schedule", stream); |
| 577 | -// redisTemplate.delete("stream:status:" + stream); | |
| 597 | +// redisTemplate.delete("jt1078:stream:status:" + stream); | |
| 578 | 598 | // createStreamProxy(stream,httpPort); |
| 599 | + streamProxyService.stop1("schedule", stream); | |
| 600 | + | |
| 579 | 601 | } else { |
| 580 | 602 | Object val = this.redisTemplate.opsForValue().get("timeout:" + stream); |
| 581 | 603 | if (Objects.nonNull(val) && StringUtils.equals(val.toString(), "1")) { |
| ... | ... | @@ -586,7 +608,13 @@ public class Jt1078OfCarController { |
| 586 | 608 | } |
| 587 | 609 | } |
| 588 | 610 | } |
| 611 | + | |
| 612 | + Integer countVal = stremProxyService1078.stopCount(stream); | |
| 613 | + if (Objects.nonNull(countVal)) { | |
| 614 | + index = countVal; | |
| 615 | + } | |
| 589 | 616 | if (index >= count) { |
| 617 | + | |
| 590 | 618 | break; |
| 591 | 619 | } |
| 592 | 620 | |
| ... | ... | @@ -703,47 +731,29 @@ public class Jt1078OfCarController { |
| 703 | 731 | private HttpClientPostEntity createServerLister(String channelMapping) throws URISyntaxException, IOException { |
| 704 | 732 | |
| 705 | 733 | Integer port = RandomUtils.nextInt(this.jt1078ConfigBean.getStart1078Port(), this.jt1078ConfigBean.getEnd1078Port()); |
| 706 | - while (port % 2 == 1 || port == jt1078ConfigBean.getStart1078Port()) { | |
| 734 | + while (true) { | |
| 707 | 735 | port = RandomUtils.nextInt(this.jt1078ConfigBean.getStart1078Port(), this.jt1078ConfigBean.getEnd1078Port()); |
| 708 | - } | |
| 709 | - Integer httPort = 3333; | |
| 710 | 736 | |
| 711 | - String url; | |
| 712 | - if (!Objects.equals(port, httPort) && Objects.nonNull(port)) { | |
| 713 | - url = "jt1078:server:port:" + port; | |
| 714 | - String httpKey = "jt1078:server:httpport:" + httPort; | |
| 715 | - int index = 0; | |
| 716 | - | |
| 717 | - while (this.redisTemplate.hasKey(url) || this.redisTemplate.hasKey(httpKey)) { | |
| 718 | - port = RandomUtils.nextInt(this.jt1078ConfigBean.getStart1078Port(), this.jt1078ConfigBean.getEnd1078Port()); | |
| 719 | - httPort = RandomUtils.nextInt(this.jt1078ConfigBean.getStart1078Port(), this.jt1078ConfigBean.getEnd1078Port()); | |
| 720 | - url = "jt1078:server:port:" + port; | |
| 721 | - httpKey = "jt1078:server:httpport:" + httPort; | |
| 722 | - if (index > 1000) { | |
| 723 | - break; | |
| 724 | - } | |
| 737 | + String key = "jt1078:server:port:" + port; | |
| 738 | + if (!this.redisTemplate.hasKey(key)) { | |
| 739 | + redisTemplate.opsForValue().set("jt1078:server:port:" + port, port, 3, TimeUnit.HOURS); | |
| 740 | + break; | |
| 725 | 741 | } |
| 726 | 742 | |
| 727 | - this.redisTemplate.opsForValue().set(url, 1, 2L, TimeUnit.DAYS); | |
| 728 | - this.redisTemplate.opsForValue().set(httpKey, 1, 2L, TimeUnit.DAYS); | |
| 729 | - } else { | |
| 730 | - port = 1078; | |
| 731 | - httPort = 3333; | |
| 732 | 743 | } |
| 744 | +// Integer port = 11078; | |
| 745 | + Integer httPort = 3333; | |
| 733 | 746 | |
| 734 | - url = this.jt1078ConfigBean.formatPushURL(channelMapping, port, httPort); | |
| 747 | + String url = this.jt1078ConfigBean.formatPushURL(channelMapping, port, httPort); | |
| 735 | 748 | |
| 736 | 749 | try { |
| 737 | - HttpClientPostEntity httpClientPostEntity = this.httpClientUtil.doGet(url, (String) null); | |
| 738 | - if (Objects.isNull(httpClientPostEntity)) { | |
| 739 | - return null; | |
| 740 | - } else { | |
| 741 | - httpClientPostEntity.setHttpPort(httPort); | |
| 742 | - httpClientPostEntity.setPort(port); | |
| 743 | - return httpClientPostEntity; | |
| 744 | - } | |
| 745 | - } catch (Exception var7) { | |
| 746 | - Exception e = var7; | |
| 750 | + HttpClientPostEntity httpClientPostEntity = new HttpClientPostEntity(); | |
| 751 | + | |
| 752 | + httpClientPostEntity.setHttpPort(httPort); | |
| 753 | + httpClientPostEntity.setPort(port); | |
| 754 | + return httpClientPostEntity; | |
| 755 | + | |
| 756 | + } catch (Exception e) { | |
| 747 | 757 | log.error("url:[{}]", url, e); |
| 748 | 758 | return null; |
| 749 | 759 | } |
| ... | ... | @@ -792,32 +802,40 @@ public class Jt1078OfCarController { |
| 792 | 802 | while (index < 100) { |
| 793 | 803 | index++; |
| 794 | 804 | try { |
| 805 | + Integer countVal = stremProxyService1078.stopCount(stream); | |
| 806 | + if (Objects.nonNull(countVal)) { | |
| 807 | + index = countVal; | |
| 808 | + } | |
| 809 | + | |
| 795 | 810 | boolean flag = httpClientUtil.doGetNoResult(jt1078ConfigBean.formatVideoURL(stream)); |
| 796 | 811 | if (flag) { |
| 797 | - String url = StringUtils.replace(jt1078ConfigBean.getGetURL(), "{stream}", stream); | |
| 798 | - url = StringUtils.replace(url, "{port}", port + ""); | |
| 799 | - StreamProxyItem item = new StreamProxyItem(); | |
| 800 | - item.setApp("schedule"); | |
| 801 | - item.setEnable(true); | |
| 802 | - item.setEnableAudio(true); | |
| 803 | - item.setRtpType("default"); | |
| 804 | - item.setStream(stream); | |
| 805 | - item.setMediaServerId(mediaConfig.getId()); | |
| 806 | - item.setUrl(url); | |
| 807 | - item.setFfmpegCmdKey("ffmpeg.cmd"); | |
| 808 | - item.setEnable(true); | |
| 809 | - item.setEnableAudio(true); | |
| 810 | - item.setEnableMp4(false); | |
| 811 | - item.setEnableRemoveNoneReader(false); | |
| 812 | - item.setEnableDisableNoneReader(false); | |
| 813 | - item.setName(stream); | |
| 814 | - streamProxyController.save(item); | |
| 812 | +// String url = StringUtils.replace(jt1078ConfigBean.getGetURL(), "{stream}", stream); | |
| 813 | +// url = StringUtils.replace(url, "{port}", port + ""); | |
| 814 | +// StreamProxyItem item = new StreamProxyItem(); | |
| 815 | +// item.setApp("schedule"); | |
| 816 | +// item.setEnable(true); | |
| 817 | +// item.setEnableAudio(true); | |
| 818 | +// item.setRtpType("default"); | |
| 819 | +// item.setStream(stream); | |
| 820 | +// item.setMediaServerId(mediaConfig.getId()); | |
| 821 | +// item.setUrl(url); | |
| 822 | +// item.setFfmpegCmdKey("ffmpeg.cmd"); | |
| 823 | +// item.setEnable(true); | |
| 824 | +// item.setEnableAudio(true); | |
| 825 | +// item.setEnableMp4(false); | |
| 826 | +// item.setEnableRemoveNoneReader(false); | |
| 827 | +// item.setEnableDisableNoneReader(false); | |
| 828 | +// item.setName(stream); | |
| 829 | +// streamProxyController.save(item); | |
| 815 | 830 | break; |
| 816 | 831 | } |
| 832 | + Thread.sleep(1000); | |
| 817 | 833 | } catch (URISyntaxException e) { |
| 818 | 834 | log.info(e.getMessage()); |
| 819 | 835 | } catch (IOException e) { |
| 820 | 836 | log.info(e.getMessage()); |
| 837 | + } catch (InterruptedException e) { | |
| 838 | + throw new RuntimeException(e); | |
| 821 | 839 | } |
| 822 | 840 | |
| 823 | 841 | } |
| ... | ... | @@ -825,5 +843,25 @@ public class Jt1078OfCarController { |
| 825 | 843 | }).start(); |
| 826 | 844 | |
| 827 | 845 | |
| 846 | +// String url = StringUtils.replace(jt1078ConfigBean.getGetURL(), "{stream}", stream); | |
| 847 | +// url = StringUtils.replace(url, "{port}", port + ""); | |
| 848 | +// StreamProxyItem item = new StreamProxyItem(); | |
| 849 | +// item.setApp("schedule"); | |
| 850 | +// item.setEnable(true); | |
| 851 | +// item.setEnableAudio(true); | |
| 852 | +// item.setRtpType("default"); | |
| 853 | +// item.setStream(stream); | |
| 854 | +// item.setMediaServerId(mediaConfig.getId()); | |
| 855 | +// item.setUrl(url); | |
| 856 | +// item.setFfmpegCmdKey("ffmpeg.cmd"); | |
| 857 | +// item.setEnable(true); | |
| 858 | +// item.setEnableAudio(true); | |
| 859 | +// item.setEnableMp4(false); | |
| 860 | +// item.setEnableRemoveNoneReader(false); | |
| 861 | +// item.setEnableDisableNoneReader(false); | |
| 862 | +// item.setName(stream); | |
| 863 | +// streamProxyController.save(item); | |
| 864 | + | |
| 865 | + | |
| 828 | 866 | } |
| 829 | 867 | } | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/Jt1078ConfigBean.java
| 1 | -package com.genersoft.iot.vmp.vmanager.jt1078.platform.config; import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.RtspConfigBean; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Jt1078ConfigBean { @Value("${tuohua.bsth.jt1078.url}") private String jt1078Url; @Value("${tuohua.bsth.jt1078.sendPort}") private String jt1078SendPort; @Value("${tuohua.bsth.jt1078.stopSendPort}") private String stopSendPort; @Value("${tuohua.bsth.jt1078.historyListPort}") private String historyListPort; @Value("${tuohua.bsth.jt1078.playHistoryPort}") private String playHistoryPort; @Value("${tuohua.bsth.jt1078.ports}") private String portsOf1078; @Value("${tuohua.bsth.jt1078.pushURL}") private String pushURL; @Value("${tuohua.bsth.jt1078.stopPushURL}") private String stopPUshURL; private Integer start1078Port; private Integer end1078Port; @Value("${tuohua.bsth.jt1078.get.url}") private String getURL; @Value("${tuohua.bsth.jt1078.addPortVal}") private Integer addPort; @Value("${tuohua.bsth.jt1078.ws}") private String ws; @Value("${tuohua.bsth.jt1078.wss}") private String wss; @Value("${tuohua.bsth.jt1078.videoURL}") private String videoURL; private static final String SEND_IO_MESSAGE_RTSP = "{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}"; private static final String SEND_IO_MESSAGE_RTSP_STOP = "{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}"; private static final String SEND_IO_HISTORY_RTSP = "{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}"; private static final String SEND_IO_PLAY_RTSP = "{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}"; public String formatMessageId(String sim, String channel, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}", "{clientId}", sim); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{channelNo}", channel); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatMessageStop(String sim, String channel) { String msg = StringUtils.replace("{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}", "{clientId}", sim); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryListRTSP(String sim, String channel, String startTime, String endTime) { String msg = StringUtils.replace("{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryPlayRTSP(String sim, String channel, String startTime, String endTime, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); msg = StringUtils.replace(msg, "{channelNo}", channel); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{sim}", sim); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.pushURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatStopPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.stopPUshURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatVideoURL(String stream){ return StringUtils.replace(videoURL,"{stream}",stream); } public String getJt1078Url() { return this.jt1078Url; } public String getJt1078SendPort() { return this.jt1078SendPort; } public String getStopSendPort() { return this.stopSendPort; } public String getHistoryListPort() { return this.historyListPort; } public String getPlayHistoryPort() { return this.playHistoryPort; } public String getPushURL() { return this.pushURL; } public Integer getStart1078Port() { if (Objects.isNull(this.start1078Port)) this.start1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringBefore(this.portsOf1078, ","))); return this.start1078Port; } public Integer getEnd1078Port() { if (Objects.isNull(this.end1078Port)) this.end1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringAfter(this.portsOf1078, ","))); return this.end1078Port; } public String getStopPUshURL() { return this.stopPUshURL; } public String getGetURL() { return this.getURL; } public Integer getAddPort() { return this.addPort; } public String getWs() { return this.ws; } public String getWss() { return this.wss; } public String getPortsOf1078() { return portsOf1078; } } | |
| 2 | 1 | \ No newline at end of file |
| 2 | +package com.genersoft.iot.vmp.vmanager.jt1078.platform.config; import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.RtspConfigBean; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Jt1078ConfigBean { @Value("${tuohua.bsth.jt1078.url}") private String jt1078Url; @Value("${tuohua.bsth.jt1078.sendPort}") private String jt1078SendPort; @Value("${tuohua.bsth.jt1078.stopSendPort}") private String stopSendPort; @Value("${tuohua.bsth.jt1078.historyListPort}") private String historyListPort; @Value("${tuohua.bsth.jt1078.playHistoryPort}") private String playHistoryPort; @Value("${tuohua.bsth.jt1078.ports}") private String portsOf1078; @Value("${tuohua.bsth.jt1078.pushURL}") private String pushURL; @Value("${tuohua.bsth.jt1078.stopPushURL}") private String stopPUshURL; private Integer start1078Port; private Integer end1078Port; @Value("${tuohua.bsth.jt1078.get.url}") private String getURL; @Value("${tuohua.bsth.jt1078.addPortVal}") private Integer addPort; @Value("${tuohua.bsth.jt1078.ws}") private String ws; @Value("${tuohua.bsth.jt1078.wss}") private String wss; @Value("${tuohua.bsth.jt1078.downloadFLV}") private String downloadFlv; private static final String SEND_IO_MESSAGE_RTSP = "{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}"; private static final String SEND_IO_MESSAGE_RTSP_STOP = "{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}"; private static final String SEND_IO_HISTORY_RTSP = "{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}"; private static final String SEND_IO_PLAY_RTSP = "{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}"; public String formatMessageId(String sim, String channel, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}", "{clientId}", sim); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{channelNo}", channel); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatMessageStop(String sim, String channel) { String msg = StringUtils.replace("{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}", "{clientId}", sim); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryListRTSP(String sim, String channel, String startTime, String endTime) { String msg = StringUtils.replace("{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryPlayRTSP(String sim, String channel, String startTime, String endTime, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); msg = StringUtils.replace(msg, "{channelNo}", channel); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{sim}", sim); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.pushURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatStopPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.stopPUshURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatVideoURL(String stream){ String url = StringUtils.replace(getGetURL(),"{stream}",stream); if(!StringUtils.endsWith(url,".flv")){ url = url+".flv"; } return url; } public String getJt1078Url() { return this.jt1078Url; } public String getJt1078SendPort() { return this.jt1078SendPort; } public String getStopSendPort() { return this.stopSendPort; } public String getHistoryListPort() { return this.historyListPort; } public String getPlayHistoryPort() { return this.playHistoryPort; } public String getPushURL() { return this.pushURL; } public Integer getStart1078Port() { if (Objects.isNull(this.start1078Port)) this.start1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringBefore(this.portsOf1078, ","))); return this.start1078Port; } public Integer getEnd1078Port() { if (Objects.isNull(this.end1078Port)) this.end1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringAfter(this.portsOf1078, ","))); return this.end1078Port; } public String getStopPUshURL() { return this.stopPUshURL; } public String getGetURL() { return this.getURL; } public Integer getAddPort() { return this.addPort; } public String getWs() { return this.ws; } public String getWss() { return this.wss; } public String getDownloadFlv() { return downloadFlv; } public String getPortsOf1078() { return portsOf1078; } } | |
| 3 | 3 | \ No newline at end of file | ... | ... |
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/handler/HttpClientUtil.java
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
| ... | ... | @@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.Operation; |
| 19 | 19 | import io.swagger.v3.oas.annotations.Parameter; |
| 20 | 20 | import io.swagger.v3.oas.annotations.security.SecurityRequirement; |
| 21 | 21 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 22 | +import org.apache.commons.lang3.StringUtils; | |
| 22 | 23 | import org.slf4j.Logger; |
| 23 | 24 | import org.slf4j.LoggerFactory; |
| 24 | 25 | import org.springframework.beans.factory.annotation.Autowired; |
| ... | ... | @@ -28,6 +29,7 @@ import org.springframework.util.ObjectUtils; |
| 28 | 29 | import org.springframework.web.bind.annotation.*; |
| 29 | 30 | import org.springframework.web.context.request.async.DeferredResult; |
| 30 | 31 | |
| 32 | +import java.util.Objects; | |
| 31 | 33 | import java.util.UUID; |
| 32 | 34 | import java.util.concurrent.TimeUnit; |
| 33 | 35 | |
| ... | ... | @@ -124,14 +126,23 @@ public class StreamProxyController { |
| 124 | 126 | }); |
| 125 | 127 | |
| 126 | 128 | streamProxyService.save(param, (code, msg, streamInfo) -> { |
| 127 | - logger.info("[拉流代理] {}", code == ErrorCode.SUCCESS.getCode()? "成功":"失败: " + msg); | |
| 129 | + String stream = null; | |
| 130 | + if(Objects.nonNull(streamInfo)){ | |
| 131 | + stream = streamInfo.getStream(); | |
| 132 | + } | |
| 133 | + logger.info("[拉流代理] {}[{}]", code == ErrorCode.SUCCESS.getCode()? "成功":"失败: " + msg,stream); | |
| 128 | 134 | if (code == ErrorCode.SUCCESS.getCode()) { |
| 129 | 135 | requestMessage.setData(new StreamContent(streamInfo)); |
| 130 | 136 | }else { |
| 131 | 137 | requestMessage.setData(WVPResult.fail(code, msg)); |
| 132 | 138 | } |
| 133 | 139 | resultHolder.invokeAllResult(requestMessage); |
| 134 | - redisTemplate.opsForValue().set("timeout:"+param.getStream(),"1",30, TimeUnit.SECONDS); | |
| 140 | + if(StringUtils.equals(msg,"This stream already exists")){ | |
| 141 | + redisTemplate.opsForValue().set("timeout:"+param.getStream(),2,30, TimeUnit.SECONDS); | |
| 142 | + | |
| 143 | + }else { | |
| 144 | + redisTemplate.opsForValue().set("timeout:" + param.getStream(), 1, 30, TimeUnit.SECONDS); | |
| 145 | + } | |
| 135 | 146 | }); |
| 136 | 147 | return result; |
| 137 | 148 | } | ... | ... |
src/main/resources/app.properties
0 → 100644
| 1 | +server.port = 1078 | |
| 2 | +server.http.port = 3333 | |
| 3 | +server.backlog = 1024 | |
| 4 | + | |
| 5 | +# ffmpeg可执行文件路径,可以留空 | |
| 6 | +ffmpeg.path = D:/Tools/ffmpeg-4.2.2-win64-static/bin/ffmpeg.exe | |
| 7 | + | |
| 8 | +# 配置rtmp地址将在终端发送RTP消息包时,额外的向RTMP服务器推流 | |
| 9 | +# TAG的形式就是SIM-CHANNEL,如13800138999-2 | |
| 10 | +# 如果留空将不向RTMP服务器推流 | |
| 11 | +# rtmp.url = rtmp://192.168.0.2/live/{TAG} | |
| 12 | + | |
| 13 | +# 设置为on时,控制台将输出ffmpeg的输出 | |
| 14 | +debug.mode = off | |
| 0 | 15 | \ No newline at end of file | ... | ... |
src/main/resources/application-local.yml
| ... | ... | @@ -13,7 +13,7 @@ spring: |
| 13 | 13 | # REDIS数据库配置 |
| 14 | 14 | redis: |
| 15 | 15 | # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 |
| 16 | - host: 192.168.169.124 | |
| 16 | + host: 127.0.0.1 | |
| 17 | 17 | # [必须修改] 端口号 |
| 18 | 18 | port: 6379 |
| 19 | 19 | # [可选] 数据库 DB |
| ... | ... | @@ -163,9 +163,10 @@ tuohua: |
| 163 | 163 | stopSendPort: 9102 |
| 164 | 164 | ws: ws://192.168.169.100:1091/schedule/{stream}.live.flv |
| 165 | 165 | wss: wss://192.168.169.100:443/schedule/{stream}.live.flv |
| 166 | + downloadFLV: http://192.168.169.100:1091/schedule/{stream}.live.flv | |
| 166 | 167 | get: |
| 167 | 168 | #url: http://192.169.1.92:{port}/video/{stream}.flv |
| 168 | - url: http://192.169.1.92:3333/video/{stream}.flv | |
| 169 | + url: http://192.169.1.92:3333/video/{stream} | |
| 169 | 170 | playURL: /play/wasm/ws%3A%2F%2F{ip}%3A{port}%2Fschedule%2F{sim}-{channel}.live.flv%3FcallId%{publickey} |
| 170 | 171 | |
| 171 | 172 | ... | ... |
web_src/config/index.js
web_src/src/components/DeviceList1078.vue
| ... | ... | @@ -8,7 +8,7 @@ |
| 8 | 8 | <el-container> |
| 9 | 9 | <el-header>设备列表</el-header> |
| 10 | 10 | <el-main style="background-color: #ffffff;"> |
| 11 | - <tree :nodes="nodes" @onClick="onClick" @onCheck="onCheck" @onExpand="onExpand" @onCreated="handleCreated" :props="defaultProps"/> | |
| 11 | + <tree :nodes="nodes" @onClick="onClick" @onCheck="onCheck" @onExpand="onExpand" @onRightClick="treeRightMenuFun" @onCreated="handleCreated" :props="defaultProps"/> | |
| 12 | 12 | </el-main> |
| 13 | 13 | </el-container> |
| 14 | 14 | </div> |
| ... | ... | @@ -43,6 +43,22 @@ |
| 43 | 43 | </el-container> |
| 44 | 44 | </el-container> |
| 45 | 45 | |
| 46 | + <div id="carRMenu" class="rMenu"> | |
| 47 | + <ul> | |
| 48 | + <li id="m_add" @click="OneClickPlayback();">一键播放视频</li> | |
| 49 | + <li id="m_del" @click="closeSelectCarItem();">一键关闭选中车辆流</li> | |
| 50 | + </ul> | |
| 51 | + </div> | |
| 52 | + | |
| 53 | + <div id="channelCarRMenu" class="rMenu"> | |
| 54 | + <ul> | |
| 55 | + <li id="m_add" @click="OneClickPlayback();">一键播放视频</li> | |
| 56 | + <li id="m_del" @click="closeSelectCarItem();">一键关闭选中车辆流</li> | |
| 57 | + <li id="m_del" @click="openViewDIalog();">历史数据</li> | |
| 58 | + </ul> | |
| 59 | + </div> | |
| 60 | + | |
| 61 | + | |
| 46 | 62 | |
| 47 | 63 | <el-dialog title="历史视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" |
| 48 | 64 | v-if="showVideoDialog" width="800px" :close-delay="5000" :fullscreen="true" :show-close="false"> |
| ... | ... | @@ -117,6 +133,7 @@ export default { |
| 117 | 133 | historyPlayListHtml:'', |
| 118 | 134 | hisotoryPlayFlag:false, |
| 119 | 135 | downloadURL:null, |
| 136 | + rightMenuId:null, | |
| 120 | 137 | port:-1, |
| 121 | 138 | httpPort:-1, |
| 122 | 139 | stream:"", |
| ... | ... | @@ -265,16 +282,13 @@ export default { |
| 265 | 282 | console.log(res.data.data.data); |
| 266 | 283 | |
| 267 | 284 | if(!that.isEmpty(res.data.data.data)){ |
| 268 | - console.log("============================>0"); | |
| 269 | 285 | that.downloadURL = res.data.data.data.flv; |
| 270 | - console.log("============================>1"); | |
| 271 | 286 | if (location.protocol === "https:") { |
| 272 | 287 | videoUrl = res.data.data.data.wss_flv; |
| 273 | 288 | } else { |
| 274 | 289 | videoUrl = res.data.data.data.ws_flv; |
| 275 | 290 | } |
| 276 | 291 | console.log(videoUrl); |
| 277 | - console.log("============================>"); | |
| 278 | 292 | |
| 279 | 293 | |
| 280 | 294 | itemData.playUrl = videoUrl; |
| ... | ... | @@ -697,6 +711,9 @@ export default { |
| 697 | 711 | close() { |
| 698 | 712 | let pageObj = this; |
| 699 | 713 | this.showLoading(); |
| 714 | + this.historyPlayListHtml =null; | |
| 715 | + this.startTime = null; | |
| 716 | + this.endTime = null; | |
| 700 | 717 | if(this.hisotoryPlayFlag){ |
| 701 | 718 | this.sendIORequestStop(this.sim,this.channel,function(){ |
| 702 | 719 | pageObj.showVideoDialog = false; |
| ... | ... | @@ -809,6 +826,7 @@ export default { |
| 809 | 826 | } else { |
| 810 | 827 | videoUrl1 = res.data.data.data.ws_flv; |
| 811 | 828 | } |
| 829 | + pageObj.downloadURL = res.data.data.data.flv; | |
| 812 | 830 | pageObj.port=res.data.data.port; |
| 813 | 831 | pageObj.httpPort = res.data.data.httpPort; |
| 814 | 832 | pageObj.stream = res.data.data.stream; |
| ... | ... | @@ -883,15 +901,20 @@ export default { |
| 883 | 901 | |
| 884 | 902 | this.spilt = 9; |
| 885 | 903 | |
| 886 | - for(let i=0;i<9;i++){ | |
| 887 | - let item = new Object(); | |
| 888 | - item.deviceId = this.sim; | |
| 889 | - item.channelId = 1+i; | |
| 890 | - this.playerIdx=i; | |
| 891 | - this.sendDevicePush(item); | |
| 892 | - } | |
| 893 | - | |
| 894 | - | |
| 904 | + this.playOneAllChannel(0); | |
| 905 | + }, | |
| 906 | + playOneAllChannel(channel){ | |
| 907 | + if(channel ==9){ | |
| 908 | + return; | |
| 909 | + } | |
| 910 | + let item = new Object(); | |
| 911 | + item.deviceId = this.sim; | |
| 912 | + item.channelId = 1+channel; | |
| 913 | + this.playerIdx=channel; | |
| 914 | + let that = this; | |
| 915 | + this.sendDevicePush(item,function(){ | |
| 916 | + that.playOneAllChannel(1+channel); | |
| 917 | + }); | |
| 895 | 918 | }, |
| 896 | 919 | spiltClickFun(val){ |
| 897 | 920 | this.spilt = val; |
| ... | ... | @@ -924,7 +947,49 @@ export default { |
| 924 | 947 | } |
| 925 | 948 | |
| 926 | 949 | |
| 950 | + }, | |
| 951 | + treeRightMenuFun(event,treeId,treeNode){ | |
| 952 | + if(treeNode.type == '301' || treeNode.type==301){ | |
| 953 | + this.rightMenuId = "carRMenu"; | |
| 954 | + this.showRMenu(event); | |
| 955 | + this.carTreeNode = treeNode; | |
| 956 | + this.sim = treeNode.sim; | |
| 957 | + this.channel = null; | |
| 958 | + }else if(treeNode.type == '401' || treeNode.type==401){ | |
| 959 | + this.rightMenuId ="channelCarRMenu"; | |
| 960 | + this.showRMenu(event); | |
| 961 | + this.channel = treeNode.id; | |
| 962 | + }else{ | |
| 963 | + this.carTreeNode = null; | |
| 964 | + this.sim = null; | |
| 965 | + this.channel = null; | |
| 966 | + hidden(); | |
| 967 | + } | |
| 968 | + }, | |
| 969 | + showRMenu(event) { | |
| 970 | + if(this.isEmpty(this.rightMenuId)){ | |
| 971 | + return ; | |
| 972 | + } | |
| 973 | + let carMenu = document.getElementById(this.rightMenuId); | |
| 974 | + carMenu.setAttribute("style","display:block"); | |
| 975 | + | |
| 976 | + let x = event.clientX; | |
| 977 | + let y = event.clientY; | |
| 978 | + carMenu.setAttribute("style","display:block;top:"+y+"px;left:"+x+"px"); | |
| 979 | + document.addEventListener("click", this.hideMenu); | |
| 980 | + | |
| 981 | + console.log(this.rightMenuId); | |
| 982 | + | |
| 983 | + }, | |
| 984 | + hideMenu() { | |
| 985 | + if(this.isEmpty(this.rightMenuId)){ | |
| 986 | + return ; | |
| 987 | + } | |
| 988 | + let carMenu = document.getElementById(this.rightMenuId); | |
| 989 | + carMenu.setAttribute("style","display:none"); | |
| 990 | + this.rightMenuId = null; | |
| 927 | 991 | } |
| 992 | + | |
| 928 | 993 | } |
| 929 | 994 | }; |
| 930 | 995 | |
| ... | ... | @@ -975,6 +1040,37 @@ export default { |
| 975 | 1040 | overflow-y:auto; |
| 976 | 1041 | overflow-x:hidden; |
| 977 | 1042 | } |
| 1043 | + | |
| 1044 | + | |
| 1045 | + /* 菜单的样式 */ | |
| 1046 | +.rMenu { | |
| 1047 | + position:absolute; | |
| 1048 | + top:0; | |
| 1049 | + display: none; | |
| 1050 | + margin: 0; | |
| 1051 | + padding: 0; | |
| 1052 | + text-align: left; | |
| 1053 | + border: 1px solid #BFBFBF; | |
| 1054 | + border-radius: 3px; | |
| 1055 | + background-color: #EEE; | |
| 1056 | + box-shadow: 0 0 10px #AAA; | |
| 1057 | +} | |
| 1058 | + | |
| 1059 | +.rMenu li { | |
| 1060 | + width: 170px; | |
| 1061 | + list-style: none outside none; | |
| 1062 | + cursor: default; | |
| 1063 | + color: #666; | |
| 1064 | + margin-left: -20px; | |
| 1065 | +} | |
| 1066 | +.rMenu li:hover { | |
| 1067 | + color: #EEE; | |
| 1068 | + background-color: #666; | |
| 1069 | +} | |
| 1070 | + | |
| 1071 | +li#menu-item-delete, li#menu-item-rename { | |
| 1072 | + margin-top: 1px; | |
| 1073 | +} | |
| 978 | 1074 | </style> |
| 979 | 1075 | <style> |
| 980 | 1076 | .videoList { | ... | ... |