Commit 4bfd97b73e4e3819a1884ff0218518b4a81f7597

Authored by liujun001
1 parent f82dd1bd

优化

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