diff options
Diffstat (limited to 'src/net/java/sip/communicator/impl/neomedia')
4 files changed, 174 insertions, 140 deletions
diff --git a/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java b/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java index a3b88a9..dfb49c6 100644 --- a/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java +++ b/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java @@ -88,18 +88,16 @@ public class CallRecordingConfigForm */ private void loadValues() { - ConfigurationService configurationService + ConfigurationService configuration = NeomediaActivator.getConfigurationService(); - String callFormat - = configurationService.getString(Recorder.CALL_FORMAT); + String format = configuration.getString(Recorder.FORMAT); formatsComboBox.setSelectedItem( - (callFormat == null) + (format == null) ? SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT - : callFormat); + : format); - savedCallsDir - = configurationService.getString(Recorder.SAVED_CALLS_PATH); + savedCallsDir = configuration.getString(Recorder.SAVED_CALLS_PATH); saveCallsToCheckBox.setSelected(savedCallsDir != null); callDirTextField.setText(savedCallsDir); callDirTextField.setEnabled(saveCallsToCheckBox.isSelected()); @@ -192,7 +190,7 @@ public class CallRecordingConfigForm { NeomediaActivator .getConfigurationService() - .setProperty(Recorder.CALL_FORMAT, event.getItem()); + .setProperty(Recorder.FORMAT, event.getItem()); } } }); diff --git a/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java b/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java index aa37588..c8ab59c 100644 --- a/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java @@ -13,7 +13,6 @@ import javax.media.*; import javax.media.protocol.*; import net.java.sip.communicator.impl.neomedia.device.*; -import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.MediaException; // disambiguation import net.java.sip.communicator.util.*; @@ -28,11 +27,6 @@ import net.java.sip.communicator.util.*; public class RecorderImpl implements Recorder { - /** - * The <tt>Logger</tt> used by the <tt>RecorderImpl</tt> class and its - * instances for logging output. - */ - private static final Logger logger = Logger.getLogger(RecorderImpl.class); /** * The list of formats in which <tt>RecorderImpl</tt> instances support @@ -49,15 +43,22 @@ public class RecorderImpl }; /** + * The <tt>AudioMixerMediaDevice</tt> which is to be or which is already + * being recorded by this <tt>Recorder</tt>. + */ + private final AudioMixerMediaDevice device; + + /** * The <tt>MediaDeviceSession</tt> is used to create an output data source. */ private MediaDeviceSession deviceSession; /** - * The format of {@link #deviceSession} in particular and of the recording - * produced by this <tt>Recorder</tt> in general. + * The <tt>List</tt> of <tt>Recorder.Listener</tt>s interested in + * notifications from this <tt>Recorder</tt>. */ - private final String format; + private final List<Recorder.Listener> listeners + = new ArrayList<Recorder.Listener>(); /** * <tt>DataSink</tt> used to save the output data. @@ -75,39 +76,27 @@ public class RecorderImpl if (device == null) throw new NullPointerException("device"); - ConfigurationService configuration - = NeomediaActivator.getConfigurationService(); - String format = configuration.getString(Recorder.CALL_FORMAT); + this.device = device; + } - if (format == null) - format = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT; + /** + * Adds a new <tt>Recorder.Listener</tt> to the list of listeners interested + * in notifications from this <tt>Recorder</tt>. + * + * @param listener the new <tt>Recorder.Listener</tt> to be added to the + * list of listeners interested in notifications from this <tt>Recorder</tt> + * @see Recorder#addListener(Recorder.Listener) + */ + public void addListener(Recorder.Listener listener) + { + if (listener == null) + throw new NullPointerException("listener"); - try - { - deviceSession - = device.createRecordingSession(getContentDescriptor( - format)); - } - catch (IllegalArgumentException iaex) + synchronized (listeners) { - //seems like we had an illegal format stored in the configuration - //service - logger.debug( - "Unable to crate a " - + format - + " record. Will retry default format"); - - format = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT; - - //make sure we don't try the faulty format again. - configuration.setProperty(Recorder.CALL_FORMAT, null); - - deviceSession - = device.createRecordingSession(getContentDescriptor( - format)); + if (!listeners.contains(listener)) + listeners.add(listener); } - - this.format = format; } /** @@ -157,24 +146,48 @@ public class RecorderImpl } /** + * Removes a existing <tt>Recorder.Listener</tt> from the list of listeners + * interested in notifications from this <tt>Recorder</tt>. + * + * @param listener the existing <tt>Recorder.Listener</tt> to be removed + * from the list of listeners interested in notifications from this + * <tt>Recorder</tt> + * @see Recorder#removeListener(Recorder.Listener) + */ + public void removeListener(Recorder.Listener listener) + { + if (listener != null) + { + synchronized (listeners) + { + listeners.remove(listener); + } + } + } + + /** * Starts the recording of the media associated with this <tt>Recorder</tt> * (e.g. the media being sent and received in a <tt>Call</tt>) into a file * with a specific name. * + * @param format the format into which the media associated with this + * <tt>Recorder</tt> is to be recorded into the specified file * @param filename the name of the file into which the media associated with * this <tt>Recorder</tt> is to be recorded * @throws IOException if anything goes wrong with the input and/or output * performed by this <tt>Recorder</tt> * @throws MediaException if anything else goes wrong while starting the * recording of media performed by this <tt>Recorder</tt> - * @see Recorder#start(String) + * @see Recorder#start(String, String) */ - public void start(String filename) + public void start(String format, String filename) throws IOException, MediaException { if (this.sink == null) { + if (format == null) + throw new NullPointerException("format"); if (filename == null) throw new NullPointerException("filename"); @@ -186,18 +199,48 @@ public class RecorderImpl int extensionBeginIndex = filename.lastIndexOf('.'); if (extensionBeginIndex < 0) - filename += '.' + this.format; + filename += '.' + format; else if (extensionBeginIndex == filename.length() - 1) - filename += this.format; + filename += format; + + MediaDeviceSession deviceSession = device.createSession(); + + try + { + deviceSession.setContentDescriptor(getContentDescriptor(format)); + + /* + * This RecorderImpl will use deviceSession to get a hold of the + * media being set to the remote peers associated with the same + * AudioMixerMediaDevice i.e. this RecorderImpl needs + * deviceSession to only capture and not play back. + */ + deviceSession.start(MediaDirection.SENDONLY); + + this.deviceSession = deviceSession; + } + finally + { + if (this.deviceSession == null) + { + throw new MediaException( + "Failed to create MediaDeviceSession from" + + " AudioMixerMediaDevice for the purposes of" + + " recording"); + } + } - DataSource outputDataSource = deviceSession.getOutputDataSource(); + Throwable exception = null; try { + DataSource outputDataSource + = deviceSession.getOutputDataSource(); DataSink sink = Manager.createDataSink( outputDataSource, new MediaLocator("file:" + filename)); + sink.open(); sink.start(); @@ -205,10 +248,18 @@ public class RecorderImpl } catch (NoDataSinkException ndsex) { - throw - new MediaException( + exception = ndsex; + } + finally + { + if ((this.sink == null) || (exception != null)) + { + stop(); + + throw new MediaException( "Failed to start recording into file " + filename, - ndsex); + exception); + } } } } @@ -232,6 +283,23 @@ public class RecorderImpl { sink.close(); sink = null; + + /* + * RecorderImpl creates the sink upon start() and it does it only if + * it is null so this RecorderImpl has really stopped only if it has + * managed to close() the (existing) sink. Notify the registered + * listeners. + */ + Recorder.Listener[] listeners; + + synchronized (this.listeners) + { + listeners + = this.listeners.toArray( + new Recorder.Listener[this.listeners.size()]); + } + for (Recorder.Listener listener : listeners) + listener.recorderStopped(this); } } } diff --git a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java index 307bab1..2d92cfa 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java @@ -206,83 +206,6 @@ public class AudioMixerMediaDevice } /** - * Create a new recording session. - * - * @param contentDescriptor the content descriptor for the session. - * @return a new <tt>MediaDeviceSession</tt> - */ - public synchronized MediaDeviceSession createRecordingSession( - final ContentDescriptor contentDescriptor) - { - if (deviceSession == null) - deviceSession = new AudioMixerMediaDeviceSession(); - - return new MediaStreamMediaDeviceSession(deviceSession) - { - /** - * Starts a specific <tt>Processor</tt> if this - * <tt>MediaDeviceSession</tt> has been started and the specified - * <tt>Processor</tt> is not started. Does not check the - * <tt>MediaDirection</tt> of this session when starting. - * - * @param processor the <tt>Processor</tt> to start - */ - @Override - protected void startProcessorInAccordWithDirection( - Processor processor) - { - if (processor.getState() != Processor.Started) - { - processor.start(); - if (logger.isTraceEnabled()) - { - logger.trace( - "Started Processor with hashCode " - + processor.hashCode()); - } - } - } - - /** - * Overrides the method to set the processor's content descriptor - * to <tt>FileTypeDescriptor.MPEG_AUDIO</tt>. - * - * @param event the <tt>ControllerEvent</tt> specifying the - * <tt>Controller</tt> which is the source of the event and the very - * type of the event - */ - @Override - protected void processorControllerUpdate(ControllerEvent event) - { - super.processorControllerUpdate(event); - - if (event instanceof ConfigureCompleteEvent) - { - Processor processor = (Processor) event. - getSourceController(); - - if (processor != null) - { - try - { - processor.setContentDescriptor(contentDescriptor); - } - catch (NotConfiguredError nce) - { - logger.error( - "Failed to set ContentDescriptor to Processor.", - nce); - } - - if (format != null) - setProcessorFormat(processor, format); - } - } - } - }; - } - - /** * Notifies all currently registered <tt>SimpleAudioLevelListener</tt>s * that our local media now has audio level <tt>level</tt>. * diff --git a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java index 6bdc0d1..06af083 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java @@ -71,6 +71,13 @@ public class MediaDeviceSession private boolean captureDeviceIsConnected; /** + * The <tt>ContentDescriptor</tt> which specifies the content type in which + * this <tt>MediaDeviceSession</tt> is to output the media captured by its + * <tt>MediaDevice</tt>. + */ + private ContentDescriptor contentDescriptor; + + /** * The <tt>MediaDevice</tt> used by this instance to capture and play back * media. */ @@ -498,6 +505,27 @@ public class MediaDeviceSession } /** + * Creates a <tt>ContentDescriptor</tt> to be set on a specific + * <tt>Processor</tt> of captured media to be sent to the remote peer. + * Allows extenders to override. The default implementation returns + * {@link ContentDescriptor#RAW_RTP}. + * + * @param processor the <tt>Processor</tt> of captured media to be sent to + * the remote peer which is to have its <tt>contentDescriptor</tt> set to + * the returned <tt>ContentDescriptor</tt> + * @return a <tt>ContentDescriptor</tt> to be set on the specified + * <tt>processor</tt> of captured media to be sent to the remote peer + */ + protected ContentDescriptor createProcessorContentDescriptor( + Processor processor) + { + return + (contentDescriptor == null) + ? new ContentDescriptor(ContentDescriptor.RAW_RTP) + : contentDescriptor; + } + + /** * Makes sure {@link #captureDevice} is disconnected. */ private void disconnectCaptureDevice() @@ -705,13 +733,13 @@ public class MediaDeviceSession continue; Format jmfFormat = trackControl.getFormat(); - MediaType type = jmfFormat instanceof VideoFormat - ? MediaType.VIDEO : MediaType.AUDIO; + MediaType type + = (jmfFormat instanceof VideoFormat) + ? MediaType.VIDEO + : MediaType.AUDIO; - if(mediaType.equals((type))) - { + if(mediaType.equals(type)) return jmfFormat; - } } } return null; @@ -1113,10 +1141,8 @@ public class MediaDeviceSession { try { - processor - .setContentDescriptor( - new ContentDescriptor( - ContentDescriptor.RAW_RTP)); + processor.setContentDescriptor( + createProcessorContentDescriptor(processor)); } catch (NotConfiguredError nce) { @@ -1220,6 +1246,25 @@ public class MediaDeviceSession } /** + * Sets the <tt>ContentDescriptor</tt> which specifies the content type in + * which this <tt>MediaDeviceSession</tt> is to output the media captured by + * its <tt>MediaDevice</tt>. The default content type in which + * <tt>MediaDeviceSession</tt> outputs the media captured by its + * <tt>MediaDevice</tt> is {@link ContentDescriptor#RAW_RTP}. + * + * @param contentDescriptor the <tt>ContentDescriptor</tt> which specifies + * the content type in which this <tt>MediaDeviceSession</tt> is to output + * the media captured by its <tt>MediaDevice</tt> + */ + public void setContentDescriptor(ContentDescriptor contentDescriptor) + { + if (contentDescriptor == null) + throw new NullPointerException("contentDescriptor"); + + this.contentDescriptor = contentDescriptor; + } + + /** * Sets the <tt>MediaFormat</tt> in which this <tt>MediaDeviceSession</tt> * outputs the media captured by its <tt>MediaDevice</tt>. * |