aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java95
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java16
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/HoldButton.java108
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java88
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/RecordButton.java351
-rw-r--r--src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java18
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java266
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java22
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java17
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java149
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java70
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/gibberish/gibberish.provider.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepositoryJabberImpl.java17
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java1
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java3
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/mock.provider.manifest.mf5
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ActiveCallsRepositorySipImpl.java15
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java1
-rw-r--r--src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java65
-rw-r--r--src/net/java/sip/communicator/service/neomedia/MediaService.java13
-rw-r--r--src/net/java/sip/communicator/service/neomedia/Recorder.java41
-rw-r--r--src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java34
-rw-r--r--src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java20
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetBasicTelephony.java (renamed from src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicTelephony.java)41
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java38
-rw-r--r--src/net/java/sip/communicator/util/SoundFileUtils.java78
28 files changed, 1360 insertions, 219 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java b/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java
new file mode 100644
index 0000000..a597c55
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/AbstractCallToggleButton.java
@@ -0,0 +1,95 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.event.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * The base class for all toggle buttons which control the call from the UI.
+ * Allows extending buttons to focus on performing their toggle actions.
+ *
+ * @author Dmitri Melnikov
+ */
+public abstract class AbstractCallToggleButton
+ extends SIPCommToggleButton
+ implements ActionListener
+{
+ /**
+ * The <tt>Call</tt> that this button controls.
+ */
+ protected final Call call;
+
+ /**
+ * Initializes a new <tt>AbstractCallToggleButton</tt> instance which is to
+ * control a toggle action for a specific <tt>Call</tt>.
+ *
+ * @param call the <tt>Call</tt> to be controlled by the instance
+ * @param fullScreen <tt>true</tt> if the new instance is to be used in
+ * full-screen UI; otherwise, <tt>false</tt>
+ * @param selected <tt>true</tt> if the new toggle button is to be initially
+ * selected; otherwise, <tt>false</tt>
+ * @param iconImageID the <tt>ImageID</tt> of the image to be used as the
+ * icon of the new instance
+ * @param toolTipTextKey the key in the <tt>ResourceManagementService</tt>
+ * of the internationalized string which is to be used as the tool tip text
+ * of the new instance
+ */
+ public AbstractCallToggleButton(
+ Call call,
+ boolean fullScreen,
+ boolean selected,
+ ImageID iconImageID,
+ String toolTipTextKey)
+ {
+ this.call = call;
+
+ ImageID bgImage;
+ ImageID bgRolloverImage;
+ ImageID pressedImage;
+
+ if (fullScreen)
+ {
+ bgImage = ImageLoader.FULL_SCREEN_BUTTON_BG;
+ bgRolloverImage = ImageLoader.FULL_SCREEN_BUTTON_BG;
+ pressedImage = ImageLoader.FULL_SCREEN_BUTTON_BG_PRESSED;
+ }
+ else
+ {
+ bgImage = ImageLoader.CALL_SETTING_BUTTON_BG;
+ bgRolloverImage = ImageLoader.CALL_SETTING_BUTTON_BG;
+ pressedImage = ImageLoader.CALL_SETTING_BUTTON_PRESSED_BG;
+ }
+ setBgImage(ImageLoader.getImage(bgImage));
+ setBgRolloverImage(ImageLoader.getImage(bgRolloverImage));
+ setPressedImage(ImageLoader.getImage(pressedImage));
+
+ setIconImage(ImageLoader.getImage(iconImageID));
+ if (toolTipTextKey != null)
+ {
+ setToolTipText(
+ GuiActivator.getResources().getI18NString(toolTipTextKey));
+ }
+
+ addActionListener(this);
+ setSelected(selected);
+ }
+
+ /**
+ * Notifies this <tt>AbstractCallToggleButton</tt> that its associated
+ * action has been performed and that it should execute its very logic.
+ *
+ * @param evt an <tt>ActionEvent</tt> which describes the specifics of the
+ * performed action
+ */
+ public abstract void actionPerformed(ActionEvent evt);
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
index 9a1d8e5..0da2ab6 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
@@ -87,6 +87,12 @@ public class CallDialog
private MuteButton muteButton;
/**
+ * The button which allows starting and stopping the recording of the
+ * {@link #call}.
+ */
+ private RecordButton recordButton;
+
+ /**
* The video button.
*/
private LocalVideoButton videoButton;
@@ -206,6 +212,7 @@ public class CallDialog
holdButton = new HoldButton(call);
muteButton = new MuteButton(call);
+ recordButton = new RecordButton(call);
videoButton = new LocalVideoButton(call);
transferCallButton = new TransferCallButton(call);
fullScreenButton = new FullScreenButton(this);
@@ -229,17 +236,21 @@ public class CallDialog
GuiActivator.getResources().getI18NString("service.gui.HANG_UP"));
hangupButton.addActionListener(this);
- // Buttons would be enabled once the call has entered in state
- // connected.
+ /*
+ * The buttons will be enabled once the call has entered in a connected
+ * state.
+ */
dialButton.setEnabled(false);
conferenceButton.setEnabled(false);
holdButton.setEnabled(false);
muteButton.setEnabled(false);
+ recordButton.setEnabled(false);
settingsPanel.add(dialButton);
settingsPanel.add(conferenceButton);
settingsPanel.add(holdButton);
settingsPanel.add(muteButton);
+ settingsPanel.add(recordButton);
if (!isLastConference)
{
@@ -454,6 +465,7 @@ public class CallDialog
conferenceButton.setEnabled(true);
holdButton.setEnabled(true);
muteButton.setEnabled(true);
+ recordButton.setEnabled(true);
if (!isLastConference)
{
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/HoldButton.java b/src/net/java/sip/communicator/impl/gui/main/call/HoldButton.java
index 040afdd..2f96696 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/HoldButton.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/HoldButton.java
@@ -9,11 +9,9 @@ package net.java.sip.communicator.impl.gui.main.call;
import java.awt.event.*;
import java.util.*;
-import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.swing.*;
/**
* Represents an UI means to put an associated <tt>CallPariticant</tt> on/off
@@ -21,24 +19,30 @@ import net.java.sip.communicator.util.swing.*;
*
* @author Lubomir Marinov
* @author Yana Stamcheva
+ * @author Dmitri Melnikov
*/
public class HoldButton
- extends SIPCommToggleButton
- implements ActionListener
+ extends AbstractCallToggleButton
{
- private static final long serialVersionUID = 0L;
-
+ /**
+ * The <tt>Logger</tt> used by the <tt>HoldButton</tt> class and its
+ * instances for logging output.
+ */
private static final Logger logger = Logger.getLogger(HoldButton.class);
- private final Call call;
+ /**
+ * The serialization-related version of the <tt>HoldButton</tt> class
+ * explicitly defined to silence a related warning (e.g. in Eclipse IDE)
+ * since the <tt>HoldButton</tt> class does not add instance fields.
+ */
+ private static final long serialVersionUID = 0L;
/**
* Initializes a new <tt>HoldButton</tt> instance which is to put a specific
- * <tt>CallPeer</tt> on/off hold.
+ * <tt>Call</tt> on/off hold.
*
- * @param call the <tt>Call</tt> to be associated with
- * the new instance and to be put on/off hold upon performing its
- * action
+ * @param call the <tt>Call</tt> to be associated with the new instance and
+ * to be put on/off hold upon performing its action
*/
public HoldButton(Call call)
{
@@ -49,66 +53,39 @@ public class HoldButton
* Initializes a new <tt>HoldButton</tt> instance which is to put a specific
* <tt>CallPeer</tt> on/off hold.
*
- * @param call the <tt>Call</tt> to be associated with
- * the new instance and to be put on/off hold upon performing
- * its action.
- * @param isFullScreenMode indicates if this button will be used in a normal
- * or full screen mode.
- * @param isSelected indicates the initial state of this toggle button -
- * selected or not.
+ * @param call the <tt>Call</tt> to be associated with the new instance and
+ * to be put on/off hold upon performing its action
+ * @param fullScreen <tt>true</tt> if the new instance is to be used in
+ * full-screen UI; otherwise, <tt>false</tt>
+ * @param selected <tt>true</tt> if the new toggle button is to be initially
+ * selected; otherwise, <tt>false</tt>
*/
- public HoldButton( Call call,
- boolean isFullScreenMode,
- boolean isSelected)
+ public HoldButton(Call call, boolean fullScreen, boolean selected)
{
- this.call = call;
-
- if (isFullScreenMode)
- {
- this.setBgImage(
- ImageLoader.getImage(ImageLoader.FULL_SCREEN_BUTTON_BG));
- this.setBgRolloverImage(
- ImageLoader.getImage(ImageLoader.FULL_SCREEN_BUTTON_BG));
- this.setIconImage(
- ImageLoader.getImage(ImageLoader.HOLD_BUTTON));
- this.setPressedImage(
- ImageLoader.getImage(ImageLoader.FULL_SCREEN_BUTTON_BG_PRESSED));
- }
- else
- {
- this.setBgImage(
- ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
- this.setBgRolloverImage(
- ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
- this.setIconImage(
- ImageLoader.getImage(ImageLoader.HOLD_BUTTON));
- this.setPressedImage(
- ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_PRESSED_BG));
- }
-
- this.addActionListener(this);
- setToolTipText(GuiActivator.getResources().getI18NString(
- "service.gui.HOLD_BUTTON_TOOL_TIP"));
- setSelected(isSelected);
+ super(
+ call,
+ fullScreen,
+ selected,
+ ImageLoader.HOLD_BUTTON,
+ "service.gui.HOLD_BUTTON_TOOL_TIP");
}
/**
- * Holds on or off call peers when the hold button is clicked.
- * @param evt the <tt>ActionEvent</tt> that notified us of the action
+ * Holds on or off the associated <tt>Call</tt> when this button is clicked.
+ *
+ * @param evt an <tt>ActionEvent</tt> which describes the specifics of the
+ * performed action
+ * @see AbstractCallToggleButton#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent evt)
{
if (call != null)
{
- OperationSetBasicTelephony telephony =
- call.getProtocolProvider()
- .getOperationSet(OperationSetBasicTelephony.class);
-
Iterator<? extends CallPeer> peers = call.getCallPeers();
-
- // Obtain the isSelected property before invoking putOnHold,
- // because the property could change after putting on/off hold.
- boolean isHoldSelected = isSelected();
+ boolean on = isSelected();
+ OperationSetBasicTelephony telephony
+ = call.getProtocolProvider().getOperationSet(
+ OperationSetBasicTelephony.class);
while (peers.hasNext())
{
@@ -116,17 +93,16 @@ public class HoldButton
try
{
- if (isHoldSelected)
+ if (on)
telephony.putOnHold(callPeer);
else
telephony.putOffHold(callPeer);
}
- catch (OperationFailedException ex)
+ catch (OperationFailedException ofex)
{
- if (isHoldSelected)
- logger.error("Failed to put on hold.", ex);
- else
- logger.error("Failed to put off hold.", ex);
+ logger.error(
+ "Failed to put " + (on ? "on" : "off") + " hold.",
+ ofex);
}
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java b/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java
index d6be652..e81e074 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java
@@ -8,32 +8,33 @@ package net.java.sip.communicator.impl.gui.main.call;
import java.awt.event.*;
-import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.swing.*;
/**
- * Represents an UI means to mute the audio stream sent to an associated
- * <tt>CallPariticant</tt>.
+ * Represents an UI means to mute the audio stream sent in an associated
+ * <tt>Call</tt>.
*
* @author Lubomir Marinov
* @author Yana Stamcheva
+ * @author Dmitri Melnikov
*/
public class MuteButton
- extends SIPCommToggleButton
- implements ActionListener
+ extends AbstractCallToggleButton
{
+ /**
+ * The serialization-related version of the <tt>MuteButton</tt> class
+ * explicitly defined to silence a related warning (e.g. in Eclipse IDE)
+ * since the <tt>MuteButton</tt> class does not add instance fields.
+ */
private static final long serialVersionUID = 0L;
- private final Call call;
-
/**
* Initializes a new <tt>MuteButton</tt> instance which is to mute the audio
* stream to a specific <tt>CallPeer</tt>.
*
- * @param call the <tt>Call</tt> to be associated with
- * the new instance and to have the audio stream sent to muted
+ * @param call the <tt>Call</tt> to be associated with the new instance and
+ * to have the audio stream sent to muted
*/
public MuteButton(Call call)
{
@@ -42,64 +43,41 @@ public class MuteButton
/**
* Initializes a new <tt>MuteButton</tt> instance which is to mute the audio
- * stream to a specific <tt>CallPeer</tt>.
+ * stream to a specific <tt>Call</tt>.
*
- * @param call the <tt>Call</tt> to be associated with
- * the new instance and to be put on/off hold upon performing
- * its action.
- * @param isFullScreenMode indicates if this button will be used in a normal
- * or full screen mode.
- * @param isSelected indicates the initial state of this toggle button -
- * selected or not.
+ * @param call the <tt>Call</tt> to be associated with the new instance and
+ * whose audio stream is to be muted upon performing its action
+ * @param fullScreen <tt>true</tt> if the new instance is to be used in
+ * full-screen UI; otherwise, <tt>false</tt>
+ * @param selected <tt>true</tt> if the new toggle button is to be initially
+ * selected; otherwise, <tt>false</tt>
*/
- public MuteButton(Call call, boolean isFullScreenMode, boolean isSelected)
+ public MuteButton(Call call, boolean fullScreen, boolean selected)
{
- this.call = call;
-
- if (isFullScreenMode)
- {
- this.setBgImage(
- ImageLoader.getImage(ImageLoader.FULL_SCREEN_BUTTON_BG));
- this.setBgRolloverImage(
- ImageLoader.getImage(ImageLoader.FULL_SCREEN_BUTTON_BG));
- this.setIconImage(
- ImageLoader.getImage(ImageLoader.MUTE_BUTTON));
- this.setPressedImage(
- ImageLoader.getImage(ImageLoader.FULL_SCREEN_BUTTON_BG_PRESSED));
- }
- else
- {
- this.setBgImage(
- ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
- this.setBgRolloverImage(
- ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_BG));
- this.setIconImage(
- ImageLoader.getImage(ImageLoader.MUTE_BUTTON));
- this.setPressedImage(
- ImageLoader.getImage(ImageLoader.CALL_SETTING_BUTTON_PRESSED_BG));
- }
-
- this.addActionListener(this);
- setToolTipText(GuiActivator.getResources().getI18NString(
- "service.gui.MUTE_BUTTON_TOOL_TIP"));
- setSelected(isSelected);
+ super(
+ call,
+ fullScreen,
+ selected,
+ ImageLoader.MUTE_BUTTON,
+ "service.gui.MUTE_BUTTON_TOOL_TIP");
}
/**
- * Mutes or unmutes call peers when the mute button is clicked.
- * @param evt the <tt>ActionEvent</tt> that notified us of the action
+ * Mutes or unmutes the associated <tt>Call</tt> upon clicking this button.
+ *
+ * @param evt an <tt>ActionEvent</tt> which describes the specifics of the
+ * performed action
+ * @see AbstractCallToggleButton#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent evt)
{
if (call != null)
{
OperationSetBasicTelephony telephony
- = call.getProtocolProvider()
- .getOperationSet(OperationSetBasicTelephony.class);
+ = call.getProtocolProvider().getOperationSet(
+ OperationSetBasicTelephony.class);
- // Obtain the isSelected property before invoking setMute.
- boolean isMuteSelected = isSelected();
- telephony.setMute(call, isMuteSelected);
+ telephony.setMute(call, isSelected());
}
}
}
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
new file mode 100644
index 0000000..371d828
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/RecordButton.java
@@ -0,0 +1,351 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license. See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.neomedia.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * The button that starts/stops the call recording.
+ *
+ * @author Dmitri Melnikov
+ */
+public class RecordButton
+ extends AbstractCallToggleButton
+{
+ /**
+ * Resource service.
+ */
+ private static ResourceManagementService resources
+ = GuiActivator.getResources();
+
+ /**
+ * Configuration service.
+ */
+ private static ConfigurationService configurationService
+ = GuiActivator.getConfigurationService();
+
+ /**
+ * The date format used in file names.
+ */
+ private static SimpleDateFormat format
+ = new SimpleDateFormat("yyyy-MM-dd@HH.mm.ss");
+
+ /**
+ * <tt>true</tt> when the default directory to save calls to is set,
+ * <tt>false</tt> otherwise.
+ */
+ private boolean isCallDirSet = false;
+
+ /**
+ * The full filename of the saved call on the file system.
+ */
+ private String callFilename;
+
+ /**
+ * Input panel.
+ */
+ private InputPanel inputPanel;
+
+ /**
+ * Initializes a new <tt>RecordButton</tt> instance which is to record the
+ * audio stream.
+ *
+ * @param call the <tt>Call</tt> to be associated with the new instance and
+ * to have the audio stream recorded
+ */
+ public RecordButton(Call call)
+ {
+ this(call, false, false);
+ }
+
+ /**
+ * Initializes a new <tt>RecordButton</tt> instance which is to record the
+ * audio stream.
+ *
+ * @param call the <tt>Call</tt> to be associated with the new instance and
+ * to have its audio stream recorded
+ * @param fullScreen <tt>true</tt> if the new instance is to be used in
+ * full-screen UI; otherwise, <tt>false</tt>
+ * @param selected <tt>true</tt> if the new toggle button is to be initially
+ * selected; otherwise, <tt>false</tt>
+ */
+ public RecordButton(Call call, boolean fullScreen, boolean selected)
+ {
+ super(call, fullScreen, selected, ImageLoader.RECORD_BUTTON, null);
+
+ inputPanel = new InputPanel();
+
+ String toolTip
+ = resources.getI18NString("service.gui.RECORD_BUTTON_TOOL_TIP");
+ String saveDir
+ = configurationService.getString(Recorder.SAVED_CALLS_PATH);
+ if (saveDir != null)
+ {
+ isCallDirSet = true;
+ toolTip = toolTip + " (" + saveDir + ")";
+ }
+ setToolTipText(toolTip);
+ }
+
+ /**
+ * Starts/stops the recording of the call when this button is pressed.
+ *
+ * @param evt the <tt>ActionEvent</tt> that notified us of the action
+ */
+ public void actionPerformed(ActionEvent evt)
+ {
+ if (call != null)
+ {
+ OperationSetBasicTelephony<?> telephony =
+ call.getProtocolProvider().getOperationSet(
+ OperationSetBasicTelephony.class);
+
+ boolean isRecordSelected = isSelected();
+ // start recording
+ if (isRecordSelected)
+ {
+ // ask user input about where to save the call
+ if (!isCallDirSet)
+ {
+ int status =
+ JOptionPane
+ .showConfirmDialog(
+ this,
+ inputPanel,
+ resources
+ .getI18NString("plugin.callrecordingconfig.SAVE_CALL"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE);
+ if (status == JOptionPane.OK_OPTION)
+ {
+ callFilename = inputPanel.getSelectedFilename();
+ configurationService.setProperty(Recorder.CALL_FORMAT,
+ inputPanel.getSelectedFormat());
+ }
+ else
+ {
+ // user canceled the recording
+ setSelected(false);
+ return;
+ }
+ }
+ else
+ callFilename = createDefaultFilename();
+
+ telephony.startRecording(call, callFilename);
+ }
+ // stop recording
+ else
+ {
+ telephony.stopRecording(call);
+ JOptionPane.showMessageDialog(this,
+ resources.getI18NString(
+ "plugin.callrecordingconfig.CALL_SAVED_TO", new String[]
+ { callFilename }),
+ resources
+ .getI18NString("plugin.callrecordingconfig.CALL_SAVED"),
+ JOptionPane.INFORMATION_MESSAGE);
+ }
+ }
+ }
+
+ /**
+ * Creates a full filename for the call by combining the directory, file
+ * prefix and extension. If the directory is <tt>null</tt> user's home
+ * directory is used.
+ *
+ * @return a full filename for the call
+ */
+ private String createDefaultFilename()
+ {
+ String callsDir
+ = configurationService.getString(Recorder.SAVED_CALLS_PATH);
+
+ // set to user's home when null
+ if (callsDir == null)
+ {
+ try
+ {
+ callsDir
+ = GuiActivator
+ .getFileAccessService()
+ .getDefaultDownloadDirectory()
+ .getAbsolutePath();
+ }
+ catch (IOException ioex)
+ {
+ // Leave it in the current directory.
+ }
+ }
+
+ String ext = configurationService.getString(Recorder.CALL_FORMAT);
+
+ if (ext == null)
+ ext = SoundFileUtils.mp2;
+
+ return
+ ((callsDir == null) ? "" : (callsDir + File.separator))
+ + generateCallFilename(ext);
+ }
+
+ /**
+ * Generates a file name for the call based on the current date.
+ *
+ * @param ext file extension
+ * @return the file name for the call
+ */
+ private String generateCallFilename(String ext)
+ {
+ return format.format(new Date()) + "-confcall." + ext;
+ }
+
+ private static class InputPanel
+ extends TransparentPanel
+ {
+ /**
+ * Call file chooser.
+ */
+ private SipCommFileChooser callFileChooser;
+
+ /**
+ * Selected file.
+ */
+ private String selectedFilename;
+
+ /**
+ * Format combo box.
+ */
+ private JComboBox formatComboBox;
+
+ /**
+ * Builds the panel.
+ */
+ public InputPanel()
+ {
+ super(new BorderLayout());
+
+ initComponents();
+
+ callFileChooser =
+ GenericFileDialog.create(null, resources
+ .getI18NString("plugin.callrecordingconfig.SAVE_CALL"),
+ SipCommFileChooser.SAVE_FILE_OPERATION);
+ }
+
+ /**
+ * Returns the selected file.
+ *
+ * @return the selected file
+ */
+ public String getSelectedFilename()
+ {
+ return selectedFilename;
+ }
+
+ /**
+ * Returns the selected format.
+ *
+ * @return the selected format
+ */
+ public String getSelectedFormat()
+ {
+ return (String) formatComboBox.getSelectedItem();
+ }
+
+ /**
+ * Initializes the UI components.
+ */
+ private void initComponents()
+ {
+ JPanel labelsPanel = new TransparentPanel(new GridLayout(2, 1));
+ JLabel formatLabel =
+ new JLabel(resources
+ .getI18NString("plugin.callrecordingconfig.FORMAT"));
+ JLabel locationLabel =
+ new JLabel(resources
+ .getI18NString("plugin.callrecordingconfig.LOCATION"));
+ labelsPanel.add(formatLabel);
+ labelsPanel.add(locationLabel);
+
+ JPanel dirPanel =
+ new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
+ final JTextField callDirTextField = new JTextField();
+ callDirTextField.setPreferredSize(new Dimension(200, 30));
+ callDirTextField.setEditable(false);
+ dirPanel.add(callDirTextField);
+ JButton callDirChooseButton =
+ new JButton(new ImageIcon(resources
+ .getImageInBytes("plugin.notificationconfig.FOLDER_ICON")));
+ callDirChooseButton.setMinimumSize(new Dimension(30, 30));
+ callDirChooseButton.setPreferredSize(new Dimension(30, 30));
+ callDirChooseButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent arg0)
+ {
+ File selectedFile = callFileChooser.getFileFromDialog();
+
+ if (selectedFile != null)
+ {
+ selectedFilename = selectedFile.getAbsolutePath();
+ callDirTextField.setText(selectedFilename);
+ }
+ }
+ });
+ dirPanel.add(callDirChooseButton);
+
+ JPanel comboPanel =
+ new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
+ JLabel emptyLabel = new JLabel();
+ emptyLabel.setPreferredSize(new Dimension(30, 30));
+ comboPanel.add(createFormatsComboBox());
+ comboPanel.add(emptyLabel);
+
+ JPanel valuesPanel = new TransparentPanel(new GridLayout(2, 1));
+ valuesPanel.add(comboPanel);
+ valuesPanel.add(dirPanel);
+
+ this.add(labelsPanel, BorderLayout.WEST);
+ this.add(valuesPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * Creates a combo box with supported audio formats.
+ *
+ * @return a combo box with supported audio formats
+ */
+ private Component createFormatsComboBox()
+ {
+ ComboBoxModel formatsComboBoxModel =
+ new DefaultComboBoxModel(
+ new String[] {
+ SoundFileUtils.mp2,
+ SoundFileUtils.wav,
+ SoundFileUtils.au,
+ SoundFileUtils.aif,
+ SoundFileUtils.gsm });
+
+ formatComboBox = new JComboBox();
+ formatComboBox.setPreferredSize(new Dimension(200, 30));
+ formatComboBox.setModel(formatsComboBoxModel);
+ return formatComboBox;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
index 5bff83c..9424ae9 100644
--- a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
+++ b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
@@ -30,6 +30,10 @@ import net.java.sip.communicator.util.swing.*;
*/
public class ImageLoader
{
+ /**
+ * The <tt>Logger</tt> used by the <tt>ImageLoader</tt> class and its
+ * instances for logging output.
+ */
private static final Logger logger = Logger.getLogger(ImageLoader.class);
/**
@@ -447,6 +451,12 @@ public class ImageLoader
= new ImageID("service.gui.buttons.MUTE_BUTTON");
/**
+ * A record button icon. The icon shown in the CallPeer panel.
+ */
+ public static final ImageID RECORD_BUTTON
+ = new ImageID("service.gui.buttons.RECORD_BUTTON");
+
+ /**
* A local video button icon. The icon shown in the CallPeer panel.
*/
public static final ImageID LOCAL_VIDEO_BUTTON
@@ -498,13 +508,13 @@ public class ImageLoader
* The security button: encrypted and SAS verified, encrypted only,
* security off.
*/
- public static final ImageID ENCR_VERIFIED = new ImageID(
- "service.gui.buttons.ENCR_VERIFIED");
+ public static final ImageID ENCR_VERIFIED
+ = new ImageID("service.gui.buttons.ENCR_VERIFIED");
public static final ImageID ENCR = new ImageID("service.gui.buttons.ENCR");
- public static final ImageID ENCR_DISABLED = new ImageID(
- "service.gui.buttons.ENCR_DISABLED");
+ public static final ImageID ENCR_DISABLED
+ = new ImageID("service.gui.buttons.ENCR_DISABLED");
/**
* The button icon of the Enter Full Screen command. The icon shown in the
diff --git a/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java b/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java
new file mode 100644
index 0000000..941e92a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/neomedia/CallRecordingConfigForm.java
@@ -0,0 +1,266 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license. See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.neomedia;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.neomedia.*;
+import net.java.sip.communicator.service.resources.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * The saved calls management and configuration form.
+ *
+ * @author Dmitri Melnikov
+ */
+public class CallRecordingConfigForm
+ extends TransparentPanel
+ implements ActionListener
+{
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger
+ = Logger.getLogger(CallRecordingConfigForm.class);
+
+ /**
+ * The resource service.
+ */
+ private static final ResourceManagementService resources
+ = NeomediaActivator.getResources();
+
+ /**
+ * Directory where calls are stored. Default is SC_HOME/calls.
+ */
+ private String savedCallsDir;
+
+ /**
+ * Directory choose dialog.
+ */
+ private SipCommFileChooser dirChooser;
+
+ /**
+ * UI components.
+ */
+ private JButton callDirChooseButton;
+ private JTextField callDirTextField;
+ private JComboBox formatsComboBox;
+ private JCheckBox saveCallsToCheckBox;
+
+ /**
+ * Creates an instance of the <tt>CallConfigurationPanel</tt>.
+ * Checks for the <tt>SAVED_CALLS_PATH</tt> and sets it if it does not
+ * exist.
+ */
+ public CallRecordingConfigForm()
+ {
+ super(new BorderLayout());
+
+ initComponents();
+ loadValues();
+
+ dirChooser =
+ GenericFileDialog.create(null, resources
+ .getI18NString("plugin.callrecordingconfig.CHOOSE_DIR"),
+ SipCommFileChooser.LOAD_FILE_OPERATION);
+ ((JFileChooser) dirChooser)
+ .setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ }
+
+ /**
+ * Loads values from the configuration and sets the UI components to these
+ * values.
+ */
+ private void loadValues()
+ {
+ ConfigurationService configurationService
+ = NeomediaActivator.getConfigurationService();
+
+ String callFormat = configurationService.getString(Recorder.CALL_FORMAT);
+ formatsComboBox.setSelectedItem(callFormat == null ? SoundFileUtils.mp2
+ : callFormat);
+
+ savedCallsDir = configurationService.getString(Recorder.SAVED_CALLS_PATH);
+ saveCallsToCheckBox.setSelected(savedCallsDir != null);
+ callDirTextField.setText(savedCallsDir);
+ callDirTextField.setEnabled(saveCallsToCheckBox.isSelected());
+ callDirChooseButton.setEnabled(saveCallsToCheckBox.isSelected());
+ }
+
+ /**
+ * Creates a panel with call management components.
+ */
+ private void initComponents()
+ {
+ // labels panel
+ JPanel labelsPanel = new TransparentPanel(new GridLayout(2, 1));
+
+ JLabel formatsLabel = new JLabel(
+ resources.getI18NString("plugin.callrecordingconfig.SUPPORTED_FORMATS"));
+ saveCallsToCheckBox =
+ new SIPCommCheckBox(resources
+ .getI18NString("plugin.callrecordingconfig.SAVE_CALLS"));
+ saveCallsToCheckBox.addActionListener(this);
+
+ labelsPanel.add(formatsLabel);
+ labelsPanel.add(saveCallsToCheckBox);
+
+ // combo box panel
+ JPanel comboPanel =
+ new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
+
+ JLabel emptyLabel = new JLabel();
+ emptyLabel.setPreferredSize(new Dimension(30, 30));
+ comboPanel.add(createFormatsComboBox());
+ comboPanel.add(emptyLabel);
+
+ // saved calls directory panel
+ JPanel callDirPanel =
+ new TransparentPanel(new FlowLayout(FlowLayout.LEFT));
+
+ callDirTextField = new JTextField();
+ callDirTextField.setPreferredSize(new Dimension(200, 30));
+ callDirTextField.addActionListener(this);
+ callDirPanel.add(callDirTextField);
+
+ callDirChooseButton =
+ new JButton(new ImageIcon(resources
+ .getImageInBytes("plugin.notificationconfig.FOLDER_ICON")));
+ callDirChooseButton.setMinimumSize(new Dimension(30,30));
+ callDirChooseButton.setPreferredSize(new Dimension(30,30));
+ callDirChooseButton.addActionListener(this);
+ callDirPanel.add(callDirChooseButton);
+
+ // values panel
+ JPanel valuesPanel = new TransparentPanel(new GridLayout(2, 1));
+ valuesPanel.add(comboPanel);
+ valuesPanel.add(callDirPanel);
+
+ // main panel
+ JPanel mainPanel = new TransparentPanel(new BorderLayout());
+ mainPanel.add(labelsPanel, BorderLayout.WEST);
+ mainPanel.add(valuesPanel, BorderLayout.CENTER);
+
+ this.add(mainPanel, BorderLayout.NORTH);
+ }
+
+ /**
+ * Creates a combo box with supported audio formats.
+ *
+ * @return a combo box with supported audio formats
+ */
+ private Component createFormatsComboBox()
+ {
+ ComboBoxModel formatsComboBoxModel =
+ new DefaultComboBoxModel(
+ new String[] {
+ SoundFileUtils.mp2,
+ SoundFileUtils.wav,
+ SoundFileUtils.au,
+ SoundFileUtils.aif,
+ SoundFileUtils.gsm });
+
+ formatsComboBox = new JComboBox();
+ formatsComboBox.setPreferredSize(new Dimension(200, 30));
+ formatsComboBox.setModel(formatsComboBoxModel);
+
+ formatsComboBox.addItemListener(new ItemListener()
+ {
+ public void itemStateChanged(ItemEvent event)
+ {
+ if (event.getStateChange() == ItemEvent.SELECTED)
+ NeomediaActivator
+ .getConfigurationService()
+ .setProperty(Recorder.CALL_FORMAT, event.getItem());
+ }
+ });
+ return formatsComboBox;
+ }
+
+ /**
+ * Indicates that one of the contained in this panel components has
+ * performed an action.
+ *
+ * @param e the <tt>ActionEvent</tt> that notified us
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Object source = e.getSource();
+ if (source == saveCallsToCheckBox)
+ {
+ boolean selected = saveCallsToCheckBox.isSelected();
+ callDirTextField.setEnabled(selected);
+ callDirChooseButton.setEnabled(selected);
+ if (selected)
+ {
+ // set default directory
+ try
+ {
+ changeCallsDir(
+ NeomediaActivator
+ .getFileAccessService()
+ .getDefaultDownloadDirectory());
+ }
+ catch (IOException ioex)
+ {
+ }
+ }
+ else
+ {
+ // remove default directory prop
+ NeomediaActivator
+ .getConfigurationService()
+ .setProperty(Recorder.SAVED_CALLS_PATH, null);
+ callDirTextField.setText(null);
+ }
+ }
+ else if (source == callDirChooseButton)
+ {
+ File newDir = dirChooser.getFileFromDialog();
+ changeCallsDir(newDir);
+ }
+ else if (source == callDirTextField)
+ {
+ File newDir = new File(callDirTextField.getText());
+ changeCallsDir(newDir);
+ }
+ }
+
+ /**
+ * Sets the new directory for the saved calls to <tt>dir</tt>.
+ *
+ * @param dir the new chosen directory
+ * @return <tt>true</tt> if directory was changed successfully,
+ * <tt>false</tt> otherwise
+ */
+ private boolean changeCallsDir(File dir)
+ {
+ if (dir != null && dir.isDirectory())
+ {
+ savedCallsDir = dir.getAbsolutePath();
+ callDirTextField.setText(savedCallsDir);
+ NeomediaActivator
+ .getConfigurationService()
+ .setProperty(Recorder.SAVED_CALLS_PATH, savedCallsDir);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Calls directory changed to " + savedCallsDir);
+ return true;
+ }
+ else
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Calls directory not changed.");
+ return false;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
index 2a45352..465098d 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
@@ -21,6 +21,7 @@ import net.java.sip.communicator.service.neomedia.format.*;
* Implements <tt>MediaService</tt> for JMF.
*
* @author Lubomir Marinov
+ * @author Dmitri Melnikov
*/
public class MediaServiceImpl
implements MediaService
@@ -563,6 +564,26 @@ public class MediaServiceImpl
}
/**
+ * Creates a new <tt>Recorder</tt> instance that can be used to record a
+ * call which captures and plays back media using a specific
+ * <tt>MediaDevice</tt>.
+ *
+ * @param device the <tt>MediaDevice</tt> which is used for media capture
+ * and playback by the call to be recorded
+ * @return a new <tt>Recorder</tt> instance that can be used to record a
+ * call which captures and plays back media using the specified
+ * <tt>MediaDevice</tt>
+ * @see MediaService#createRecorder(MediaDevice)
+ */
+ public Recorder createRecorder(MediaDevice device)
+ {
+ if (device instanceof AudioMixerMediaDevice)
+ return new RecorderImpl((AudioMixerMediaDevice) device);
+ else
+ return null;
+ }
+
+ /**
* Returns a {@link Map} that binds indicates whatever preferences this
* media service implementation may have for the RTP payload type numbers
* that get dynamically assigned to {@link MediaFormat}s with no static
@@ -586,7 +607,6 @@ public class MediaServiceImpl
dynamicPayloadTypePreferences.put(telephoneEvent, (byte)101);
}
-
return dynamicPayloadTypePreferences;
}
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java b/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java
index cdeb8de..9ae36bf 100644
--- a/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java
+++ b/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java
@@ -187,6 +187,23 @@ public class NeomediaActivator
if (logger.isInfoEnabled())
logger.info("Audio Notifier Service ...[REGISTERED]");
+
+ // Call Recording
+ Dictionary<String, String> callRecordingProps
+ = new Hashtable<String, String>();
+ callRecordingProps.put(
+ ConfigurationForm.FORM_TYPE,
+ ConfigurationForm.ADVANCED_TYPE);
+ bundleContext.registerService(
+ ConfigurationForm.class.getName(),
+ new LazyConfigurationForm(
+ CallRecordingConfigForm.class.getName(),
+ getClass().getClassLoader(),
+ null,
+ "plugin.callrecordingconfig.CALL_RECORDING_CONFIG",
+ 1100,
+ true),
+ callRecordingProps);
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java b/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java
new file mode 100644
index 0000000..d388c5c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/neomedia/RecorderImpl.java
@@ -0,0 +1,149 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.neomedia;
+
+import java.io.*;
+
+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.util.*;
+
+/**
+ * The call recording implementation.
+ * Provides the capability to start and stop call recording.
+ *
+ * @author Dmitri Melnikov
+ */
+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 <tt>MediaDeviceSession</tt> is used to create an output data source.
+ */
+ private MediaDeviceSession deviceSession;
+
+ /**
+ * <tt>DataSink</tt> used to save the output data.
+ */
+ private DataSink sink;
+
+ /**
+ * <tt>true</tt> if recording was started, <tt>false</tt>
+ * otherwise.
+ */
+ private boolean recording = false;
+
+ /**
+ * Constructs the <tt>RecorderImpl</tt> with the provided session.
+ *
+ * @param device device that can create a session that provides the output
+ * data source
+ */
+ public RecorderImpl(AudioMixerMediaDevice device)
+ {
+ if (device == null)
+ throw new NullPointerException("device");
+
+ ConfigurationService configurationService
+ = NeomediaActivator.getConfigurationService();
+ String format = configurationService.getString(Recorder.CALL_FORMAT);
+
+ if (format == null)
+ format = SoundFileUtils.mp2;
+ deviceSession
+ = device.createRecordingSession(getContentDescriptor(format));
+ }
+
+ /**
+ * Starts the call recording.
+ *
+ * @param filename call filename, when <tt>null</tt> a default filename is
+ * used
+ */
+ public void startRecording(String filename)
+ {
+ if (!recording)
+ {
+ if (filename == null)
+ throw new NullPointerException("filename");
+
+ DataSource outputDataSource = deviceSession.getOutputDataSource();
+
+ try
+ {
+ sink
+ = Manager.createDataSink(
+ outputDataSource,
+ new MediaLocator("file:" + filename));
+ sink.open();
+ sink.start();
+ }
+ catch (NoDataSinkException ndsex)
+ {
+ logger.error("No datasink can be found", ndsex);
+ }
+ catch (IOException ioex)
+ {
+ logger.error("Writing to datasink failed", ioex);
+ }
+
+ recording = true;
+ }
+ }
+
+ /**
+ * Stops the call recording.
+ */
+ public void stopRecording()
+ {
+ if (recording)
+ {
+ deviceSession.close();
+ deviceSession = null;
+
+ if (sink != null)
+ {
+ sink.close();
+ sink = null;
+ }
+
+ recording = false;
+ }
+ }
+
+ /**
+ * Returns a content descriptor to create a recording session with.
+ *
+ * @param format the format that corresponding to the content descriptor
+ * @return content descriptor
+ */
+ private ContentDescriptor getContentDescriptor(String format)
+ {
+ String type = FileTypeDescriptor.MPEG_AUDIO;
+
+ if (SoundFileUtils.wav.equals(format))
+ type = FileTypeDescriptor.WAVE;
+ else if (SoundFileUtils.gsm.equals(format))
+ type = FileTypeDescriptor.GSM;
+ else if (SoundFileUtils.au.equals(format))
+ type = FileTypeDescriptor.BASIC_AUDIO;
+ else if (SoundFileUtils.aif.equals(format))
+ type = FileTypeDescriptor.AIFF;
+
+ return new ContentDescriptor(type);
+ }
+}
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 6bb798b..e7a6362 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
@@ -184,7 +184,7 @@ public class AudioMixerMediaDevice
* captured by this <tt>MediaDevice</tt>
* @see AbstractMediaDevice#createOutputDataSource()
*/
- AudioMixingPushBufferDataSource createOutputDataSource()
+ public AudioMixingPushBufferDataSource createOutputDataSource()
{
return getAudioMixer().createOutputDataSource();
}
@@ -205,6 +205,74 @@ public class AudioMixerMediaDevice
return new MediaStreamMediaDeviceSession(deviceSession);
}
+ 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 39cfcaf..6bdc0d1 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
@@ -1613,7 +1613,7 @@ public class MediaDeviceSession
*
* @param processor the <tt>Processor</tt> to start
*/
- private void startProcessorInAccordWithDirection(Processor processor)
+ protected void startProcessorInAccordWithDirection(Processor processor)
{
if (startedDirection.allowsSending()
&& (processor.getState() != Processor.Started))
diff --git a/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java b/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java
index b62a525..abe631f 100644
--- a/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicTelephonyGibberishImpl.java
@@ -11,10 +11,12 @@ import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
/**
* A Gibberish implementation of a basic telephony operation set.
+ *
* @author Yana Stamcheva
*/
public class OperationSetBasicTelephonyGibberishImpl
diff --git a/src/net/java/sip/communicator/impl/protocol/gibberish/gibberish.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/gibberish/gibberish.provider.manifest.mf
index ec9208e..dde4396 100644
--- a/src/net/java/sip/communicator/impl/protocol/gibberish/gibberish.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/gibberish/gibberish.provider.manifest.mf
@@ -10,4 +10,5 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.configuration.event,
net.java.sip.communicator.util,
net.java.sip.communicator.service.protocol,
- net.java.sip.communicator.service.protocol.event
+ net.java.sip.communicator.service.protocol.event,
+ net.java.sip.communicator.service.protocol.media
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepositoryJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepositoryJabberImpl.java
index d044ccd..d000403 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepositoryJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ActiveCallsRepositoryJabberImpl.java
@@ -24,7 +24,8 @@ public class ActiveCallsRepositoryJabberImpl
OperationSetBasicTelephonyJabberImpl>
{
/**
- * logger of this class
+ * The <tt>Logger</tt> used by the <tt>ActiveCallsRepositoryJabberImpl</tt>
+ * class and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(ActiveCallsRepositoryJabberImpl.class);
@@ -89,4 +90,18 @@ public class ActiveCallsRepositoryJabberImpl
return null;
}
+
+ /**
+ * Creates and dispatches a <tt>CallEvent</tt> notifying registered
+ * listeners that an event with id <tt>eventID</tt> has occurred on
+ * <tt>sourceCall</tt>.
+ *
+ * @param eventID the ID of the event to dispatch
+ * @param sourceCall the call on which the event has occurred
+ * @see ActiveCallsRepository#fireCallEvent(int, Call)
+ */
+ protected void fireCallEvent(int eventID, Call sourceCall)
+ {
+ parentOperationSet.fireCallEvent(eventID, sourceCall);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
index 37a89f0..aedaf5e 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
@@ -11,6 +11,7 @@ import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
index b4cff03..920a503 100644
--- a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java
@@ -11,10 +11,11 @@ import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
/**
- * A mock implementation of a basic telephony opearation set
+ * A mock implementation of a basic telephony operation set
*
* @author Damian Minkov
*/
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/mock.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/mock/mock.provider.manifest.mf
index a751ec9..c7e72e2 100644
--- a/src/net/java/sip/communicator/impl/protocol/mock/mock.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/mock/mock.provider.manifest.mf
@@ -8,5 +8,6 @@ Import-Package: net.java.sip.communicator.service.contactlist,
org.osgi.framework,
net.java.sip.communicator.util,
net.java.sip.communicator.service.protocol,
- net.java.sip.communicator.service.protocol.event
-Export-Package: net.java.sip.communicator.impl.protocol.mock,
+ net.java.sip.communicator.service.protocol.event,
+ net.java.sip.communicator.service.protocol.media
+Export-Package: net.java.sip.communicator.impl.protocol.mock
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ActiveCallsRepositorySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ActiveCallsRepositorySipImpl.java
index 6a83b61..08cd155 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ActiveCallsRepositorySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ActiveCallsRepositorySipImpl.java
@@ -12,7 +12,6 @@ import javax.sip.*;
import javax.sip.header.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
@@ -273,4 +272,18 @@ public class ActiveCallsRepositorySipImpl
return (peer == null)? null : peer.getCall();
}
+
+ /**
+ * Creates and dispatches a <tt>CallEvent</tt> notifying registered
+ * listeners that an event with id <tt>eventID</tt> has occurred on
+ * <tt>sourceCall</tt>.
+ *
+ * @param eventID the ID of the event to dispatch
+ * @param sourceCall the call on which the event has occurred
+ * @see ActiveCallsRepository#fireCallEvent(int, Call)
+ */
+ protected void fireCallEvent(int eventID, Call sourceCall)
+ {
+ parentOperationSet.fireCallEvent(eventID, sourceCall);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index 2a4a7f1..531b15d 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -20,6 +20,7 @@ import javax.sip.message.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
/**
diff --git a/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java b/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java
index 9665eeb..3f113bd 100644
--- a/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java
+++ b/src/net/java/sip/communicator/plugin/notificationconfiguration/SoundFilter.java
@@ -7,9 +7,10 @@
package net.java.sip.communicator.plugin.notificationconfiguration;
-import java.io.File;
+import java.io.*;
-import net.java.sip.communicator.util.swing.SipCommFileFilter;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.swing.*;
/**
* Filter to display only the sound files in the filechooser
@@ -38,28 +39,7 @@ public class SoundFilter
/*
* Else, it tests if the exension is correct
*/
- String extension = Utils.getExtension(f);
- if (extension != null)
- {
- if (extension.equals(Utils.wav) ||
- extension.equals(Utils.mid) ||
- extension.equals(Utils.mp2) ||
- extension.equals(Utils.mp3) ||
- extension.equals(Utils.mod) ||
- extension.equals(Utils.ogg) ||
- extension.equals(Utils.wma) ||
- extension.equals(Utils.au) ||
- extension.equals(Utils.ram))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- return false;
+ return SoundFileUtils.isSoundFile(f);
}
/**
* Method which describes, in the file chooser, the text representing the permit extension
@@ -72,40 +52,3 @@ public class SoundFilter
"*.wav, *.wma)";
}
}
-
-/**
- * class which defines the different permit extension file
- * @author Alexandre Maillard
- */
-class Utils
-{
- /*
- * Differents extension of a sound file
- */
- public final static String wav = "wav";
- public final static String mid = "mid";
- public final static String mp2 = "mp2";
- public final static String mp3 = "mp3";
- public final static String mod = "mod";
- public final static String ram = "ram";
- public final static String wma = "wma";
- public final static String ogg = "ogg";
- public final static String au = "au";
-
- /*
- * Gets the file extension.
- * @param File which wants the extension
- * @return Return the extension as a String
- */
- public static String getExtension(File f)
- {
- String ext = null;
- String s = f.getName();
- int i = s.lastIndexOf('.');
-
- if (i > 0 && i < s.length() - 1)
- ext = s.substring(i+1).toLowerCase();
-
- return ext;
- }
-}
diff --git a/src/net/java/sip/communicator/service/neomedia/MediaService.java b/src/net/java/sip/communicator/service/neomedia/MediaService.java
index 5a8c326..86a6ae5 100644
--- a/src/net/java/sip/communicator/service/neomedia/MediaService.java
+++ b/src/net/java/sip/communicator/service/neomedia/MediaService.java
@@ -152,6 +152,19 @@ public interface MediaService
public ScreenDevice getDefaultScreenDevice();
/**
+ * Creates a new <tt>Recorder</tt> instance that can be used to record a
+ * call which captures and plays back media using a specific
+ * <tt>MediaDevice</tt>.
+ *
+ * @param device the <tt>MediaDevice</tt> which is used for media capture
+ * and playback by the call to be recorded
+ * @return a new <tt>Recorder</tt> instance that can be used to record a
+ * call which captures and plays back media using the specified
+ * <tt>MediaDevice</tt>
+ */
+ public Recorder createRecorder(MediaDevice device);
+
+ /**
* Returns a {@link Map} that binds indicates whatever preferences the
* media service implementation may have for the RTP payload type numbers
* that get dynamically assigned to {@link MediaFormat}s with no static
diff --git a/src/net/java/sip/communicator/service/neomedia/Recorder.java b/src/net/java/sip/communicator/service/neomedia/Recorder.java
new file mode 100644
index 0000000..651dca9
--- /dev/null
+++ b/src/net/java/sip/communicator/service/neomedia/Recorder.java
@@ -0,0 +1,41 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.neomedia;
+
+
+/**
+ * The call recording interface.
+ * Provides the capability to start and stop call recording.
+ *
+ * @author Dmitri Melnikov
+ */
+public interface Recorder
+{
+ /**
+ * Configuration property for the full path to the directory with saved
+ * calls.
+ */
+ public static final String SAVED_CALLS_PATH =
+ "net.java.sip.communicator.impl.neomedia.SAVED_CALLS_PATH";
+ /**
+ * Configuration property format of the saved call.
+ */
+ public static final String CALL_FORMAT =
+ "net.java.sip.communicator.impl.neomedia.CALL_FORMAT";
+
+ /**
+ * Starts the call recording.
+ *
+ * @param callFilename call filename
+ */
+ public void startRecording(String callFilename);
+
+ /**
+ * Stops the call recording.
+ */
+ public void stopRecording();
+}
diff --git a/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java b/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java
index 11a1faf..a8a775d 100644
--- a/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java
+++ b/src/net/java/sip/communicator/service/protocol/ActiveCallsRepository.java
@@ -19,28 +19,27 @@ import net.java.sip.communicator.util.*;
*
* @author Emil Ivov
*/
-public class ActiveCallsRepository<T extends Call,
- U extends AbstractOperationSetBasicTelephony>
+public abstract class ActiveCallsRepository<T extends Call,
+ U extends OperationSetBasicTelephony>
extends CallChangeAdapter
{
/**
* The <tt>Logger</tt> used by the <tt>ActiveCallsRepository</tt>
* class and its instances for logging output.
*/
- private static final Logger logger = Logger
- .getLogger(ActiveCallsRepository.class.getName());
+ private static final Logger logger
+ = Logger.getLogger(ActiveCallsRepository.class);
/**
* A table mapping call ids against call instances.
*/
- private Hashtable<String, T> activeCalls
- = new Hashtable<String, T>();
+ private final Hashtable<String, T> activeCalls = new Hashtable<String, T>();
/**
* The operation set that created us. Instance is mainly used for firing
* events when necessary.
*/
- private final U parentOperationSet;
+ protected final U parentOperationSet;
/**
* Creates a new instance of this repository.
@@ -72,7 +71,7 @@ public class ActiveCallsRepository<T extends Call,
public void callStateChanged(CallChangeEvent evt)
{
if(evt.getEventType().equals(CallChangeEvent.CALL_STATE_CHANGE)
- && evt.getNewValue().equals(CallState.CALL_ENDED))
+ && evt.getNewValue().equals(CallState.CALL_ENDED))
{
T sourceCall =
this.activeCalls.remove(evt.getSourceCall().getCallID());
@@ -81,8 +80,7 @@ public class ActiveCallsRepository<T extends Call,
logger.trace("Removing call " + sourceCall + " from the list of "
+ "active calls because it entered an ENDED state");
- this.parentOperationSet.fireCallEvent(
- CallEvent.CALL_ENDED, sourceCall);
+ fireCallEvent(CallEvent.CALL_ENDED, sourceCall);
}
}
@@ -112,4 +110,20 @@ public class ActiveCallsRepository<T extends Call,
}
}
+ /**
+ * Creates and dispatches a <tt>CallEvent</tt> notifying registered
+ * listeners that an event with id <tt>eventID</tt> has occurred on
+ * <tt>sourceCall</tt>.
+ * <p>
+ * TODO The method is ugly because it can be implemented if
+ * <tt>parentOperationSet</tt> is an
+ * <tt>AbstractOperationSetBasicTelephony</tt>. But after the move of the
+ * latter in the <tt>.service.protocol.media</tt> package, it is not visible
+ * here.
+ * </p>
+ *
+ * @param eventID the ID of the event to dispatch
+ * @param sourceCall the call on which the event has occurred.
+ */
+ protected abstract void fireCallEvent(int eventID, Call sourceCall);
}
diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java
index 31dcb05..2b32c99 100644
--- a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java
+++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java
@@ -6,9 +6,10 @@
*/
package net.java.sip.communicator.service.protocol;
-import net.java.sip.communicator.service.protocol.event.*;
-import java.util.*;
import java.text.*;
+import java.util.*;
+
+import net.java.sip.communicator.service.protocol.event.*;
/**
* An Operation Set defining all basic telephony operations such as conducting
@@ -159,4 +160,19 @@ public interface OperationSetBasicTelephony<T extends ProtocolProviderService>
* this operation set.
*/
public T getProtocolProvider();
+
+ /**
+ * Starts the recording of the <tt>Call</tt>.
+ *
+ * @param call the <tt>Call</tt> to start recording
+ * @param callFilename call filename
+ */
+ public void startRecording(Call call, String callFilename);
+
+ /**
+ * Stops the recording of the <tt>Call</tt>.
+ *
+ * @param call the <tt>Call</tt> to stop recording
+ */
+ public void stopRecording(Call call);
}
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetBasicTelephony.java
index f56dd79..feaaf06 100644
--- a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicTelephony.java
+++ b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetBasicTelephony.java
@@ -4,10 +4,11 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.service.protocol;
+package net.java.sip.communicator.service.protocol.media;
import java.util.*;
+import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
@@ -21,16 +22,19 @@ import net.java.sip.communicator.util.*;
*
* @author Lubomir Marinov
* @author Emil Ivov
+ * @author Dmitri Melnikov
*/
public abstract class AbstractOperationSetBasicTelephony
<T extends ProtocolProviderService>
implements OperationSetBasicTelephony<T>
{
/**
- * Our class logger
+ * The <tt>Logger</tt> used by the
+ * <tt>AbstractOperationSetBasicTelephony</tt> class and its instances for
+ * logging output.
*/
- private static final Logger logger =
- Logger.getLogger(AbstractOperationSetBasicTelephony.class);
+ private static final Logger logger
+ = Logger.getLogger(AbstractOperationSetBasicTelephony.class);
/**
* A list of listeners registered for call events.
@@ -74,11 +78,8 @@ public abstract class AbstractOperationSetBasicTelephony
logger.debug("Dispatching a CallEvent to " + listeners.size()
+ " listeners. event is: " + cEvent);
- for (Iterator<CallListener> listenerIter
- = listeners.iterator(); listenerIter.hasNext();)
+ for (CallListener listener : listeners)
{
- CallListener listener = listenerIter.next();
-
switch (eventID)
{
case CallEvent.CALL_INITIATED:
@@ -116,7 +117,7 @@ public abstract class AbstractOperationSetBasicTelephony
*
* @param call the <tt>Call</tt> whose mute state is to be set
* @param mute <tt>true</tt> to mute the call streams being sent to
- * <tt>peers</tt>; otherwise, <tt>false</tt>
+ * <tt>peers</tt>; otherwise, <tt>false</tt>
*/
public void setMute(Call call, boolean mute)
{
@@ -126,4 +127,26 @@ public abstract class AbstractOperationSetBasicTelephony
* this implementation takes inspiration from them.
*/
}
+
+ /**
+ * Starts the recording of the <tt>Call</tt>.
+ *
+ * @param call the <tt>Call</tt> to start recording
+ * @param callFilename call filename, when <tt>null</tt> a default filename
+ * is used
+ */
+ public void startRecording(Call call, String callFilename)
+ {
+ ((MediaAwareCall<?, ?, ?>) call).startRecording(callFilename);
+ }
+
+ /**
+ * Stops the recording of the <tt>Call</tt>.
+ *
+ * @param call the <tt>Call</tt> to stop recording
+ */
+ public void stopRecording(Call call)
+ {
+ ((MediaAwareCall<?, ?, ?>) call).stopRecording();
+ }
}
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 5dbb3b6..e16f631 100644
--- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java
+++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java
@@ -79,6 +79,11 @@ public abstract class MediaAwareCall<
private boolean mute = false;
/**
+ * The <tt>Recorder</tt> used to record this call.
+ */
+ private Recorder recorder;
+
+ /**
* Device used in call will be chosen according to <tt>MediaUseCase</tt>.
*/
protected MediaUseCase mediaUseCase = MediaUseCase.ANY;
@@ -330,7 +335,7 @@ public abstract class MediaAwareCall<
MediaDevice device = mediaService.getDefaultDevice(mediaType,
mediaUseCase);
- if (MediaType.AUDIO.equals(mediaType) && isConferenceFocus())
+ if (MediaType.AUDIO.equals(mediaType))
{
if (conferenceAudioMixer == null)
{
@@ -565,4 +570,35 @@ public abstract class MediaAwareCall<
peer.removeVideoPropertyChangeListener(listener);
}
}
+
+ /**
+ * Stops the recording of this call.
+ */
+ public void stopRecording()
+ {
+ recorder.stopRecording();
+ }
+
+ /**
+ * Starts the recording of this call.
+ * @param callFilename call filename
+ */
+ public void startRecording(String callFilename)
+ {
+ MediaService mediaService = ProtocolMediaActivator.getMediaService();
+ recorder =
+ mediaService.createRecorder(getDefaultDevice(MediaType.AUDIO));
+
+ recorder.startRecording(callFilename);
+ }
+
+ /**
+ * Returns the recorder used by this instance to record the call.
+ *
+ * @return call recorder
+ */
+ public Recorder getRecorder()
+ {
+ return recorder;
+ }
}
diff --git a/src/net/java/sip/communicator/util/SoundFileUtils.java b/src/net/java/sip/communicator/util/SoundFileUtils.java
new file mode 100644
index 0000000..ec49256
--- /dev/null
+++ b/src/net/java/sip/communicator/util/SoundFileUtils.java
@@ -0,0 +1,78 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.util;
+
+import java.io.*;
+
+/**
+ * Defines the different permit extension file.
+ *
+ * @author Alexandre Maillard
+ * @author Dmitri Melnikov
+ */
+public class SoundFileUtils
+{
+ /**
+ * Different extension of a sound file
+ */
+ public final static String wav = "wav";
+ public final static String mid = "midi";
+ public final static String mp2 = "mp2";
+ public final static String mp3 = "mp3";
+ public final static String mod = "mod";
+ public final static String ram = "ram";
+ public final static String wma = "wma";
+ public final static String ogg = "ogg";
+ public final static String gsm = "gsm";
+ public final static String aif = "aiff";
+ public final static String au = "au";
+
+ /**
+ * Checks whether this file is a sound file.
+ *
+ * @param f <tt>File</tt> to check
+ * @return <tt>true</tt> if it's a sound file, <tt>false</tt> otherwise
+ */
+ public static boolean isSoundFile(File f)
+ {
+ String extension = SoundFileUtils.getExtension(f);
+ if (extension != null)
+ {
+ return extension.equals(SoundFileUtils.wav) ||
+ extension.equals(SoundFileUtils.mid) ||
+ extension.equals(SoundFileUtils.mp2) ||
+ extension.equals(SoundFileUtils.mp3) ||
+ extension.equals(SoundFileUtils.mod) ||
+ extension.equals(SoundFileUtils.ogg) ||
+ extension.equals(SoundFileUtils.wma) ||
+ extension.equals(SoundFileUtils.gsm) ||
+ extension.equals(SoundFileUtils.au) ||
+ extension.equals(SoundFileUtils.ram);
+ }
+ return false;
+ }
+
+ /**
+ * Gets the file extension.
+ * TODO: There are at least 2 other methods like this scattered around
+ * the SC code, we should move them all to util package.
+ *
+ * @param f which wants the extension
+ * @return Return the extension as a String
+ */
+ private static String getExtension(File f)
+ {
+ String ext = null;
+ String s = f.getName();
+ int i = s.lastIndexOf('.');
+
+ if (i > 0 && i < s.length() - 1)
+ ext = s.substring(i+1).toLowerCase();
+
+ return ext;
+ }
+}