diff options
author | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2010-09-10 15:23:37 +0000 |
---|---|---|
committer | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2010-09-10 15:23:37 +0000 |
commit | d613851fa2aa6f19b1ab5c8d89f471398f0168c0 (patch) | |
tree | 3582f303f559fb3d611dfe8fb0f5eedc294b513e /src | |
parent | 21c58834fb806aacd5ad154f803ec2ef17a0923e (diff) | |
download | jitsi-d613851fa2aa6f19b1ab5c8d89f471398f0168c0.zip jitsi-d613851fa2aa6f19b1ab5c8d89f471398f0168c0.tar.gz jitsi-d613851fa2aa6f19b1ab5c8d89f471398f0168c0.tar.bz2 |
- Fixes call recording-related issues reported off-list by Emil Ivov.
- Adds javadocs, fixes warnings.
Diffstat (limited to 'src')
13 files changed, 343 insertions, 178 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/RecordButton.java b/src/net/java/sip/communicator/impl/gui/main/call/RecordButton.java index d5bada8..c396cfa 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/RecordButton.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/RecordButton.java @@ -137,6 +137,27 @@ public class RecordButton } setSelected(startedRecording); } + /* + * Notify the user the call has been successfully saved upon the + * recorder stopping. + */ + if (startedRecording && (recorder != null)) + { + recorder.addListener( + new Recorder.Listener() + { + public void recorderStopped(Recorder recorder) + { + NotificationManager.fireNotification( + NotificationManager.CALL_SAVED, + resources.getI18NString( + "plugin.callrecordingconfig.CALL_SAVED"), + resources.getI18NString( + "plugin.callrecordingconfig.CALL_SAVED_TO", + new String[] { callFilename })); + } + }); + } } // stop recording else if (recorder != null) @@ -144,13 +165,6 @@ public class RecordButton try { recorder.stop(); - NotificationManager.fireNotification( - NotificationManager.CALL_SAVED, - resources.getI18NString( - "plugin.callrecordingconfig.CALL_SAVED"), - resources.getI18NString( - "plugin.callrecordingconfig.CALL_SAVED_TO", - new String[] { callFilename })); } finally { @@ -189,7 +203,7 @@ public class RecordButton } } - String ext = configuration.getString(Recorder.CALL_FORMAT); + String ext = configuration.getString(Recorder.FORMAT); // Use a default format when the configured one seems invalid. if ((ext == null) @@ -298,6 +312,7 @@ public class RecordButton { String savedCallsPath = configuration.getString(Recorder.SAVED_CALLS_PATH); + String callFormat; // Ask the user where to save the call. if ((savedCallsPath == null) || (savedCallsPath.length() == 0)) @@ -381,7 +396,7 @@ public class RecordButton * OS X at least) i.e. no format, then it is not obvious that we * have to override the set Recorder.CALL_FORMAT. */ - String callFormat = SoundFileUtils.getExtension(selectedFile); + callFormat = SoundFileUtils.getExtension(selectedFile); if ((callFormat != null) && (callFormat.length() != 0)) { @@ -407,7 +422,7 @@ public class RecordButton = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT; callFilename += '.' + callFormat; } - configuration.setProperty(Recorder.CALL_FORMAT, callFormat); + configuration.setProperty(Recorder.FORMAT, callFormat); } } else @@ -417,7 +432,10 @@ public class RecordButton } } else + { callFilename = createDefaultFilename(savedCallsPath); + callFormat = SoundFileUtils.getExtension(new File(callFilename)); + } Throwable exception = null; @@ -426,7 +444,12 @@ public class RecordButton Recorder recorder = getRecorder(); if (recorder != null) - recorder.start(callFilename); + { + if ((callFormat == null) || (callFormat.length() <= 0)) + callFormat = SoundFileUtils.DEFAULT_CALL_RECORDING_FORMAT; + + recorder.start(callFormat, callFilename); + } this.recorder = recorder; } diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java index c993f0e..26f4d7d 100755 --- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java @@ -46,6 +46,10 @@ public class ChatConversationPanel MouseListener, ClipboardOwner { + /** + * The <tt>Logger</tt> used by the <tt>ChatConversationPanel</tt> class and + * its instances for logging output. + */ private static final Logger logger = Logger.getLogger(ChatConversationPanel.class); @@ -335,7 +339,8 @@ public class ChatConversationPanel * Processes the message given by the parameters. * * @param chatMessage the message - * @param keyword + * @param keyword a substring of <tt>chatMessage</tt> to be highlighted upon + * display of <tt>chatMessage</tt> in the UI * @return the processed message */ public String processMessage(ChatMessage chatMessage, String keyword) @@ -1266,12 +1271,13 @@ public class ChatConversationPanel * @param event the <tt>MouseEvent</tt> * @return the string to be used as the tooltip for <i>event</i>. */ + @Override public String getToolTipText(MouseEvent event) { - if(currentHref != null && currentHref.length() != 0) - return currentHref; - else - return null; + return + ((currentHref != null) && (currentHref.length() != 0)) + ? currentHref + : null; } } 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>. * diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index b40c9ce..2b4092b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -1189,6 +1189,15 @@ public class ProtocolProviderServiceJabberImpl return connection; } + /** + * Determines whether a specific <tt>XMPPException</tt> signals that + * attempted authentication has failed. + * + * @param ex the <tt>XMPPException</tt> which is to be determined whether it + * signals that attempted authentication has failed + * @return <tt>true</tt> if the specified <tt>ex</tt> signals that attempted + * authentication has failed; otherwise, <tt>false</tt> + */ private boolean isAuthenticationFailed(XMPPException ex) { String exMsg = ex.getMessage().toLowerCase(); @@ -1196,16 +1205,12 @@ public class ProtocolProviderServiceJabberImpl // as there are no types or reasons for XMPPException // we try determine the reason according to their message // all messages that were found in smack 3.1.0 were took in count - if(exMsg.indexOf("authentication failed") != -1 - || (exMsg.indexOf("authentication") != -1 - && exMsg.indexOf("failed") != -1) - || exMsg.indexOf("login failed") != -1 - || exMsg.indexOf("unable to determine password") != -1) - { - return true; - } - else - return false; + return + (exMsg.indexOf("authentication failed") != -1) + || ((exMsg.indexOf("authentication") != -1) + && (exMsg.indexOf("failed") != -1)) + || (exMsg.indexOf("login failed") != -1) + || (exMsg.indexOf("unable to determine password") != -1); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java index 143d28e..2f30fe5 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java @@ -3176,7 +3176,7 @@ public class OperationSetPresenceSipImpl /** * Implements the corresponding <tt>SipListener</tt> method by - * terminating the corresponding subsctiption and polling the related + * terminating the corresponding subscription and polling the related * contact. * * @param requestEvent the event containing the request that was \ diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java index 0478c57..7e0fff4 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -301,7 +301,26 @@ public class ProtocolProviderServiceSipImpl return this.registeredEvents; } - + /** + * Overrides + * {@link AbstractProtocolProviderService#fireRegistrationStateChanged( + * RegistrationState, RegistrationState, int, String)} in order to add + * enabling/disabling XCAP functionality in accord with the current + * <tt>RegistrationState</tt> of this + * <tt>ProtocolProviderServiceSipImpl</tt>. + * + * @param oldState the state that the provider had before the change + * occurred + * @param newState the state that the provider is currently in + * @param reasonCode a value corresponding to one of the REASON_XXX fields + * of the <tt>RegistrationStateChangeEvent</tt> class, indicating the reason + * for this state transition + * @param reason a <tt>String</tt> further explaining the reason code or + * <tt>null</tt> if no such explanation is necessary + * @see AbstractProtocolProviderService#fireRegistrationStateChanged( + * RegistrationState, RegistrationState, int, String) + */ + @Override public void fireRegistrationStateChanged(RegistrationState oldState, RegistrationState newState, int reasonCode, diff --git a/src/net/java/sip/communicator/service/neomedia/Recorder.java b/src/net/java/sip/communicator/service/neomedia/Recorder.java index 0122b03..90b0e9e 100644 --- a/src/net/java/sip/communicator/service/neomedia/Recorder.java +++ b/src/net/java/sip/communicator/service/neomedia/Recorder.java @@ -31,8 +31,17 @@ public interface Recorder * format in which media is to be recorded by <tt>Recorder</tt> (e.g. the * media being sent and received in a <tt>Call</tt>). */ - public static final String CALL_FORMAT - = "net.java.sip.communicator.impl.neomedia.CALL_FORMAT"; + public static final String FORMAT + = "net.java.sip.communicator.impl.neomedia.Recorder.FORMAT"; + + /** + * Adds a new <tt>Listener</tt> to the list of listeners interested in + * notifications from this <tt>Recorder</tt>. + * + * @param listener the new <tt>Listener</tt> to be added to the list of + * listeners interested in notifications from this <tt>Recorder</tt> + */ + public void addListener(Listener listener); /** * Gets a list of the formats in which this <tt>Recorder</tt> supports @@ -44,10 +53,21 @@ public interface Recorder public List<String> getSupportedFormats(); /** + * Removes an existing <tt>Listener</tt> from the list of listeners + * interested in notifications from this <tt>Recorder</tt>. + * + * @param listener the existing <tt>Listener</tt> to be removed from the + * list of listeners interested in notifications from this <tt>Recorder</tt> + */ + public void removeListener(Listener 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 @@ -55,7 +75,7 @@ public interface Recorder * @throws MediaException if anything else goes wrong while starting the * recording of media performed by this <tt>Recorder</tt> */ - public void start(String filename) + public void start(String format, String filename) throws IOException, MediaException; @@ -65,4 +85,21 @@ public interface Recorder * been started and prepares this <tt>Recorder</tt> for garbage collection. */ public void stop(); + + /** + * Represents a listener interested in notifications from a <tt>Recorder</tt>. + * + * @author Lubomir Marinov + */ + public interface Listener + { + /** + * Notifies this <tt>Listener</tt> that a specific <tt>Recorder</tt> has + * stopped recording the media associated with it. + * + * @param recorder the <tt>Recorder</tt> which has stopped recording its + * associated media + */ + public void recorderStopped(Recorder recorder); + } } diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java index 6500a38..db7f24a 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java @@ -570,8 +570,40 @@ public abstract class MediaAwareCall< public Recorder createRecorder() throws OperationFailedException { - return - ProtocolMediaActivator.getMediaService().createRecorder( + final Recorder recorder + = ProtocolMediaActivator.getMediaService().createRecorder( getDefaultDevice(MediaType.AUDIO)); + + if (recorder != null) + { + // Make sure the recorder is stopped when this call ends. + final CallChangeListener callChangeListener + = new CallChangeAdapter() + { + @Override + public void callStateChanged(CallChangeEvent evt) + { + if (CallState.CALL_ENDED.equals(evt.getNewValue())) + recorder.stop(); + } + }; + + addCallChangeListener(callChangeListener); + + /* + * If the recorder gets stopped earlier than this call ends, don't + * wait for the end of the call because callChangeListener will keep + * a reference to the stopped recorder. + */ + recorder.addListener( + new Recorder.Listener() + { + public void recorderStopped(Recorder recorder) + { + removeCallChangeListener(callChangeListener); + } + }); + } + return recorder; } } diff --git a/src/net/java/sip/communicator/util/NetworkUtils.java b/src/net/java/sip/communicator/util/NetworkUtils.java index 2012d7e..9a09d1b 100644 --- a/src/net/java/sip/communicator/util/NetworkUtils.java +++ b/src/net/java/sip/communicator/util/NetworkUtils.java @@ -21,8 +21,11 @@ import java.text.*; */ public class NetworkUtils { - private static final Logger logger - = Logger.getLogger(NetworkUtils.class); + /** + * The <tt>Logger</tt> used by the <tt>NetworkUtils</tt> class for logging + * output. + */ + private static final Logger logger = Logger.getLogger(NetworkUtils.class); /** * A string containing the "any" local address for IPv6. @@ -436,7 +439,7 @@ public class NetworkUtils * The records are ordered against the SRV record priority * @param domain the name of the domain we'd like to resolve (_proto._tcp * included). - * @param port + * @param port the port number of the returned <tt>InetSocketAddress</tt> * @return an array of InetSocketAddress containing records returned by the DNS * server - address and port . * @throws ParseException if <tt>domain</tt> is not a valid domain name. @@ -472,7 +475,7 @@ public class NetworkUtils * The records are ordered against the SRV record priority * @param domain the name of the domain we'd like to resolve (_proto._tcp * included). - * @param port + * @param port the port number of the returned <tt>InetSocketAddress</tt> * @return an array of InetSocketAddress containing records returned by the DNS * server - address and port . * @throws ParseException if <tt>domain</tt> is not a valid domain name. diff --git a/src/net/java/sip/communicator/util/swing/SipCommFileDialogImpl.java b/src/net/java/sip/communicator/util/swing/SipCommFileDialogImpl.java index 112fe9f..bf6a30b 100644 --- a/src/net/java/sip/communicator/util/swing/SipCommFileDialogImpl.java +++ b/src/net/java/sip/communicator/util/swing/SipCommFileDialogImpl.java @@ -18,6 +18,12 @@ public class SipCommFileDialogImpl extends FileDialog implements SipCommFileChooser { + /** + * The serialization-related version of the <tt>SipCommFileDialogImpl</tt> + * class explicitly defined to silence a related warning (e.g. in Eclipse + * IDE) since the <tt>SipCommFileDialogImpl</tt> class does not add instance + * fields. + */ private static final long serialVersionUID = 0L; /** @@ -67,7 +73,7 @@ public class SipCommFileDialogImpl if ((file != null) && !file.isDirectory()) { setDirectory(file.getParent()); - setFile(path); + setFile(file.getName()); } else setDirectory(path); |