aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/neomedia/device/AudioMediaDeviceSession.java
blob: 4de2de3f478c1de5912f7f851049a160bc31a431 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package net.java.sip.communicator.impl.neomedia.device;

import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;

import net.java.sip.communicator.impl.neomedia.audiolevel.*;
import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.util.*;

/**
 * Extends <tt>MediaDeviceSession</tt> to add audio-specific functionality.
 *
 * @author Emil Ivov
 * @author Damian Minkov
 */
public class AudioMediaDeviceSession
    extends MediaDeviceSession
{
    /**
     * Our class logger.
     */
    private Logger logger = Logger.getLogger(AudioMediaDeviceSession.class);

    /**
     * The effect that we will register with our datasource in order to measure
     * audio levels of the local user audio.
     */
    private final AudioLevelEffect localUserAudioLevelEffect
        = new AudioLevelEffect();

    /**
     * The effect that we will register with our stream in order to measure
     * audio levels of the remote user audio.
     */
    private final AudioLevelEffect streamAudioLevelEffect
        = new AudioLevelEffect();

    /**
     * Initializes a new <tt>MediaDeviceSession</tt> instance which is to
     * represent the use of a specific <tt>MediaDevice</tt> by a
     * <tt>MediaStream</tt>.
     *
     * @param device the <tt>MediaDevice</tt> the use of which by a
     * <tt>MediaStream</tt> is to be represented by the new instance
     */
    protected AudioMediaDeviceSession(AbstractMediaDevice device)
    {
        super(device);
    }

    /**
     * Initializes a new <tt>MediaDeviceSession</tt> instance which is to
     * represent the use of a specific <tt>MediaDevice</tt> by a
     * <tt>MediaStream</tt>.
     *
     * @param session previous <tt>MediaDeviceSession</tt>
     * @param device the <tt>MediaDevice</tt> the use of which by a
     * <tt>MediaStream</tt> is to be represented by the new instance
     */
    protected AudioMediaDeviceSession(AbstractMediaDevice device,
            MediaDeviceSession session)
    {
        this(device);
        transferRenderingSession(session);
    }

    /**
     * Called by {@link MediaDeviceSession#playerControllerUpdate(
     * ControllerEvent event)} when the player associated with this session's
     * <tt>ReceiveStream</tt> moves enters the <tt>Configured</tt> state, so
     * we use the occasion to add our audio level effect.
     *
     * @param player the <tt>Player</tt> which is the source of a
     * <tt>ConfigureCompleteEvent</tt>
     * @see MediaDeviceSession#playerConfigureComplete(Processor)
     */
    @Override
    protected void playerConfigureComplete(Processor player)
    {
        super.playerConfigureComplete(player);

        try
        {
            TrackControl tcs[] = player.getTrackControls();
            if (tcs != null)
            {
                for (TrackControl tc : tcs)
                {
                    if (tc.getFormat() instanceof AudioFormat)
                    {
                        // Assume there is only one audio track
                        registerStreamAudioLevelJMFEffect(tc);
                        break;
                    }
                }
            }
        }
        catch (UnsupportedPlugInException ex)
        {
            logger.error("The processor does not support effects", ex);
        }
    }

    /**
     * Gets notified about <tt>ControllerEvent</tt>s generated by the
     * processor reading our capture data source, calls the corresponding
     * method from the parent class so that it would initialize the processor
     * and then adds the level effect for the local user audio levels.
     *
     * @param event the <tt>ControllerEvent</tt> specifying the
     * <tt>Controller</tt> which is the source of the event and the very type of
     * the event
     */
    @Override
    protected void processorControllerUpdate(ControllerEvent event)
    {
        super.processorControllerUpdate(event);

        if (event instanceof ConfigureCompleteEvent)
        {
            Processor processor = (Processor) event.getSourceController();

            if (processor != null)
            {
                registerLocalUserAudioLevelEffect(processor);
            }
        }
    }

    /**
     * Creates an audio level effect and add its to the codec chain of the
     * <tt>TrackControl</tt> assuming that it only contains a single track.
     *
     * @param processor the processor on which track control we need
     * to register a level effect with.
     */
    protected void registerLocalUserAudioLevelEffect(Processor processor)
    {
        //we register the effect regardless of whether or not we have any
        //listeners at this point because we won't get a second chance.
        //however the effect would do next to nothing unless we register a
        //first listener with it.
        //
        //XXX: i am assuming that a single effect could be reused multiple times
        // if that turns out not to be the case we need to create a new instance
        // here.

        // here we add sound level indicator for captured media
        // from the microphone if there are interested listeners
        try
        {
            TrackControl tcs[] = processor.getTrackControls();

            if (tcs != null)
                for (TrackControl tc : tcs)
                    if (tc.getFormat() instanceof AudioFormat)
                    {
                        //we assume a single track
                        tc.setCodecChain(
                                new Codec[] { localUserAudioLevelEffect });
                        break;
                    }
        }
        catch (UnsupportedPlugInException ex)
        {
            logger.error(
                "Effects are not supported by the datasource.", ex);
        }
    }

    /**
     * Adds an audio level effect to the tracks of the specified
     * <tt>trackControl</tt> and so that we would notify interested listeners
     * of audio level changes.
     *
     * @param trackControl the <tt>TrackControl</tt> where we need to register
     * a level effect that would measure the audio levels of the
     * <tt>ReceiveStream</tt> associated with this class.
     *
     * @throws UnsupportedPlugInException if we fail to add our sound level
     * effect to the track control of <tt>mediaStream</tt>'s processor.
     */
    private void registerStreamAudioLevelJMFEffect(TrackControl trackControl)
        throws UnsupportedPlugInException
    {
        //we register the effect regardless of whether or not we have any
        //listeners at this point because we won't get a second chance.
        //however the effect would do next to nothing unless we register a
        //first listener with it.
        // Assume there is only one audio track
        trackControl.setCodecChain(new Codec[] { streamAudioLevelEffect });
    }

    /**
     * Sets the  <tt>SimpleAudioLevelListener</tt> that this session should be
     * notifying about changes in local audio level related information. This
     * class only supports a single listener for audio changes per source
     * (i.e. stream or data source). Audio changes are generally quite time
     * intensive (~ 50 per second) so we are doing this in order to reduce the
     * number of objects associated with the process (such as event instances
     * listener list iterators and sync copies).
     *
     * @param listener the <tt>SimpleAudioLevelListener</tt> to add
     */
    public void setLocalUserAudioLevelListener(
                                            SimpleAudioLevelListener listener)
    {
        this.localUserAudioLevelEffect.setAudioLevelListener(listener);
    }

    /**
     * Sets <tt>listener</tt> as the <tt>SimpleAudioLevelListener</tt> that we
     * are going to notify every time a change occurs in the audio level of
     * the media that this device session is receiving from the remote party.
     * This class only supports a single listener for audio changes per source
     * (i.e. stream or data source). Audio changes are generally quite time
     * intensive (~ 50 per second) so we are doing this in order to reduce the
     * number of objects associated with the process (such as event instances
     * listener list iterators and sync copies).
     *
     * @param listener the <tt>SimpleAudioLevelListener</tt> that we want
     * notified for audio level changes in the remote participant's media.
     */
    public void setStreamAudioLevelListener(SimpleAudioLevelListener listener)
    {
        this.streamAudioLevelEffect.setAudioLevelListener(listener);
    }

    /**
     * Returns the last audio level that was measured by this device session
     * for the specified <tt>ssrc</tt>.
     *
     * @param ssrc the SSRC ID whose last measured audio level we'd like to
     * retrieve.
     *
     * @return the audio level that was last measured for the specified
     * <tt>ssrc</tt> or <tt>-1</tt> if no level has been cached for that ID.
     */
    public int getLastMeasuredAudioLevel(long ssrc)
    {
        return -1;
    }

    /**
     * Returns the last audio level that was measured by the underlying
     * mixer for local user.
     *
     * @return the audio level that was last measured for the local user.
     */
    public int getLastMeasuredLocalUserAudioLevel()
    {
        return -1;
    }
}