diff --git a/src/main/java/org/traccar/media/VideoStreamManager.java b/src/main/java/org/traccar/media/VideoStreamManager.java index 2ef3a3b6d..5f73dadeb 100644 --- a/src/main/java/org/traccar/media/VideoStreamManager.java +++ b/src/main/java/org/traccar/media/VideoStreamManager.java @@ -58,6 +58,7 @@ public class VideoStreamManager { private final LinkedHashMap segments = new LinkedHashMap<>(); private ByteBuf currentSegment; private int segmentIndex; + private long firstTimestamp; synchronized void addFrame(ByteBuf nalData, long timestamp, boolean isKeyFrame) { if (isKeyFrame && currentSegment != null) { @@ -66,9 +67,12 @@ public class VideoStreamManager { if (currentSegment == null) { currentSegment = Unpooled.buffer(); + if (firstTimestamp == 0) { + firstTimestamp = timestamp; + } } - writer.write(currentSegment, nalData, timestamp, isKeyFrame); + writer.write(currentSegment, nalData, timestamp - firstTimestamp, isKeyFrame); } private void finalizeSegment() { diff --git a/src/main/java/org/traccar/media/VideoStreamWriter.java b/src/main/java/org/traccar/media/VideoStreamWriter.java index cac7b24ad..850cddc35 100644 --- a/src/main/java/org/traccar/media/VideoStreamWriter.java +++ b/src/main/java/org/traccar/media/VideoStreamWriter.java @@ -36,8 +36,9 @@ public class VideoStreamWriter { writePmt(output); } - ByteBuf pesPacket = createPes(nalData, pts); - writePesPackets(output, pesPacket, isKeyFrame, pts * 90); + long pts90k = pts * 90; + ByteBuf pesPacket = createPes(nalData, pts90k); + writePesPackets(output, pesPacket, isKeyFrame, pts90k); pesPacket.release(); } @@ -113,8 +114,7 @@ public class VideoStreamWriter { } } - private ByteBuf createPes(ByteBuf nalData, long pts) { - long pts90k = pts * 90; // convert ms to 90kHz + private ByteBuf createPes(ByteBuf nalData, long pts90k) { ByteBuf pes = Unpooled.buffer(); diff --git a/src/main/java/org/traccar/protocol/Jt1078ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt1078ProtocolDecoder.java index 4093f90f0..ad46e9856 100644 --- a/src/main/java/org/traccar/protocol/Jt1078ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Jt1078ProtocolDecoder.java @@ -16,6 +16,8 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Protocol; @@ -31,6 +33,10 @@ public class Jt1078ProtocolDecoder extends BaseProtocolDecoder { private DeviceLookupService deviceLookupService; private VideoStreamManager streamManager; + private CompositeByteBuf frameBuffer; + private int frameDataType; + private long frameTimestamp; + public Jt1078ProtocolDecoder(Protocol protocol) { super(protocol); } @@ -58,7 +64,9 @@ public class Jt1078ProtocolDecoder extends BaseProtocolDecoder { String uniqueId = HuabaoProtocolDecoder.decodeId(buf.readSlice(6)); int logicalChannel = buf.readUnsignedByte(); - int dataType = BitUtil.from(buf.readUnsignedByte(), 4); + int rawType = buf.readUnsignedByte(); + int dataType = BitUtil.from(rawType, 4); + int subpackageType = BitUtil.to(rawType, 4); long timestamp = buf.readLong(); if (dataType <= 2) { @@ -68,16 +76,48 @@ public class Jt1078ProtocolDecoder extends BaseProtocolDecoder { int bodyLength = buf.readUnsignedShort(); if (bodyLength == 0 || dataType > 2) { - return null; // skip audio and transparent data + return null; } - boolean isKeyFrame = dataType == 0; // i-frame - if (deviceLookupService.lookup(new String[]{uniqueId}) == null) { return null; } - streamManager.handleFrame(uniqueId, logicalChannel, buf.readSlice(bodyLength), timestamp, isKeyFrame); + ByteBuf body = buf.readRetainedSlice(bodyLength); + + if (subpackageType == 0) { + // atomic packet - complete frame + boolean isKeyFrame = dataType == 0; + streamManager.handleFrame(uniqueId, logicalChannel, body, timestamp, isKeyFrame); + body.release(); + } else if (subpackageType == 1) { + // first subpackage + if (frameBuffer != null) { + frameBuffer.release(); + } + frameBuffer = Unpooled.compositeBuffer(); + frameBuffer.addComponent(true, body); + frameDataType = dataType; + frameTimestamp = timestamp; + } else if (subpackageType == 3) { + // middle subpackage + if (frameBuffer != null) { + frameBuffer.addComponent(true, body); + } else { + body.release(); + } + } else if (subpackageType == 2) { + // last subpackage + if (frameBuffer != null) { + frameBuffer.addComponent(true, body); + boolean isKeyFrame = frameDataType == 0; + streamManager.handleFrame(uniqueId, logicalChannel, frameBuffer, frameTimestamp, isKeyFrame); + frameBuffer.release(); + frameBuffer = null; + } else { + body.release(); + } + } return null; }