Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
L
libjitsi
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Model registry
Analyze
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
ZRTP
libjitsi
Commits
adce5ec6
Commit
adce5ec6
authored
11 years ago
by
Lyubomir Marinov
Browse files
Options
Downloads
Patches
Plain Diff
Removes duplication.
parent
ec4e1301
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/org/jitsi/impl/neomedia/device/AudioSystem2.java
+468
-0
468 additions, 0 deletions
src/org/jitsi/impl/neomedia/device/AudioSystem2.java
src/org/jitsi/impl/neomedia/device/UpdateAvailableDeviceListListener.java
+42
-0
42 additions, 0 deletions
...pl/neomedia/device/UpdateAvailableDeviceListListener.java
with
510 additions
and
0 deletions
src/org/jitsi/impl/neomedia/device/AudioSystem2.java
0 → 100644
+
468
−
0
View file @
adce5ec6
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package
org.jitsi.impl.neomedia.device
;
import
java.lang.ref.*
;
import
java.util.*
;
import
java.util.regex.*
;
import
org.jitsi.util.*
;
/**
*
* @author Lyubomir Marinov
*/
public
abstract
class
AudioSystem2
extends
AudioSystem
{
/**
* The <tt>Logger</tt> used by the <tt>AudioSystem2</tt> class and its
* instances for logging output.
*/
private
static
final
Logger
logger
=
Logger
.
getLogger
(
AudioSystem2
.
class
);
/**
* The number of times that {@link #willOpenStream()} has been invoked
* without an intervening {@link #didOpenStream()} i.e. the number of API
* clients who are currently executing a <tt>Pa_OpenStream</tt>-like
* function and which are thus inhibiting
* <tt>updateAvailableDeviceList()</tt>.
*/
private
int
openStream
=
0
;
/**
* The <tt>Object</tt> which synchronizes that access to {@link #openStream}
* and {@link #updateAvailableDeviceList}.
*/
private
final
Object
openStreamSyncRoot
=
new
Object
();
/**
* The number of times that {@link #willUpdateAvailableDeviceList()} has
* been invoked without an intervening
* {@link #didUpdateAvailableDeviceList()} i.e. the number of API clients
* who are currently executing <tt>updateAvailableDeviceList()</tt> and who
* are thus inhibiting <tt>openStream</tt>.
*/
private
int
updateAvailableDeviceList
=
0
;
/**
* The list of <tt>UpdateAvailableDeviceListListener</tt>s which are to be
* notified before and after this <tt>AudioSystem</tt>'s method
* <tt>updateAvailableDeviceList()</tt> is invoked.
*/
private
final
List
<
WeakReference
<
UpdateAvailableDeviceListListener
>>
updateAvailableDeviceListListeners
=
new
LinkedList
<
WeakReference
<
UpdateAvailableDeviceListListener
>>();
/**
* The <tt>Object</tt> which ensures that this <tt>AudioSystem</tt>'s
* function to update the list of available devices will not be invoked
* concurrently. The condition should hold true on the native side but,
* anyway, it should not hurt (much) to enforce it on the Java side as well.
*/
private
final
Object
updateAvailableDeviceListSyncRoot
=
new
Object
();
protected
AudioSystem2
(
String
locatorProtocol
,
int
features
)
throws
Exception
{
super
(
locatorProtocol
,
features
);
}
/**
* Adds a listener which is to be notified before and after this
* <tt>AudioSystem</tt>'s method <tt>updateAvailableDeviceList()</tt> is
* invoked.
* <p>
* <b>Note</b>: The <tt>AudioSystem2</tt> class keeps a
* <tt>WeakReference</tt> to the specified <tt>listener</tt> in order to
* avoid memory leaks.
* </p>
*
* @param listener the <tt>UpdateAvailableDeviceListListener</tt> to be
* notified before and after this <tt>AudioSystem</tt>'s method
* <tt>updateAvailableDeviceList()</tt> is invoked
*/
public
void
addUpdateAvailableDeviceListListener
(
UpdateAvailableDeviceListListener
listener
)
{
if
(
listener
==
null
)
throw
new
NullPointerException
(
"listener"
);
synchronized
(
updateAvailableDeviceListListeners
)
{
Iterator
<
WeakReference
<
UpdateAvailableDeviceListListener
>>
i
=
updateAvailableDeviceListListeners
.
iterator
();
boolean
add
=
true
;
while
(
i
.
hasNext
())
{
UpdateAvailableDeviceListListener
l
=
i
.
next
().
get
();
if
(
l
==
null
)
i
.
remove
();
else
if
(
l
.
equals
(
listener
))
add
=
false
;
}
if
(
add
)
{
updateAvailableDeviceListListeners
.
add
(
new
WeakReference
<
UpdateAvailableDeviceListListener
>(
listener
));
}
}
}
/**
* Sorts a specific list of <tt>CaptureDeviceInfo2</tt>s so that the
* ones representing USB devices appear at the beginning/top of the
* specified list.
*
* @param devices the list of <tt>CaptureDeviceInfo2</tt>s to be
* sorted so that the ones representing USB devices appear at the
* beginning/top of the list
*/
protected
static
void
bubbleUpUsbDevices
(
List
<
CaptureDeviceInfo2
>
devices
)
{
if
(!
devices
.
isEmpty
())
{
List
<
CaptureDeviceInfo2
>
nonUsbDevices
=
new
ArrayList
<
CaptureDeviceInfo2
>(
devices
.
size
());
for
(
Iterator
<
CaptureDeviceInfo2
>
i
=
devices
.
iterator
();
i
.
hasNext
();)
{
CaptureDeviceInfo2
d
=
i
.
next
();
if
(!
d
.
isSameTransportType
(
"USB"
))
{
nonUsbDevices
.
add
(
d
);
i
.
remove
();
}
}
if
(!
nonUsbDevices
.
isEmpty
())
{
for
(
CaptureDeviceInfo2
d
:
nonUsbDevices
)
devices
.
add
(
d
);
}
}
}
/**
* Notifies this <tt>AudioSystem</tt> that an API client finished executing
* a <tt>Pa_OpenStream</tt>-like function.
*/
public
void
didOpenStream
()
{
synchronized
(
openStreamSyncRoot
)
{
openStream
--;
if
(
openStream
<
0
)
openStream
=
0
;
openStreamSyncRoot
.
notifyAll
();
}
}
/**
* Notifies this <tt>AudioSystem</tt> that a it has finished executing
* <tt>updateAvailableDeviceList()</tt>.
*/
private
void
didUpdateAvailableDeviceList
()
{
synchronized
(
openStreamSyncRoot
)
{
updateAvailableDeviceList
--;
if
(
updateAvailableDeviceList
<
0
)
updateAvailableDeviceList
=
0
;
openStreamSyncRoot
.
notifyAll
();
}
fireUpdateAvailableDeviceListEvent
(
false
);
}
/**
* Notifies the registered <tt>UpdateAvailableDeviceListListener</tt>s that
* this <tt>AudioSystem</tt>'s method <tt>updateAvailableDeviceList()</tt>
* will be or was invoked.
*
* @param will <tt>true</tt> if this <tt>AudioSystem</tt>'s method
* <tt>updateAvailableDeviceList()</tt> will be invoked or <tt>false</tt>
* if it was invoked
*/
private
void
fireUpdateAvailableDeviceListEvent
(
boolean
will
)
{
try
{
List
<
WeakReference
<
UpdateAvailableDeviceListListener
>>
ls
;
synchronized
(
updateAvailableDeviceListListeners
)
{
ls
=
new
ArrayList
<
WeakReference
<
UpdateAvailableDeviceListListener
>>(
updateAvailableDeviceListListeners
);
}
for
(
WeakReference
<
UpdateAvailableDeviceListListener
>
wr
:
ls
)
{
UpdateAvailableDeviceListListener
l
=
wr
.
get
();
if
(
l
!=
null
)
{
try
{
if
(
will
)
l
.
willUpdateAvailableDeviceList
();
else
l
.
didUpdateAvailableDeviceList
();
}
catch
(
Throwable
t
)
{
if
(
t
instanceof
ThreadDeath
)
{
throw
(
ThreadDeath
)
t
;
}
else
{
logger
.
error
(
"UpdateAvailableDeviceListListener."
+
(
will
?
"will"
:
"did"
)
+
"UpdateAvailableDeviceList failed."
,
t
);
}
}
}
}
}
catch
(
Throwable
t
)
{
if
(
t
instanceof
InterruptedException
)
Thread
.
currentThread
().
interrupt
();
else
if
(
t
instanceof
ThreadDeath
)
throw
(
ThreadDeath
)
t
;
}
}
/**
* Attempts to reorder specific lists of capture and playback/notify
* <tt>CaptureDeviceInfo2</tt>s so that devices from the same
* hardware appear at the same indices in the respective lists. The judgment
* with respect to the belonging to the same hardware is based on the names
* of the specified <tt>CaptureDeviceInfo2</tt>s. The implementation
* is provided as a fallback to stand in for scenarios in which more
* accurate relevant information is not available.
*
* @param captureDevices
* @param playbackDevices
*/
protected
static
void
matchDevicesByName
(
List
<
CaptureDeviceInfo2
>
captureDevices
,
List
<
CaptureDeviceInfo2
>
playbackDevices
)
{
Iterator
<
CaptureDeviceInfo2
>
captureIter
=
captureDevices
.
iterator
();
Pattern
pattern
=
Pattern
.
compile
(
"array|headphones|microphone|speakers|\\p{Space}|\\(|\\)"
,
Pattern
.
CASE_INSENSITIVE
);
LinkedList
<
CaptureDeviceInfo2
>
captureDevicesWithPlayback
=
new
LinkedList
<
CaptureDeviceInfo2
>();
LinkedList
<
CaptureDeviceInfo2
>
playbackDevicesWithCapture
=
new
LinkedList
<
CaptureDeviceInfo2
>();
int
count
=
0
;
while
(
captureIter
.
hasNext
())
{
CaptureDeviceInfo2
captureDevice
=
captureIter
.
next
();
String
captureName
=
captureDevice
.
getName
();
if
(
captureName
!=
null
)
{
captureName
=
pattern
.
matcher
(
captureName
).
replaceAll
(
""
);
if
(
captureName
.
length
()
!=
0
)
{
Iterator
<
CaptureDeviceInfo2
>
playbackIter
=
playbackDevices
.
iterator
();
CaptureDeviceInfo2
matchingPlaybackDevice
=
null
;
while
(
playbackIter
.
hasNext
())
{
CaptureDeviceInfo2
playbackDevice
=
playbackIter
.
next
();
String
playbackName
=
playbackDevice
.
getName
();
if
(
playbackName
!=
null
)
{
playbackName
=
pattern
.
matcher
(
playbackName
)
.
replaceAll
(
""
);
if
(
captureName
.
equals
(
playbackName
))
{
playbackIter
.
remove
();
matchingPlaybackDevice
=
playbackDevice
;
break
;
}
}
}
if
(
matchingPlaybackDevice
!=
null
)
{
captureIter
.
remove
();
captureDevicesWithPlayback
.
add
(
captureDevice
);
playbackDevicesWithCapture
.
add
(
matchingPlaybackDevice
);
count
++;
}
}
}
}
for
(
int
i
=
count
-
1
;
i
>=
0
;
i
--)
{
captureDevices
.
add
(
0
,
captureDevicesWithPlayback
.
get
(
i
));
playbackDevices
.
add
(
0
,
playbackDevicesWithCapture
.
get
(
i
));
}
}
/**
* Reinitializes this <tt>AudioSystem</tt> in order to bring it up to date
* with possible changes in the list of available devices. Invokes
* <tt>updateAvailableDeviceList()</tt> to update the devices on the
* native side and then {@link #initialize()} to reflect any changes on the
* Java side. Invoked by the native side of this <tt>AudioSystem</tt> when
* it detects that the list of available devices has changed.
*
* @throws Exception if there was an error during the invocation of
* <tt>updateAvailableDeviceList()</tt> and
* <tt>DeviceSystem.initialize()</tt>
*/
protected
void
reinitialize
()
throws
Exception
{
synchronized
(
updateAvailableDeviceListSyncRoot
)
{
willUpdateAvailableDeviceList
();
try
{
updateAvailableDeviceList
();
}
finally
{
didUpdateAvailableDeviceList
();
}
}
/*
* XXX We will likely minimize the risk of crashes on the native side
* even further by invoking initialize() with
* updateAvailableDeviceList() locked. Unfortunately, that will likely
* increase the risks of deadlocks on the Java side.
*/
invokeDeviceSystemInitialize
(
this
);
}
public
void
removeUpdateAvailableDeviceListListener
(
UpdateAvailableDeviceListListener
listener
)
{
if
(
listener
==
null
)
return
;
synchronized
(
updateAvailableDeviceListListeners
)
{
Iterator
<
WeakReference
<
UpdateAvailableDeviceListListener
>>
i
=
updateAvailableDeviceListListeners
.
iterator
();
while
(
i
.
hasNext
())
{
UpdateAvailableDeviceListListener
l
=
i
.
next
().
get
();
if
((
l
==
null
)
||
l
.
equals
(
listener
))
i
.
remove
();
}
}
}
protected
abstract
void
updateAvailableDeviceList
();
/**
* Waits for all API clients to finish executing a
* <tt>Pa_OpenStream</tt>-like function.
*/
private
void
waitForOpenStream
()
{
boolean
interrupted
=
false
;
while
(
openStream
>
0
)
{
try
{
openStreamSyncRoot
.
wait
();
}
catch
(
InterruptedException
ie
)
{
interrupted
=
true
;
}
}
if
(
interrupted
)
Thread
.
currentThread
().
interrupt
();
}
/**
* Waits for all API clients to finish executing
* <tt>updateAvailableDeviceList()</tt>.
*/
private
void
waitForUpdateAvailableDeviceList
()
{
boolean
interrupted
=
false
;
while
(
updateAvailableDeviceList
>
0
)
{
try
{
openStreamSyncRoot
.
wait
();
}
catch
(
InterruptedException
ie
)
{
interrupted
=
true
;
}
}
if
(
interrupted
)
Thread
.
currentThread
().
interrupt
();
}
/**
* Notifies this <tt>AudioSystem</tt> that an API client will start
* executing a <tt>Pa_OpenStream</tt>-like function.
*/
public
void
willOpenStream
()
{
synchronized
(
openStreamSyncRoot
)
{
waitForUpdateAvailableDeviceList
();
openStream
++;
openStreamSyncRoot
.
notifyAll
();
}
}
/**
* Notifies this <tt>AudioSystem</tt> that it will start executing
* <tt>updateAvailableDeviceList()</tt>.
*/
private
void
willUpdateAvailableDeviceList
()
{
synchronized
(
openStreamSyncRoot
)
{
waitForOpenStream
();
updateAvailableDeviceList
++;
openStreamSyncRoot
.
notifyAll
();
}
fireUpdateAvailableDeviceListEvent
(
true
);
}
}
This diff is collapsed.
Click to expand it.
src/org/jitsi/impl/neomedia/device/UpdateAvailableDeviceListListener.java
0 → 100644
+
42
−
0
View file @
adce5ec6
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package
org.jitsi.impl.neomedia.device
;
import
java.util.*
;
/**
* Represents a listener which is to be notified before and after an associated
* <tt>DeviceSystem</tt>'s function to update the list of available devices is
* invoked.
*
* @author Lyubomir Marinov
*/
public
interface
UpdateAvailableDeviceListListener
extends
EventListener
{
/**
* Notifies this listener that the associated <tt>DeviceSystem</tt>'s
* function to update the list of available devices was invoked.
*
* @throws Exception if this implementation encounters an error. Any
* <tt>Throwable</tt> apart from <tt>ThreadDeath</tt> will be ignored
* after it is logged for debugging purposes.
*/
void
didUpdateAvailableDeviceList
()
throws
Exception
;
/**
* Notifies this listener that the associated <tt>DeviceSystem</tt>'s
* function to update the list of available devices will be invoked.
*
* @throws Exception if this implementation encounters an error. Any
* <tt>Throwable</tt> apart from <tt>ThreadDeath</tt> will be ignored
* after it is logged for debugging purposes.
*/
void
willUpdateAvailableDeviceList
()
throws
Exception
;
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment