mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#30 WIP - adds stream manager to receive multiple audio stream's packets and convert the audio and persist to disk. Broadcaster will now receive converted audio files to stream out to the server.
This commit is contained in:
@@ -35,6 +35,10 @@ import gui.editor.Editor;
|
||||
import gui.editor.EmptyEditor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.AudioRecorder;
|
||||
import record.mp3.MP3Recorder;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class BroadcastFactory
|
||||
{
|
||||
@@ -157,4 +161,19 @@ public class BroadcastFactory
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an audio recorder for the specified broadcast format using the specified path output file name
|
||||
*/
|
||||
public static AudioRecorder getAudioRecorder(Path path, BroadcastFormat broadcastFormat)
|
||||
{
|
||||
switch (broadcastFormat)
|
||||
{
|
||||
case MP3:
|
||||
return new MP3Recorder(path);
|
||||
default:
|
||||
mLog.debug("Unrecognized broadcast format [" + broadcastFormat + "] cannot create audio recorder");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@ package audio.broadcast;
|
||||
|
||||
public enum BroadcastFormat
|
||||
{
|
||||
MP3("audio/mpeg");
|
||||
MP3("audio/mpeg", ".mp3");
|
||||
|
||||
private String mValue;
|
||||
private String mFileExtension;
|
||||
|
||||
private BroadcastFormat(String value)
|
||||
private BroadcastFormat(String value, String fileExtension)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
@@ -33,4 +34,9 @@ public enum BroadcastFormat
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public String getFileExtension()
|
||||
{
|
||||
return mFileExtension;
|
||||
}
|
||||
}
|
||||
|
||||
253
src/audio/broadcast/StreamManager.java
Normal file
253
src/audio/broadcast/StreamManager.java
Normal file
@@ -0,0 +1,253 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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 audio.broadcast;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.AudioRecorder;
|
||||
import sample.Listener;
|
||||
import util.TimeStamp;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StreamManager implements Listener<AudioPacket>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
|
||||
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private BroadcastFormat mBroadcastFormat;
|
||||
private Listener<StreamableAudioRecording> mOutputListener;
|
||||
private long mDelay;
|
||||
private Path mTempDirectory;
|
||||
private Map<Integer, AudioRecorder> mStreamRecorders = new HashMap<>();
|
||||
private Runnable mRecorderMonitor;
|
||||
private ScheduledFuture<?> mRecorderMonitorFuture;
|
||||
|
||||
/**
|
||||
* Stream manager processes all incoming audio packets and reassembles individual audio streams, converts audio
|
||||
* to desired output format and persists each stream to disc. Each recording is capped at a maximum length to
|
||||
* ensure that recordings don't run too long before they are streamed out and to ensure that inactive recordings
|
||||
* are closed in a timely fashion.
|
||||
*
|
||||
* Completed streamable audio recordings are nominated to the output listener for streaming once a specified delay
|
||||
* has elapsed.
|
||||
*
|
||||
* @param threadPoolManager for scheduling runnables
|
||||
* @param broadcastFormat defines the output recording format
|
||||
* @param outputListener to receive completed streamable audio recordings
|
||||
* @param delay from recording start until audio recording is nominated to output listener
|
||||
* @param tempDirectory where to store temporary audio recordings
|
||||
*/
|
||||
public StreamManager(ThreadPoolManager threadPoolManager,
|
||||
BroadcastFormat broadcastFormat,
|
||||
Listener<StreamableAudioRecording> outputListener,
|
||||
long delay,
|
||||
Path tempDirectory)
|
||||
{
|
||||
assert(tempDirectory != null && Files.isDirectory(tempDirectory));
|
||||
assert(mThreadPoolManager != null);
|
||||
assert(mBroadcastFormat != null);
|
||||
|
||||
mThreadPoolManager = threadPoolManager;
|
||||
mBroadcastFormat = broadcastFormat;
|
||||
mOutputListener = outputListener;
|
||||
mDelay = delay;
|
||||
mTempDirectory = tempDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the stream manager. Schedules a processor to monitor for inactive temporary stream recorders
|
||||
* every 2 seconds.
|
||||
*/
|
||||
public void start()
|
||||
{
|
||||
if(mRecorderMonitorFuture == null)
|
||||
{
|
||||
if(mRecorderMonitor == null)
|
||||
{
|
||||
mRecorderMonitor = new RecorderMonitor();
|
||||
}
|
||||
|
||||
mRecorderMonitorFuture = mThreadPoolManager
|
||||
.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING, mRecorderMonitor, 2,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the stream manager. Stops the inactive temporary stream recorder monitoring thread.
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
if(mRecorderMonitorFuture != null)
|
||||
{
|
||||
mRecorderMonitorFuture.cancel(true);
|
||||
}
|
||||
|
||||
synchronized (mStreamRecorders)
|
||||
{
|
||||
mStreamRecorders.entrySet().stream().forEach(entry ->
|
||||
{
|
||||
removeRecorder(entry.getKey());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioPacket audioPacket)
|
||||
{
|
||||
if(audioPacket.hasAudioMetadata())
|
||||
{
|
||||
synchronized (mStreamRecorders)
|
||||
{
|
||||
int sourceChannelID = audioPacket.getAudioMetadata().getSource();
|
||||
|
||||
AudioPacket.Type type = audioPacket.getType();
|
||||
|
||||
if(type == AudioPacket.Type.AUDIO)
|
||||
{
|
||||
if(mStreamRecorders.containsKey(sourceChannelID))
|
||||
{
|
||||
AudioRecorder recorder = mStreamRecorders.get(sourceChannelID);
|
||||
|
||||
if(recorder != null)
|
||||
{
|
||||
recorder.receive(audioPacket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
||||
mBroadcastFormat);
|
||||
recorder.start(mThreadPoolManager.getScheduledExecutorService());
|
||||
recorder.receive(audioPacket);
|
||||
mStreamRecorders.put(sourceChannelID, recorder);
|
||||
}
|
||||
}
|
||||
else if(type == AudioPacket.Type.END)
|
||||
{
|
||||
removeRecorder(sourceChannelID);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info("Unrecognized Audio Packet Type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the recorder associated with the source channel ID.
|
||||
*
|
||||
* Note: this method invocation is not thread safe and must be invoked by a thread safe mechanism that protects the
|
||||
* mStreamRecorders map.
|
||||
*
|
||||
* @param sourceChannelID identifying the recorder
|
||||
*/
|
||||
private void removeRecorder(Integer sourceChannelID)
|
||||
{
|
||||
if(mStreamRecorders.containsKey(sourceChannelID))
|
||||
{
|
||||
AudioRecorder recorder = mStreamRecorders.remove(sourceChannelID);
|
||||
recorder.stop();
|
||||
|
||||
StreamableAudioRecording streamableAudioRecording = new StreamableAudioRecording(recorder.getPath(),
|
||||
recorder.getMetadata());
|
||||
|
||||
if(mDelay <= 0)
|
||||
{
|
||||
mOutputListener.receive(streamableAudioRecording);
|
||||
}
|
||||
else
|
||||
{
|
||||
long waitTimeMillis = mDelay - (System.currentTimeMillis() - recorder.getTimeRecordingStart());
|
||||
|
||||
mThreadPoolManager.scheduleOnce(new RecordingDispatcher(streamableAudioRecording),
|
||||
waitTimeMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary streaming recording file path
|
||||
*/
|
||||
private Path getTemporaryRecordingPath()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("temporary_streaming_file_");
|
||||
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
|
||||
sb.append( mBroadcastFormat.getFileExtension() );
|
||||
|
||||
Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString());
|
||||
|
||||
return temporaryRecordingPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the streamable audio recording to the output listener.
|
||||
*/
|
||||
public class RecordingDispatcher implements Runnable
|
||||
{
|
||||
StreamableAudioRecording mStreamableAudioRecording;
|
||||
|
||||
public RecordingDispatcher(StreamableAudioRecording recording)
|
||||
{
|
||||
mStreamableAudioRecording = recording;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(mOutputListener != null)
|
||||
{
|
||||
mOutputListener.receive(mStreamableAudioRecording);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors recorders to ensure they don't exceed the maximum life-span allowed for the recording.
|
||||
*/
|
||||
public class RecorderMonitor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
synchronized (mStreamRecorders)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
mStreamRecorders.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().getTimeRecordingStart() + MAXIMUM_RECORDER_LIFESPAN_MILLIS < now)
|
||||
.forEach(entry ->
|
||||
{
|
||||
removeRecorder(entry.getKey());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/audio/broadcast/StreamableAudioRecording.java
Normal file
51
src/audio/broadcast/StreamableAudioRecording.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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 audio.broadcast;
|
||||
|
||||
import audio.metadata.AudioMetadata;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class StreamableAudioRecording
|
||||
{
|
||||
private Path mPath;
|
||||
private AudioMetadata mAudioMetadata;
|
||||
|
||||
public StreamableAudioRecording(Path path, AudioMetadata audioMetadata)
|
||||
{
|
||||
mPath = path;
|
||||
mAudioMetadata = audioMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to the completed audio recording
|
||||
*/
|
||||
public Path getPath()
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional audio metadata for the recording.
|
||||
*/
|
||||
public AudioMetadata getAudioMetadata()
|
||||
{
|
||||
return mAudioMetadata;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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/>
|
||||
******************************************************************************/
|
||||
@@ -32,288 +32,305 @@ import alias.id.priority.Priority;
|
||||
|
||||
public class AudioMetadata implements Listener<Metadata>
|
||||
{
|
||||
protected static final Logger mLog = LoggerFactory.getLogger( AudioMetadata.class );
|
||||
protected static final Logger mLog = LoggerFactory.getLogger(AudioMetadata.class);
|
||||
|
||||
private int mSource;
|
||||
private Map<MetadataType,Metadata> mMetadata = new HashMap<>();
|
||||
private boolean mUpdated = false;
|
||||
private int mSource;
|
||||
private Map<MetadataType, Metadata> mMetadata = new HashMap<>();
|
||||
private boolean mUpdated = false;
|
||||
|
||||
/* Channel Selected == highest priority */
|
||||
private boolean mSelected = false;
|
||||
/* Channel Selected == highest priority */
|
||||
private boolean mSelected = false;
|
||||
|
||||
/* Call Priority */
|
||||
private int mPriority = Priority.DEFAULT_PRIORITY;
|
||||
/* Call Priority */
|
||||
private int mPriority = Priority.DEFAULT_PRIORITY;
|
||||
|
||||
/* Indicates if the overall source is recordable */
|
||||
private boolean mSourceRecordable = false;
|
||||
|
||||
/* Indicates the current recordable state */
|
||||
private boolean mRecordable = false;
|
||||
|
||||
/* A unique identifier for this metadata */
|
||||
private String mIdentifier;
|
||||
|
||||
public AudioMetadata( int source, boolean recordable )
|
||||
{
|
||||
mSource = source;
|
||||
|
||||
setIdentifier( mSource );
|
||||
|
||||
mSourceRecordable = recordable;
|
||||
|
||||
setRecordable( mSourceRecordable );
|
||||
}
|
||||
/* Indicates if the overall source is recordable */
|
||||
private boolean mSourceRecordable = false;
|
||||
|
||||
/**
|
||||
* Returns a unique identifier for this audio metadata that contains the
|
||||
* source id and optionally the TO metadata string.
|
||||
*/
|
||||
public String getIdentifier()
|
||||
{
|
||||
return mIdentifier;
|
||||
}
|
||||
|
||||
public void setIdentifier( int source )
|
||||
{
|
||||
setIdentifier( source, null );
|
||||
}
|
||||
|
||||
public void setIdentifier( int source, String id )
|
||||
{
|
||||
mIdentifier = "SRC:" + source + " ID:" + ( id == null ? "UNKNOWN" : id );
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the current audio metadata.
|
||||
*/
|
||||
public AudioMetadata copyOf()
|
||||
{
|
||||
AudioMetadata copy = new AudioMetadata( mSource, mSourceRecordable );
|
||||
|
||||
copy.mPriority = mPriority;
|
||||
copy.mSelected = mSelected;
|
||||
copy.mRecordable = mRecordable;
|
||||
/* Indicates the current recordable state */
|
||||
private boolean mRecordable = false;
|
||||
|
||||
copy.mMetadata.putAll( mMetadata );
|
||||
copy.mUpdated = mUpdated;
|
||||
copy.mIdentifier = new String( mIdentifier );
|
||||
|
||||
mUpdated = false;
|
||||
|
||||
return copy;
|
||||
}
|
||||
/* Indicates the current streamable state */
|
||||
private boolean mStreamable = false;
|
||||
|
||||
/**
|
||||
* Removes any accumulated temporal metadata and returns to a default set.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
/* Reset recordable state, identifier and audio call priority levels
|
||||
* and reprocess the metadata */
|
||||
mRecordable = mSourceRecordable;
|
||||
setIdentifier( mSource );
|
||||
mPriority = Priority.DEFAULT_PRIORITY;
|
||||
|
||||
List<MetadataType> toRemove = new ArrayList<>();
|
||||
/* A unique identifier for this metadata */
|
||||
private String mIdentifier;
|
||||
|
||||
for( MetadataType type: mMetadata.keySet() )
|
||||
{
|
||||
Metadata metadata = mMetadata.get( type );
|
||||
public AudioMetadata(int source, boolean recordable)
|
||||
{
|
||||
mSource = source;
|
||||
|
||||
if( metadata != null )
|
||||
{
|
||||
if( metadata.isTemporal() )
|
||||
{
|
||||
toRemove.add( type );
|
||||
}
|
||||
else
|
||||
{
|
||||
processMetadata( metadata );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( MetadataType type: toRemove )
|
||||
{
|
||||
mMetadata.remove( type );
|
||||
}
|
||||
setIdentifier(mSource);
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
private void processMetadata( Metadata metadata )
|
||||
{
|
||||
if( metadata.hasAlias() )
|
||||
{
|
||||
Alias alias = metadata.getAlias();
|
||||
|
||||
if( metadata.getMetadataType() == MetadataType.FROM ||
|
||||
metadata.getMetadataType() == MetadataType.TO )
|
||||
{
|
||||
if( !alias.isRecordable() )
|
||||
{
|
||||
mRecordable = false;
|
||||
}
|
||||
}
|
||||
mSourceRecordable = recordable;
|
||||
|
||||
if( metadata.getMetadataType() == MetadataType.TO )
|
||||
{
|
||||
setIdentifier( mSource, metadata.getValue() );
|
||||
}
|
||||
setRecordable(mSourceRecordable);
|
||||
}
|
||||
|
||||
if( alias != null && alias.getCallPriority() < mPriority )
|
||||
{
|
||||
mPriority = alias.getCallPriority();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio metadata contains updated information. The flag
|
||||
* will only be set when new data is added and will be reset by a copyOf()
|
||||
* method invocation.
|
||||
*/
|
||||
public boolean isUpdated()
|
||||
{
|
||||
return mUpdated;
|
||||
}
|
||||
|
||||
public void setUpdated( boolean updated )
|
||||
{
|
||||
mUpdated = updated;
|
||||
}
|
||||
/**
|
||||
* Returns a unique identifier for this audio metadata that contains the
|
||||
* source id and optionally the TO metadata string.
|
||||
*/
|
||||
public String getIdentifier()
|
||||
{
|
||||
return mIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the metadata. Any temporal metadata will be removed when the
|
||||
* dispose() method is invoked.
|
||||
*/
|
||||
@Override
|
||||
public void receive( Metadata metadata )
|
||||
{
|
||||
if( metadata.isReset() )
|
||||
{
|
||||
reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Metadata existing = mMetadata.get( metadata.getMetadataType() );
|
||||
public void setIdentifier(int source)
|
||||
{
|
||||
setIdentifier(source, null);
|
||||
}
|
||||
|
||||
if( existing == null || !existing.equals( metadata ) )
|
||||
{
|
||||
mMetadata.put( metadata.getMetadataType(), metadata );
|
||||
processMetadata( metadata );
|
||||
setUpdated( true );
|
||||
}
|
||||
}
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete set of metadata
|
||||
*/
|
||||
public Map<MetadataType,Metadata> getMetadata()
|
||||
{
|
||||
return mMetadata;
|
||||
}
|
||||
public void setIdentifier(int source, String id)
|
||||
{
|
||||
mIdentifier = "SRC:" + source + " ID:" + (id == null ? "UNKNOWN" : id);
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata object matching the type or null
|
||||
*/
|
||||
public Metadata getMetadata( MetadataType type )
|
||||
{
|
||||
return mMetadata.get( type );
|
||||
}
|
||||
/**
|
||||
* Returns a copy of the current audio metadata.
|
||||
*/
|
||||
public AudioMetadata copyOf()
|
||||
{
|
||||
AudioMetadata copy = new AudioMetadata(mSource, mSourceRecordable);
|
||||
|
||||
/**
|
||||
* Source channel ID
|
||||
*/
|
||||
public void setSource( int source )
|
||||
{
|
||||
mSource = source;
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
public int getSource()
|
||||
{
|
||||
return mSource;
|
||||
}
|
||||
copy.mPriority = mPriority;
|
||||
copy.mSelected = mSelected;
|
||||
copy.mRecordable = mRecordable;
|
||||
copy.mStreamable = mStreamable;
|
||||
|
||||
/**
|
||||
* Sets the priority of this audio packet within the defined min/max priority range */
|
||||
public void setPriority( int priority )
|
||||
{
|
||||
mPriority = priority;
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
copy.mMetadata.putAll(mMetadata);
|
||||
copy.mUpdated = mUpdated;
|
||||
copy.mIdentifier = new String(mIdentifier);
|
||||
|
||||
/**
|
||||
* Returns the priority of this audio metadata
|
||||
*
|
||||
* @return - priority of audio packet where lower numbers are higher priority
|
||||
*/
|
||||
public int getPriority()
|
||||
{
|
||||
return mPriority;
|
||||
}
|
||||
|
||||
public boolean isDoNotMonitor()
|
||||
{
|
||||
return mPriority == Priority.DO_NOT_MONITOR;
|
||||
}
|
||||
mUpdated = false;
|
||||
|
||||
/**
|
||||
* Selected audio channel. A selected channel will override the priority
|
||||
* level so that the audio can be listened to immediately.
|
||||
*/
|
||||
public void setSelected( boolean selected )
|
||||
{
|
||||
mSelected = selected;
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public boolean isSelected()
|
||||
{
|
||||
return mSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if audio associated with this metadata is recordable.
|
||||
*/
|
||||
public boolean isRecordable()
|
||||
{
|
||||
return mRecordable;
|
||||
}
|
||||
|
||||
public void setRecordable( boolean recordable )
|
||||
{
|
||||
mRecordable = recordable;
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Audio Metadata - source:" );
|
||||
sb.append( getSource() );
|
||||
sb.append( " priority:" );
|
||||
sb.append( getPriority() );
|
||||
sb.append( " updated:" + isUpdated() );
|
||||
sb.append( " " );
|
||||
|
||||
for( Entry<MetadataType,Metadata> entry: getMetadata().entrySet() )
|
||||
{
|
||||
sb.append( entry.getValue().getKey() );
|
||||
sb.append( ":" );
|
||||
sb.append( entry.getValue().getValue() );
|
||||
sb.append( " " );
|
||||
}
|
||||
/**
|
||||
* Removes any accumulated temporal metadata and returns to a default set.
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
//Reset recordable state, identifier and audio call priority levels and reprocess the metadata
|
||||
mRecordable = mSourceRecordable;
|
||||
mStreamable = false;
|
||||
setIdentifier(mSource);
|
||||
mPriority = Priority.DEFAULT_PRIORITY;
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
List<MetadataType> toRemove = new ArrayList<>();
|
||||
|
||||
for (MetadataType type : mMetadata.keySet())
|
||||
{
|
||||
Metadata metadata = mMetadata.get(type);
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
if (metadata.isTemporal())
|
||||
{
|
||||
toRemove.add(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
processMetadata(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (MetadataType type : toRemove)
|
||||
{
|
||||
mMetadata.remove(type);
|
||||
}
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
private void processMetadata(Metadata metadata)
|
||||
{
|
||||
if (metadata.hasAlias())
|
||||
{
|
||||
Alias alias = metadata.getAlias();
|
||||
|
||||
if (metadata.getMetadataType() == MetadataType.FROM || metadata.getMetadataType() == MetadataType.TO)
|
||||
{
|
||||
if (!alias.isRecordable())
|
||||
{
|
||||
mRecordable = false;
|
||||
}
|
||||
|
||||
if (alias.isStreamable())
|
||||
{
|
||||
mStreamable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata.getMetadataType() == MetadataType.TO)
|
||||
{
|
||||
setIdentifier(mSource, metadata.getValue());
|
||||
}
|
||||
|
||||
if (alias != null && alias.getCallPriority() < mPriority)
|
||||
{
|
||||
mPriority = alias.getCallPriority();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this audio metadata contains updated information. The flag
|
||||
* will only be set when new data is added and will be reset by a copyOf()
|
||||
* method invocation.
|
||||
*/
|
||||
public boolean isUpdated()
|
||||
{
|
||||
return mUpdated;
|
||||
}
|
||||
|
||||
public void setUpdated(boolean updated)
|
||||
{
|
||||
mUpdated = updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the metadata. Any temporal metadata will be removed when the
|
||||
* dispose() method is invoked.
|
||||
*/
|
||||
@Override
|
||||
public void receive(Metadata metadata)
|
||||
{
|
||||
if (metadata.isReset())
|
||||
{
|
||||
reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Metadata existing = mMetadata.get(metadata.getMetadataType());
|
||||
|
||||
if (existing == null || !existing.equals(metadata))
|
||||
{
|
||||
mMetadata.put(metadata.getMetadataType(), metadata);
|
||||
processMetadata(metadata);
|
||||
setUpdated(true);
|
||||
}
|
||||
}
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete set of metadata
|
||||
*/
|
||||
public Map<MetadataType, Metadata> getMetadata()
|
||||
{
|
||||
return mMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata object matching the type or null
|
||||
*/
|
||||
public Metadata getMetadata(MetadataType type)
|
||||
{
|
||||
return mMetadata.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source channel ID
|
||||
*/
|
||||
public void setSource(int source)
|
||||
{
|
||||
mSource = source;
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
public int getSource()
|
||||
{
|
||||
return mSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority of this audio packet within the defined min/max priority range
|
||||
*/
|
||||
public void setPriority(int priority)
|
||||
{
|
||||
mPriority = priority;
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the priority of this audio metadata
|
||||
*
|
||||
* @return - priority of audio packet where lower numbers are higher priority
|
||||
*/
|
||||
public int getPriority()
|
||||
{
|
||||
return mPriority;
|
||||
}
|
||||
|
||||
public boolean isDoNotMonitor()
|
||||
{
|
||||
return mPriority == Priority.DO_NOT_MONITOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected audio channel. A selected channel will override the priority
|
||||
* level so that the audio can be listened to immediately.
|
||||
*/
|
||||
public void setSelected(boolean selected)
|
||||
{
|
||||
mSelected = selected;
|
||||
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
public boolean isSelected()
|
||||
{
|
||||
return mSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if audio associated with this metadata is recordable.
|
||||
*/
|
||||
public boolean isRecordable()
|
||||
{
|
||||
return mRecordable;
|
||||
}
|
||||
|
||||
public void setRecordable(boolean recordable)
|
||||
{
|
||||
mRecordable = recordable;
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the audio associated with this metadata is streamable
|
||||
*/
|
||||
public boolean isStreamable()
|
||||
{
|
||||
return mStreamable;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Audio Metadata - source:");
|
||||
sb.append(getSource());
|
||||
sb.append(" priority:");
|
||||
sb.append(getPriority());
|
||||
sb.append(" updated:" + isUpdated());
|
||||
sb.append(" ");
|
||||
|
||||
for (Entry<MetadataType, Metadata> entry : getMetadata().entrySet())
|
||||
{
|
||||
sb.append(entry.getValue().getKey());
|
||||
sb.append(":");
|
||||
sb.append(entry.getValue().getValue());
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user