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