mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 23:57:25 -05:00
515 lines
22 KiB
Java
515 lines
22 KiB
Java
/*******************************************************************************
|
|
* sdrtrunk
|
|
* Copyright (C) 2014-2017 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
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
******************************************************************************/
|
|
package module.decode;
|
|
|
|
import alias.AliasList;
|
|
import alias.AliasModel;
|
|
import audio.AudioModule;
|
|
import channel.metadata.Metadata;
|
|
import channel.state.AlwaysUnsquelchedDecoderState;
|
|
import channel.traffic.TrafficChannelManager;
|
|
import controller.channel.Channel;
|
|
import controller.channel.Channel.ChannelType;
|
|
import controller.channel.ChannelModel;
|
|
import controller.channel.ChannelProcessingManager;
|
|
import controller.channel.map.ChannelMap;
|
|
import controller.channel.map.ChannelMapModel;
|
|
import dsp.filter.FilterFactory;
|
|
import dsp.filter.fir.FIRFilterSpecification;
|
|
import filter.AllPassFilter;
|
|
import filter.FilterSet;
|
|
import filter.IFilter;
|
|
import gui.editor.EmptyValidatingEditor;
|
|
import gui.editor.ValidatingEditor;
|
|
import message.Message;
|
|
import message.MessageDirection;
|
|
import module.Module;
|
|
import module.decode.am.AMDecoder;
|
|
import module.decode.am.AMDecoderEditor;
|
|
import module.decode.am.DecodeConfigAM;
|
|
import module.decode.config.AuxDecodeConfiguration;
|
|
import module.decode.config.DecodeConfiguration;
|
|
import module.decode.fleetsync2.Fleetsync2Decoder;
|
|
import module.decode.fleetsync2.Fleetsync2DecoderState;
|
|
import module.decode.fleetsync2.FleetsyncMessageFilter;
|
|
import module.decode.lj1200.LJ1200Decoder;
|
|
import module.decode.lj1200.LJ1200DecoderState;
|
|
import module.decode.lj1200.LJ1200MessageFilter;
|
|
import module.decode.ltrnet.DecodeConfigLTRNet;
|
|
import module.decode.ltrnet.LTRNetDecoder;
|
|
import module.decode.ltrnet.LTRNetDecoderEditor;
|
|
import module.decode.ltrnet.LTRNetDecoderState;
|
|
import module.decode.ltrnet.LTRNetMessageFilter;
|
|
import module.decode.ltrstandard.DecodeConfigLTRStandard;
|
|
import module.decode.ltrstandard.LTRStandardDecoder;
|
|
import module.decode.ltrstandard.LTRStandardDecoderEditor;
|
|
import module.decode.ltrstandard.LTRStandardDecoderState;
|
|
import module.decode.ltrstandard.LTRStandardMessageFilter;
|
|
import module.decode.mdc1200.MDCDecoder;
|
|
import module.decode.mdc1200.MDCDecoderState;
|
|
import module.decode.mdc1200.MDCMessageFilter;
|
|
import module.decode.mpt1327.DecodeConfigMPT1327;
|
|
import module.decode.mpt1327.MPT1327Decoder;
|
|
import module.decode.mpt1327.MPT1327Decoder.Sync;
|
|
import module.decode.mpt1327.MPT1327DecoderEditor;
|
|
import module.decode.mpt1327.MPT1327DecoderState;
|
|
import module.decode.mpt1327.MPT1327MessageFilter;
|
|
import module.decode.nbfm.DecodeConfigNBFM;
|
|
import module.decode.nbfm.NBFMDecoder;
|
|
import module.decode.nbfm.NBFMDecoderEditor;
|
|
import module.decode.p25.DecodeConfigP25Phase1;
|
|
import module.decode.p25.P25Decoder.Modulation;
|
|
import module.decode.p25.P25DecoderEditor;
|
|
import module.decode.p25.P25DecoderState;
|
|
import module.decode.p25.P25_C4FMDecoder;
|
|
import module.decode.p25.P25_LSMDecoder;
|
|
import module.decode.p25.audio.P25AudioModule;
|
|
import module.decode.p25.message.filter.P25MessageFilterSet;
|
|
import module.decode.passport.DecodeConfigPassport;
|
|
import module.decode.passport.PassportDecoder;
|
|
import module.decode.passport.PassportDecoderEditor;
|
|
import module.decode.passport.PassportDecoderState;
|
|
import module.decode.passport.PassportMessageFilter;
|
|
import module.decode.tait.Tait1200Decoder;
|
|
import module.decode.tait.Tait1200DecoderState;
|
|
import module.demodulate.am.AMDemodulatorModule;
|
|
import module.demodulate.audio.DemodulatedAudioFilterModule;
|
|
import module.demodulate.fm.FMDemodulatorModule;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class DecoderFactory
|
|
{
|
|
private final static Logger mLog = LoggerFactory.getLogger(DecoderFactory.class);
|
|
|
|
//Low-pass filter with ~60 dB attenuation and 89 taps
|
|
private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
|
.sampleRate( 48000 ).gridDensity( 16 ).passBandCutoff( 3200 ).passBandAmplitude( 1.0 ).passBandRipple( 0.02 )
|
|
.stopBandStart( 4000 ).stopBandAmplitude( 0.0 ).stopBandRipple( 0.03 ).build();
|
|
|
|
private static final FIRFilterSpecification P25_C4FM_IQ_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
|
.sampleRate(48000).gridDensity(16).passBandCutoff(6750).passBandAmplitude(1.0).passBandRipple(0.01)
|
|
.stopBandStart(7000).stopBandAmplitude(0.0).stopBandRipple(0.008).build();
|
|
|
|
private static final FIRFilterSpecification P25_C4FM_DEMOD_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
|
.sampleRate(48000).gridDensity(16).passBandCutoff(2500).passBandAmplitude(1.0).passBandRipple(0.01)
|
|
.stopBandStart(4000).stopBandAmplitude(0.0).stopBandRipple(0.008).build();
|
|
|
|
private static float[] MPT1327_LOWPASS_FILTER;
|
|
private static float[] P25_C4FM_IQ_FILTER;
|
|
private static float[] P25_C4FM_DEMOD_FILTER;
|
|
|
|
static
|
|
{
|
|
try
|
|
{
|
|
MPT1327_LOWPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION);
|
|
P25_C4FM_IQ_FILTER = FilterFactory.getTaps(P25_C4FM_IQ_SPECIFICATION);
|
|
P25_C4FM_DEMOD_FILTER = FilterFactory.getTaps(P25_C4FM_DEMOD_SPECIFICATION);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
mLog.error("Couldn't design startup filter(s)");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a list of one primary decoder and any auxiliary decoders, as
|
|
* specified in the configurations.
|
|
*
|
|
* @return list of configured decoders
|
|
*/
|
|
public static List<Module> getModules(ChannelModel channelModel,
|
|
ChannelMapModel channelMapModel,
|
|
ChannelProcessingManager channelProcessingManager,
|
|
AliasModel aliasModel,
|
|
Channel channel,
|
|
Metadata metadata)
|
|
{
|
|
|
|
/* Get the optional alias list for the decode modules to use */
|
|
AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName());
|
|
|
|
List<Module> modules = getPrimaryModules(channelModel, channelMapModel, channelProcessingManager, aliasList,
|
|
channel, metadata);
|
|
|
|
modules.addAll(getAuxiliaryDecoders(channel.getAuxDecodeConfiguration(), aliasList));
|
|
|
|
return modules;
|
|
}
|
|
|
|
/**
|
|
* Constructs a primary decoder as specified in the decode configuration
|
|
*/
|
|
public static List<Module> getPrimaryModules(ChannelModel channelModel,
|
|
ChannelMapModel channelMapModel,
|
|
ChannelProcessingManager channelProcessingManager,
|
|
AliasList aliasList,
|
|
Channel channel,
|
|
Metadata metadata)
|
|
{
|
|
List<Module> modules = new ArrayList<Module>();
|
|
|
|
ChannelType channelType = channel.getChannelType();
|
|
|
|
/* Baseband low-pass filter pass and stop frequencies */
|
|
DecodeConfiguration decodeConfig = channel.getDecodeConfiguration();
|
|
|
|
int iqPass = decodeConfig.getDecoderType().getChannelBandwidth() / 2;
|
|
int iqStop = iqPass + 1250;
|
|
|
|
switch(decodeConfig.getDecoderType())
|
|
{
|
|
case AM:
|
|
modules.add(new AMDecoder(decodeConfig));
|
|
modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.AM, channel.getName()));
|
|
modules.add(new AMDemodulatorModule());
|
|
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
|
|
modules.add(new AudioModule(metadata));
|
|
break;
|
|
case NBFM:
|
|
modules.add(new NBFMDecoder(decodeConfig));
|
|
modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.NBFM, channel.getName()));
|
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
|
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
|
|
modules.add(new AudioModule(metadata));
|
|
break;
|
|
case LTR_STANDARD:
|
|
MessageDirection direction = ((DecodeConfigLTRStandard) decodeConfig).getMessageDirection();
|
|
modules.add(new LTRStandardDecoder(aliasList, direction));
|
|
modules.add(new LTRStandardDecoderState(aliasList));
|
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
|
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
|
|
modules.add(new AudioModule(metadata));
|
|
break;
|
|
case LTR_NET:
|
|
modules.add(new LTRNetDecoder((DecodeConfigLTRNet) decodeConfig, aliasList));
|
|
modules.add(new LTRNetDecoderState(aliasList));
|
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
|
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
|
|
modules.add(new AudioModule(metadata));
|
|
break;
|
|
case MPT1327:
|
|
DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327) decodeConfig;
|
|
|
|
ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName());
|
|
|
|
Sync sync = mptConfig.getSync();
|
|
|
|
modules.add(new MPT1327Decoder(aliasList, sync));
|
|
|
|
|
|
modules.add(new MPT1327DecoderState(aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000));
|
|
|
|
if(channelType == ChannelType.STANDARD)
|
|
{
|
|
modules.add(new TrafficChannelManager(channelModel, decodeConfig,
|
|
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
|
|
(aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize()));
|
|
}
|
|
|
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
|
modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
|
|
modules.add(new AudioModule(metadata));
|
|
break;
|
|
case PASSPORT:
|
|
modules.add(new PassportDecoder(decodeConfig, aliasList));
|
|
modules.add(new PassportDecoderState(aliasList));
|
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
|
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
|
|
modules.add(new AudioModule(metadata));
|
|
break;
|
|
case P25_PHASE1:
|
|
DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1) decodeConfig;
|
|
|
|
Modulation modulation = p25Config.getModulation();
|
|
|
|
|
|
switch(modulation)
|
|
{
|
|
case C4FM:
|
|
modules.add(new FMDemodulatorModule(P25_C4FM_IQ_FILTER));
|
|
modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
|
|
modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
|
|
modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
|
|
p25Config.getIgnoreDataCalls()));
|
|
break;
|
|
case CQPSK:
|
|
modules.add(new P25_LSMDecoder(aliasList));
|
|
modules.add(new P25DecoderState(aliasList, channelType, Modulation.CQPSK, p25Config.getIgnoreDataCalls()));
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + modulation + "]");
|
|
}
|
|
|
|
if(channelType == ChannelType.STANDARD)
|
|
{
|
|
modules.add(new TrafficChannelManager(channelModel, decodeConfig,
|
|
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
|
|
(aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize()));
|
|
}
|
|
|
|
modules.add(new P25AudioModule(metadata));
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Unknown decoder type [" + decodeConfig.getDecoderType().toString() + "]");
|
|
}
|
|
|
|
return modules;
|
|
}
|
|
|
|
/**
|
|
* Constructs a list of auxiliary decoders, as specified in the configuration
|
|
*
|
|
* @param config - auxiliary configuration
|
|
* @param aliasList - optional alias list
|
|
* @return - list of auxiliary decoders
|
|
*/
|
|
public static List<Module> getAuxiliaryDecoders(AuxDecodeConfiguration config,
|
|
AliasList aliasList)
|
|
{
|
|
List<Module> modules = new ArrayList<>();
|
|
|
|
if(config != null)
|
|
{
|
|
for(DecoderType auxDecoder : config.getAuxDecoders())
|
|
{
|
|
switch(auxDecoder)
|
|
{
|
|
case FLEETSYNC2:
|
|
modules.add(new Fleetsync2Decoder(aliasList));
|
|
modules.add(new Fleetsync2DecoderState(aliasList));
|
|
break;
|
|
case MDC1200:
|
|
modules.add(new MDCDecoder(aliasList));
|
|
modules.add(new MDCDecoderState(aliasList));
|
|
break;
|
|
case LJ_1200:
|
|
modules.add(new LJ1200Decoder(aliasList));
|
|
modules.add(new LJ1200DecoderState(aliasList));
|
|
break;
|
|
case TAIT_1200:
|
|
modules.add(new Tait1200Decoder(aliasList));
|
|
modules.add(new Tait1200DecoderState(aliasList));
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Unrecognized auxiliary "
|
|
+ "decoder type [" + auxDecoder + "]");
|
|
}
|
|
}
|
|
}
|
|
|
|
return modules;
|
|
}
|
|
|
|
/**
|
|
* Assembles a filter set containing filters for the primary channel
|
|
* decoder and each of the auxiliary decoders
|
|
*/
|
|
public static FilterSet<Message> getMessageFilters(List<Module> modules)
|
|
{
|
|
FilterSet<Message> filterSet = new FilterSet<>();
|
|
|
|
for(Module module : modules)
|
|
{
|
|
if(module instanceof Decoder)
|
|
{
|
|
filterSet.addFilters(getMessageFilter(
|
|
((Decoder) module).getDecoderType()));
|
|
}
|
|
}
|
|
|
|
/* If we don't have any filters, add an ALL-PASS filter */
|
|
if(filterSet.getFilters().isEmpty())
|
|
{
|
|
filterSet.addFilter(new AllPassFilter<Message>());
|
|
}
|
|
|
|
return filterSet;
|
|
}
|
|
|
|
/**
|
|
* Returns a set of IMessageFilter objects (FilterSets or Filters) that
|
|
* can process all of the messages produced by the specified decoder type.
|
|
*/
|
|
public static List<IFilter<Message>> getMessageFilter(DecoderType decoder)
|
|
{
|
|
ArrayList<IFilter<Message>> filters = new ArrayList<IFilter<Message>>();
|
|
|
|
switch(decoder)
|
|
{
|
|
case FLEETSYNC2:
|
|
filters.add(new FleetsyncMessageFilter());
|
|
break;
|
|
case LJ_1200:
|
|
filters.add(new LJ1200MessageFilter());
|
|
break;
|
|
case LTR_NET:
|
|
filters.add(new LTRNetMessageFilter());
|
|
break;
|
|
case LTR_STANDARD:
|
|
filters.add(new LTRStandardMessageFilter());
|
|
break;
|
|
case MDC1200:
|
|
filters.add(new MDCMessageFilter());
|
|
break;
|
|
case MPT1327:
|
|
filters.add(new MPT1327MessageFilter());
|
|
break;
|
|
case P25_PHASE1:
|
|
case P25_PHASE2:
|
|
filters.add(new P25MessageFilterSet());
|
|
break;
|
|
case PASSPORT:
|
|
filters.add(new PassportMessageFilter());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return filters;
|
|
}
|
|
|
|
public static DecodeConfiguration getDefaultDecodeConfiguration()
|
|
{
|
|
return getDecodeConfiguration(DecoderType.NBFM);
|
|
}
|
|
|
|
public static DecodeConfiguration getDecodeConfiguration(DecoderType decoder)
|
|
{
|
|
DecodeConfiguration retVal;
|
|
|
|
switch(decoder)
|
|
{
|
|
case AM:
|
|
return new DecodeConfigAM();
|
|
case LTR_NET:
|
|
return new DecodeConfigLTRNet();
|
|
case LTR_STANDARD:
|
|
return new DecodeConfigLTRStandard();
|
|
case MPT1327:
|
|
return new DecodeConfigMPT1327();
|
|
case NBFM:
|
|
return new DecodeConfigNBFM();
|
|
case PASSPORT:
|
|
return new DecodeConfigPassport();
|
|
case P25_PHASE1:
|
|
return new DecodeConfigP25Phase1();
|
|
default:
|
|
throw new IllegalArgumentException("DecodeConfigFactory - unknown decoder type [" + decoder.toString() + "]");
|
|
}
|
|
}
|
|
|
|
public static ValidatingEditor<Channel> getEditor(DecoderType type, ChannelMapModel model)
|
|
{
|
|
switch(type)
|
|
{
|
|
case AM:
|
|
return new AMDecoderEditor();
|
|
case LTR_NET:
|
|
return new LTRNetDecoderEditor();
|
|
case LTR_STANDARD:
|
|
return new LTRStandardDecoderEditor();
|
|
case MPT1327:
|
|
return new MPT1327DecoderEditor(model);
|
|
case NBFM:
|
|
return new NBFMDecoderEditor();
|
|
case P25_PHASE1:
|
|
return new P25DecoderEditor();
|
|
case PASSPORT:
|
|
return new PassportDecoderEditor();
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return new EmptyValidatingEditor<Channel>("a decoder");
|
|
}
|
|
|
|
/**
|
|
* Creates a copy of the configuration
|
|
*/
|
|
public static DecodeConfiguration copy(DecodeConfiguration config)
|
|
{
|
|
if(config != null)
|
|
{
|
|
switch(config.getDecoderType())
|
|
{
|
|
case AM:
|
|
DecodeConfigAM originalAM = (DecodeConfigAM) config;
|
|
DecodeConfigAM copyAM = new DecodeConfigAM();
|
|
copyAM.setAFC(originalAM.getAFC());
|
|
copyAM.setAFCMaximumCorrection(originalAM.getAFCMaximumCorrection());
|
|
return copyAM;
|
|
case LTR_NET:
|
|
DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet) config;
|
|
DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet();
|
|
copyLTRNet.setAFC(originalLTRNet.getAFC());
|
|
copyLTRNet.setAFCMaximumCorrection(originalLTRNet.getAFCMaximumCorrection());
|
|
copyLTRNet.setMessageDirection(originalLTRNet.getMessageDirection());
|
|
return copyLTRNet;
|
|
case LTR_STANDARD:
|
|
DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard) config;
|
|
DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard();
|
|
copyLTRStandard.setAFC(originalLTRStandard.getAFC());
|
|
copyLTRStandard.setAFCMaximumCorrection(originalLTRStandard.getAFCMaximumCorrection());
|
|
copyLTRStandard.setMessageDirection(originalLTRStandard.getMessageDirection());
|
|
return copyLTRStandard;
|
|
case MPT1327:
|
|
DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327) config;
|
|
DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327();
|
|
copyMPT.setAFC(originalMPT.getAFC());
|
|
copyMPT.setAFCMaximumCorrection(originalMPT.getAFCMaximumCorrection());
|
|
copyMPT.setCallTimeout(originalMPT.getCallTimeout());
|
|
copyMPT.setChannelMapName(originalMPT.getChannelMapName());
|
|
copyMPT.setSync(originalMPT.getSync());
|
|
copyMPT.setTrafficChannelPoolSize(originalMPT.getTrafficChannelPoolSize());
|
|
return copyMPT;
|
|
case NBFM:
|
|
DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM) config;
|
|
DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM();
|
|
copyNBFM.setAFC(originalNBFM.getAFC());
|
|
copyNBFM.setAFCMaximumCorrection(originalNBFM.getAFCMaximumCorrection());
|
|
return copyNBFM;
|
|
case P25_PHASE1:
|
|
DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1) config;
|
|
DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1();
|
|
copyP25.setAFC(originalP25.getAFC());
|
|
copyP25.setAFCMaximumCorrection(originalP25.getAFCMaximumCorrection());
|
|
copyP25.setIgnoreDataCalls(originalP25.getIgnoreDataCalls());
|
|
copyP25.setModulation(originalP25.getModulation());
|
|
copyP25.setTrafficChannelPoolSize(originalP25.getTrafficChannelPoolSize());
|
|
return copyP25;
|
|
case PASSPORT:
|
|
DecodeConfigPassport originalPass = (DecodeConfigPassport) config;
|
|
DecodeConfigPassport copyPass = new DecodeConfigPassport();
|
|
copyPass.setAFC(originalPass.getAFC());
|
|
copyPass.setAFCMaximumCorrection(originalPass.getAFCMaximumCorrection());
|
|
return copyPass;
|
|
default:
|
|
throw new IllegalArgumentException("Unrecognized decoder configuration type:" + config.getDecoderType());
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|