Ticket #78: roar-plugin.2.c

File roar-plugin.2.c, 10.3 KB (added by themaister, 9 years ago)

Another take on VLC plugin. Added more proper volume mixing.

Line 
1/*****************************************************************************
2 * roar-plugin.c : RoarAudio module for VLC
3 *****************************************************************************
4 * Copyright (C) 2010 the VideoLAN team
5 * Copyright (C) 2011 - Hans-Kristian Arntzen
6 * Copyright (C) 2011 - Philipp Schafft
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24/*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27
28#ifdef HAVE_CONFIG_H
29# include "config.h"
30#endif
31
32#include <roaraudio.h>
33
34#include <vlc_common.h>
35#include <vlc_plugin.h>
36#include <vlc_fs.h>
37
38#include <vlc_aout.h>
39
40#define N_(str) (str)
41
42/*****************************************************************************
43 * aout_sys_t: RoarAudio audio output method descriptor
44 *****************************************************************************
45 * This structure is part of the audio output thread descriptor.
46 *****************************************************************************/
47struct aout_sys_t
48{
49    roar_vs_t *vss;
50    vlc_thread_t thread;
51    float volume;
52    bool set_volume;
53    vlc_sem_t mixer_lock;
54};
55
56/*****************************************************************************
57 * Local prototypes
58 *****************************************************************************/
59static int  Open         ( vlc_object_t * );
60static void Close        ( vlc_object_t * );
61
62static void Play         ( aout_instance_t * );
63static void* ROARThread   ( void * );
64static int VolumeSet ( aout_instance_t *, audio_volume_t, bool mute );
65
66static mtime_t BufferDuration( aout_instance_t * p_aout );
67
68#define CONNECT_STRING_OPTION_SERVER "roar-connect-server"
69#define CONNECT_STRING_SERVER N_("RoarAudio server:")
70#define CONNECT_STRING_LONGTEXT_SERVER N_("Defines which server to connect to. A blank field will connect to localhost.")
71
72#define CONNECT_STRING_OPTION_NAME "roar-connect-name"
73#define CONNECT_STRING_NAME N_("Client name:")
74#define CONNECT_STRING_LONGTEXT_NAME N_("Defines a name for this client.")
75
76#define CONNECT_STRING_OPTION_ROLE "roar-connect-role"
77#define CONNECT_STRING_ROLE N_("Role:")
78#define CONNECT_STRING_LONGTEXT_ROLE N_("Defines the role for this client.")
79
80/*****************************************************************************
81 * Module descriptor
82 *****************************************************************************/
83
84vlc_module_begin ()
85    set_shortname( "RoarAudio" )
86    set_description( N_("RoarAudio audio output") )
87
88    set_category( CAT_AUDIO )
89    set_subcategory( SUBCAT_AUDIO_AOUT )
90
91    add_string(CONNECT_STRING_OPTION_SERVER, NULL, CONNECT_STRING_SERVER,
92          CONNECT_STRING_LONGTEXT_SERVER, true)
93    add_string(CONNECT_STRING_OPTION_NAME, NULL, CONNECT_STRING_NAME,
94          CONNECT_STRING_LONGTEXT_NAME, true)
95    add_string(CONNECT_STRING_OPTION_ROLE, NULL, CONNECT_STRING_ROLE,
96          CONNECT_STRING_LONGTEXT_ROLE, true)
97
98    set_capability( "audio output", 160 )
99    set_callbacks( Open, Close )
100vlc_module_end ()
101
102
103/*****************************************************************************
104 * Open: open the connection
105 *****************************************************************************/
106static int Open( vlc_object_t *p_this )
107{
108    aout_instance_t * p_aout = (aout_instance_t *)p_this;
109    struct aout_sys_t * p_sys;
110
111    /* Allocate structure */
112    p_aout->output.p_sys = p_sys = calloc( 1, sizeof( aout_sys_t ) );
113    if( p_sys == NULL )
114        return VLC_ENOMEM;
115
116    int channels = aout_FormatNbChannels ( &p_aout->output.output );
117    int rate = p_aout->output.output.i_rate;
118
119    char *server = var_InheritString ( p_aout, CONNECT_STRING_OPTION_SERVER );
120    char *name = var_InheritString ( p_aout, CONNECT_STRING_OPTION_NAME );
121    char *role = var_InheritString ( p_aout, CONNECT_STRING_OPTION_ROLE );
122
123    /* Sets up channel mapping */
124    switch (channels)
125    {
126        case 8:
127            p_aout->output.output.i_physical_channels
128                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
129                | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
130                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
131                | AOUT_CHAN_LFE;
132            break;
133        case 6:
134            p_aout->output.output.i_physical_channels
135                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
136                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
137                | AOUT_CHAN_LFE;
138            break;
139
140        case 4:
141            p_aout->output.output.i_physical_channels
142                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
143                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
144            break;
145
146        case 2:
147            p_aout->output.output.i_physical_channels
148                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
149            break;
150
151        case 1:
152            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
153            break;
154
155        default:
156            msg_Err(p_aout,"Invalid number of channels");
157            free(p_sys);
158            return VLC_EGENERIC;
159    }
160
161    // Hardcode for now. TODO: Fix this!
162    p_aout->output.output.i_format = VLC_CODEC_S16N;
163    p_aout->output.i_nb_samples = 4096; // Just pick something relatively small. Anything is fine.
164    p_aout->output.pf_play = Play;
165
166    aout_VolumeNoneInit( p_aout );
167    p_aout->output.pf_volume_set = VolumeSet;
168
169    if ( (p_sys->vss = roar_vs_new_simple (server, name, rate, channels, ROAR_CODEC_PCM_S_LE, 16, ROAR_DIR_PLAY, NULL)) == NULL )
170    {
171       msg_Err ( p_aout, "Cannot connect to server.");
172       free( p_sys );
173
174       if ( server )
175          free( server );
176       if ( name )
177          free( name );
178
179       return VLC_EGENERIC;
180    }
181
182    if ( role )
183    {
184       roar_vs_role ( p_sys->vss, roar_str2role( role ), NULL );
185       free ( role );
186    }
187
188    if ( server )
189       free( server );
190    if ( name )
191       free( name );
192
193#ifdef ROAR_VS_CMD_SET_FREE_VOLUME
194    int tmp = 1;
195    roar_vs_ctl( p_sys->vss, ROAR_VS_CMD_SET_FREE_VOLUME, &tmp, NULL );
196#endif
197
198    vlc_sem_init( &p_sys->mixer_lock, 1 );
199
200    /* Create RSound thread and wait for its readiness. */
201    if( vlc_clone( &p_sys->thread, ROARThread, p_aout,
202                           VLC_THREAD_PRIORITY_OUTPUT ) )
203    {
204        msg_Err( p_aout, "cannot create Roar thread (%m)" );
205        roar_vs_close(p_sys->vss, ROAR_VS_TRUE, NULL);
206        vlc_sem_destroy( &p_sys->mixer_lock );
207        free( p_sys );
208        return VLC_ENOMEM;
209    }
210
211    return VLC_SUCCESS;
212}
213
214/*****************************************************************************
215 * Play: nothing to do
216 *****************************************************************************/
217static void Play( aout_instance_t *p_aout )
218{
219    VLC_UNUSED(p_aout);
220}
221
222/*****************************************************************************
223 * Close: close the connection
224 *****************************************************************************/
225static void Close( vlc_object_t * p_this )
226{
227    aout_instance_t *p_aout = (aout_instance_t *)p_this;
228    struct aout_sys_t * p_sys = p_aout->output.p_sys;
229
230    vlc_object_kill( p_aout );
231    vlc_join(p_sys->thread, NULL);
232    p_aout->b_die = false;
233
234    roar_vs_close(p_sys->vss, ROAR_VS_TRUE, NULL);
235    vlc_sem_destroy( &p_sys->mixer_lock );
236    free( p_sys );
237}
238
239/*****************************************************************************
240 * BufferDuration: buffer status query
241 *****************************************************************************
242 * This function returns the duration in microseconds of the current buffer.
243 *****************************************************************************/
244static mtime_t BufferDuration( aout_instance_t * p_aout )
245{
246    struct aout_sys_t * p_sys = p_aout->output.p_sys;
247    return (mtime_t)(roar_vs_latency(p_sys->vss, ROAR_VS_BACKEND_DEFAULT, NULL)); 
248}
249
250/*****************************************************************************
251 * Thread loop
252 ****************************************************************************/
253
254static void* ROARThread( void *p_this )
255{
256    aout_instance_t * p_aout = p_this;
257    struct aout_sys_t * p_sys = p_aout->output.p_sys;
258    int canc = vlc_savecancel();
259
260    while ( vlc_object_alive( p_aout ) )
261    {
262        aout_buffer_t * p_buffer = NULL;
263        uint8_t * p_bytes;
264        int i_size;
265
266        mtime_t buffered = BufferDuration( p_aout );
267
268        p_buffer = aout_OutputNextBuffer( p_aout, mdate() + buffered, false );
269
270        // Here we will underrun ALOT if we pause :(
271        if ( p_buffer == NULL )
272        {
273           msleep(BufferDuration(p_aout)/8);
274           continue;
275        }
276
277        p_bytes = p_buffer->p_buffer;
278        i_size = p_buffer->i_buffer;
279
280        if ( roar_vs_write(p_sys->vss, p_bytes, i_size, NULL) <= 0 )
281        {
282            msg_Err(p_aout, "roar_vs_write() failed. Connection was closed?");
283        }
284
285        aout_BufferFree(p_buffer);
286
287        vlc_sem_wait( &p_sys->mixer_lock );
288        if (p_sys->set_volume)
289        {
290           roar_vs_volume_mono(p_sys->vss, p_sys->volume, NULL);
291           p_sys->set_volume = false;
292        }
293        vlc_sem_post( &p_sys->mixer_lock );
294    }
295
296    vlc_restorecancel(canc);
297    return NULL;
298}
299
300static int VolumeSet(aout_instance_t *aout, audio_volume_t vol, bool mute)
301{
302   struct aout_sys_t * p_sys = aout->output.p_sys;
303
304   vlc_sem_wait( &p_sys->mixer_lock );
305   if (mute)
306      p_sys->volume = 0.0;
307#ifndef ROAR_VS_CMD_SET_FREE_VOLUME
308   else if (vol > AOUT_VOLUME_DEFAULT)
309      p_sys->volume = 1.0;
310#endif
311   else
312      p_sys->volume = (float)vol / AOUT_VOLUME_DEFAULT;
313
314   p_sys->set_volume = true;
315   vlc_sem_post( &p_sys->mixer_lock );
316
317   return 0;
318}