]> git.sesse.net Git - vlc/blob - modules/stream_out/raop.c
c0ee052eeb1396bc4cf66c07f80229fa8eaaa730
[vlc] / modules / stream_out / raop.c
1 /*****************************************************************************
2  * raop.c: Remote Audio Output Protocol streaming support
3  *****************************************************************************
4  * Copyright (C) 2008 the VideoLAN team
5  * $Id$
6  *
7  * Author: Michael Hanselmann
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 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <gcrypt.h>
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_sout.h>
38 #include <vlc_block.h>
39 #include <vlc_network.h>
40 #include <vlc_strings.h>
41 #include <vlc_charset.h>
42 #include <vlc_gcrypt.h>
43 #include <vlc_es.h>
44
45 #define RAOP_PORT 5000
46 #define RAOP_USER_AGENT "VLC " VERSION
47
48
49 static const char ps_raop_rsa_pubkey[] =
50     "\xe7\xd7\x44\xf2\xa2\xe2\x78\x8b\x6c\x1f\x55\xa0\x8e\xb7\x05\x44"
51     "\xa8\xfa\x79\x45\xaa\x8b\xe6\xc6\x2c\xe5\xf5\x1c\xbd\xd4\xdc\x68"
52     "\x42\xfe\x3d\x10\x83\xdd\x2e\xde\xc1\xbf\xd4\x25\x2d\xc0\x2e\x6f"
53     "\x39\x8b\xdf\x0e\x61\x48\xea\x84\x85\x5e\x2e\x44\x2d\xa6\xd6\x26"
54     "\x64\xf6\x74\xa1\xf3\x04\x92\x9a\xde\x4f\x68\x93\xef\x2d\xf6\xe7"
55     "\x11\xa8\xc7\x7a\x0d\x91\xc9\xd9\x80\x82\x2e\x50\xd1\x29\x22\xaf"
56     "\xea\x40\xea\x9f\x0e\x14\xc0\xf7\x69\x38\xc5\xf3\x88\x2f\xc0\x32"
57     "\x3d\xd9\xfe\x55\x15\x5f\x51\xbb\x59\x21\xc2\x01\x62\x9f\xd7\x33"
58     "\x52\xd5\xe2\xef\xaa\xbf\x9b\xa0\x48\xd7\xb8\x13\xa2\xb6\x76\x7f"
59     "\x6c\x3c\xcf\x1e\xb4\xce\x67\x3d\x03\x7b\x0d\x2e\xa3\x0c\x5f\xff"
60     "\xeb\x06\xf8\xd0\x8a\xdd\xe4\x09\x57\x1a\x9c\x68\x9f\xef\x10\x72"
61     "\x88\x55\xdd\x8c\xfb\x9a\x8b\xef\x5c\x89\x43\xef\x3b\x5f\xaa\x15"
62     "\xdd\xe6\x98\xbe\xdd\xf3\x59\x96\x03\xeb\x3e\x6f\x61\x37\x2b\xb6"
63     "\x28\xf6\x55\x9f\x59\x9a\x78\xbf\x50\x06\x87\xaa\x7f\x49\x76\xc0"
64     "\x56\x2d\x41\x29\x56\xf8\x98\x9e\x18\xa6\x35\x5b\xd8\x15\x97\x82"
65     "\x5e\x0f\xc8\x75\x34\x3e\xc7\x82\x11\x76\x25\xcd\xbf\x98\x44\x7b";
66
67 static const char ps_raop_rsa_exp[] = "\x01\x00\x01";
68
69 static const char psz_delim_space[] = " ";
70 static const char psz_delim_colon[] = ":";
71 static const char psz_delim_equal[] = "=";
72 static const char psz_delim_semicolon[] = ";";
73
74
75 /*****************************************************************************
76  * Prototypes
77  *****************************************************************************/
78 static int Open( vlc_object_t * );
79 static void Close( vlc_object_t * );
80
81 static sout_stream_id_t *Add( sout_stream_t *, es_format_t * );
82 static int Del( sout_stream_t *, sout_stream_id_t * );
83 static int Send( sout_stream_t *, sout_stream_id_t *, block_t* );
84
85 static int VolumeCallback( vlc_object_t *p_this, char const *psz_cmd,
86                            vlc_value_t oldval, vlc_value_t newval,
87                            void *p_data );
88
89 typedef enum
90 {
91     JACK_TYPE_NONE = 0,
92     JACK_TYPE_ANALOG,
93     JACK_TYPE_DIGITAL,
94 } jack_type_t;
95
96 struct sout_stream_sys_t
97 {
98     /* Input parameters */
99     char *psz_host;
100     int i_volume;
101
102     /* Plugin status */
103     sout_stream_id_t *p_audio_stream;
104     bool b_alac_warning;
105     bool b_volume_callback;
106
107     /* Connection state */
108     int i_control_fd;
109     int i_stream_fd;
110
111     uint8_t ps_aes_key[16];
112     uint8_t ps_aes_iv[16];
113     gcry_cipher_hd_t aes_ctx;
114
115     char *psz_url;
116     char *psz_client_instance;
117     char *psz_session;
118
119     int i_cseq;
120     int i_server_port;
121     int i_audio_latency;
122     int i_jack_type;
123
124     /* Send buffer */
125     size_t i_sendbuf_len;
126     uint8_t *p_sendbuf;
127 };
128
129 struct sout_stream_id_t
130 {
131     es_format_t fmt;
132 };
133
134
135 /*****************************************************************************
136  * Module descriptor
137  *****************************************************************************/
138 #define SOUT_CFG_PREFIX "sout-raop-"
139
140 #define HOST_TEXT N_("Host")
141 #define HOST_LONGTEXT N_("Hostname or IP address of target device")
142
143 #define VOLUME_TEXT N_("Volume")
144 #define VOLUME_LONGTEXT N_("Output volume for analog output: 0 for silence, " \
145                            "1..255 from almost silent to very loud.")
146
147 vlc_module_begin()
148     set_shortname( N_("RAOP") )
149     set_description( N_("Remote Audio Output Protocol stream output") )
150     set_capability( "sout stream", 0 )
151     add_shortcut( "raop" )
152     set_category( CAT_SOUT )
153     set_subcategory( SUBCAT_SOUT_STREAM )
154     add_string( SOUT_CFG_PREFIX "host", "", NULL,
155                 HOST_TEXT, HOST_LONGTEXT, false )
156     add_integer_with_range( SOUT_CFG_PREFIX "volume", 100, 0, 255, NULL,
157                             VOLUME_TEXT, VOLUME_LONGTEXT, false )
158     set_callbacks( Open, Close )
159 vlc_module_end()
160
161 static const char *const ppsz_sout_options[] = {
162     "host",
163     "volume",
164     NULL
165 };
166
167
168 /*****************************************************************************
169  * Utilities:
170  *****************************************************************************/
171 static void FreeSys( vlc_object_t *p_this, sout_stream_sys_t *p_sys )
172 {
173     sout_stream_t *p_stream = (sout_stream_t*)p_this;
174
175     if ( p_sys->i_control_fd >= 0 )
176         net_Close( p_sys->i_control_fd );
177     if ( p_sys->i_stream_fd >= 0 )
178         net_Close( p_sys->i_stream_fd );
179     if ( p_sys->b_volume_callback )
180         var_DelCallback( p_stream, SOUT_CFG_PREFIX "volume",
181                          VolumeCallback, NULL );
182
183     gcry_cipher_close( p_sys->aes_ctx );
184
185     free( p_sys->p_sendbuf );
186     free( p_sys->psz_host );
187     free( p_sys->psz_url );
188     free( p_sys->psz_session );
189     free( p_sys->psz_client_instance );
190     free( p_sys );
191 }
192
193 static void FreeId( sout_stream_id_t *id )
194 {
195     free( id );
196 }
197
198 static void RemoveBase64Padding( char *str )
199 {
200     char *ps_pos = strchr( str, '=' );
201     if ( ps_pos != NULL )
202         *ps_pos = '\0';
203 }
204
205 static int CheckForGcryptErrorWithLine( sout_stream_t *p_stream,
206                                         gcry_error_t i_gcrypt_err,
207                                         unsigned int i_line )
208 {
209     if ( i_gcrypt_err != GPG_ERR_NO_ERROR )
210     {
211         msg_Err( p_stream, "gcrypt error (line %d): %s", i_line,
212                  gpg_strerror( i_gcrypt_err ) );
213         return 1;
214     }
215
216     return 0;
217 }
218
219 /* Wrapper to pass line number for easier debugging */
220 #define CheckForGcryptError( p_this, i_gcrypt_err ) \
221     CheckForGcryptErrorWithLine( p_this, i_gcrypt_err, __LINE__ )
222
223 /* MGF1 is specified in RFC2437, section 10.2.1. Variables are named after the
224  * specification.
225  */
226 static int MGF1( vlc_object_t *p_this,
227                  unsigned char *mask, size_t l,
228                  const unsigned char *Z, const size_t zLen,
229                  const int Hash )
230 {
231     sout_stream_t *p_stream = (sout_stream_t*)p_this;
232     gcry_error_t i_gcrypt_err;
233     gcry_md_hd_t md_handle = NULL;
234     unsigned int hLen;
235     unsigned char *ps_md;
236     uint32_t counter = 0;
237     uint8_t C[4];
238     size_t i_copylen;
239     int i_err = VLC_SUCCESS;
240
241     assert( mask != NULL );
242     assert( Z != NULL );
243
244     hLen = gcry_md_get_algo_dlen( Hash );
245
246     i_gcrypt_err = gcry_md_open( &md_handle, Hash, 0 );
247     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
248     {
249         i_err = VLC_EGENERIC;
250         goto error;
251     }
252
253     while ( l > 0 )
254     {
255         /* 3. For counter from 0 to \lceil{l / hLen}\rceil-1, do the following:
256          * a. Convert counter to an octet string C of length 4 with the
257          *    primitive I2OSP: C = I2OSP (counter, 4)
258          */
259         C[0] = (counter >> 24) & 0xff;
260         C[1] = (counter >> 16) & 0xff;
261         C[2] = (counter >> 8) & 0xff;
262         C[3] = counter & 0xff;
263         ++counter;
264
265         /* b. Concatenate the hash of the seed Z and C to the octet string T:
266          *    T = T || Hash (Z || C)
267          */
268         gcry_md_reset( md_handle );
269         gcry_md_write( md_handle, Z, zLen );
270         gcry_md_write( md_handle, C, 4 );
271         ps_md = gcry_md_read( md_handle, Hash );
272
273         /* 4. Output the leading l octets of T as the octet string mask. */
274         i_copylen = __MIN( l, hLen );
275         memcpy( mask, ps_md, i_copylen );
276         mask += i_copylen;
277         l -= i_copylen;
278     }
279
280 error:
281     gcry_md_close( md_handle );
282
283     return i_err;
284 }
285
286 /* EME-OAEP-ENCODE is specified in RFC2437, section 9.1.1.1. Variables are
287  * named after the specification.
288  */
289 static int AddOaepPadding( vlc_object_t *p_this,
290                            unsigned char *EM, const size_t emLenWithPrefix,
291                            const unsigned char *M, const size_t mLen,
292                            const unsigned char *P, const size_t pLen )
293 {
294     const int Hash = GCRY_MD_SHA1;
295     const unsigned int hLen = gcry_md_get_algo_dlen( Hash );
296     unsigned char *seed = NULL;
297     unsigned char *DB = NULL;
298     unsigned char *dbMask = NULL;
299     unsigned char *seedMask = NULL;
300     size_t emLen;
301     size_t psLen;
302     size_t i;
303     int i_err = VLC_SUCCESS;
304
305     /* Space for 0x00 prefix in EM. */
306     emLen = emLenWithPrefix - 1;
307
308     /* Step 2:
309      * If ||M|| > emLen-2hLen-1 then output "message too long" and stop.
310      */
311     if ( mLen > (emLen - (2 * hLen) - 1) )
312     {
313         msg_Err( p_this , "Message too long" );
314         goto error;
315     }
316
317     /* Step 3:
318      * Generate an octet string PS consisting of emLen-||M||-2hLen-1 zero
319      * octets. The length of PS may be 0.
320      */
321     psLen = emLen - mLen - (2 * hLen) - 1;
322
323     /*
324      * Step 5:
325      * Concatenate pHash, PS, the message M, and other padding to form a data
326      * block DB as: DB = pHash || PS || 01 || M
327      */
328     DB = calloc( 1, hLen + psLen + 1 + mLen );
329     dbMask = calloc( 1, emLen - hLen );
330     seedMask = calloc( 1, hLen );
331
332     if ( DB == NULL || dbMask == NULL || seedMask == NULL )
333     {
334         i_err = VLC_ENOMEM;
335         goto error;
336     }
337
338     /* Step 4:
339      * Let pHash = Hash(P), an octet string of length hLen.
340      */
341     gcry_md_hash_buffer( Hash, DB, P, pLen );
342
343     /* Step 3:
344      * Generate an octet string PS consisting of emLen-||M||-2hLen-1 zero
345      * octets. The length of PS may be 0.
346      */
347     memset( DB + hLen, 0, psLen );
348
349     /* Step 5:
350      * Concatenate pHash, PS, the message M, and other padding to form a data
351      * block DB as: DB = pHash || PS || 01 || M
352      */
353     DB[hLen + psLen] = 0x01;
354     memcpy( DB + hLen + psLen + 1, M, mLen );
355
356     /* Step 6:
357      * Generate a random octet string seed of length hLen
358      */
359     seed = gcry_random_bytes( hLen, GCRY_STRONG_RANDOM );
360     if ( seed == NULL )
361     {
362         i_err = VLC_ENOMEM;
363         goto error;
364     }
365
366     /* Step 7:
367      * Let dbMask = MGF(seed, emLen-hLen).
368      */
369     i_err = MGF1( p_this, dbMask, emLen - hLen, seed, hLen, Hash );
370     if ( i_err != VLC_SUCCESS )
371         goto error;
372
373     /* Step 8:
374      * Let maskedDB = DB \xor dbMask.
375      */
376     for ( i = 0; i < (emLen - hLen); ++i )
377         DB[i] ^= dbMask[i];
378
379     /* Step 9:
380      * Let seedMask = MGF(maskedDB, hLen).
381      */
382     i_err = MGF1( p_this, seedMask, hLen, DB, emLen - hLen, Hash );
383     if ( i_err != VLC_SUCCESS )
384         goto error;
385
386     /* Step 10:
387      * Let maskedSeed = seed \xor seedMask.
388      */
389     for ( i = 0; i < hLen; ++i )
390         seed[i] ^= seedMask[i];
391
392     /* Step 11:
393      * Let EM = maskedSeed || maskedDB.
394      */
395     assert( (1 + hLen + (hLen + psLen + 1 + mLen)) == emLenWithPrefix );
396     EM[0] = 0x00;
397     memcpy( EM + 1, seed, hLen );
398     memcpy( EM + 1 + hLen, DB, hLen + psLen + 1 + mLen );
399
400     /* Step 12:
401      * Output EM.
402      */
403
404 error:
405     free( DB );
406     free( dbMask );
407     free( seedMask );
408     free( seed );
409
410     return i_err;
411 }
412
413 static int EncryptAesKeyBase64( vlc_object_t *p_this, char **result )
414 {
415     sout_stream_t *p_stream = (sout_stream_t*)p_this;
416     sout_stream_sys_t *p_sys = p_stream->p_sys;
417     gcry_error_t i_gcrypt_err;
418     gcry_sexp_t sexp_rsa_params = NULL;
419     gcry_sexp_t sexp_input = NULL;
420     gcry_sexp_t sexp_encrypted = NULL;
421     gcry_sexp_t sexp_token_a = NULL;
422     gcry_mpi_t mpi_pubkey = NULL;
423     gcry_mpi_t mpi_exp = NULL;
424     gcry_mpi_t mpi_input = NULL;
425     gcry_mpi_t mpi_output = NULL;
426     unsigned char ps_padded_key[256];
427     unsigned char *ps_value;
428     size_t i_value_size;
429     int i_err = VLC_SUCCESS;
430
431     /* Add RSA-OAES-SHA1 padding */
432     i_err = AddOaepPadding( p_this,
433                             ps_padded_key, sizeof( ps_padded_key ),
434                             p_sys->ps_aes_key, sizeof( p_sys->ps_aes_key ),
435                             NULL, 0 );
436     if ( i_err != VLC_SUCCESS )
437         goto error;
438
439     /* Read public key */
440     i_gcrypt_err = gcry_mpi_scan( &mpi_pubkey, GCRYMPI_FMT_USG,
441                                   ps_raop_rsa_pubkey,
442                                   sizeof( ps_raop_rsa_pubkey ) - 1, NULL );
443     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
444     {
445         i_err = VLC_EGENERIC;
446         goto error;
447     }
448
449     /* Read exponent */
450     i_gcrypt_err = gcry_mpi_scan( &mpi_exp, GCRYMPI_FMT_USG, ps_raop_rsa_exp,
451                                   sizeof( ps_raop_rsa_exp ) - 1, NULL );
452     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
453     {
454         i_err = VLC_EGENERIC;
455         goto error;
456     }
457
458     /* If the input data starts with a set bit (0x80), gcrypt thinks it's a
459      * signed integer and complains. Prefixing it with a zero byte (\0)
460      * works, but involves more work. Converting it to an MPI in our code is
461      * cleaner.
462      */
463     i_gcrypt_err = gcry_mpi_scan( &mpi_input, GCRYMPI_FMT_USG,
464                                   ps_padded_key, sizeof( ps_padded_key ),
465                                   NULL);
466     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
467     {
468         i_err = VLC_EGENERIC;
469         goto error;
470     }
471
472     /* Build S-expression with RSA parameters */
473     i_gcrypt_err = gcry_sexp_build( &sexp_rsa_params, NULL,
474                                     "(public-key(rsa(n %m)(e %m)))",
475                                     mpi_pubkey, mpi_exp );
476     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
477     {
478         i_err = VLC_EGENERIC;
479         goto error;
480     }
481
482     /* Build S-expression for data */
483     i_gcrypt_err = gcry_sexp_build( &sexp_input, NULL, "(data(value %m))",
484                                     mpi_input );
485     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
486     {
487         i_err = VLC_EGENERIC;
488         goto error;
489     }
490
491     /* Encrypt data */
492     i_gcrypt_err = gcry_pk_encrypt( &sexp_encrypted, sexp_input,
493                                     sexp_rsa_params );
494     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
495     {
496         i_err = VLC_EGENERIC;
497         goto error;
498     }
499
500     /* Extract encrypted data */
501     sexp_token_a = gcry_sexp_find_token( sexp_encrypted, "a", 0 );
502     if ( !sexp_token_a )
503     {
504         msg_Err( p_this , "Token 'a' not found in result S-expression" );
505         i_err = VLC_EGENERIC;
506         goto error;
507     }
508
509     mpi_output = gcry_sexp_nth_mpi( sexp_token_a, 1, GCRYMPI_FMT_USG );
510     if ( !mpi_output )
511     {
512         msg_Err( p_this, "Unable to extract MPI from result" );
513         i_err = VLC_EGENERIC;
514         goto error;
515     }
516
517     /* Copy encrypted data into char array */
518     i_gcrypt_err = gcry_mpi_aprint( GCRYMPI_FMT_USG, &ps_value, &i_value_size,
519                                     mpi_output );
520     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
521     {
522         i_err = VLC_EGENERIC;
523         goto error;
524     }
525
526     /* Encode in Base64 */
527     *result = vlc_b64_encode_binary( ps_value, i_value_size );
528
529 error:
530     gcry_sexp_release( sexp_rsa_params );
531     gcry_sexp_release( sexp_input );
532     gcry_sexp_release( sexp_encrypted );
533     gcry_sexp_release( sexp_token_a );
534     gcry_mpi_release( mpi_pubkey );
535     gcry_mpi_release( mpi_exp );
536     gcry_mpi_release( mpi_input );
537     gcry_mpi_release( mpi_output );
538
539     return i_err;
540 }
541
542 /* Splits the value of a received header.
543  *
544  * Example: "Transport: RTP/AVP/TCP;unicast;mode=record;server_port=6000"
545  */
546 static int SplitHeader( char **ppsz_next, char **ppsz_name,
547                         char **ppsz_value )
548 {
549     /* Find semicolon (separator between assignments) */
550     *ppsz_name = strsep( ppsz_next, psz_delim_semicolon );
551     if ( *ppsz_name )
552     {
553         /* Skip spaces */
554         *ppsz_name += strspn( *ppsz_name, psz_delim_space );
555
556         /* Get value */
557         *ppsz_value = *ppsz_name;
558         strsep( ppsz_value, psz_delim_equal );
559     }
560     else
561         *ppsz_value = NULL;
562
563     return !!*ppsz_name;
564 }
565
566 static void FreeHeader( void *p_value, void *p_data )
567 {
568     VLC_UNUSED( p_data );
569     free( p_value );
570 }
571
572 static int ReadStatusLine( vlc_object_t *p_this )
573 {
574     sout_stream_t *p_stream = (sout_stream_t*)p_this;
575     sout_stream_sys_t *p_sys = p_stream->p_sys;
576     char *psz_original = NULL;
577     char *psz_line = NULL;
578     char *psz_token;
579     char *psz_next;
580     int i_err = VLC_SUCCESS;
581
582     psz_line = net_Gets( p_this, p_sys->i_control_fd, NULL );
583     if ( !psz_line )
584     {
585         i_err = VLC_EGENERIC;
586         goto error;
587     }
588
589     psz_original = strdup( psz_line );
590     psz_next = psz_line;
591
592     /* Protocol field */
593     psz_token = strsep( &psz_next, psz_delim_space );
594     if ( !psz_token || strncmp( psz_token, "RTSP/1.", 7 ) != 0 )
595     {
596         msg_Err( p_this, "Unknown protocol (%s)", psz_original );
597         i_err = VLC_EGENERIC;
598         goto error;
599     }
600
601     /* Status field */
602     psz_token = strsep( &psz_next, psz_delim_space );
603     if ( !psz_token || strcmp( psz_token, "200" ) != 0 )
604     {
605         msg_Err( p_this, "Request failed (%s)", psz_original );
606         i_err = VLC_EGENERIC;
607         goto error;
608     }
609
610 error:
611     free( psz_original );
612     free( psz_line );
613
614     return i_err;
615 }
616
617 static int ReadHeader( vlc_object_t *p_this,
618                        vlc_dictionary_t *p_resp_headers,
619                        int *done )
620 {
621     sout_stream_t *p_stream = (sout_stream_t*)p_this;
622     sout_stream_sys_t *p_sys = p_stream->p_sys;
623     char *psz_original = NULL;
624     char *psz_line = NULL;
625     char *psz_token;
626     char *psz_next;
627     char *psz_name;
628     char *psz_value;
629     int i_err = VLC_SUCCESS;
630
631     psz_line = net_Gets( p_this, p_sys->i_control_fd, NULL );
632     if ( !psz_line )
633     {
634         i_err = VLC_EGENERIC;
635         goto error;
636     }
637
638     /* Empty line for response end */
639     if ( psz_line[0] == '\0' )
640         *done = 1;
641     else if ( p_resp_headers )
642     {
643         psz_original = strdup( psz_line );
644         psz_next = psz_line;
645
646         psz_token = strsep( &psz_next, psz_delim_colon );
647         if ( !psz_token || psz_next[0] != ' ' )
648         {
649             msg_Err( p_this, "Invalid header format (%s)", psz_original );
650             i_err = VLC_EGENERIC;
651             goto error;
652         }
653
654         psz_name = psz_token;
655         psz_value = psz_next + 1;
656
657         vlc_dictionary_insert( p_resp_headers, psz_name, strdup( psz_value ) );
658     }
659
660 error:
661     free( psz_original );
662     free( psz_line );
663
664     return i_err;
665 }
666
667 static int WriteAuxHeaders( vlc_object_t *p_this,
668                             vlc_dictionary_t *p_req_headers )
669 {
670     sout_stream_t *p_stream = (sout_stream_t*)p_this;
671     sout_stream_sys_t *p_sys = p_stream->p_sys;
672     char **ppsz_keys = NULL;
673     char *psz_key;
674     char *psz_value;
675     int i_err = VLC_SUCCESS;
676     int i_rc;
677     size_t i;
678
679     ppsz_keys = vlc_dictionary_all_keys( p_req_headers );
680     for ( i = 0; ppsz_keys[i]; ++i )
681     {
682         psz_key = ppsz_keys[i];
683         psz_value = vlc_dictionary_value_for_key( p_req_headers, psz_key );
684
685         i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
686                            "%s: %s\r\n", psz_key, psz_value );
687         if ( i_rc < 0 )
688         {
689             i_err = VLC_EGENERIC;
690             goto error;
691         }
692     }
693
694 error:
695     for ( i = 0; ppsz_keys[i]; ++i )
696         free( ppsz_keys[i] );
697     free( ppsz_keys );
698
699     return i_err;
700 }
701
702 static int SendRequest( vlc_object_t *p_this, const char *psz_method,
703                         const char *psz_content_type, const char *psz_body,
704                         vlc_dictionary_t *p_req_headers )
705 {
706     sout_stream_t *p_stream = (sout_stream_t*)p_this;
707     sout_stream_sys_t *p_sys = p_stream->p_sys;
708     const unsigned char psz_headers_end[] = "\r\n";
709     size_t i_body_length = 0;
710     int i_err = VLC_SUCCESS;
711     int i_rc;
712
713     i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
714                        "%s %s RTSP/1.0\r\n"
715                        "User-Agent: " RAOP_USER_AGENT "\r\n"
716                        "Client-Instance: %s\r\n"
717                        "CSeq: %d\r\n",
718                        psz_method, p_sys->psz_url,
719                        p_sys->psz_client_instance,
720                        ++p_sys->i_cseq );
721     if ( i_rc < 0 )
722     {
723         i_err = VLC_EGENERIC;
724         goto error;
725     }
726
727     if ( psz_content_type )
728     {
729         i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
730                            "Content-Type: %s\r\n", psz_content_type );
731         if ( i_rc < 0 )
732         {
733             i_err = VLC_ENOMEM;
734             goto error;
735         }
736     }
737
738     if ( psz_body )
739     {
740         i_body_length = strlen( psz_body );
741
742         i_rc = net_Printf( p_this, p_sys->i_control_fd, NULL,
743                            "Content-Length: %u\r\n",
744                            (unsigned int)i_body_length );
745         if ( i_rc < 0 )
746         {
747             i_err = VLC_ENOMEM;
748             goto error;
749         }
750     }
751
752     if ( p_req_headers )
753     {
754         i_err = WriteAuxHeaders( p_this, p_req_headers );
755         if ( i_err != VLC_SUCCESS )
756             goto error;
757     }
758
759     i_rc = net_Write( p_this, p_sys->i_control_fd, NULL,
760                       psz_headers_end, sizeof( psz_headers_end ) - 1 );
761     if ( i_rc < 0 )
762     {
763         i_err = VLC_ENOMEM;
764         goto error;
765     }
766
767     if ( psz_body )
768         net_Write( p_this, p_sys->i_control_fd, NULL,
769                    psz_body, i_body_length );
770
771 error:
772     return i_err;
773 }
774
775 static int ExecRequest( vlc_object_t *p_this, const char *psz_method,
776                         const char *psz_content_type, const char *psz_body,
777                         vlc_dictionary_t *p_req_headers,
778                         vlc_dictionary_t *p_resp_headers )
779 {
780     sout_stream_t *p_stream = (sout_stream_t*)p_this;
781     sout_stream_sys_t *p_sys = p_stream->p_sys;
782     int headers_done;
783     int i_err = VLC_SUCCESS;
784
785     if ( p_sys->i_control_fd < 0 )
786     {
787         msg_Err( p_this, "Control connection not open" );
788         i_err = VLC_EGENERIC;
789         goto error;
790     }
791
792     /* Send request */
793     i_err = SendRequest( p_this, psz_method, psz_content_type, psz_body,
794                          p_req_headers);
795     if ( i_err != VLC_SUCCESS )
796         goto error;
797
798     /* Read status line */
799     i_err = ReadStatusLine( p_this );
800     if ( i_err != VLC_SUCCESS )
801         goto error;
802
803     if ( p_resp_headers )
804         vlc_dictionary_clear( p_resp_headers, FreeHeader, NULL );
805
806     /* Read headers */
807     headers_done = 0;
808     while ( !headers_done )
809     {
810         i_err = ReadHeader( p_this, p_resp_headers, &headers_done );
811         if ( i_err != VLC_SUCCESS )
812             goto error;
813     }
814
815 error:
816     return i_err;
817 }
818
819 static int AnnounceSDP( vlc_object_t *p_this, char *psz_local,
820                         uint32_t i_session_id )
821 {
822     sout_stream_t *p_stream = (sout_stream_t*)p_this;
823     sout_stream_sys_t *p_sys = p_stream->p_sys;
824     vlc_dictionary_t req_headers;
825     vlc_dictionary_t resp_headers;
826     unsigned char ps_sac[16];
827     char *psz_sdp = NULL;
828     char *psz_sac_base64 = NULL;
829     char *psz_aes_key_base64 = NULL;
830     char *psz_aes_iv_base64 = NULL;
831     int i_err = VLC_SUCCESS;
832     int i_rc;
833
834     vlc_dictionary_init( &req_headers, 0 );
835     vlc_dictionary_init( &resp_headers, 0 );
836
837     /* Encrypt AES key and encode it in Base64 */
838     i_rc = EncryptAesKeyBase64( p_this, &psz_aes_key_base64 );
839     if ( i_rc != VLC_SUCCESS || psz_aes_key_base64 == NULL )
840     {
841         i_err = VLC_EGENERIC;
842         goto error;
843     }
844     RemoveBase64Padding( psz_aes_key_base64 );
845
846     /* Encode AES IV in Base64 */
847     psz_aes_iv_base64 = vlc_b64_encode_binary( p_sys->ps_aes_iv,
848                                                sizeof( p_sys->ps_aes_iv ) );
849     if ( psz_aes_iv_base64 == NULL )
850     {
851         i_err = VLC_EGENERIC;
852         goto error;
853     }
854     RemoveBase64Padding( psz_aes_iv_base64 );
855
856     /* Random bytes for Apple-Challenge header */
857     gcry_randomize( ps_sac, sizeof( ps_sac ), GCRY_STRONG_RANDOM );
858
859     psz_sac_base64 = vlc_b64_encode_binary( ps_sac, sizeof( ps_sac ) );
860     if ( psz_sac_base64 == NULL )
861     {
862         i_err = VLC_EGENERIC;
863         goto error;
864     }
865     RemoveBase64Padding( psz_sac_base64 );
866
867     /* Build SDP
868      * Note: IPv6 addresses also use "IP4". Make sure not to include the
869      * scope ID.
870      */
871     i_rc = asprintf( &psz_sdp,
872                      "v=0\r\n"
873                      "o=iTunes %u 0 IN IP4 %s\r\n"
874                      "s=iTunes\r\n"
875                      "c=IN IP4 %s\r\n"
876                      "t=0 0\r\n"
877                      "m=audio 0 RTP/AVP 96\r\n"
878                      "a=rtpmap:96 AppleLossless\r\n"
879                      "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n"
880                      "a=rsaaeskey:%s\r\n"
881                      "a=aesiv:%s\r\n",
882                      i_session_id, psz_local, p_sys->psz_host,
883                      psz_aes_key_base64, psz_aes_iv_base64 );
884
885     if ( i_rc < 0 )
886     {
887         i_err = VLC_ENOMEM;
888         goto error;
889     }
890
891     /* Build and send request */
892     vlc_dictionary_insert( &req_headers, "Apple-Challenge", psz_sac_base64 );
893
894     i_err = ExecRequest( p_this, "ANNOUNCE", "application/sdp", psz_sdp,
895                          &req_headers, &resp_headers);
896     if ( i_err != VLC_SUCCESS )
897         goto error;
898
899 error:
900     vlc_dictionary_clear( &req_headers, NULL, NULL );
901     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
902
903     free( psz_sdp );
904     free( psz_sac_base64 );
905     free( psz_aes_key_base64 );
906     free( psz_aes_iv_base64 );
907
908     return i_err;
909 }
910
911 static int SendSetup( vlc_object_t *p_this )
912 {
913     sout_stream_t *p_stream = (sout_stream_t*)p_this;
914     sout_stream_sys_t *p_sys = p_stream->p_sys;
915     vlc_dictionary_t req_headers;
916     vlc_dictionary_t resp_headers;
917     int i_err = VLC_SUCCESS;
918     char *psz_tmp;
919     char *psz_next;
920     char *psz_name;
921     char *psz_value;
922
923     vlc_dictionary_init( &req_headers, 0 );
924     vlc_dictionary_init( &resp_headers, 0 );
925
926     vlc_dictionary_insert( &req_headers, "Transport",
927                            ((void*)"RTP/AVP/TCP;unicast;interleaved=0-1;"
928                             "mode=record") );
929
930     i_err = ExecRequest( p_this, "SETUP", NULL, NULL,
931                          &req_headers, &resp_headers );
932     if ( i_err != VLC_SUCCESS )
933         goto error;
934
935     psz_tmp = vlc_dictionary_value_for_key( &resp_headers, "Session" );
936     if ( !psz_tmp )
937     {
938         msg_Err( p_this, "Missing 'Session' header during setup" );
939         i_err = VLC_EGENERIC;
940         goto error;
941     }
942
943     free( p_sys->psz_session );
944     p_sys->psz_session = strdup( psz_tmp );
945
946     /* Get server_port */
947     psz_next = vlc_dictionary_value_for_key( &resp_headers, "Transport" );
948     while ( SplitHeader( &psz_next, &psz_name, &psz_value ) )
949     {
950         if ( psz_value && strcmp( psz_name, "server_port" ) == 0 )
951         {
952             p_sys->i_server_port = atoi( psz_value );
953             break;
954         }
955     }
956
957     if ( !p_sys->i_server_port )
958     {
959         msg_Err( p_this, "Missing 'server_port' during setup" );
960         i_err = VLC_EGENERIC;
961         goto error;
962     }
963
964     /* Get jack type */
965     psz_next = vlc_dictionary_value_for_key( &resp_headers,
966                                              "Audio-Jack-Status" );
967     while ( SplitHeader( &psz_next, &psz_name, &psz_value ) )
968     {
969         if ( strcmp( psz_name, "type" ) != 0 )
970             continue;
971
972         if ( strcmp( psz_value, "analog" ) == 0 )
973             p_sys->i_jack_type = JACK_TYPE_ANALOG;
974
975         else if ( strcmp( psz_value, "digital" ) == 0 )
976             p_sys->i_jack_type = JACK_TYPE_DIGITAL;
977
978         break;
979     }
980
981 error:
982     vlc_dictionary_clear( &req_headers, NULL, NULL );
983     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
984
985     return i_err;
986 }
987
988 static int SendRecord( vlc_object_t *p_this )
989 {
990     sout_stream_t *p_stream = (sout_stream_t*)p_this;
991     sout_stream_sys_t *p_sys = p_stream->p_sys;
992     vlc_dictionary_t req_headers;
993     vlc_dictionary_t resp_headers;
994     int i_err = VLC_SUCCESS;
995     char *psz_value;
996
997     vlc_dictionary_init( &req_headers, 0 );
998     vlc_dictionary_init( &resp_headers, 0 );
999
1000     vlc_dictionary_insert( &req_headers, "Range", (void *)"npt=0-" );
1001     vlc_dictionary_insert( &req_headers, "RTP-Info",
1002                            (void *)"seq=0;rtptime=0" );
1003     vlc_dictionary_insert( &req_headers, "Session",
1004                            (void *)p_sys->psz_session );
1005
1006     i_err = ExecRequest( p_this, "RECORD", NULL, NULL,
1007                          &req_headers, &resp_headers );
1008     if ( i_err != VLC_SUCCESS )
1009         goto error;
1010
1011     psz_value = vlc_dictionary_value_for_key( &resp_headers, "Audio-Latency" );
1012     if ( psz_value )
1013         p_sys->i_audio_latency = atoi( psz_value );
1014     else
1015         p_sys->i_audio_latency = 0;
1016
1017 error:
1018     vlc_dictionary_clear( &req_headers, NULL, NULL );
1019     vlc_dictionary_clear( &resp_headers, FreeHeader, NULL );
1020
1021     return i_err;
1022 }
1023
1024 static int SendFlush( vlc_object_t *p_this )
1025 {
1026     VLC_UNUSED( p_this );
1027
1028     vlc_dictionary_t req_headers;
1029     int i_err = VLC_SUCCESS;
1030
1031     vlc_dictionary_init( &req_headers, 0 );
1032
1033     vlc_dictionary_insert( &req_headers, "RTP-Info",
1034                            (void *)"seq=0;rtptime=0" );
1035
1036     i_err = ExecRequest( p_this, "FLUSH", NULL, NULL, &req_headers, NULL );
1037     if ( i_err != VLC_SUCCESS )
1038         goto error;
1039
1040 error:
1041     vlc_dictionary_clear( &req_headers, NULL, NULL );
1042
1043     return i_err;
1044 }
1045
1046 static int SendTeardown( vlc_object_t *p_this )
1047 {
1048     int i_err = VLC_SUCCESS;
1049
1050     i_err = ExecRequest( p_this, "TEARDOWN", NULL, NULL, NULL, NULL );
1051     if ( i_err != VLC_SUCCESS )
1052         goto error;
1053
1054 error:
1055     return i_err;
1056 }
1057
1058 static int UpdateVolume( vlc_object_t *p_this )
1059 {
1060     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1061     sout_stream_sys_t *p_sys = p_stream->p_sys;
1062     vlc_dictionary_t req_headers;
1063     char *psz_parameters = NULL;
1064     double d_volume;
1065     int i_err = VLC_SUCCESS;
1066     int i_rc;
1067
1068     vlc_dictionary_init( &req_headers, 0 );
1069
1070     /* Our volume is 0..255, RAOP is -144..0 (-144 off, -30..0 on) */
1071
1072     /* Limit range */
1073     p_sys->i_volume = __MAX( 0, __MIN( p_sys->i_volume, 255 ) );
1074
1075     if ( p_sys->i_volume == 0 )
1076         d_volume = -144.0;
1077     else
1078         d_volume = -30 + ( ( (double)p_sys->i_volume ) * 30.0 / 255.0 );
1079
1080     /* Format without using locales */
1081     i_rc = us_asprintf( &psz_parameters, "volume: %0.6f\r\n", d_volume );
1082     if ( i_rc < 0 )
1083     {
1084         i_err = VLC_ENOMEM;
1085         goto error;
1086     }
1087
1088     vlc_dictionary_insert( &req_headers, "Session",
1089                            (void *)p_sys->psz_session );
1090
1091     i_err = ExecRequest( p_this, "SET_PARAMETER",
1092                          "text/parameters", psz_parameters,
1093                          &req_headers, NULL );
1094     if ( i_err != VLC_SUCCESS )
1095         goto error;
1096
1097 error:
1098     vlc_dictionary_clear( &req_headers, NULL, NULL );
1099     free( psz_parameters );
1100
1101     return i_err;
1102 }
1103
1104 static void LogInfo( vlc_object_t *p_this )
1105 {
1106     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1107     sout_stream_sys_t *p_sys = p_stream->p_sys;
1108     const char *psz_jack_name;
1109
1110     msg_Info( p_this, "Audio latency: %d", p_sys->i_audio_latency );
1111
1112     switch ( p_sys->i_jack_type )
1113     {
1114         case JACK_TYPE_ANALOG:
1115             psz_jack_name = "analog";
1116             break;
1117
1118         case JACK_TYPE_DIGITAL:
1119             psz_jack_name = "digital";
1120             break;
1121
1122         case JACK_TYPE_NONE:
1123         default:
1124             psz_jack_name = "none";
1125             break;
1126     }
1127
1128     msg_Info( p_this, "Jack type: %s", psz_jack_name );
1129 }
1130
1131 static void SendAudio( sout_stream_t *p_stream, block_t *p_buffer )
1132 {
1133     sout_stream_sys_t *p_sys = p_stream->p_sys;
1134     gcry_error_t i_gcrypt_err;
1135     block_t *p_next;
1136     size_t i_len;
1137     size_t i_payload_len;
1138     size_t i_realloc_len;
1139     int rc;
1140
1141     const uint8_t header[16] = {
1142         0x24, 0x00, 0x00, 0x00,
1143         0xf0, 0xff, 0x00, 0x00,
1144         0x00, 0x00, 0x00, 0x00,
1145         0x00, 0x00, 0x00, 0x00,
1146     };
1147
1148     while ( p_buffer )
1149     {
1150         i_len = sizeof( header ) + p_buffer->i_buffer;
1151
1152         /* Buffer resize needed? */
1153         if ( i_len > p_sys->i_sendbuf_len || p_sys->p_sendbuf == NULL )
1154         {
1155             /* Grow in blocks of 4K */
1156             i_realloc_len = (1 + (i_len / 4096)) * 4096;
1157
1158             p_sys->p_sendbuf = realloc( p_sys->p_sendbuf, i_realloc_len );
1159             if ( p_sys->p_sendbuf == NULL )
1160                 goto error;
1161
1162             p_sys->i_sendbuf_len = i_realloc_len;
1163         }
1164
1165         /* Fill buffer */
1166         memcpy( p_sys->p_sendbuf, header, sizeof( header ) );
1167         memcpy( p_sys->p_sendbuf + sizeof( header ),
1168                 p_buffer->p_buffer, p_buffer->i_buffer );
1169
1170         /* Calculate payload length and update header */
1171         i_payload_len = i_len - 4;
1172         if ( i_payload_len > 0xffff )
1173         {
1174             msg_Err( p_stream, "Buffer is too long (%u bytes)",
1175                      (unsigned int)i_payload_len );
1176             goto error;
1177         }
1178
1179         p_sys->p_sendbuf[2] = ( i_payload_len >> 8 ) & 0xff;
1180         p_sys->p_sendbuf[3] = i_payload_len & 0xff;
1181
1182         /* Reset cipher */
1183         i_gcrypt_err = gcry_cipher_reset( p_sys->aes_ctx );
1184         if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1185             goto error;
1186
1187         /* Set IV */
1188         i_gcrypt_err = gcry_cipher_setiv( p_sys->aes_ctx, p_sys->ps_aes_iv,
1189                                           sizeof( p_sys->ps_aes_iv ) );
1190         if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1191             goto error;
1192
1193         /* Encrypt in place. Only full blocks of 16 bytes are encrypted,
1194          * the rest (0-15 bytes) is left unencrypted.
1195          */
1196         i_gcrypt_err =
1197             gcry_cipher_encrypt( p_sys->aes_ctx,
1198                                  p_sys->p_sendbuf + sizeof( header ),
1199                                  ( p_buffer->i_buffer / 16 ) * 16,
1200                                  NULL, 0 );
1201         if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1202             goto error;
1203
1204         /* Send data */
1205         rc = net_Write( p_stream, p_sys->i_stream_fd, NULL,
1206                         p_sys->p_sendbuf, i_len );
1207         if ( rc < 0 )
1208             goto error;
1209
1210         p_next = p_buffer->p_next;
1211         block_Release( p_buffer );
1212         p_buffer = p_next;
1213     }
1214
1215 error:
1216     block_ChainRelease( p_buffer );
1217     return;
1218 }
1219
1220
1221 /*****************************************************************************
1222  * Open:
1223  *****************************************************************************/
1224 static int Open( vlc_object_t *p_this )
1225 {
1226     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1227     sout_stream_sys_t *p_sys;
1228     char psz_local[NI_MAXNUMERICHOST];
1229     gcry_error_t i_gcrypt_err;
1230     int i_err = VLC_SUCCESS;
1231     uint32_t i_session_id;
1232     uint64_t i_client_instance;
1233
1234     vlc_gcrypt_init();
1235
1236     config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
1237                        p_stream->p_cfg );
1238
1239     p_sys = calloc( 1, sizeof( *p_sys ) );
1240     if ( p_sys == NULL )
1241     {
1242         i_err = VLC_ENOMEM;
1243         goto error;
1244     }
1245
1246     p_stream->p_sys = p_sys;
1247     p_stream->pf_add = Add;
1248     p_stream->pf_del = Del;
1249     p_stream->pf_send = Send;
1250     p_stream->p_sout->i_out_pace_nocontrol++;
1251
1252     p_sys->i_control_fd = -1;
1253     p_sys->i_stream_fd = -1;
1254     p_sys->i_volume = var_GetInteger( p_stream, SOUT_CFG_PREFIX "volume");
1255     p_sys->i_jack_type = JACK_TYPE_NONE;
1256
1257     p_sys->psz_host = var_GetNonEmptyString( p_stream,
1258                                              SOUT_CFG_PREFIX "host" );
1259     if ( p_sys->psz_host == NULL )
1260     {
1261         msg_Err( p_this, "Missing host" );
1262         i_err = VLC_EGENERIC;
1263         goto error;
1264     }
1265
1266     var_AddCallback( p_stream, SOUT_CFG_PREFIX "volume",
1267                      VolumeCallback, NULL );
1268     p_sys->b_volume_callback = true;
1269
1270     /* Open control connection */
1271     p_sys->i_control_fd = net_ConnectTCP( p_stream, p_sys->psz_host,
1272                                           RAOP_PORT );
1273     if ( p_sys->i_control_fd < 0 )
1274     {
1275         msg_Err( p_this, "Cannot establish control connection to %s:%d (%m)",
1276                  p_sys->psz_host, RAOP_PORT );
1277         i_err = VLC_EGENERIC;
1278         goto error;
1279     }
1280
1281     /* Get local IP address */
1282     if ( net_GetSockAddress( p_sys->i_control_fd, psz_local, NULL ) )
1283     {
1284         msg_Err( p_this, "cannot get local IP address" );
1285         i_err = VLC_EGENERIC;
1286         goto error;
1287     }
1288
1289     /* Random session ID */
1290     gcry_randomize( &i_session_id, sizeof( i_session_id ),
1291                     GCRY_STRONG_RANDOM );
1292
1293     /* Random client instance */
1294     gcry_randomize( &i_client_instance, sizeof( i_client_instance ),
1295                     GCRY_STRONG_RANDOM );
1296     if ( asprintf( &p_sys->psz_client_instance, "%016llX",
1297                    i_client_instance ) < 0 )
1298     {
1299         i_err = VLC_ENOMEM;
1300         goto error;
1301     }
1302
1303     /* Build session URL */
1304     if ( asprintf( &p_sys->psz_url, "rtsp://%s/%u",
1305                    psz_local, i_session_id ) < 0 )
1306     {
1307         i_err = VLC_ENOMEM;
1308         goto error;
1309     }
1310
1311     /* Generate AES key and IV */
1312     gcry_randomize( p_sys->ps_aes_key, sizeof( p_sys->ps_aes_key ),
1313                     GCRY_STRONG_RANDOM );
1314     gcry_randomize( p_sys->ps_aes_iv, sizeof( p_sys->ps_aes_iv ),
1315                     GCRY_STRONG_RANDOM );
1316
1317     /* Setup AES */
1318     i_gcrypt_err = gcry_cipher_open( &p_sys->aes_ctx, GCRY_CIPHER_AES,
1319                                      GCRY_CIPHER_MODE_CBC, 0 );
1320     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1321     {
1322         i_err = VLC_EGENERIC;
1323         goto error;
1324     }
1325
1326     /* Set key */
1327     i_gcrypt_err = gcry_cipher_setkey( p_sys->aes_ctx, p_sys->ps_aes_key,
1328                                        sizeof( p_sys->ps_aes_key ) );
1329     if ( CheckForGcryptError( p_stream, i_gcrypt_err ) )
1330     {
1331         i_err = VLC_EGENERIC;
1332         goto error;
1333     }
1334
1335     /* Protocol handshake */
1336     i_err = AnnounceSDP( p_this, psz_local, i_session_id );
1337     if ( i_err != VLC_SUCCESS )
1338         goto error;
1339
1340     i_err = SendSetup( p_this );
1341     if ( i_err != VLC_SUCCESS )
1342         goto error;
1343
1344     i_err = SendRecord( p_this );
1345     if ( i_err != VLC_SUCCESS )
1346         goto error;
1347
1348     i_err = UpdateVolume( p_this );
1349     if ( i_err != VLC_SUCCESS )
1350         goto error;
1351
1352     LogInfo( p_this );
1353
1354     /* Open stream connection */
1355     p_sys->i_stream_fd = net_ConnectTCP( p_stream, p_sys->psz_host,
1356                                          p_sys->i_server_port );
1357     if ( p_sys->i_stream_fd < 0 )
1358     {
1359         msg_Err( p_this, "Cannot establish stream connection to %s:%d (%m)",
1360                  p_sys->psz_host, p_sys->i_server_port );
1361         i_err = VLC_EGENERIC;
1362         goto error;
1363     }
1364
1365 error:
1366     if ( i_err != VLC_SUCCESS )
1367         FreeSys( p_this, p_sys );
1368
1369     return i_err;
1370 }
1371
1372
1373 /*****************************************************************************
1374  * Close:
1375  *****************************************************************************/
1376 static void Close( vlc_object_t *p_this )
1377 {
1378     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1379     sout_stream_sys_t *p_sys = p_stream->p_sys;
1380
1381     SendFlush( p_this );
1382     SendTeardown( p_this );
1383
1384     FreeSys( p_this, p_sys );
1385
1386     p_stream->p_sout->i_out_pace_nocontrol--;
1387 }
1388
1389
1390 /*****************************************************************************
1391  * Add:
1392  *****************************************************************************/
1393 static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
1394 {
1395     sout_stream_sys_t *p_sys = p_stream->p_sys;
1396     sout_stream_id_t *id = NULL;
1397
1398     id = calloc( 1, sizeof( *id ) );
1399     if ( id == NULL )
1400         goto error;
1401
1402     es_format_Copy( &id->fmt, p_fmt );
1403
1404     switch ( id->fmt.i_cat )
1405     {
1406     case AUDIO_ES:
1407         if ( id->fmt.i_codec == VLC_FOURCC('a', 'l', 'a', 'c') )
1408         {
1409             if ( p_sys->p_audio_stream )
1410             {
1411                 msg_Warn( p_stream, "Only the first Apple Lossless audio "
1412                                     "stream is used" );
1413             }
1414             else if ( id->fmt.audio.i_rate != 44100 ||
1415                       id->fmt.audio.i_channels != 2 )
1416             {
1417                 msg_Err( p_stream, "The Apple Lossless audio stream must be "
1418                                    "encoded with 44100 Hz and 2 channels" );
1419             }
1420             else
1421             {
1422                 /* Use this stream */
1423                 p_sys->p_audio_stream = id;
1424             }
1425         }
1426         else if ( !p_sys->b_alac_warning )
1427         {
1428             msg_Err( p_stream, "Apple Lossless is the only codec supported. "
1429                                "Use the \"transcode\" module for conversion "
1430                                "(e.g. \"transcode{acodec=alac,"
1431                                "channels=2}\")." );
1432             p_sys->b_alac_warning = true;
1433         }
1434
1435         break;
1436
1437     default:
1438         /* Leave other stream types alone */
1439         break;
1440     }
1441
1442     return id;
1443
1444 error:
1445     FreeId( id );
1446
1447     return NULL;
1448 }
1449
1450
1451 /*****************************************************************************
1452  * Del:
1453  *****************************************************************************/
1454 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
1455 {
1456     sout_stream_sys_t *p_sys = p_stream->p_sys;
1457     int i_err = VLC_SUCCESS;
1458
1459     if ( p_sys->p_audio_stream == id )
1460         p_sys->p_audio_stream = NULL;
1461
1462     FreeId( id );
1463
1464     return i_err;
1465 }
1466
1467
1468 /*****************************************************************************
1469  * Send:
1470  *****************************************************************************/
1471 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
1472                  block_t *p_buffer )
1473 {
1474     sout_stream_sys_t *p_sys = p_stream->p_sys;
1475
1476     if ( id->fmt.i_cat == AUDIO_ES && id == p_sys->p_audio_stream )
1477     {
1478         /* SendAudio takes care of releasing the buffers */
1479         SendAudio( p_stream, p_buffer );
1480     }
1481     else
1482     {
1483         block_ChainRelease( p_buffer );
1484     }
1485
1486     return VLC_SUCCESS;
1487 }
1488
1489
1490 /*****************************************************************************
1491  * VolumeCallback: called when the volume is changed on the fly.
1492  *****************************************************************************/
1493 static int VolumeCallback( vlc_object_t *p_this, char const *psz_cmd,
1494                            vlc_value_t oldval, vlc_value_t newval,
1495                            void *p_data )
1496 {
1497     VLC_UNUSED(psz_cmd);
1498     VLC_UNUSED(oldval);
1499     VLC_UNUSED(p_data);
1500     VLC_UNUSED(newval);
1501     sout_stream_t *p_stream = (sout_stream_t*)p_this;
1502     sout_stream_sys_t *p_sys = p_stream->p_sys;
1503
1504     /* TODO: Implement volume change */
1505     VLC_UNUSED(p_sys);
1506
1507     return VLC_SUCCESS;
1508 }