raop.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:43k
源码类别:

midi

开发平台:

Unix_Linux

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