From b391ebbb5fa8602d4629caa94343a960cb731fa6 Mon Sep 17 00:00:00 2001 From: Dennis Sheirer Date: Sun, 15 Oct 2023 08:47:42 -0400 Subject: [PATCH] #1670 Resolves missing audio segments for some DMR Cap+ call events. --- .../decode/dmr/audio/DMRAudioModule.java | 74 ++++++++----------- .../CapacityPlusVoiceChannelUser.java | 4 +- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java b/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java index 21c423dbf..691771733 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/audio/DMRAudioModule.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,7 +38,6 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.full.AbstractVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator; -import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions; import io.github.dsheirer.module.decode.dmr.message.voice.VoiceEMBMessage; import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage; import io.github.dsheirer.preference.UserPreferences; @@ -105,35 +104,48 @@ public void receive(IMessage message) { if(hasAudioCodec() && message.getTimeslot() == getTimeslot()) { - //DCDM doesn't provide FLCs or EMBs ... assume that the call is unencrypted. - if(!mEncryptedCallStateEstablished && message instanceof VoiceMessage vm && - vm.getSyncPattern().isDirectMode()) + //Attempt to set the audio encryption state from certain types of messages + if(!mEncryptedCallStateEstablished) { - mEncryptedCallStateEstablished = true; - mEncryptedCall = false; - List frames = vm.getAMBEFrames(); - for(byte[] frame: frames) + //DCDM doesn't provide FLCs or EMBs ... assume that the call is unencrypted. + if(message instanceof VoiceMessage vm && vm.getSyncPattern().isDirectMode()) { - processAudio(frame, message.getTimestamp()); + mEncryptedCallStateEstablished = true; + mEncryptedCall = false; } - } - //Both Motorola and Hytera signal their Basic Privacy (BP) scrambling in some of the Voice B-F frames - //in the EMB field. - else if(!mEncryptedCallStateEstablished && message instanceof VoiceEMBMessage voiceMessage) - { - if(voiceMessage.getEMB().isValid()) + //Both Motorola and Hytera signal their Basic Privacy (BP) scrambling in some of the Voice B-F frames + //in the EMB field. + else if(message instanceof VoiceEMBMessage voiceMessage && voiceMessage.getEMB().isValid()) { mEncryptedCallStateEstablished = true; mEncryptedCall = voiceMessage.getEMB().isEncrypted(); } + else if(message instanceof VoiceHeader voiceHeader) + { + LCMessage lc = voiceHeader.getLCMessage(); - List frames = voiceMessage.getAMBEFrames(); - for(byte[] frame: frames) + if(lc instanceof FullLCMessage flc) //We know it is, but this null checks as well + { + if(flc.isValid()) + { + mEncryptedCallStateEstablished = true; + mEncryptedCall = flc.isEncrypted(); + } + } + } + //Note: the DMRMessageProcessor extracts Full Link Control messages from Voice Frames B-C and sends them + // independent of any DMR Burst messaging. When encountered, it can be assumed that they are part of + // an ongoing call and can be used to establish encryption state when the FLC is a voice channel user. + else if(message instanceof AbstractVoiceChannelUser avcu) { - processAudio(frame, message.getTimestamp()); + mEncryptedCallStateEstablished = true; + mEncryptedCall = avcu.getServiceOptions().isEncrypted(); } } - else if(message instanceof VoiceMessage voiceMessage) + + //Queue or process audio frames. Note: audio frames are held/queued until encrypted state is established + //before any audio is generated for the audio segment. + if(message instanceof VoiceMessage voiceMessage) { List frames = voiceMessage.getAMBEFrames(); for(byte[] frame: frames) @@ -141,28 +153,6 @@ else if(message instanceof VoiceMessage voiceMessage) processAudio(frame, message.getTimestamp()); } } - else if(!mEncryptedCallStateEstablished && message instanceof VoiceHeader voiceHeader) - { - LCMessage lc = voiceHeader.getLCMessage(); - - if(lc instanceof FullLCMessage flc) //We know it is, but this null checks as well - { - if(flc.isValid()) - { - mEncryptedCallStateEstablished = true; - mEncryptedCall = flc.isEncrypted(); - } - } - } - //Note: the DMRMessageProcess extracts Full Link Control messages from Voice Frames B-C and sends them - // independent of any DMR Burst messaging. When encountered, it can be assumed that they are part of - // an ongoing call and can be used to establish encryption state when the FLC is a voice channel user. - else if(!mEncryptedCallStateEstablished && message instanceof AbstractVoiceChannelUser avcu) - { - ServiceOptions serviceOptions = avcu.getServiceOptions(); - mEncryptedCallStateEstablished = true; - mEncryptedCall = serviceOptions.isEncrypted(); - } else if(message instanceof Terminator) { reset(); diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java index 2a8da6c51..b6641a27e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/full/motorola/CapacityPlusVoiceChannelUser.java @@ -21,13 +21,13 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage; import io.github.dsheirer.module.decode.dmr.message.IServiceOptionsProvider; -import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage; +import io.github.dsheirer.module.decode.dmr.message.data.lc.full.AbstractVoiceChannelUser; import io.github.dsheirer.module.decode.dmr.message.type.CapacityPlusServiceOptions; /** * Any Capacity Plus Voice Channel User link control message that contains vendor-specific service options. */ -public abstract class CapacityPlusVoiceChannelUser extends FullLCMessage implements IServiceOptionsProvider +public abstract class CapacityPlusVoiceChannelUser extends AbstractVoiceChannelUser implements IServiceOptionsProvider { private static final int[] SERVICE_OPTIONS = new int[]{16, 17, 18, 19, 20, 21, 22, 23}; //Reed Solomon FEC: 72-95