diff --git a/build.xml b/build.xml index 516ed7bc3..62ec76c7f 100644 --- a/build.xml +++ b/build.xml @@ -653,8 +653,10 @@ - + + + @@ -997,6 +1001,8 @@ prefix="net/java/sip/communicator/impl/media"/> + @@ -1017,6 +1023,8 @@ prefix="net/java/sip/communicator/impl/media"/> + diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index 62ffe9cd4..8c42b32c0 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -130,7 +130,6 @@ felix.auto.start.60= \ reference:file:sc-bundles/callhistory.jar \ reference:file:sc-bundles/filehistory.jar \ reference:file:sc-bundles/metahistory.jar \ - reference:file:sc-bundles/audionotifier.jar \ reference:file:sc-bundles/keybindings.jar \ reference:file:sc-bundles/notification.jar diff --git a/lib/os-specific/linux/installer-exclude/jmf.jar b/lib/os-specific/linux/installer-exclude/jmf.jar index 50fa7f011..8505b4bc8 100644 Binary files a/lib/os-specific/linux/installer-exclude/jmf.jar and b/lib/os-specific/linux/installer-exclude/jmf.jar differ diff --git a/lib/os-specific/mac/installer-exclude/jmf.jar b/lib/os-specific/mac/installer-exclude/jmf.jar index 406039d10..9345c520d 100644 Binary files a/lib/os-specific/mac/installer-exclude/jmf.jar and b/lib/os-specific/mac/installer-exclude/jmf.jar differ diff --git a/lib/os-specific/solaris/installer-exclude/jmf.jar b/lib/os-specific/solaris/installer-exclude/jmf.jar index bd86a16d5..3dad76dc0 100644 Binary files a/lib/os-specific/solaris/installer-exclude/jmf.jar and b/lib/os-specific/solaris/installer-exclude/jmf.jar differ diff --git a/lib/os-specific/windows/installer-exclude/jmf.jar b/lib/os-specific/windows/installer-exclude/jmf.jar index 55dea3c46..afbe92bdf 100644 Binary files a/lib/os-specific/windows/installer-exclude/jmf.jar and b/lib/os-specific/windows/installer-exclude/jmf.jar differ diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 822955844..c35925bee 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -791,7 +791,10 @@ plugin.profiler.PLUGIN_NAME=Profiler4j impl.media.configform.NO_DEVICE= impl.media.configform.TITLE=Media -impl.media.configform.AUDIO=&Audio: +impl.media.configform.AUDIO=&Audio System: +impl.media.configform.AUDIO_IN=Audio &In: +impl.media.configform.AUDIO_OUT=Audio &Out: +impl.media.configform.AUDIO_NOTIFY=&Notifications: impl.media.configform.DOWN=&Down impl.media.configform.ENCODINGS=&Encodings: impl.media.configform.NO_PREVIEW=Preview diff --git a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c index cb83342d5..92040cfca 100644 --- a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c +++ b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c @@ -166,11 +166,70 @@ Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1Write (*env)->ReleaseByteArrayElements(env, buffer, data, 0); - if (paNoError != errorCode) + if (paNoError != errorCode && errorCode != paOutputUnderflowed) PortAudio_throwException(env, errorCode); + +/* + if(errorCode == paOutputUnderflowed) + { + printf("OutputUnderflowed\n");fflush(stdout); + } +*/ } } +JNIEXPORT void JNICALL +Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1ReadStream + (JNIEnv *env, jclass clazz, jlong stream, jbyteArray buffer, jlong frames) +{ + jbyte* data = (*env)->GetByteArrayElements(env, buffer, NULL); + PaError errorCode = Pa_ReadStream( + ((PortAudioStream *) stream)->stream, + data, + frames); + (*env)->ReleaseByteArrayElements(env, buffer, data, 0); + + if (paNoError != errorCode && errorCode != paInputOverflowed) + PortAudio_throwException(env, errorCode); +} + +JNIEXPORT jlong JNICALL +Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamReadAvailable + (JNIEnv *env, jclass clazz, jlong stream) +{ + return Pa_GetStreamReadAvailable(((PortAudioStream *) stream)->stream); +} + +JNIEXPORT jlong JNICALL +Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamWriteAvailable + (JNIEnv *env, jclass clazz, jlong stream) +{ + return Pa_GetStreamWriteAvailable(((PortAudioStream *) stream)->stream); +} + +JNIEXPORT jint JNICALL +Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetSampleSize + (JNIEnv *env, jclass clazz, jlong format) +{ + return Pa_GetSampleSize(format); +} + +JNIEXPORT jboolean JNICALL +Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1IsFormatSupported + (JNIEnv *env, jclass clazz, + jlong inputParameters, + jlong outputParameters, + jdouble sampleRate) +{ + if(Pa_IsFormatSupported( + (PaStreamParameters *) inputParameters, + (PaStreamParameters *) outputParameters, + sampleRate) == paFormatIsSupported) + return JNI_TRUE; + else + return JNI_FALSE; +} + JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getMaxInputChannels( JNIEnv *env, jclass clazz, jlong deviceInfo) @@ -195,6 +254,13 @@ Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceI return name ? (*env)->NewStringUTF(env, name) : NULL; } +JNIEXPORT jdouble JNICALL +Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getDefaultSampleRate + (JNIEnv *env, jclass clazz, jlong deviceInfo) +{ + return ((PaDeviceInfo *) deviceInfo)->defaultSampleRate; +} + JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaStreamParameters_1new( JNIEnv *env, @@ -211,7 +277,7 @@ Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaStreamP streamParameters->device = deviceIndex; streamParameters->channelCount = channelCount; streamParameters->sampleFormat = sampleFormat; - streamParameters->suggestedLatency = 0; + streamParameters->suggestedLatency = 0,4; streamParameters->hostApiSpecificStreamInfo = NULL; } return (jlong) streamParameters; @@ -319,7 +385,7 @@ PortAudioStream_callback( return paAbort; } - return + return (*env) ->CallIntMethod( env, diff --git a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h index a4a3f43ef..b71b66e44 100644 --- a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h +++ b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h @@ -23,6 +23,45 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_media_protocol_portau JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1WriteStream (JNIEnv *, jclass, jlong, jbyteArray, jlong); +/* + * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio + * Method: Pa_ReadStream + * Signature: (J[BJ)V + */ +JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1ReadStream + (JNIEnv *, jclass, jlong, jbyteArray, jlong); + +/* + * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio + * Method: Pa_GetStreamReadAvailable + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamReadAvailable + (JNIEnv *, jclass, jlong); + +/* + * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio + * Method: Pa_GetStreamWriteAvailable + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamWriteAvailable + (JNIEnv *, jclass, jlong); + +/* + * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio + * Method: Pa_GetSampleSize + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetSampleSize + (JNIEnv *, jclass, jlong); + +/* + * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio + * Method: Pa_IsFormatSupported + * Signature: (JJD)Z + */ +JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1IsFormatSupported + (JNIEnv *, jclass, jlong, jlong, jdouble); /* * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio @@ -96,6 +135,14 @@ JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_media_protocol_portau JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getName (JNIEnv *, jclass, jlong); +/* + * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio + * Method: PaDeviceInfo_getDefaultSampleRate + * Signature: (J)D + */ +JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getDefaultSampleRate + (JNIEnv *, jclass, jlong); + /* * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio * Method: PaStreamParameters_new diff --git a/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java b/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java index aa9206a44..74502815d 100644 --- a/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java +++ b/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java @@ -20,15 +20,31 @@ public class DeviceConfigurationComboBoxModel implements ComboBoxModel { + /** + * Encapsulates CaptureDeviceInfo + */ public static class CaptureDevice { + /** + * Compares two CaptureDeviceInfo + * @param a + * @param b + * @return whether a is equal to b + */ public static boolean equals(CaptureDeviceInfo a, CaptureDeviceInfo b) { return (a == null) ? (b == null) : a.equals(b); } + /** + * The encapsulated info. + */ public final CaptureDeviceInfo info; + /** + * Creates the wrapper. + * @param info the info object we wrap. + */ public CaptureDevice(CaptureDeviceInfo info) { this.info = info; @@ -43,10 +59,31 @@ public String toString() } } + /** + * Type of the model - audio. + */ public static final int AUDIO = 1; + /** + * Type of the model - video. + */ public static final int VIDEO = 2; + /** + * Audio Capture Device. + */ + public static final int AUDIO_CAPTURE = 3; + + /** + * Audio playback device. + */ + public static final int AUDIO_PLAYBACK = 4; + + /** + * Audio device for notification sounds. + */ + public static final int AUDIO_NOTIFY = 5; + private final DeviceConfiguration deviceConfiguration; private CaptureDevice[] devices; @@ -56,12 +93,19 @@ public String toString() private final int type; + /** + * Creates device combobx model + * @param deviceConfiguration the current device configuration + * @param type the device - audio/video + */ public DeviceConfigurationComboBoxModel( DeviceConfiguration deviceConfiguration, int type) { if (deviceConfiguration == null) throw new IllegalArgumentException("deviceConfiguration"); - if ((type != AUDIO) && (type != VIDEO)) + if ((type != AUDIO_CAPTURE) && (type != AUDIO_NOTIFY) && + (type != AUDIO_PLAYBACK) && + (type != AUDIO) && (type != VIDEO)) throw new IllegalArgumentException("type"); this.deviceConfiguration = deviceConfiguration; @@ -77,6 +121,11 @@ public void addListDataListener(ListDataListener listener) listeners.add(listener); } + /** + * Change of the content. + * @param index0 from index. + * @param index1 to index. + */ protected void fireContentsChanged(int index0, int index1) { ListDataListener[] listeners = @@ -101,12 +150,19 @@ private CaptureDevice[] getDevices() if (devices != null) return devices; - DeviceConfiguration deviceConfiguration = getDeviceConfiguration(); + CaptureDeviceInfo[] infos; switch (type) { - case AUDIO: - infos = deviceConfiguration.getAvailableAudioCaptureDevices(); + case AUDIO_CAPTURE: + // supply only portaudio devices, as we are in case specifying + // capture devices available only for portaudio + infos = deviceConfiguration.getAvailableAudioCaptureDevices( + DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO); + break; + case AUDIO_NOTIFY: + case AUDIO_PLAYBACK: + infos = deviceConfiguration.getAvailableAudioPlaybackDevices(); break; case VIDEO: infos = deviceConfiguration.getAvailableVideoCaptureDevices(); @@ -125,20 +181,20 @@ private CaptureDevice[] getDevices() return devices; } - public Object getElementAt(int index) - { - return getDevices()[index]; - } - private CaptureDevice getSelectedDevice() { - DeviceConfiguration deviceConfiguration = getDeviceConfiguration(); CaptureDeviceInfo info; switch (type) { - case AUDIO: + case AUDIO_CAPTURE: info = deviceConfiguration.getAudioCaptureDevice(); break; + case AUDIO_NOTIFY: + info = deviceConfiguration.getAudioNotifyDevice(); + break; + case AUDIO_PLAYBACK: + info = deviceConfiguration.getAudioPlaybackDevice(); + break; case VIDEO: info = deviceConfiguration.getVideoCaptureDevice(); break; @@ -154,14 +210,28 @@ private CaptureDevice getSelectedDevice() return null; } + public Object getElementAt(int index) + { + if(type == AUDIO) + return deviceConfiguration.getAvailableAudioSystems()[index]; + else + return getDevices()[index]; + } + public Object getSelectedItem() { - return getSelectedDevice(); + if(type == AUDIO) + return deviceConfiguration.getAudioSystem(); + else + return getSelectedDevice(); } public int getSize() { - return getDevices().length; + if(type == AUDIO) + return deviceConfiguration.getAvailableAudioSystems().length; + else + return getDevices().length; } public void removeListDataListener(ListDataListener listener) @@ -184,9 +254,15 @@ private void setSelectedDevice(CaptureDevice device) DeviceConfiguration deviceConfiguration = getDeviceConfiguration(); switch (type) { - case AUDIO: + case AUDIO_CAPTURE: deviceConfiguration.setAudioCaptureDevice(device.info); break; + case AUDIO_NOTIFY: + deviceConfiguration.setAudioNotifyDevice(device.info); + break; + case AUDIO_PLAYBACK: + deviceConfiguration.setAudioPlaybackDevice(device.info); + break; case VIDEO: deviceConfiguration.setVideoCaptureDevice(device.info); break; @@ -198,6 +274,17 @@ private void setSelectedDevice(CaptureDevice device) public void setSelectedItem(Object item) { - setSelectedDevice((CaptureDevice) item); + if(type == AUDIO) + { + String systemName = (String)item; + + if(!systemName.equals(deviceConfiguration.getAudioSystem())) + { + deviceConfiguration.setAudioSystem(systemName, null); + fireContentsChanged(-1, -1); + } + } + else + setSelectedDevice((CaptureDevice) item); } } diff --git a/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java b/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java index 351193121..3cc073578 100644 --- a/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java +++ b/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java @@ -16,6 +16,7 @@ import javax.swing.event.*; import javax.swing.table.*; +import net.java.sip.communicator.impl.media.device.*; import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; @@ -66,6 +67,9 @@ private static MediaServiceImpl getMediaService() */ private Player videoPlayerInPreview; + /** + * Creates the panel. + */ public MediaConfigurationPanel() { super(new GridLayout(0, 1, HGAP, VGAP)); @@ -90,12 +94,71 @@ private void controllerUpdateForPreview(ControllerEvent event, } } + private void createPortAudioControls(Container portAudioPanel) + { + portAudioPanel.add(new JLabel(getLabelText( + DeviceConfigurationComboBoxModel.AUDIO_CAPTURE))); + JComboBox captureCombo = new JComboBox(); + captureCombo.setEditable(false); + captureCombo.setModel( + new DeviceConfigurationComboBoxModel( + mediaService.getDeviceConfiguration(), + DeviceConfigurationComboBoxModel.AUDIO_CAPTURE)); + portAudioPanel.add(captureCombo); + + portAudioPanel.add(new JLabel(getLabelText( + DeviceConfigurationComboBoxModel.AUDIO_PLAYBACK))); + JComboBox playbackCombo = new JComboBox(); + playbackCombo.setEditable(false); + playbackCombo.setModel( + new DeviceConfigurationComboBoxModel( + mediaService.getDeviceConfiguration(), + DeviceConfigurationComboBoxModel.AUDIO_PLAYBACK)); + portAudioPanel.add(playbackCombo); + + portAudioPanel.add(new JLabel(getLabelText( + DeviceConfigurationComboBoxModel.AUDIO_NOTIFY))); + JComboBox notifyCombo = new JComboBox(); + notifyCombo.setEditable(false); + notifyCombo.setModel( + new DeviceConfigurationComboBoxModel( + mediaService.getDeviceConfiguration(), + DeviceConfigurationComboBoxModel.AUDIO_NOTIFY)); + portAudioPanel.add(notifyCombo); + } + private Component createControls(int type) { final JComboBox comboBox = new JComboBox(); comboBox.setEditable(false); comboBox.setModel(new DeviceConfigurationComboBoxModel(mediaService .getDeviceConfiguration(), type)); + final Container portAudioPanel = + new TransparentPanel(new GridLayout(3, 2, HGAP, VGAP)); + comboBox.addItemListener(new ItemListener() { + + public void itemStateChanged(ItemEvent e) + { + if(e.getStateChange() == ItemEvent.SELECTED) + { + if(e.getItem().equals( + DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO)) + { + createPortAudioControls(portAudioPanel); + } + else + { + portAudioPanel.removeAll(); + + revalidate(); + repaint(); + } + } + } + }); + if(comboBox.getSelectedItem().equals( + DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO)) + createPortAudioControls(portAudioPanel); JLabel label = new JLabel(getLabelText(type)); label.setDisplayedMnemonic(getDisplayedMnemonic(type)); @@ -111,6 +174,13 @@ private Component createControls(int type) firstConstraints.gridx = 1; firstConstraints.weightx = 1; firstContainer.add(comboBox, firstConstraints); + + firstConstraints.gridx = 0; + firstConstraints.gridy = 1; + firstConstraints.weightx = 1; + firstConstraints.gridwidth = 2; + firstConstraints.insets = new Insets(VGAP, 0, 0, 0); + firstContainer.add(portAudioPanel, firstConstraints); Container secondContainer = new TransparentPanel(new GridLayout(1, 0, HGAP, VGAP)); @@ -392,6 +462,15 @@ private String getLabelText(int type) case DeviceConfigurationComboBoxModel.AUDIO: return MediaActivator.getResources().getI18NString( "impl.media.configform.AUDIO"); + case DeviceConfigurationComboBoxModel.AUDIO_CAPTURE: + return MediaActivator.getResources().getI18NString( + "impl.media.configform.AUDIO_IN"); + case DeviceConfigurationComboBoxModel.AUDIO_NOTIFY: + return MediaActivator.getResources().getI18NString( + "impl.media.configform.AUDIO_NOTIFY"); + case DeviceConfigurationComboBoxModel.AUDIO_PLAYBACK: + return MediaActivator.getResources().getI18NString( + "impl.media.configform.AUDIO_OUT"); case DeviceConfigurationComboBoxModel.VIDEO: return MediaActivator.getResources().getI18NString( "impl.media.configform.VIDEO"); diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java index f290e1aee..63b1d0df1 100644 --- a/src/net/java/sip/communicator/impl/media/MediaControl.java +++ b/src/net/java/sip/communicator/impl/media/MediaControl.java @@ -175,13 +175,18 @@ public void initialize( DeviceConfiguration deviceConfig, { public void propertyChange(PropertyChangeEvent evt) { - try + if(evt.getPropertyName().equals( + DeviceConfiguration.AUDIO_CAPTURE_DEVICE)) { - initCaptureDevices(); - } - catch (MediaException e) - { - logger.error("Cannot init capture devices after change", e); + try + { + initCaptureDevices(); + } + catch (MediaException e) + { + logger.error( + "Cannot init capture devices after change", e); + } } } }); diff --git a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java index 4400d5fa3..dda43227f 100644 --- a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java +++ b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java @@ -14,6 +14,8 @@ import net.java.sip.communicator.impl.media.codec.*; import net.java.sip.communicator.impl.media.device.*; +import net.java.sip.communicator.impl.media.notify.*; +import net.java.sip.communicator.service.audionotifier.*; import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.media.event.*; import net.java.sip.communicator.service.protocol.*; @@ -409,6 +411,9 @@ public void run() defaultMediaControl. initialize(deviceConfiguration, encodingConfiguration); sdpFactory = SdpFactory.getInstance(); + + registerAudioNotifyService(); + isStarted = true; } catch (Throwable ex) @@ -423,6 +428,27 @@ public void run() } } + private void registerAudioNotifyService() + { + //Create the audio notifier service + AudioNotifierServiceImpl audioNotifier = + new AudioNotifierServiceImpl(deviceConfiguration); + + audioNotifier.setMute( + !MediaActivator.getConfigurationService() + .getBoolean( + "net.java.sip.communicator.impl.sound.isSoundEnabled", + true)); + + MediaActivator.getBundleContext() + .registerService( + AudioNotifierService.class.getName(), + audioNotifier, + null); + + logger.info("Audio Notifier Service ...[REGISTERED]"); + } + /** * A utility method that would block until the media service has been * started or, in case it already is started, return immediately. diff --git a/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java b/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java index 5a3abd6b1..237bc4036 100644 --- a/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java +++ b/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java @@ -31,11 +31,25 @@ public class DeviceConfiguration /** * The name of the DeviceConfiguration property which - * represents the device used by DeviceConfiguration for video + * represents the device used by DeviceConfiguration for audio * capture. */ public static final String AUDIO_CAPTURE_DEVICE = "AUDIO_CAPTURE_DEVICE"; + /** + * The name of the DeviceConfiguration property which + * represents the device used by DeviceConfiguration for audio + * playback. + */ + public static final String AUDIO_PLAYBACK_DEVICE = "AUDIO_PLAYBACK_DEVICE"; + + /** + * The name of the DeviceConfiguration property which + * represents the device used by DeviceConfiguration for audio + * notify. + */ + public static final String AUDIO_NOTIFY_DEVICE = "AUDIO_NOTIFY_DEVICE"; + /** * The name of the DeviceConfiguration property which * represents the device used by DeviceConfiguration for video @@ -43,9 +57,30 @@ public class DeviceConfiguration */ public static final String VIDEO_CAPTURE_DEVICE = "VIDEO_CAPTURE_DEVICE"; + /** + * When audio is disabled the selected audio system is with name None. + */ + public static final String AUDIO_SYSTEM_NONE = "None"; + + /** + * JavaSound sound system. + */ + public static final String AUDIO_SYSTEM_JAVASOUND = "JavaSound"; + + /** + * PortAudio sound system. + */ + public static final String AUDIO_SYSTEM_PORTAUDIO = "PortAudio"; + private static final String PROP_AUDIO_DEVICE = "net.java.sip.communicator.impl.media.audiodev"; + private static final String PROP_AUDIO_PLAYBACK_DEVICE = + "net.java.sip.communicator.impl.media.audio.playbackdev"; + + private static final String PROP_AUDIO_NOTIFY_DEVICE = + "net.java.sip.communicator.impl.media.audio.notifydev"; + private static final String PROP_AUDIO_DEVICE_IS_DISABLED = "net.java.sip.communicator.impl.media.audiodevIsDisabled"; @@ -65,11 +100,19 @@ public class DeviceConfiguration */ private CaptureDeviceInfo audioCaptureDevice = null; + private CaptureDeviceInfo audioPlaybackDevice = null; + + private CaptureDeviceInfo audioNotifyDevice = null; + /** * The device that we'll be using for video capture. */ private CaptureDeviceInfo videoCaptureDevice; + private static Vector audioSystems = new Vector(); + + private String audioSystem = null; + /** * Default constructor. */ @@ -109,11 +152,15 @@ private void extractConfiguredCaptureDevices() CaptureDeviceInfo[] audioCaptureDevices = getAvailableAudioCaptureDevices(); if (config.getBoolean(PROP_AUDIO_DEVICE_IS_DISABLED, false)) + { audioCaptureDevice = null; + audioSystem = AUDIO_SYSTEM_NONE; + } else if (audioCaptureDevices.length < 1) { logger.warn("No Audio Device was found."); audioCaptureDevice = null; + audioSystem = AUDIO_SYSTEM_NONE; } else { @@ -123,17 +170,33 @@ else if (audioCaptureDevices.length < 1) String audioDevName = config.getString(PROP_AUDIO_DEVICE); if(audioDevName == null) - audioCaptureDevice = audioCaptureDevices[0]; + { + // the default behaviour if nothing set is to use javasound + // this will also choose the capture device + setAudioSystem(AUDIO_SYSTEM_JAVASOUND, null); + } else { for (CaptureDeviceInfo captureDeviceInfo : audioCaptureDevices) { if (audioDevName.equals(captureDeviceInfo.getName())) { - audioCaptureDevice = captureDeviceInfo; + setAudioSystem(getAudioSystem(captureDeviceInfo), + captureDeviceInfo); break; } } + + if(getAudioSystem() == null) + { + logger.warn("Computer sound config changed or " + + "there is a problem since last config was saved, " + + "will back to default javasound"); + setAudioPlaybackDevice(null); + setAudioNotifyDevice(null); + setAudioCaptureDevice(null); + setAudioSystem(AUDIO_SYSTEM_JAVASOUND, null); + } } if (audioCaptureDevice != null) logger.info("Found " + audioCaptureDevice.getName() @@ -221,6 +284,56 @@ public CaptureDeviceInfo[] getAvailableAudioCaptureDevices() return audioCaptureDevices.toArray(NO_CAPTURE_DEVICES); } + /** + * Gets the list of audio capture devices which are available through this + * DeviceConfiguration, amongst which is + * {@link #getAudioCaptureDevice()} and represent acceptable values + * for {@link #setAudioCaptureDevice(CaptureDeviceInfo)} + * + * @param soundSystem + * filter capture devices only from the supplied audio system. + * + * @return an array of CaptureDeviceInfo describing the audio + * capture devices available through this + * DeviceConfiguration + */ + public CaptureDeviceInfo[] getAvailableAudioCaptureDevices(String soundSystem) + { + String protocol = null; + if(soundSystem.equals(AUDIO_SYSTEM_JAVASOUND)) + protocol = "javasound"; + else if(soundSystem.equals(AUDIO_SYSTEM_PORTAUDIO)) + protocol = "portaudio"; + + Vector res = new Vector(); + + if(protocol != null) + { + CaptureDeviceInfo[] all = getAvailableAudioCaptureDevices(); + for(int i = 0; i < all.length; i++) + { + CaptureDeviceInfo cDeviceInfo = all[i]; + if(cDeviceInfo.getLocator().getProtocol().equals(protocol)) + { + res.add(cDeviceInfo); + } + } + } + + return res.toArray(NO_CAPTURE_DEVICES); + } + + /** + * Lists all the playback devices. These are only portaudio devices + * as we can only set particular device for playback when using portaudio. + * + * @return the devices that can be used for playback. + */ + public CaptureDeviceInfo[] getAvailableAudioPlaybackDevices() + { + return PortAudioAuto.playbackDevices; + } + /** * Gets the list of video capture devices which are available through this * DeviceConfiguration, amongst which is @@ -300,11 +413,14 @@ public void setAudioCaptureDevice(CaptureDeviceInfo device) ConfigurationService config = MediaActivator.getConfigurationService(); - config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED, - audioCaptureDevice == null); + if (audioCaptureDevice != null) + { config.setProperty(PROP_AUDIO_DEVICE, audioCaptureDevice .getName()); + } + else + config.setProperty(PROP_AUDIO_DEVICE, null); firePropertyChange(AUDIO_CAPTURE_DEVICE, oldDevice, device); } @@ -329,4 +445,327 @@ public boolean isVideoCaptureSupported() { return this.videoCaptureDevice != null; } + + /** + * Return the installed Audio Systems. + * @return the audio systems names. + */ + public String[] getAvailableAudioSystems() + { + return audioSystems.toArray(new String[0]); + } + + /** + * Adds audio system. + * @param audioSystemName the name of the audio system. + */ + public static void addAudioSystem(String audioSystemName) + { + audioSystems.add(audioSystemName); + } + + /** + * The current selected audio system. + * @return the name of the current audio system. + */ + public String getAudioSystem() + { + return audioSystem; + } + + private String getAudioSystem(CaptureDeviceInfo cdi) + { + String res = null; + // Here we iterate over the available audio systems + // to be sure that the audio system + // is available and enabled on the system we are running on + if(cdi.getLocator().getProtocol().equals("javasound")) + { + Iterator iter = audioSystems.iterator(); + while (iter.hasNext()) + { + String asName = iter.next(); + if(asName.equals(AUDIO_SYSTEM_JAVASOUND)) + res = asName; + } + } + else if(cdi.getLocator().getProtocol().equals("portaudio")) + { + Iterator iter = audioSystems.iterator(); + while (iter.hasNext()) + { + String asName = iter.next(); + if(asName.equals(AUDIO_SYSTEM_PORTAUDIO)) + res = asName; + } + } + + if(res == null) + res = AUDIO_SYSTEM_NONE; + + return res; + } + + /** + * Changes the current audio system. + * When javasound is selected we also change the capture device. + * + * @param name the name of the audio system. + * @param captureDevice the selected capture device, if is null we will + * choose a default one. Param used when first time initing and + * extracting config. + */ + public void setAudioSystem(String name, CaptureDeviceInfo captureDevice) + { + ConfigurationService config = MediaActivator.getConfigurationService(); + + audioSystem = name; + + if(name.equals(AUDIO_SYSTEM_NONE)) + { + setAudioCaptureDevice(null); + setAudioNotifyDevice(null); + setAudioPlaybackDevice(null); + } + else if(name.equals(AUDIO_SYSTEM_JAVASOUND)) + { + setAudioNotifyDevice(null); + setAudioPlaybackDevice(null); + + // as there is only one device for javasound + // lets search for it + if(captureDevice != null) + setAudioCaptureDevice(captureDevice); + else + { + CaptureDeviceInfo[] audioCaptureDevices = + getAvailableAudioCaptureDevices(); + for (CaptureDeviceInfo captureDeviceInfo : audioCaptureDevices) + { + if(captureDeviceInfo.getLocator().getProtocol(). + equals("javasound")) + { + setAudioCaptureDevice(captureDeviceInfo); + break; + } + } + } + + // if we have inited the audiocaptureDevice, it means javasound is + // available and everything is ok + if (audioCaptureDevice != null) + { + removePortAudioRenderer(); + initJavaSoundRenderer(); + } + } + else if(name.equals(AUDIO_SYSTEM_PORTAUDIO)) + { + // changed to portaudio, so lets clear current device selection + // as we must select them + // if this is first time call devices will be already null + // and nothing will happen + setAudioCaptureDevice(null); + setAudioNotifyDevice(null); + setAudioPlaybackDevice(null); + + // we don't save anything cause it will be saved + // when the devices are stored + // if nothing is set we consider it as not configured + // so when we restart we will end up with default config + // till restart will use latest config + + // capture device is not null when we are called for the + // first time, we will also extract playback devices here + if(captureDevice != null) + { + setAudioCaptureDevice(captureDevice); + + String audioDevName = config.getString(PROP_AUDIO_NOTIFY_DEVICE); + if(audioDevName != null) + { + for (CaptureDeviceInfo captureDeviceInfo : + PortAudioAuto.playbackDevices) + { + if (audioDevName.equals(captureDeviceInfo.getName())) + { + this.audioNotifyDevice = captureDeviceInfo; + break; + } + } + } + + audioDevName = config.getString(PROP_AUDIO_PLAYBACK_DEVICE); + if(audioDevName != null) + { + for (CaptureDeviceInfo captureDeviceInfo : + PortAudioAuto.playbackDevices) + { + if (audioDevName.equals(captureDeviceInfo.getName())) + { + this.audioPlaybackDevice = captureDeviceInfo; + setDeviceToRenderer(audioPlaybackDevice); + removeJavaSoundRenderer(); + break; + } + } + } + } + + // return here to prevent clearing the last config that was saved + return; + } + else + { + // not expected behaviour + logger.error("Unknown audio system! Name:" + name); + audioSystem = null; + } + + config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED, + audioCaptureDevice == null); + } + + /** + * Installs the PortAudio Renderer + */ + protected static void initPortAudioRenderer() + { + PlugInManager.addPlugIn( + "net.java.sip.communicator.impl.media.renderer.audio.PortAudioRenderer", + net.java.sip.communicator.impl.media.renderer.audio. + PortAudioRenderer.supportedInputFormats, + null, + PlugInManager.RENDERER); + } + + private void removeJavaSoundRenderer() + { + PlugInManager.removePlugIn( + "com.sun.media.renderer.audio.JavaSoundRenderer", + PlugInManager.RENDERER); + } + + private void removePortAudioRenderer() + { + PlugInManager.removePlugIn( + "net.java.sip.communicator.impl.media.renderer.audio.PortAudioRenderer", + PlugInManager.RENDERER); + } + + private void initJavaSoundRenderer() + { + try + { + PlugInManager.addPlugIn( + "com.sun.media.renderer.audio.JavaSoundRenderer", + new com.sun.media.renderer.audio.JavaSoundRenderer() + .getSupportedInputFormats(), + null, + PlugInManager.RENDERER); + } + catch (Exception e) + { + // if class is missing + logger.error("Problem init javasound renderer", e); + } + } + + private void setDeviceToRenderer(CaptureDeviceInfo devInfo) + { + // no need to change device to renderer it will not be used anyway + if(devInfo == null) + return; + + try + { + net.java.sip.communicator.impl.media.renderer.audio. + PortAudioRenderer.setDevice(devInfo.getLocator()); + } + catch (Exception e) + { + logger.error("error setting device to renderer", e); + } + } + + /** + * @return the audioPlaybackDevice + */ + public CaptureDeviceInfo getAudioPlaybackDevice() + { + return audioPlaybackDevice; + } + + /** + * @return the audioNotifyDevice + */ + public CaptureDeviceInfo getAudioNotifyDevice() + { + return audioNotifyDevice; + } + + /** + * @param audioPlaybackDevice the audioPlaybackDevice to set + */ + public void setAudioPlaybackDevice(CaptureDeviceInfo audioPlaybackDevice) + { + if(this.audioPlaybackDevice != audioPlaybackDevice) + { + CaptureDeviceInfo oldDev = this.audioPlaybackDevice; + this.audioPlaybackDevice = audioPlaybackDevice; + setDeviceToRenderer(audioPlaybackDevice); + + // we changed playback device, so we are using portaudio + // lets use it, remove javasound renderer to be sure + // its not used anymore and install the portaudio one + removeJavaSoundRenderer(); + initPortAudioRenderer(); + + ConfigurationService config = + MediaActivator.getConfigurationService(); + + if (audioPlaybackDevice != null) + { + config.setProperty(PROP_AUDIO_PLAYBACK_DEVICE, + audioPlaybackDevice.getName()); + + config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED, false); + } + else + config.setProperty(PROP_AUDIO_PLAYBACK_DEVICE, null); + + firePropertyChange(AUDIO_PLAYBACK_DEVICE, + oldDev, audioPlaybackDevice); + } + } + + /** + * @param audioNotifyDevice the audioNotifyDevice to set + */ + public void setAudioNotifyDevice(CaptureDeviceInfo audioNotifyDevice) + { + if(this.audioNotifyDevice != audioNotifyDevice) + { + CaptureDeviceInfo oldDev = this.audioNotifyDevice; + this.audioNotifyDevice = audioNotifyDevice; + + ConfigurationService config = + MediaActivator.getConfigurationService(); + + if (audioNotifyDevice != null) + { + config.setProperty(PROP_AUDIO_NOTIFY_DEVICE, + audioNotifyDevice.getName()); + + // atleast notify or playback must be set to consider + // portaudio for enabled + config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED, false); + } + else + config.setProperty(PROP_AUDIO_NOTIFY_DEVICE, null); + + firePropertyChange(AUDIO_NOTIFY_DEVICE, + oldDev, audioNotifyDevice); + } + } } diff --git a/src/net/java/sip/communicator/impl/media/device/DirectSoundAuto.java b/src/net/java/sip/communicator/impl/media/device/DirectSoundAuto.java deleted file mode 100644 index 1c33dafa3..000000000 --- a/src/net/java/sip/communicator/impl/media/device/DirectSoundAuto.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - * - * File based on: - * @(#)DirectSoundAuto.java 1.3 01/03/13 - * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. - */ -package net.java.sip.communicator.impl.media.device; - -import java.util.*; -import javax.media.*; -import javax.media.format.*; - -import net.java.sip.communicator.util.*; - -public class DirectSoundAuto { - - private static final Logger logger = Logger.getLogger(DirectSoundAuto.class); - - private static final String detectClass = "com.sun.media.protocol.dsound.DSound"; - CaptureDeviceInfo[] devices = null; - - public static void main(String[] args) { - new DirectSoundAuto(); - System.exit(0); - } - - private boolean supports(AudioFormat af) { - try { - com.sun.media.protocol.dsound.DSound ds; - ds = new com.sun.media.protocol.dsound.DSound(af, 1024); - ds.open(); - ds.close(); - } catch (Exception e) { - logger.error(e); - return false; - } - return true; - } - - @SuppressWarnings("unchecked") //legacy JMF code. - public DirectSoundAuto() { - boolean supported = false; - // instance JavaSoundDetector to check is javasound's capture is availabe - try { - Class.forName(detectClass); - supported = true; - } catch (Throwable t) { - supported = false; - // t.printStackTrace(); - } - - logger.info("DirectSound Capture Supported = " + supported); - - if (supported) { - // It's there, start to register JavaSound with CaptureDeviceManager - Vector devices - = (Vector)CaptureDeviceManager - .getDeviceList(null).clone(); - - // remove the old direct sound capturers - String name; - Enumeration enumeration = devices.elements(); - while (enumeration.hasMoreElements()) { - CaptureDeviceInfo cdi = enumeration.nextElement(); - name = cdi.getName(); - if (name.startsWith(com.sun.media.protocol.dsound.DataSource.NAME)) - CaptureDeviceManager.removeDevice(cdi); - } - int LE = AudioFormat.LITTLE_ENDIAN; - int SI = AudioFormat.SIGNED; - int US = AudioFormat.UNSIGNED; - int UN = AudioFormat.NOT_SPECIFIED; - float [] Rates = new float[] { - 48000, 44100, 32000, 22050, 16000, 11025, 8000 - }; - Vector formats = new Vector(4); - for (int rateIndex = 0; rateIndex < Rates.length; rateIndex++) { - float rate = Rates[rateIndex]; - AudioFormat af; - af = new AudioFormat(AudioFormat.LINEAR, rate, 16, 2, LE, SI); - if (supports(af)) formats.addElement(af); - af = new AudioFormat(AudioFormat.LINEAR, rate, 16, 1, LE, SI); - if (supports(af)) formats.addElement(af); - af = new AudioFormat(AudioFormat.LINEAR, rate, 8, 2, UN, US); - if (supports(af)) formats.addElement(af); - af = new AudioFormat(AudioFormat.LINEAR, rate, 8, 1, UN, US); - if (supports(af)) formats.addElement(af); - } - - AudioFormat [] formatArray = new AudioFormat[formats.size()]; - for (int fa = 0; fa < formatArray.length; fa++) - formatArray[fa] = formats.elementAt(fa); - - CaptureDeviceInfo cdi = new CaptureDeviceInfo( - com.sun.media.protocol.dsound.DataSource.NAME, - new MediaLocator("dsound://"), - formatArray); - CaptureDeviceManager.addDevice(cdi); - try { - CaptureDeviceManager.commit(); - logger.info("DirectSoundAuto: Committed ok"); - } catch (java.io.IOException ioe) { - logger.error("DirectSoundAuto: error committing cdm"); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/media/device/JMFInit.java b/src/net/java/sip/communicator/impl/media/device/JMFInit.java deleted file mode 100644 index 63d792da9..000000000 --- a/src/net/java/sip/communicator/impl/media/device/JMFInit.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - * - * File based on: - * @(#)JMFInit.java 1.14 03/04/30 - * Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved. - */ -package net.java.sip.communicator.impl.media.device; - -import java.io.*; -import java.util.*; -import javax.media.*; -import javax.media.format.*; - -import net.java.sip.communicator.util.*; - - -public class JMFInit - implements Runnable { - - private static final Logger logger = Logger.getLogger(JMFInit.class); - - public JMFInit() { - -// try { -// Registry.commit(); -// } -// catch (Exception e) { -// logger.error("Failed to commit to JMFRegistry!", e ); -// } - - - Thread detectThread = new Thread(this); - detectThread.run(); - -/* - int slept = 0; - while (!done && slept < 60 * 1000 * 2) { - try { - Thread.currentThread().sleep(500); - } - catch (InterruptedException ie) { - } - slept += 500; - } - - if (!done) { - console.error("Detection is taking too long! Aborting!"); - message("Detection is taking too long! Aborting!"); - } - - try { - Thread.currentThread().sleep(2000); - } - catch (InterruptedException ie) { - } -*/ - } - - /** - * Detect all capture devices - */ - public void run() { - detectDirectAudio(); - detectS8DirectAudio(); - detectCaptureDevices(); - } - - private void detectCaptureDevices() { - // check if JavaSound capture is available - logger.info("Looking for Audio capturer"); - Class dsauto = null; - try { - dsauto = Class.forName( - "net.java.sip.communicator.impl.media.device.DirectSoundAuto"); - dsauto.newInstance(); - logger.info("Finished detecting DirectSound capturer"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - logger.warn("DirectSound capturer detection failed!", t); - } - - Class jsauto = null; - try { - jsauto = Class.forName( - "net.java.sip.communicator.impl.media.device.JavaSoundAuto"); - jsauto.newInstance(); - logger.info("Finished detecting JavaSound capturer"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - logger.warn("JavaSound capturer detection failed!", t); - } - - // Check if VFWAuto or SunVideoAuto is available - logger.info("Looking for video capture devices"); - Class auto = null; - Class autoPlus = null; - try { - auto = Class.forName( - "net.java.sip.communicator.impl.media.device.VFWAuto"); - } - catch (Exception e) { - logger.warn("VFWAuto capturer detection failed!", e); - } - if (auto == null) { - try { - auto = Class.forName( - "net.java.sip.communicator.impl.media.device.SunVideoAuto"); - } - catch (Exception ee) { - logger.warn("SunVideoAuto capturer detection failed!", ee); - } - try { - autoPlus = Class.forName( - "net.java.sip.communicator.impl.media.device.SunVideoPlusAuto"); - } - catch (Exception ee) { - logger.warn("SunVideoPlusAuto capturer detection failed!", ee); - } - } - if (auto == null) { - try { - auto = Class.forName( - "net.java.sip.communicator.impl.media.device.V4LAuto"); - } - catch (Exception ee) { - logger.warn("V4lAuto capturer detection failed!", ee); - } - } - try { - auto.newInstance(); - if (autoPlus != null) { - autoPlus.newInstance(); - } - logger.info("Finished detecting video capture devices"); - } - catch (ThreadDeath td) { - throw td; - } - catch (Throwable t) { - logger.error("Capture device detection failed!", t); - } - } - - @SuppressWarnings("unchecked") //legacy JMF code. - private void detectDirectAudio() { - Class cls; - int plType = PlugInManager.RENDERER; - String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; - try { - // Check if this is the Windows Performance Pack - hack - cls = Class.forName( - "net.java.sip.communicator.impl.media.device.VFWAuto"); - // Check if DS capture is supported, otherwise fail DS renderer - // since NT doesn't have capture - cls = Class.forName("com.sun.media.protocol.dsound.DSound"); - // Find the renderer class and instantiate it. - cls = Class.forName(dar); - - Renderer rend = (Renderer) cls.newInstance(); - try { - // Set the format and open the device - AudioFormat af = new AudioFormat(AudioFormat.LINEAR, - 44100, 16, 2); - rend.setInputFormat(af); - rend.open(); - Format[] inputFormats = rend.getSupportedInputFormats(); - // Register the device - PlugInManager.addPlugIn(dar, inputFormats, new Format[0], - plType); - // Move it to the top of the list - Vector rendList = - PlugInManager.getPlugInList(null, null, plType); - int listSize = rendList.size(); - if (rendList.elementAt(listSize - 1).equals(dar)) { - rendList.removeElementAt(listSize - 1); - rendList.insertElementAt(dar, 0); - PlugInManager.setPlugInList(rendList, plType); - PlugInManager.commit(); - //System.err.println("registered"); - } - rend.close(); - } - catch (Throwable t) { - //System.err.println("Error " + t); - } - } - catch (Throwable tt) { - } - } - - private void detectS8DirectAudio() { - try - { - new S8DirectAudioAuto(); - } - catch (Throwable tt) - { - } - } - - /** - * Runs JMFInit the first time the application is started so that capture - * devices are properly detected and initialized by JMF. - */ - public static void setupJMF() - { - try - { - logger.logEntry(); - - // .jmf is the place where we store the jmf.properties file used - // by JMF. if the directory does not exist or it does not contain - // a jmf.properties file, or if the jmf.properties file has 0 length - // then this is the first time we're running and should detect capture - // devices - String homeDir = System.getProperty("user.home"); - File jmfDir = new File(homeDir, ".jmf"); - String classpath = System.getProperty("java.class.path"); - classpath += System.getProperty("path.separator") + - jmfDir.getAbsolutePath(); - System.setProperty("java.class.path", classpath); - - if (!jmfDir.exists()) - jmfDir.mkdir(); - - File jmfProperties = new File(jmfDir, "jmf.properties"); - - if (!jmfProperties.exists()) { - try { - jmfProperties.createNewFile(); - } - catch (IOException ex) { - logger.error( - "Failed to create jmf.properties - " + - jmfProperties.getAbsolutePath()); - } - } - - //if we're running on linux checkout that libjmutil.so is where it - //should be and put it there. -// runLinuxPreInstall(); - - if (jmfProperties.length() == 0) { - new JMFInit(); - } - } - finally - { - logger.logExit(); - } - - } - - -// private static void runLinuxPreInstall() -// { -// try { -// logger.logEntry(); -// -// if (Utils.getProperty("os.name") == null -// || !Utils.getProperty("os.name").equalsIgnoreCase("Linux")) -// return; -// -// try { -// System.loadLibrary("jmv4l"); -// console.debug("Successfully loaded libjmv4l.so"); -// } -// catch (UnsatisfiedLinkError err) { -// console.debug("Failed to load libjmv4l.so. Will try and copy libjmutil.so", err); -// -// String destinationPathStr = Utils.getProperty("java.home") -// + File.separator + "lib" -// + File.separator + "i386"; -// String libjmutilFileStr = "libjmutil.so"; -// -// try { -// InputStream libIS = -// MediaManager.class.getClassLoader(). -// getResourceAsStream(libjmutilFileStr); -// File outFile = new File(destinationPathStr -// +File.separator + libjmutilFileStr); -// -// //Check if file is already there - Ben Asselstine -// if (outFile.exists()) { -// //if we're here then libjmutil is already where it should be -// // but yet we failed to load libjmv4l. -// //so notify log and bail out -// console.error( -// "An error occurred while trying to load JMF. This " -// +"error is probably due to a JMF installation problem. " -// +"Please copy libjmutil.so to a location contained by " -// + "$LD_LIBRARY_PATH and try again!", -// err); -// return; -// -// } -// -// outFile.createNewFile(); -// -// console.debug("jmutil"); -// -// FileOutputStream fileOS = new FileOutputStream(outFile); -// int available = libIS.available(); -// byte[] bytes = new byte[libIS.available()]; -// int read = 0; -// int i = 0; -// for (i = 0; i playbackDevVector = + new Vector(); + + for (; deviceIndex < deviceCount; deviceIndex++) + { + long deviceInfo = PortAudio.Pa_GetDeviceInfo(deviceIndex); + int maxInputChannels = + PortAudio.PaDeviceInfo_getMaxInputChannels(deviceInfo); + int maxOutputChannels = + PortAudio.PaDeviceInfo_getMaxOutputChannels(deviceInfo); - formats[0] = PortAudioStream.audioFormat; + CaptureDeviceInfo jmfInfo = + new CaptureDeviceInfo( + PortAudio.PaDeviceInfo_getName(deviceInfo), + new MediaLocator( + PortAudioStream.LOCATOR_PREFIX + deviceIndex), + PortAudioStream.getFormats()); - CaptureDeviceInfo jmfInfo = - new CaptureDeviceInfo("portaudio:1", - new MediaLocator("portaudio:#" + 1), formats); + if(maxInputChannels > 0) + { + CaptureDeviceManager.addDevice(jmfInfo); + } - CaptureDeviceManager.addDevice(jmfInfo); + if(maxOutputChannels > 0) + { + playbackDevVector.add(jmfInfo); + } + } + + playbackDevices = playbackDevVector.toArray(new CaptureDeviceInfo[0]); CaptureDeviceManager.commit(); + + // Enables Portaudio Renderer + DeviceConfiguration.initPortAudioRenderer(); + + // now add it as available audio system to DeviceConfiguration + DeviceConfiguration.addAudioSystem( + DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO); } } diff --git a/src/net/java/sip/communicator/impl/media/device/S8DirectAudioAuto.java b/src/net/java/sip/communicator/impl/media/device/S8DirectAudioAuto.java deleted file mode 100644 index e28b6d4c5..000000000 --- a/src/net/java/sip/communicator/impl/media/device/S8DirectAudioAuto.java +++ /dev/null @@ -1,62 +0,0 @@ -package net.java.sip.communicator.impl.media.device; - -import java.util.*; - -import javax.media.*; - -import com.sun.media.*; - -/** - * Probes for JMF Solaris 8 direct audio. - * - * @author Emil Ivov - * @author Ken Larson - */ -public class S8DirectAudioAuto -{ - @SuppressWarnings("unchecked") //legacy JMF code. - public S8DirectAudioAuto() throws Exception - { - Class cls; - int plType = PlugInManager.RENDERER; - String dar = "com.sun.media.renderer.audio.DirectAudioRenderer"; - - // Check if this is the solaris Performance Pack - hack - cls = Class.forName( - "net.java.sip.communicator.impl.media.device.SunVideoAuto"); - - // Find the renderer class and instantiate it. - cls = Class.forName(dar); - - Renderer rend = (Renderer) cls.newInstance(); - - if (rend instanceof ExclusiveUse && - ! ( (ExclusiveUse) rend).isExclusive()) - { - // sol8+, DAR supports mixing - Vector rendList = PlugInManager.getPlugInList(null, null, - plType); - int listSize = rendList.size(); - boolean found = false; - String rname = null; - - for (int i = 0; i < listSize; i++) - { - rname = rendList.elementAt(i); - if (rname.equals(dar)) - { // DAR is in the registry - found = true; - rendList.removeElementAt(i); - break; - } - } - - if (found) - { - rendList.insertElementAt(dar, 0); - PlugInManager.setPlugInList(rendList, plType); - PlugInManager.commit(); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/media/device/SunVideoAuto.java b/src/net/java/sip/communicator/impl/media/device/SunVideoAuto.java deleted file mode 100644 index c387f09f2..000000000 --- a/src/net/java/sip/communicator/impl/media/device/SunVideoAuto.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - * - * File based on: - * @(#)SunVideoAuto.java 1.8 01/03/13 - * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. - */ -package net.java.sip.communicator.impl.media.device; - -import java.io.*; -import java.util.*; -import javax.media.*; -import javax.media.format.*; - -import java.awt.*; - -import net.java.sip.communicator.util.*; -import com.sun.media.protocol.sunvideo.*; - - -public class SunVideoAuto -{ - - private static final Logger logger = Logger.getLogger(SunVideoAuto.class); - - private static String DEVICE_PREFIX = "/dev/rtvc"; - private static String PROTOCOL = "sunvideo"; - private static String LOCATOR_PREFIX = PROTOCOL + "://"; - CaptureDeviceInfo [] devices = null; - int currentID = -1; - - XILCapture xilCap; - - Vector formats = null; - - int [] ports = { 1, 2, 0 }; // most likely ports for a device - - int [] scales = { 2, 4, 1 }; // supported scales / sizes - - /** - * Default constructor. Does nothing. - */ - public SunVideoAuto() - { - } - - /** - * Removes from the CaptureDeviceManager all currently detected vfw devices - * and runs a new detection loop to rediscover those that are currently - * available. - * - * @return the number of devices detected. - */ - @SuppressWarnings("unchecked") //legacy JMF code. - public int autoDetectDevices() - { - Vector devices - = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); - Enumeration enumeration = devices.elements(); - while (enumeration.hasMoreElements()) { - CaptureDeviceInfo cdi = enumeration.nextElement(); - String devName = cdi.getLocator().getProtocol(); - if (devName.equals(PROTOCOL)) - CaptureDeviceManager.removeDevice(cdi); - } - - int nDevices = 0; - for (int i = 0; i < 7; i++) { - File fl = new File(DEVICE_PREFIX + i); - if (fl.exists()) { - doDevice(i); - nDevices++; - } - } - try { - CaptureDeviceManager.commit(); - } catch (java.io.IOException ioe) { - logger.error("SunVideoAuto: error committing cdm", ioe); - return 0; - } - - return nDevices; - } - - private void addFormat(Format fin) - { - Enumeration enumeration = formats.elements(); - while (enumeration.hasMoreElements()) { - Format format = enumeration.nextElement(); - if (format.equals(fin)) - return; - } - - //System.err.println("New format = " + fin); - formats.addElement(fin); - } - - private void doDevice(int index) - { - - xilCap = new XILCapture(null); - formats = new Vector(); - //boolean gotPort = false; - - if (!xilCap.connect(index)) { - dummyDevice(index); - return; - } - - - for (int i = 0; i < ports.length; i++) { - if (xilCap.setPort(ports[i])) { - getJpegFormats(); - getRGBFormats(); - } - } - xilCap.disconnect(); - - if (formats.size() > 0) - addDevice(index); - else - dummyDevice(index); - } - - - private void getRGBFormats() - { - if (!xilCap.setCompress("RGB")) - return; - for (int i = 0; i < scales.length; i++) { - xilCap.setScale(scales[i]); - // To get the real values, start the device - if (xilCap.start()) { - Dimension size = new Dimension(xilCap.getWidth(), - xilCap.getHeight()); - int stride = xilCap.getLineStride(); - int maxbuf = stride * size.width; - addFormat(new RGBFormat(size, maxbuf, byte[].class, - 15f, - 24, - 3, 2, 1, 3, stride, - Format.FALSE, - Format.NOT_SPECIFIED)); - } - xilCap.stop(); - } - } - - private void getJpegFormats() { - if (!xilCap.setCompress("Jpeg")) - return; - for (int i = 0; i < scales.length; i++) { - xilCap.setScale(scales[i]); - // To get the real values, start the device - if (xilCap.start()) { - Dimension size = new Dimension(xilCap.getWidth(), - xilCap.getHeight()); - // approximate the max for high quality - int maxbuf = 3 * size.width * size.height; - addFormat(new VideoFormat(VideoFormat.JPEG, size, maxbuf, - byte[].class, 15f)); - } - xilCap.stop(); - } - } - - private void dummyDevice(int index) - { - // Can't get to the device, use the barest formats - addFormat(new VideoFormat(VideoFormat.JPEG)); - addFormat(new RGBFormat()); - addDevice(index); - } - - - private void addDevice(int index) - { - - String name = "SunVideo device " + index; - String locator = LOCATOR_PREFIX + index; - - Format [] farray = new Format[formats.size()]; - Enumeration enumeration = formats.elements(); - - int i = 0; - while (enumeration.hasMoreElements()) { - Format format = enumeration.nextElement(); - farray[i++] = format; - } - - CaptureDeviceInfo cdi = new CaptureDeviceInfo(name, - new MediaLocator(locator), farray); - CaptureDeviceManager.addDevice(cdi); - } - - public static void main(String [] args) - { - SunVideoAuto sunVideoAuto = new SunVideoAuto(); - sunVideoAuto.autoDetectDevices(); - System.exit(0); - } -} - diff --git a/src/net/java/sip/communicator/impl/media/device/SunVideoPlusAuto.java b/src/net/java/sip/communicator/impl/media/device/SunVideoPlusAuto.java deleted file mode 100644 index eafd7f892..000000000 --- a/src/net/java/sip/communicator/impl/media/device/SunVideoPlusAuto.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - * - * File based on: - * @(#)SunVideoPlusAuto.java 1.6 01/03/13 - * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. - */ -package net.java.sip.communicator.impl.media.device; - -import java.io.*; -import java.util.*; -import javax.media.*; -import javax.media.format.*; -import javax.media.protocol.*; - -import java.awt.*; - -import com.sun.media.protocol.sunvideoplus.*; -import net.java.sip.communicator.util.*; - -/** - * SIP Communicator modifications. - * @author Emil Ivov - */ -public class SunVideoPlusAuto -{ - private static final Logger logger - = Logger.getLogger(SunVideoPlusAuto.class); - - private static String DEVICE_PREFIX = "/dev/o1k"; - private static String PROTOCOL = "sunvideoplus"; - private static String LOCATOR_PREFIX = PROTOCOL + "://"; - - private static boolean DO_PAL = false; - - int currentID = -1; - - /** - * Removes from the CaptureDeviceManager all currently detected sun video - * devices and runs a new detection loop to rediscover those that are - * currently available. - * - * @return the number of devices detected. - */ - @SuppressWarnings("unchecked") //legacy JMF code. - public int autoDetectDevices() - { - /* - * First remove any old entries - */ - Vector devices = (Vector) CaptureDeviceManager. - getDeviceList(null).clone(); - Enumeration enumeration = devices.elements(); - while (enumeration.hasMoreElements()) - { - CaptureDeviceInfo cdi = enumeration.nextElement(); - String devName = cdi.getLocator().getProtocol(); - if (devName.equals(PROTOCOL)) - CaptureDeviceManager.removeDevice(cdi); - } - - int nDevices = 0; - for (int i = 0; i < 10; i++) - { - File fl = new File(DEVICE_PREFIX + i); - if (fl.exists()) - { - if (DO_PAL) - { - generalDevice(i, "PAL"); - // If generating PAL, do both - // Garbage collect to release the PAL datasource - // otherwise it sometimes hangs before completing NTSC - System.gc(); - generalDevice(i, "NTSC"); - } - else - { - generalDevice(i, null); - } - // No longer generate specific configurations, - // let capture preview handle selection. - // doDevice(i); - nDevices++; - } - } - - try - { - CaptureDeviceManager.commit(); - } - catch (java.io.IOException ioe) - { - logger.error("SunVideoPlusAuto: error committing cdm", ioe); - return 0; - } - - return nDevices; - } - - protected void generalDevice(int id, String signal) - { - // Add the general device - javax.media.protocol.DataSource dsource = null; - String url = LOCATOR_PREFIX + id; - if (signal != null) - url += "////" + signal.toLowerCase(); - try - { - dsource = Manager.createDataSource(new MediaLocator(url)); - } - catch (Exception ex) - { - } - if (dsource != null && dsource instanceof - com.sun.media.protocol.sunvideoplus.DataSource) - { - CaptureDeviceInfo cdi = ( (CaptureDevice) dsource). - getCaptureDeviceInfo(); - if (cdi != null) - { - String name = cdi.getName(); - if (signal == null) - { - CaptureDeviceManager.addDevice(cdi); - } - else - { - name = cdi.getName() + " (" + signal + ")"; - CaptureDeviceManager.addDevice(new CaptureDeviceInfo(name, - cdi.getLocator(), cdi.getFormats())); - } - System.err.println("CaptureDeviceInfo = " - + name + " " - + cdi.getLocator()); - } - dsource.disconnect(); - } - } - - protected void doDevice(int id) - { - currentID = id; - FormatSetup fd = new FormatSetup(currentID); - Vector cdiv = fd.getDeviceInfo(); - if (cdiv != null && cdiv.size() > 0) - { - for (int i = 0; i < cdiv.size(); i++) - { - CaptureDeviceInfo cdi = cdiv.elementAt(i); - // At the moment, the name and locator are identical - System.err.println("CaptureDeviceInfo = " - + cdi.getName()); -// System.err.println("CaptureDeviceInfo = " -// + cdi.getName() + " " -// + cdi.getLocator()); - } - } - } - - static class FormatSetup - { - - int id; - - boolean fullVideo = false; - boolean anyVideo = true; - - String sAnalog, sPort, sVideoFormat, sSize; - - Hashtable videoFormats = new Hashtable(); - - OPICapture opiVidCap = null; - - public FormatSetup(int id) - { - this.id = id; - opiVidCap = new OPICapture(null); - if (!opiVidCap.connect(id)) - { - throw new Error("Unable to connect to device"); - } - - } - - private void addVideoFormat(Format fin) - { - String sVideo = sPort + "/" + sVideoFormat + "/" - + sSize + "/" - + sAnalog; - System.err.println("New format " + sVideo + " = " + fin); - videoFormats.put(sVideo, fin); - } - - public void mydispose() - { - opiVidCap.disconnect(); - System.err.println("Disconnected driver"); - } - - public void doFormat() - { - if (anyVideo) - { - doVideoFormats(); - } - } - - public void doVideoFormats() - { - if (!anyVideo) - { - // add a dummy format entry - videoFormats.put("off", new VideoFormat(VideoFormat.RGB)); - } - - sAnalog = "ntsc"; - if (DO_PAL) - sAnalog = "pal"; - if (!opiVidCap.setSignal(sAnalog)) - { - System.err.println("Video analog signal not recognized"); - return; - } - int port = 1; - if (!opiVidCap.setPort(port)) - { - System.err.println("Video source not recognized on port"); - return; - } - sPort = "" + port; - opiVidCap.setScale(2); - sSize = "cif"; - getVideoFormats(); - } - - private void getVideoFormats() - { - sVideoFormat = "h261"; - getH261Format(); - sVideoFormat = "h263"; - getH263Format(); - sVideoFormat = "jpeg"; - getJpegFormat(); - sVideoFormat = "rgb"; - getRGBFormat(); - sVideoFormat = "yuv"; - getYUVFormat(); - } - - private void getRGBFormat() - { - if (!opiVidCap.setCompress("RGB")) - return; - /* - * If sizes are wanted, the only valid sizes are - * NTSC - * fcif (640 x 480) - * cif (320 x 240) - * qcif (160 x 120) - * PAL - * fcif (768 x 576) - * cif (384 x 288) - * qcif (192 x 144) - */ - Dimension size = new Dimension(opiVidCap.getWidth(), - opiVidCap.getHeight()); - addVideoFormat(new RGBFormat(size, Format.NOT_SPECIFIED, - Format.byteArray, - Format.NOT_SPECIFIED, - 16, - 0xF800, 0x7E0, 0x1F, 2, - Format.NOT_SPECIFIED, - Format.FALSE, - Format.NOT_SPECIFIED)); - } - - private void getYUVFormat() - { - if (!opiVidCap.setCompress("YUV")) - return; - /* - * If sizes are wanted, the only valid sizes are - * NTSC - * fcif (640 x 480) - * cif (320 x 240) - * qcif (160 x 120) - * PAL - * fcif (768 x 576) - * cif (384 x 288) - * qcif (192 x 144) - * - * The capture stream is actually interleaved YVYU format. - * This is defined in the offset values below. - */ - Dimension size = new Dimension(opiVidCap.getWidth(), - opiVidCap.getHeight()); - addVideoFormat(new YUVFormat(size, Format.NOT_SPECIFIED, - Format.byteArray, - Format.NOT_SPECIFIED, - YUVFormat.YUV_YUYV, - Format.NOT_SPECIFIED, - Format.NOT_SPECIFIED, - 0, 3, 1)); - } - - private void getJpegFormat() - { - if (!opiVidCap.setCompress("Jpeg")) - return; - /* - * If sizes are wanted, the only valid sizes are - * NTSC - * cif (320 x 240) - * qcif (160 x 120) - * PAL - * cif (384 x 288) - * qcif (192 x 144) - */ - Dimension size = new Dimension(opiVidCap.getWidth(), - opiVidCap.getHeight()); - addVideoFormat(new VideoFormat(VideoFormat.JPEG, size, - Format.NOT_SPECIFIED, - Format.byteArray, - Format.NOT_SPECIFIED)); - } - - private void getH261Format() - { - if (!opiVidCap.setCompress("H261")) - return; - /* - * If sizes are wanted, the only valid sizes are - * cif (352 x 288) - * qcif (176 x 144) - */ - Dimension size = new Dimension(opiVidCap.getWidth(), - opiVidCap.getHeight()); - addVideoFormat(new VideoFormat(VideoFormat.H261, size, - Format.NOT_SPECIFIED, - Format.byteArray, - Format.NOT_SPECIFIED)); - } - - private void getH263Format() - { - if (!opiVidCap.setCompress("H263")) - return; - /* - * If sizes are wanted, the only valid sizes are - * cif (352 x 288) - * qcif (176 x 144) - */ - Dimension size = new Dimension(opiVidCap.getWidth(), - opiVidCap.getHeight()); - addVideoFormat(new VideoFormat(VideoFormat.H263, size, - Format.NOT_SPECIFIED, - Format.byteArray, - Format.NOT_SPECIFIED)); - } - - public void issueError(String err) - { - System.err.println(err); - Toolkit.getDefaultToolkit().beep(); - } - - public Enumeration sortedFormats(Hashtable formats) - { - Vector sorted = new Vector(); - keyloop:for (Enumeration en = formats.keys(); - en.hasMoreElements(); ) - { - String key = en.nextElement(); - for (int i = 0; i < sorted.size(); i++) - { - if (key.compareTo( sorted.elementAt(i)) < 0) - { - sorted.insertElementAt(key, i); - continue keyloop; - } - } - sorted.addElement(key); - } - return sorted.elements(); - } - - public Vector getDeviceInfo() - { - doFormat(); - mydispose(); - - String locatorPrefix = LOCATOR_PREFIX + id; - Vector devices = new Vector(); - if (anyVideo) - { - - for (Enumeration ve = sortedFormats(videoFormats); - ve.hasMoreElements(); ) - { - String vKey = ve.nextElement(); - Format vForm = videoFormats.get(vKey); - Format[] farray = null; - farray = new Format[1]; - farray[0] = vForm; - String name = locatorPrefix + "/" + vKey; - CaptureDeviceInfo cdi = new CaptureDeviceInfo(name, - new MediaLocator(name), farray); - CaptureDeviceManager.addDevice(cdi); - devices.addElement(cdi); - } - } - return devices; - } - - } - - public static void setPALSignal(boolean pal) - { - DO_PAL = pal; - } - - public static void main(String[] args) - { - if (args.length > 0) - { - if (args.length > 1) - { - System.err.println( - "Usage: java SunVideoPlusAuto [ ntsc | pal ]"); - System.exit(1); - } - if (args[0].equalsIgnoreCase("ntsc")) - { - SunVideoPlusAuto.setPALSignal(false); - } - else if (args[0].equalsIgnoreCase("pal")) - { - SunVideoPlusAuto.setPALSignal(true); - } - else - { - System.err.println( - "Usage: java SunVideoPlusAuto [ ntsc | pal ]"); - System.exit(1); - } - } - SunVideoPlusAuto sunVideoPlus = new SunVideoPlusAuto(); - sunVideoPlus.autoDetectDevices(); - System.exit(0); - } -} - diff --git a/src/net/java/sip/communicator/impl/media/device/V4LAuto.java b/src/net/java/sip/communicator/impl/media/device/V4LAuto.java deleted file mode 100644 index 84ab8b5c2..000000000 --- a/src/net/java/sip/communicator/impl/media/device/V4LAuto.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - * - * File based on: - * @(#)V4LAuto.java 1.2 01/03/13 - * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. - */ -package net.java.sip.communicator.impl.media.device; - -import java.util.*; - -import javax.media.*; - -import net.java.sip.communicator.util.*; - -import com.sun.media.protocol.v4l.*; - -/** - * Probes for video capture devices present on linux systems. - * @author Emil Ivov - */ -public class V4LAuto { - - private static final Logger logger = Logger.getLogger(V4LAuto.class); - - /** - * Removes from the CaptureDeviceManager all currently detected devices and - * runs a new detection loop to rediscover those that are currently - * available. - * - * @return the number of devices detected. - */ - @SuppressWarnings("unchecked") //JMF legacy code - public int autoDetectDevices() - { - Vector devices - = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); - Enumeration enumeration = devices.elements(); - while (enumeration.hasMoreElements()) - { - CaptureDeviceInfo cdi = enumeration.nextElement(); - String name = cdi.getName(); - if (name.startsWith("v4l:")) - CaptureDeviceManager.removeDevice(cdi); - } - - int nDevices = 0; - for (int i = 0; i < 10; i++) - { - CaptureDeviceInfo cdi = autoDetect(i); - if (cdi != null) - nDevices++; - } - - return nDevices; - } - /** - * Runs a device query for the capture card with the specified card number - * and returns its corresponding CaptureDeviceInfo or null if detection - * failed or nos such card exists. - * @param cardNo the index of the card to discover. - * @return the CaptureDeviceInfo corresponsing to the newly discovered - * device. - */ - protected CaptureDeviceInfo autoDetect(int cardNo) - { - CaptureDeviceInfo cdi = null; - try - { - cdi = new V4LDeviceQuery(); - - ((V4LDeviceQuery)cdi).sendQuery(cardNo); - if ( cdi.getFormats() != null - && cdi.getFormats().length > 0) - { - // Commit it to disk. Its a new device - if (CaptureDeviceManager.addDevice(cdi)) - { - logger.info("Added device " + cdi); - CaptureDeviceManager.commit(); - } - } - } - catch (Throwable thr) - { - logger.debug("No device for index " - + cardNo + ". " - + thr.getMessage()); - if (thr instanceof ThreadDeath) - throw (ThreadDeath)thr; - } - - return cdi; - } - - /** - * Test method, present for testing only. - * @param args String[] - */ - public static void main(String [] args) - { - V4LAuto auto = new V4LAuto(); - auto.autoDetectDevices(); - System.exit(0); - } -} - diff --git a/src/net/java/sip/communicator/impl/media/device/VFWAuto.java b/src/net/java/sip/communicator/impl/media/device/VFWAuto.java deleted file mode 100644 index 450c538cc..000000000 --- a/src/net/java/sip/communicator/impl/media/device/VFWAuto.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - * - * File based on: - * @(#)VFWAuto.java 1.2 01/03/13 - * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. - */ -package net.java.sip.communicator.impl.media.device; - -import java.util.*; -import javax.media.*; -import com.sun.media.protocol.vfw.*; -import net.java.sip.communicator.util.*; - -/** - * SIP Communicator modifications - * @author Emil Ivov - */ -public class VFWAuto -{ - private static final Logger logger - = Logger.getLogger(VFWAuto.class); - - /** - * Removes from the CaptureDeviceManager all currently detected vfw devices - * and runs a new detection loop to rediscover those that are currently - * available. - * - * @return the number of devices detected. - */ - @SuppressWarnings("unchecked") // jmf legacy code - public int autoDetectDevices() - { - Vector devices - = (Vector) CaptureDeviceManager.getDeviceList(null).clone(); - Enumeration devicesEnum = devices.elements(); - - while (devicesEnum.hasMoreElements()) - { - CaptureDeviceInfo cdi = devicesEnum.nextElement(); - String name = cdi.getName(); - if (name.startsWith("vfw:")) - CaptureDeviceManager.removeDevice(cdi); - } - - int nDevices = 0; - for (int i = 0; i < 10; i++) - { - String name = VFWCapture.capGetDriverDescriptionName(i); - if (name != null && name.length() > 1) - { - logger.debug("Found device " + name); - logger.debug("Querying device. Please wait..."); - com.sun.media.protocol.vfw.VFWSourceStream.autoDetect(i); - nDevices++; - } - } - - return nDevices; - } - - public static void main(String [] args) - { - VFWAuto vfwAuto = new VFWAuto(); - vfwAuto.autoDetectDevices(); - System.exit(0); - } -} - diff --git a/src/net/java/sip/communicator/impl/media/media.manifest.mf b/src/net/java/sip/communicator/impl/media/media.manifest.mf index 430e71262..4d625ab6a 100644 --- a/src/net/java/sip/communicator/impl/media/media.manifest.mf +++ b/src/net/java/sip/communicator/impl/media/media.manifest.mf @@ -41,4 +41,5 @@ Export-Package: net.java.sip.communicator.service.media, net.java.sip.communicator.service.media.event, net.java.sip.communicator.impl.media, net.java.sip.communicator.impl.media.device, + net.java.sip.communicator.service.audionotifier Metadata-Location: /net/java/sip/communicator/impl/media/media.metadata.xml diff --git a/src/net/java/sip/communicator/impl/media/notify/AudioNotifierServiceImpl.java b/src/net/java/sip/communicator/impl/media/notify/AudioNotifierServiceImpl.java new file mode 100644 index 000000000..633b9f073 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/notify/AudioNotifierServiceImpl.java @@ -0,0 +1,159 @@ +/* + * 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.media.notify; + +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.impl.media.*; +import net.java.sip.communicator.impl.media.device.*; +import net.java.sip.communicator.service.audionotifier.*; + +/** + * The implementation of the AudioNotifierService. + * + * @author Yana Stamcheva + */ +public class AudioNotifierServiceImpl + implements AudioNotifierService +{ + private static final Map audioClips = + new HashMap(); + + private boolean isMute; + + /** + * Device config to look for notify device. + */ + private DeviceConfiguration deviceConfiguration; + + /** + * Creates audio notify service. + * @param deviceConfiguration the device configuration. + */ + public AudioNotifierServiceImpl(DeviceConfiguration deviceConfiguration) + { + this.deviceConfiguration = deviceConfiguration; + } + + /** + * Creates an SCAudioClip from the given URI and adds it to the list of + * available audio-s. + * + * @param uri the path where the audio file could be found + */ + public SCAudioClipImpl createAudio(String uri) + { + SCAudioClipImpl audioClip; + + synchronized (audioClips) + { + if(audioClips.containsKey(uri)) + { + audioClip = audioClips.get(uri); + } + else + { + URL url = + MediaActivator.getResources().getSoundURLForPath(uri); + + if (url == null) + { + // Not found by the class loader. Perhaps it's a local file. + try + { + url = new URL(uri); + } + catch (MalformedURLException e) + { + //logger.error("The given uri could not be parsed.", e); + return null; + } + } + + try + { + if(getDeviceConfiguration().getAudioSystem().equals( + DeviceConfiguration.AUDIO_SYSTEM_JAVASOUND)) + { + audioClip = new JMFAudioClipImpl(url, this); + } + else if(getDeviceConfiguration().getAudioSystem().equals( + DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO)) + { + audioClip = new PortAudioClipImpl(url, this); + } + else + return null; + } + catch (Throwable e) + { + // Cannot create audio to play + return null; + } + + audioClips.put(uri, audioClip); + } + } + + return audioClip; + } + + /** + * Removes the given audio from the list of available audio clips. + * + * @param audioClip the audio to destroy + */ + public void destroyAudio(SCAudioClip audioClip) + { + synchronized (audioClips) { + audioClips.remove(audioClip); + } + } + + /** + * Enables or disables the sound in the application. If FALSE, we try to + * restore all looping sounds if any. + * + * @param isMute when TRUE disables the sound, otherwise enables the sound. + */ + public void setMute(boolean isMute) + { + this.isMute = isMute; + + for (SCAudioClipImpl audioClip : audioClips.values()) + { + if (isMute) + { + audioClip.internalStop(); + } + else if (audioClip.isLooping()) + { + audioClip.playInLoop(audioClip.getLoopInterval()); + } + } + } + + /** + * Returns TRUE if the sound is currently disabled, FALSE otherwise. + * @return TRUE if the sound is currently disabled, FALSE otherwise + */ + public boolean isMute() + { + return isMute; + } + + /** + * The device configuration. + * + * @return the deviceConfiguration + */ + public DeviceConfiguration getDeviceConfiguration() + { + return deviceConfiguration; + } +} diff --git a/src/net/java/sip/communicator/impl/media/notify/JMFAudioClipImpl.java b/src/net/java/sip/communicator/impl/media/notify/JMFAudioClipImpl.java new file mode 100644 index 000000000..b277e3750 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/notify/JMFAudioClipImpl.java @@ -0,0 +1,201 @@ +/* + * 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.media.notify; + +import java.applet.*; +import java.awt.event.*; +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.security.*; + +import javax.swing.*; + +import net.java.sip.communicator.service.audionotifier.*; + +/** + * Implementation of SCAudioClip. + * + * @author Yana Stamcheva + */ +public class JMFAudioClipImpl + extends SCAudioClipImpl + implements ActionListener + +{ + private static Constructor acConstructor = null; + + private final Timer playAudioTimer = new Timer(1000, null); + + private final AudioClip audioClip; + + private final AudioNotifierService audioNotifier; + + /** + * Creates the audio clip and initialize the listener used from the + * loop timer. + * + * @param url the url pointing to the audio file + * @param audioNotifier the audio notify service + * @throws IOException cannot audio clip with supplied url. + */ + public JMFAudioClipImpl(URL url, AudioNotifierService audioNotifier) + throws IOException + { + this.audioClip = createAppletAudioClip(url.openStream()); + this.audioNotifier = audioNotifier; + + this.playAudioTimer.addActionListener(this); + } + + /** + * Plays this audio. + */ + public void play() + { + if ((audioClip != null) && !audioNotifier.isMute()) + audioClip.play(); + } + + /** + * Plays this audio in loop. + * + * @param interval the loop interval + */ + public void playInLoop(int interval) + { + if(!audioNotifier.isMute()) + { + if(interval == 0) + audioClip.loop(); + else + { + //first play the audio and then start the timer and wait + audioClip.play(); + playAudioTimer.setDelay(interval); + playAudioTimer.setRepeats(true); + + playAudioTimer.start(); + } + } + + setLoopInterval(interval); + setIsLooping(true); + } + + /** + * Stops this audio. + */ + public void stop() + { + if (audioClip != null) + audioClip.stop(); + + if (isLooping()) + { + playAudioTimer.stop(); + setIsLooping(false); + } + } + + /** + * Stops this audio without setting the isLooping property in the case of + * a looping audio. The AudioNotifier uses this method to stop the audio + * when setMute(true) is invoked. This allows us to restore all looping + * audios when the sound is restored by calling setMute(false). + */ + public void internalStop() + { + if (audioClip != null) + audioClip.stop(); + + if (isLooping()) + playAudioTimer.stop(); + } + + /** + * Creates an AppletAudioClip. + * + * @param inputstream the audio input stream + * @throws IOException + */ + private static AudioClip createAppletAudioClip(InputStream inputstream) + throws IOException + { + if (acConstructor == null) + { + try + { + acConstructor + = AccessController.doPrivileged( + new PrivilegedExceptionAction>() + { + public Constructor run() + throws ClassNotFoundException, + NoSuchMethodException, + SecurityException + { + return createAcConstructor(); + } + }); + } + catch (PrivilegedActionException paex) + { + throw + new IOException( + "Failed to get AudioClip constructor: " + + paex.getException()); + } + } + + try + { + return acConstructor.newInstance(inputstream); + } + catch (Exception ex) + { + throw new IOException("Failed to construct the AudioClip: " + ex); + } + } + + @SuppressWarnings("unchecked") + private static Constructor createAcConstructor() + throws ClassNotFoundException, + NoSuchMethodException, + SecurityException + { + Class class1; + try + { + class1 + = Class.forName( + "com.sun.media.sound.JavaSoundAudioClip", + true, + ClassLoader.getSystemClassLoader()); + } + catch (ClassNotFoundException cnfex) + { + class1 + = Class.forName("sun.audio.SunAudioClip", true, null); + } + return + (Constructor) class1.getConstructor(InputStream.class); + } + + /** + * Plays an audio clip. Used in the playAudioTimer to play an audio in loop. + * @param e the event. + */ + public void actionPerformed(ActionEvent e) + { + if (audioClip != null) + { + audioClip.stop(); + audioClip.play(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/media/notify/PortAudioClipImpl.java b/src/net/java/sip/communicator/impl/media/notify/PortAudioClipImpl.java new file mode 100644 index 000000000..b54277daa --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/notify/PortAudioClipImpl.java @@ -0,0 +1,204 @@ +/* + * 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.media.notify; + +import java.io.*; +import java.net.*; + +import javax.sound.sampled.*; + +import net.java.sip.communicator.impl.media.protocol.portaudio.*; +import net.java.sip.communicator.impl.media.device.*; +import net.java.sip.communicator.util.*; + +/** + * Implementation of SCAudioClip using PortAudio. + * + * @author Damian Minkov + */ +public class PortAudioClipImpl + extends SCAudioClipImpl + implements PropertyChangeListener +{ + private static final Logger logger + = Logger.getLogger(PortAudioClipImpl.class); + + private final AudioNotifierServiceImpl audioNotifier; + + private Thread playThread = new Thread(new PlayThread()); + + private boolean started = false; + + private long portAudioStream = 0; + + private URL url = null; + + /** + * Creates the audio clip and initialize the listener used from the + * loop timer. + * + * @param url the url pointing to the audio file + * @param audioNotifier the audio notify service + * @throws IOException cannot audio clip with supplied url. + */ + public PortAudioClipImpl(URL url, AudioNotifierServiceImpl audioNotifier) + throws IOException + { + this.audioNotifier = audioNotifier; + this.url = url; + + audioNotifier.getDeviceConfiguration().addPropertyChangeListener(this); + } + + /** + * Plays this audio. + */ + public void play() + { + if ((url != null) && !audioNotifier.isMute()) + { + started = true; + playThread.start(); + } + } + + /** + * Plays this audio in loop. + * + * @param interval the loop interval + */ + public void playInLoop(int interval) + { + setLoopInterval(interval); + setIsLooping(true); + + if(!audioNotifier.isMute()) + { + started = true; + playThread.start(); + } + } + + /** + * Stops this audio. + */ + public void stop() + { + if (url != null) + started = false; + setIsLooping(false); + } + + /** + * Stops this audio without setting the isLooping property in the case of + * a looping audio. The AudioNotifier uses this method to stop the audio + * when setMute(true) is invoked. This allows us to restore all looping + * audios when the sound is restored by calling setMute(false). + */ + public void internalStop() + { + if (url != null) + started = false; + } + + /** + * Notified when device configuration has changed. + * @param evt the event of the change + */ + public void propertyChange(PropertyChangeEvent evt) + { + if(evt.getPropertyName().equals( + DeviceConfiguration.AUDIO_NOTIFY_DEVICE)) + { + // make the stream 0, to be sure next time the new + // device will be used + portAudioStream = 0; + } + } + + private class PlayThread + implements Runnable + { + byte[] buffer = new byte[1024]; + + public void run() + { + try + { + while(true) + { + if (portAudioStream == 0) + { + long streamParameters + = PortAudio.PaStreamParameters_new( + PortAudioStream.getDeviceIndexFromLocator( + audioNotifier.getDeviceConfiguration(). + getAudioNotifyDevice().getLocator()), + 2, + PortAudio.SAMPLE_FORMAT_INT16); + + portAudioStream + = PortAudio.Pa_OpenStream( + 0, + streamParameters, + 48000, + PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED, + PortAudio.STREAM_FLAGS_NO_FLAG, + null); + + PortAudio.Pa_StartStream(portAudioStream); + } + + AudioInputStream audioStream = + AudioSystem.getAudioInputStream(url); + + if(!started) + { + PortAudio.Pa_CloseStream(portAudioStream); + return; + } + + int read = 0; + while((read = audioStream.read(buffer)) != -1) + { + PortAudio.Pa_WriteStream( + portAudioStream, + buffer, + buffer.length/audioStream.getFormat().getFrameSize()); + } + + if(!isLooping()) + { + PortAudio.Pa_CloseStream(portAudioStream); + break; + } + else + { + Thread.sleep(getLoopInterval()); + } + } + } + catch (PortAudioException e) + { + logger.error( + "Cannot open portaudio device for notifications", e); + } + catch (IOException e) + { + logger.error("Error reading from audio resource", e); + } + catch (InterruptedException e) + { + logger.error("Cannot wait the interval between plays", e); + } + catch (UnsupportedAudioFileException e) + { + logger.error("Unknown file format", e); + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/media/notify/SCAudioClipImpl.java b/src/net/java/sip/communicator/impl/media/notify/SCAudioClipImpl.java new file mode 100644 index 000000000..e925494fa --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/notify/SCAudioClipImpl.java @@ -0,0 +1,94 @@ +/* + * 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.media.notify; + +import net.java.sip.communicator.service.audionotifier.*; + +/** + * Common properties impl for SCAudioClip. + * + * @author Damian Minkov + */ +public abstract class SCAudioClipImpl + implements SCAudioClip +{ + private boolean isLooping; + + private int loopInterval; + + private boolean isInvalid; + + /** + * Returns TRUE if this audio is invalid, FALSE otherwise. + * + * @return TRUE if this audio is invalid, FALSE otherwise + */ + public boolean isInvalid() + { + return isInvalid; + } + + /** + * Marks this audio as invalid or not. + * + * @param isInvalid TRUE to mark this audio as invalid, FALSE otherwise + */ + public void setInvalid(boolean isInvalid) + { + this.setIsInvalid(isInvalid); + } + + /** + * Returns TRUE if this audio is currently playing in loop, FALSE otherwise. + * @return TRUE if this audio is currently playing in loop, FALSE otherwise. + */ + public boolean isLooping() + { + return isLooping; + } + + /** + * Returns the loop interval if this audio is looping. + * @return the loop interval if this audio is looping + */ + public int getLoopInterval() + { + return loopInterval; + } + + /** + * @param isLooping the isLooping to set + */ + public void setIsLooping(boolean isLooping) + { + this.isLooping = isLooping; + } + + /** + * @param loopInterval the loopInterval to set + */ + public void setLoopInterval(int loopInterval) + { + this.loopInterval = loopInterval; + } + + /** + * @param isInvalid the isInvalid to set + */ + public void setIsInvalid(boolean isInvalid) + { + this.isInvalid = isInvalid; + } + + /** + * Stops this audio without setting the isLooping property in the case of + * a looping audio. The AudioNotifier uses this method to stop the audio + * when setMute(true) is invoked. This allows us to restore all looping + * audios when the sound is restored by calling setMute(false). + */ + public abstract void internalStop(); +} diff --git a/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java b/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java index 179493550..846b21d13 100644 --- a/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java +++ b/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java @@ -12,10 +12,12 @@ import javax.media.protocol.*; /** + * Portaudio datasource. + * * @author Damian Minkov */ public class DataSource - extends PushBufferDataSource + extends PullBufferDataSource { private boolean connected = false; @@ -27,6 +29,7 @@ public class DataSource /** * Connect the datasource + * @throws IOException if we cannot initialize portaudio. */ public void connect() throws IOException @@ -80,7 +83,9 @@ public String getContentType() /** * Return required control from the Control[] array - * if exists, that is + * if exists. + * @param controlType the control we are interested in. + * @return the object that implements the control, or null. */ public Object getControl(String controlType) { @@ -101,7 +106,7 @@ public Object getControl(String controlType) /** * Gives control information to the caller - * + * @return the collection of object controls. */ public Object[] getControls() { @@ -120,22 +125,35 @@ public Time getDuration() } /** - * Returns an array of PushBufferStream containing all the streams + * Returns an array of PullBufferStream containing all the streams * i.e. only one in our case : only sound. * * If no stream actually exists, instantiate one on the fly. * * @return Array of one stream */ - public PushBufferStream[] getStreams() + public PullBufferStream[] getStreams() { - if (streams == null) - streams = new PortAudioStream[] { new PortAudioStream() }; + try + { + if (streams == null) + streams = new PortAudioStream[] + {new PortAudioStream(getLocator())}; + } + catch (Exception e) + { + e.printStackTrace(); + // if we cannot parse desired device we will not open a stream + // so there is no stream returned + streams = new PortAudioStream[0]; + } + return streams; } /** * Start the datasource and the underlying stream + * @throws IOException */ public void start() throws IOException @@ -146,21 +164,14 @@ public void start() if (!connected) throw new IOException("DataSource must be connected"); - try - { - streams[0].start(); - } - catch (PortAudioException paex) - { - IOException ioex = new IOException(); - ioex.initCause(paex); - throw ioex; - } + streams[0].start(); + started = true; } /** * Stop the datasource and it's underlying stream + * @throws IOException */ public void stop() throws IOException @@ -168,16 +179,8 @@ public void stop() if (!started) return; - try - { - streams[0].stop(); - } - catch (PortAudioException paex) - { - IOException ioex = new IOException(); - ioex.initCause(paex); - throw ioex; - } + streams[0].stop(); + started = false; } } diff --git a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java index 35232979f..64081f69a 100644 --- a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java +++ b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java @@ -66,12 +66,29 @@ public static native void Pa_WriteStream( long frames) throws PortAudioException; + public static native void Pa_ReadStream( + long stream, byte[] buffer, long frames) + throws PortAudioException; + + public static native long Pa_GetStreamReadAvailable(long stream); + + public static native long Pa_GetStreamWriteAvailable(long stream); + + public static native int Pa_GetSampleSize(long format); + + public static native boolean Pa_IsFormatSupported( + long inputParameters, + long outputParameters, + double sampleRate); + public static native int PaDeviceInfo_getMaxInputChannels(long deviceInfo); public static native int PaDeviceInfo_getMaxOutputChannels(long deviceInfo); public static native String PaDeviceInfo_getName(long deviceInfo); + public static native double PaDeviceInfo_getDefaultSampleRate(long deviceInfo); + public static native long PaStreamParameters_new( int deviceIndex, int channelCount, diff --git a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java index 7a589ff69..07ac1462e 100644 --- a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java +++ b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java @@ -14,22 +14,28 @@ import javax.media.format.*; import javax.media.protocol.*; +import net.java.sip.communicator.util.*; + /** * @author Damian Minkov */ public class PortAudioStream - implements PushBufferStream, - PortAudioStreamCallback + implements PullBufferStream { -// private static final Logger logger -// = Logger.getLogger(PortAudioStream.class); + private static final Logger logger = + Logger.getLogger(PortAudioStream.class); + + /** + * The locatro prefix used when creating or parsing MediaLocators. + */ + public static final String LOCATOR_PREFIX = "portaudio:#"; private final static ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW); - private BufferTransferHandler transferHandler; + private Control[] controls = new Control[0]; - public static AudioFormat audioFormat = new AudioFormat( + private static AudioFormat audioFormat = new AudioFormat( AudioFormat.LINEAR, 44100, 16, @@ -40,53 +46,47 @@ public class PortAudioStream Format.NOT_SPECIFIED, Format.byteArray); + private boolean started; private long stream = 0; - private ByteBuffer bufferToProcess = null; - private int seqNo = 0; + private int sampleSize = 1; + + final private int deviceIndex; + /** - * Returns the supported formats by this stream. - * @return supported formats + * Creates new stream. + * @param locator the locator to extract the device index from it. */ - public Format getFormat() + public PortAudioStream(MediaLocator locator) { - return audioFormat; + this.deviceIndex = getDeviceIndexFromLocator(locator); } /** - * - * @param buffer - * @throws IOException + * Return the formats supported by the datasource stream + * corresponding the maximum input channels. + * + * @return the supported formats. */ - public void read(Buffer buffer) - throws IOException + public static Format[] getFormats() { - byte[] barr = new byte[bufferToProcess.remaining()]; - - bufferToProcess.get(barr); - - buffer.setTimeStamp(System.nanoTime()); - buffer.setData(barr); - buffer.setSequenceNumber(seqNo); - buffer.setLength(barr.length); - buffer.setFlags(0); - buffer.setHeader(null); - seqNo++; + return new Format[]{audioFormat}; } /** - * - * @param transferHandler + * Returns the supported format by this stream. + * @return supported formats */ - public void setTransferHandler(BufferTransferHandler transferHandler) + public Format getFormat() { - this.transferHandler = transferHandler; + return audioFormat; } /** * We are providing access to raw data + * @return RAW content descriptor. */ public ContentDescriptor getContentDescriptor() { @@ -95,6 +95,7 @@ public ContentDescriptor getContentDescriptor() /** * We are streaming. + * @return unknown content length. */ public long getContentLength() { @@ -103,7 +104,7 @@ public long getContentLength() /** * The stream never ends. - * + * @return true if the end of the stream has been reached. */ public boolean endOfStream() { @@ -112,7 +113,7 @@ public boolean endOfStream() /** * Gives control information to the caller - * + * @return no controls currently supported. */ public Object[] getControls() { @@ -121,7 +122,10 @@ public Object[] getControls() /** * Return required control from the Control[] array - * if exists, that is + * if exists + * + * @param controlType the control class name. + * @return the object that implements the control, or null. */ public Object getControl(String controlType) { @@ -145,96 +149,130 @@ public Object getControl(String controlType) } } - public void finishedCallback() - { - } - /** * Starts the stream operation */ void start() - throws PortAudioException { - if (this.stream != 0) - throw new IllegalStateException("stream"); - - long stream = createStream(); - - try - { - PortAudio.Pa_StartStream(stream); - this.stream = stream; - } - catch (PortAudioException startException) + synchronized (this) { + this.started = true; try { - PortAudio.Pa_CloseStream(stream); + PortAudio.Pa_StartStream(getStream()); } - catch (PortAudioException closeException) + catch (PortAudioException paex) { - /* - * We couldn't start the stream so we're closing it just to free - * the native resources but if that fails as well, we cannot do - * anything about it. Besides, we have to rethrow the exception - * which was thrown on start. - */ + paex.printStackTrace(); } - - throw startException; } } void stop() - throws PortAudioException { - if (stream != 0) + synchronized (this) { - PortAudio.Pa_CloseStream(stream); - stream = 0; + this.started = false; + + try + { + PortAudio.Pa_CloseStream(getStream()); + stream = 0; + } + catch (PortAudioException paex) + { + paex.printStackTrace(); + } } } - private long createStream() + private long getStream() throws PortAudioException { - int deviceCount = PortAudio.Pa_GetDeviceCount(); - int deviceIndex = 0; - - for (; deviceIndex < deviceCount; deviceIndex++) + if (stream == 0) { - long deviceInfo = PortAudio.Pa_GetDeviceInfo(deviceIndex); - - if ((PortAudio.PaDeviceInfo_getMaxInputChannels(deviceInfo) == 2) - && (PortAudio.PaDeviceInfo_getMaxOutputChannels(deviceInfo) == 0) - && PortAudio.PaDeviceInfo_getName(deviceInfo) - .contains("Analog")) - break; + long streamParameters + = PortAudio.PaStreamParameters_new( + deviceIndex, + audioFormat.getChannels(), + PortAudio.SAMPLE_FORMAT_INT16); + + stream + = PortAudio.Pa_OpenStream( + streamParameters, + 0, + audioFormat.getSampleRate(), + PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED, + PortAudio.STREAM_FLAGS_NO_FLAG, + null); + sampleSize = + PortAudio.Pa_GetSampleSize(PortAudio.SAMPLE_FORMAT_INT16); } - long streamParameters - = PortAudio.PaStreamParameters_new( - deviceIndex, - 1, - PortAudio.SAMPLE_FORMAT_INT16); - - return - PortAudio.Pa_OpenStream( - streamParameters, - 0, - 44100, - PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED, - PortAudio.STREAM_FLAGS_NO_FLAG, - this); + return stream; } - public int callback(ByteBuffer input, ByteBuffer output) + /** + * Query if the next read will block. + * @return true if a read will block. + */ + public boolean willReadBlock() { - bufferToProcess = input; + try + { + return PortAudio.Pa_GetStreamReadAvailable(getStream()) == 0; + } + catch (PortAudioException ex) + { + logger.error("Cannot get read available", ex); + return true; + } + } - if (transferHandler != null) - transferHandler.transferData(this); + /** + * Block and read a buffer from the stream. + * @param buffer should be non-null. + * @throws IOException Thrown if an error occurs while reading. + */ + public synchronized void read(Buffer buffer) + throws IOException + { + if(!this.started) + return; - return RESULT_CONTINUE; + try + { + int canread = + (int)PortAudio.Pa_GetStreamReadAvailable(getStream()); + if(canread < 1) + canread = 512; + + byte[] bytebuff = new byte[canread*sampleSize]; + + PortAudio.Pa_ReadStream(getStream(), bytebuff, canread); + + buffer.setTimeStamp(System.nanoTime()); + buffer.setData(bytebuff); + buffer.setSequenceNumber(seqNo); + buffer.setLength(bytebuff.length); + buffer.setFlags(0); + buffer.setHeader(null); + seqNo++; + } + catch (PortAudioException e) + { + logger.error("", e); + } + } + + /** + * Extracts the device index from the locator. + * @param locator the locator containing the device index. + * @return the extracted device index. + */ + public static int getDeviceIndexFromLocator(MediaLocator locator) + { + return Integer.parseInt(locator.toExternalForm().replace( + PortAudioStream.LOCATOR_PREFIX, "")); } } diff --git a/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java b/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java index f3ebbcf65..dd3040a40 100644 --- a/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java +++ b/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java @@ -12,6 +12,7 @@ import net.java.sip.communicator.impl.media.protocol.portaudio.*; /** + * Portaudio renderer. * * @author Damian Minkov */ @@ -22,41 +23,55 @@ public class PortAudioRenderer Logger.getLogger(PortAudioRenderer.class); private static final String name = "PortAudio Renderer"; - public static Format[] supportedInputFormats = new Format[] - { - new AudioFormat( + + private static AudioFormat audioFormat = + new AudioFormat( AudioFormat.LINEAR, - 44100, + 48000, 16, - 1, + 2, AudioFormat.LITTLE_ENDIAN, AudioFormat.SIGNED, 16, Format.NOT_SPECIFIED, - Format.byteArray) + Format.byteArray); + + /** + * The supported input formats. + */ + public static Format[] supportedInputFormats = new Format[] + { + audioFormat }; - protected Object [] controls = new Object[0]; + + private Object [] controls = new Object[0]; private AudioFormat inputFormat; private long stream = 0; - public PortAudioRenderer() - { - try - { - PortAudio.initialize(); - } - catch (PortAudioException e) - { - logger.error("Cannot Initialize portaudio", e); - } - } + boolean started = false; + + private WriterThread writerThread = null; + private final Object bufferSync = new Object(); + private byte[] buffer = null; + + private static int deviceIndex = -1; + /** + * Lists the input formats supported by this Renderer. + * @return An array of Format objects that represent + * the input formats supported by this Renderer. + */ public Format[] getSupportedInputFormats() { - return supportedInputFormats; + return new Format[]{audioFormat}; } + /** + * Sets the Format of the input data. + * @param format the format to set. + * @return The Format that was set. + */ public Format setInputFormat(Format format) { if(!(format instanceof AudioFormat)) @@ -69,10 +84,16 @@ public Format setInputFormat(Format format) return inputFormat; } + /** + * Initiates the rendering process. + * When start is called, the renderer begins rendering + * any data available in its internal buffers. + */ public void start() { try - { + { + started = true; PortAudio.Pa_StartStream(stream); } catch (PortAudioException e) @@ -81,34 +102,52 @@ public void start() } } + /** + * Halts the rendering process. + */ public void stop() { - try + synchronized(bufferSync) { - PortAudio.Pa_CloseStream(stream); - } - catch (PortAudioException e) - { - logger.error("Closing portaudio stream failed", e); + try + { + PortAudio.Pa_CloseStream(stream); + writerThread = null; + started = false; + } + catch (PortAudioException e) + { + logger.error("Closing portaudio stream failed", e); + } } } + /** + * Processes the data and renders it + * to the output device represented by this Renderer. + * @param inputBuffer the input data. + * @return BUFFER_PROCESSED_OK if the processing is successful. + */ public int process(Buffer inputBuffer) { - try + synchronized(bufferSync) { byte[] buff = new byte[inputBuffer.getLength()]; - System.arraycopy( - (byte[])inputBuffer.getData(), - inputBuffer.getOffset(), - buff, - 0, - buff.length); - PortAudio.Pa_WriteStream(stream, buff, buff.length/2); + System.arraycopy( + (byte[])inputBuffer.getData(), + inputBuffer.getOffset(), + buff, + 0, + buff.length); + + buffer = buff; + bufferSync.notifyAll(); } - catch (PortAudioException e) + + if(writerThread == null) { - logger.error("Error write to device!", e); + writerThread = new WriterThread(); + writerThread.start(); } return BUFFER_PROCESSED_OK; @@ -116,12 +155,18 @@ public int process(Buffer inputBuffer) /** * Returns the name of the pluging. + * @return A String that contains the descriptive name of the plug-in. */ public String getName() { return name; } + /** + * Opens the device and stream that we will use to render data. + * @throws ResourceUnavailableException If required resources cannot + * be opened/created. + */ public void open() throws ResourceUnavailableException { @@ -129,31 +174,17 @@ public void open() { if (stream == 0) { - int deviceCount = PortAudio.Pa_GetDeviceCount(); - int deviceIndex = 0; - - for (; deviceIndex < deviceCount; deviceIndex++) - { - long deviceInfo = PortAudio.Pa_GetDeviceInfo(deviceIndex); - - if ((PortAudio.PaDeviceInfo_getMaxOutputChannels(deviceInfo) == 2) - && (PortAudio.PaDeviceInfo_getMaxInputChannels(deviceInfo) == 2) - && PortAudio.PaDeviceInfo_getName(deviceInfo) - .contains("Analog")) - break; - } - long streamParameters = PortAudio.PaStreamParameters_new( deviceIndex, - 1, + audioFormat.getChannels(), PortAudio.SAMPLE_FORMAT_INT16); stream = PortAudio.Pa_OpenStream( 0, streamParameters, - 44100, + audioFormat.getSampleRate(), PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED, PortAudio.STREAM_FLAGS_NO_FLAG, null); @@ -166,14 +197,14 @@ public void open() } /** - * + * Closes the plug-in. */ public void close() { } /** - * + * Resets the state of the plug-in. */ public void reset() { @@ -181,7 +212,7 @@ public void reset() /** * Gives control information to the caller - * + * @return the collection of object controls. */ public Object[] getControls() { @@ -190,7 +221,9 @@ public Object[] getControls() /** * Return required control from the Control[] array - * if exists, that is + * if exists. + * @param controlType the control we are interested in. + * @return the object that implements the control, or null. */ public Object getControl(String controlType) { @@ -213,4 +246,60 @@ public Object getControl(String controlType) return null; } } + + /** + * Used to set the device index used by the renderer common for all + * instances of it. + * @param locator the locator containing the device index. + */ + public static void setDevice(MediaLocator locator) + { + deviceIndex = PortAudioStream.getDeviceIndexFromLocator(locator); + } + + /** + * Writes data to the portaudio stream. If there is no data for + * particular time write some silence so we wont hear some cracks in + * the sound. + */ + private class WriterThread + extends Thread + { + + public WriterThread() + { + setName("Portaudio Renderer"); + } + + public void run() + { + while(true) + { + try + { + synchronized(bufferSync) + { + // put some silence if there is no data + if(buffer == null) + { + buffer = new byte[1024]; + } + + PortAudio.Pa_WriteStream( + stream, buffer, buffer.length/4); + + buffer = null; + bufferSync.wait(90); + } + + if(!started) + return; + } + catch (Exception e) + { + logger.error("writing to stream", e); + } + } + } + } }