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

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * mmsh.c:
  3.  *****************************************************************************
  4.  * Copyright (C) 2001, 2002 the VideoLAN team
  5.  * $Id: c0117339b77290a08ae5bfb2b3784e49dd67b6a4 $
  6.  *
  7.  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  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 <vlc_common.h>
  30. #include <vlc_access.h>
  31. #include "vlc_strings.h"
  32. #include "vlc_input.h"
  33. #include <vlc_network.h>
  34. #include "vlc_url.h"
  35. #include "asf.h"
  36. #include "buffer.h"
  37. #include "mms.h"
  38. #include "mmsh.h"
  39. /* TODO:
  40.  *  - authentication
  41.  */
  42. /*****************************************************************************
  43.  * Local prototypes
  44.  *****************************************************************************/
  45. int  MMSHOpen  ( access_t * );
  46. void MMSHClose ( access_t * );
  47. static block_t *Block( access_t *p_access );
  48. static ssize_t ReadRedirect( access_t *, uint8_t *, size_t );
  49. static int  Seek( access_t *, int64_t );
  50. static int  Control( access_t *, int, va_list );
  51. static int  Describe( access_t  *, char **ppsz_location );
  52. static int  Start( access_t *, int64_t );
  53. static void Stop( access_t * );
  54. static int  GetPacket( access_t *, chunk_t * );
  55. static void GetHeader( access_t *p_access );
  56. static int Restart( access_t * );
  57. static int Reset( access_t * );
  58. //#define MMSH_USER_AGENT "NSPlayer/4.1.0.3856"
  59. #define MMSH_USER_AGENT "NSPlayer/7.10.0.3059"
  60. /****************************************************************************
  61.  * Open: connect to ftp server and ask for file
  62.  ****************************************************************************/
  63. int MMSHOpen( access_t *p_access )
  64. {
  65.     access_sys_t    *p_sys;
  66.     char            *psz_location = NULL;
  67.     char            *psz_proxy;
  68.     STANDARD_BLOCK_ACCESS_INIT
  69.     p_sys->i_proto= MMS_PROTO_HTTP;
  70.     p_sys->fd     = -1;
  71.     /* Handle proxy */
  72.     p_sys->b_proxy = false;
  73.     memset( &p_sys->proxy, 0, sizeof(p_sys->proxy) );
  74.     /* Check proxy */
  75.     /* TODO reuse instead http-proxy from http access ? */
  76.     psz_proxy = var_CreateGetString( p_access, "mmsh-proxy" );
  77.     if( !*psz_proxy )
  78.     {
  79.         char *psz_http_proxy = config_GetPsz( p_access, "http-proxy" );
  80.         if( psz_http_proxy && *psz_http_proxy )
  81.         {
  82.             free( psz_proxy );
  83.             psz_proxy = psz_http_proxy;
  84.             var_SetString( p_access, "mmsh-proxy", psz_proxy );
  85.         }
  86.         else
  87.         {
  88.             free( psz_http_proxy );
  89.         }
  90.     }
  91.     if( *psz_proxy )
  92.     {
  93.         p_sys->b_proxy = true;
  94.         vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 );
  95.     }
  96. #ifdef HAVE_GETENV
  97.     else
  98.     {
  99.         char *psz_proxy = getenv( "http_proxy" );
  100.         if( psz_proxy && *psz_proxy )
  101.         {
  102.             p_sys->b_proxy = true;
  103.             vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 );
  104.         }
  105.     }
  106. #endif
  107.     free( psz_proxy );
  108.     if( p_sys->b_proxy )
  109.     {
  110.         if( ( p_sys->proxy.psz_host == NULL ) ||
  111.             ( *p_sys->proxy.psz_host == '' ) )
  112.         {
  113.             msg_Warn( p_access, "invalid proxy host" );
  114.             vlc_UrlClean( &p_sys->proxy );
  115.             free( p_sys );
  116.             return VLC_EGENERIC;
  117.         }
  118.         if( p_sys->proxy.i_port <= 0 )
  119.             p_sys->proxy.i_port = 80;
  120.         msg_Dbg( p_access, "Using http proxy %s:%d",
  121.                  p_sys->proxy.psz_host, p_sys->proxy.i_port );
  122.     }
  123.     /* open a tcp connection */
  124.     vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
  125.     if( ( p_sys->url.psz_host == NULL ) ||
  126.         ( *p_sys->url.psz_host == '' ) )
  127.     {
  128.         msg_Err( p_access, "invalid host" );
  129.         vlc_UrlClean( &p_sys->proxy );
  130.         vlc_UrlClean( &p_sys->url );
  131.         free( p_sys );
  132.         return VLC_EGENERIC;
  133.     }
  134.     if( p_sys->url.i_port <= 0 )
  135.         p_sys->url.i_port = 80;
  136.     if( Describe( p_access, &psz_location ) )
  137.     {
  138.         vlc_UrlClean( &p_sys->proxy );
  139.         vlc_UrlClean( &p_sys->url );
  140.         free( p_sys );
  141.         return VLC_EGENERIC;
  142.     }
  143.     /* Handle redirection */
  144.     if( psz_location && *psz_location )
  145.     {
  146.         msg_Dbg( p_access, "redirection to %s", psz_location );
  147.         input_thread_t * p_input = vlc_object_find( p_access, VLC_OBJECT_INPUT, FIND_PARENT );
  148.         input_item_t * p_new_loc;
  149.         if( !p_input )
  150.         {
  151.             vlc_UrlClean( &p_sys->proxy );
  152.             vlc_UrlClean( &p_sys->url );
  153.             free( p_sys );
  154.             free( psz_location );
  155.             return VLC_EGENERIC;
  156.         }
  157.         /** bug we do not autodelete here */
  158.         p_new_loc = input_item_New( p_access, psz_location, psz_location );
  159.         input_item_AddSubItem( input_GetItem( p_input ), p_new_loc );
  160.         vlc_gc_decref( p_new_loc );
  161.         vlc_object_release( p_input );
  162.         free( psz_location );
  163.         p_access->pf_block = NULL;
  164.         p_access->pf_read = ReadRedirect;
  165.         return VLC_SUCCESS;
  166.     }
  167.     free( psz_location );
  168.     /* Start playing */
  169.     if( Start( p_access, 0 ) )
  170.     {
  171.         msg_Err( p_access, "cannot start stream" );
  172.         free( p_sys->p_header );
  173.         vlc_UrlClean( &p_sys->proxy );
  174.         vlc_UrlClean( &p_sys->url );
  175.         free( p_sys );
  176.         return VLC_EGENERIC;
  177.     }
  178.     if( !p_sys->b_broadcast )
  179.     {
  180.         p_access->info.i_size = p_sys->asfh.i_file_size;
  181.     }
  182.     return VLC_SUCCESS;
  183. }
  184. /*****************************************************************************
  185.  * Close: free unused data structures
  186.  *****************************************************************************/
  187. void  MMSHClose ( access_t *p_access )
  188. {
  189.     access_sys_t *p_sys = p_access->p_sys;
  190.     Stop( p_access );
  191.     free( p_sys->p_header  );
  192.     vlc_UrlClean( &p_sys->proxy );
  193.     vlc_UrlClean( &p_sys->url );
  194.     free( p_sys );
  195. }
  196. /*****************************************************************************
  197.  * Control:
  198.  *****************************************************************************/
  199. static int Control( access_t *p_access, int i_query, va_list args )
  200. {
  201.     access_sys_t *p_sys = p_access->p_sys;
  202.     bool   *pb_bool;
  203.     bool    b_bool;
  204.     int64_t      *pi_64;
  205.     int          i_int;
  206.     switch( i_query )
  207.     {
  208.         /* */
  209.         case ACCESS_CAN_SEEK:
  210.             pb_bool = (bool*)va_arg( args, bool* );
  211.             *pb_bool = !p_sys->b_broadcast;
  212.             break;
  213.         case ACCESS_CAN_FASTSEEK:
  214.             pb_bool = (bool*)va_arg( args, bool* );
  215.             *pb_bool = false;
  216.             break;
  217.         case ACCESS_CAN_PAUSE:
  218.         case ACCESS_CAN_CONTROL_PACE:
  219.             pb_bool = (bool*)va_arg( args, bool* );
  220.             *pb_bool = true;
  221.             break;
  222.         /* */
  223.         case ACCESS_GET_PTS_DELAY:
  224.             pi_64 = (int64_t*)va_arg( args, int64_t * );
  225.             *pi_64 = (int64_t)var_GetInteger( p_access, "mms-caching" ) * INT64_C(1000);
  226.             break;
  227.         case ACCESS_GET_PRIVATE_ID_STATE:
  228.             i_int = (int)va_arg( args, int );
  229.             pb_bool = (bool *)va_arg( args, bool * );
  230.             if( (i_int < 0) || (i_int > 127) )
  231.                 return VLC_EGENERIC;
  232.             *pb_bool =  p_sys->asfh.stream[i_int].i_selected ? true : false;
  233.             break;
  234.         /* */
  235.         case ACCESS_SET_PAUSE_STATE:
  236.             b_bool = (bool)va_arg( args, int );
  237.             if( b_bool )
  238.                 Stop( p_access );
  239.             else
  240.                 Seek( p_access, p_access->info.i_pos );
  241.             break;
  242.         case ACCESS_GET_TITLE_INFO:
  243.         case ACCESS_SET_TITLE:
  244.         case ACCESS_SET_SEEKPOINT:
  245.         case ACCESS_SET_PRIVATE_ID_STATE:
  246.         case ACCESS_GET_CONTENT_TYPE:
  247.             return VLC_EGENERIC;
  248.         default:
  249.             msg_Warn( p_access, "unimplemented query in control" );
  250.             return VLC_EGENERIC;
  251.     }
  252.     return VLC_SUCCESS;
  253. }
  254. /*****************************************************************************
  255.  * Seek: try to go at the right place
  256.  *****************************************************************************/
  257. static int Seek( access_t *p_access, int64_t i_pos )
  258. {
  259.     access_sys_t *p_sys = p_access->p_sys;
  260.     chunk_t      ck;
  261.     off_t        i_offset;
  262.     off_t        i_packet;
  263.     msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
  264.     i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
  265.     i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
  266.     Stop( p_access );
  267.     Start( p_access, i_packet * p_sys->asfh.i_min_data_packet_size );
  268.     while( vlc_object_alive (p_access) )
  269.     {
  270.         if( GetPacket( p_access, &ck ) )
  271.             break;
  272.         /* skip headers */
  273.         if( ck.i_type != 0x4824 )
  274.             break;
  275.         msg_Warn( p_access, "skipping header" );
  276.     }
  277.     p_access->info.i_pos = i_pos;
  278.     p_access->info.b_eof = false;
  279.     p_sys->i_packet_used += i_offset;
  280.     return VLC_SUCCESS;
  281. }
  282. /*****************************************************************************
  283.  * ReadRedirect:
  284.  *****************************************************************************/
  285. static ssize_t ReadRedirect( access_t *p_access, uint8_t *p, size_t i_len )
  286. {
  287.     VLC_UNUSED(p_access); VLC_UNUSED(p); VLC_UNUSED(i_len);
  288.     return 0;
  289. }
  290. /*****************************************************************************
  291.  * Block:
  292.  *****************************************************************************/
  293. static block_t *Block( access_t *p_access )
  294. {
  295.     access_sys_t *p_sys = p_access->p_sys;
  296.     const unsigned i_packet_min = p_sys->asfh.i_min_data_packet_size;
  297.     if( p_access->info.i_pos < p_sys->i_start + p_sys->i_header )
  298.     {
  299.         const size_t i_offset = p_access->info.i_pos - p_sys->i_start;
  300.         const size_t i_copy = p_sys->i_header - i_offset;
  301.         block_t *p_block = block_New( p_access, i_copy );
  302.         if( !p_block )
  303.             return NULL;
  304.         memcpy( p_block->p_buffer, &p_sys->p_header[i_offset], i_copy );
  305.         p_access->info.i_pos += i_copy;
  306.         return p_block;
  307.     }
  308.     else if( p_sys->i_packet_length > 0 &&
  309.              p_sys->i_packet_used < __MAX( p_sys->i_packet_length, i_packet_min ) )
  310.     {
  311.         size_t i_copy = 0;
  312.         size_t i_padding = 0;
  313.         if( p_sys->i_packet_used < p_sys->i_packet_length )
  314.             i_copy = p_sys->i_packet_length - p_sys->i_packet_used;
  315.         if( __MAX( p_sys->i_packet_used, p_sys->i_packet_length ) < i_packet_min )
  316.             i_padding = i_packet_min - __MAX( p_sys->i_packet_used, p_sys->i_packet_length );
  317.         block_t *p_block = block_New( p_access, i_copy + i_padding );
  318.         if( !p_block )
  319.             return NULL;
  320.         if( i_copy > 0 )
  321.             memcpy( &p_block->p_buffer[0], &p_sys->p_packet[p_sys->i_packet_used], i_copy );
  322.         if( i_padding > 0 )
  323.             memset( &p_block->p_buffer[i_copy], 0, i_padding );
  324.         p_sys->i_packet_used += i_copy + i_padding;
  325.         p_access->info.i_pos += i_copy + i_padding;
  326.         return p_block;
  327.     }
  328.     chunk_t ck;
  329.     if( GetPacket( p_access, &ck ) )
  330.     {
  331.         int i_ret = -1;
  332.         if( p_sys->b_broadcast )
  333.         {
  334.             if( (ck.i_type == 0x4524) && (ck.i_sequence != 0) )
  335.                 i_ret = Restart( p_access );
  336.             else if( ck.i_type == 0x4324 )
  337.                 i_ret = Reset( p_access );
  338.         }
  339.         if( i_ret )
  340.         {
  341.             p_access->info.b_eof = true;
  342.             return 0;
  343.         }
  344.     }
  345.     if( ck.i_type != 0x4424 )
  346.     {
  347.         p_sys->i_packet_used = 0;
  348.         p_sys->i_packet_length = 0;
  349.     }
  350.     return NULL;
  351. }
  352. /* */
  353. static int Restart( access_t *p_access )
  354. {
  355.     access_sys_t *p_sys = p_access->p_sys;
  356.     char *psz_location = NULL;
  357.     msg_Dbg( p_access, "Restart the stream" );
  358.     p_sys->i_start = p_access->info.i_pos;
  359.     /* */
  360.     msg_Dbg( p_access, "stoping the stream" );
  361.     Stop( p_access );
  362.     /* */
  363.     msg_Dbg( p_access, "describe the stream" );
  364.     if( Describe( p_access, &psz_location ) )
  365.     {
  366.         msg_Err( p_access, "describe failed" );
  367.         return VLC_EGENERIC;
  368.     }
  369.     /* */
  370.     if( Start( p_access, 0 ) )
  371.     {
  372.         msg_Err( p_access, "Start failed" );
  373.         return VLC_EGENERIC;
  374.     }
  375.     return VLC_SUCCESS;
  376. }
  377. static int Reset( access_t *p_access )
  378. {
  379.     access_sys_t *p_sys = p_access->p_sys;
  380.     asf_header_t old_asfh = p_sys->asfh;
  381.     int i;
  382.     msg_Dbg( p_access, "Reset the stream" );
  383.     p_sys->i_start = p_access->info.i_pos;
  384.     /* */
  385.     p_sys->i_packet_sequence = 0;
  386.     p_sys->i_packet_used = 0;
  387.     p_sys->i_packet_length = 0;
  388.     p_sys->p_packet = NULL;
  389.     /* Get the next header FIXME memory loss ? */
  390.     GetHeader( p_access );
  391.     if( p_sys->i_header <= 0 )
  392.         return VLC_EGENERIC;
  393.     asf_HeaderParse ( &p_sys->asfh,
  394.                        p_sys->p_header, p_sys->i_header );
  395.     msg_Dbg( p_access, "packet count=%"PRId64" packet size=%d",
  396.              p_sys->asfh.i_data_packets_count,
  397.              p_sys->asfh.i_min_data_packet_size );
  398.     asf_StreamSelect( &p_sys->asfh,
  399.                        var_CreateGetInteger( p_access, "mms-maxbitrate" ),
  400.                        var_CreateGetInteger( p_access, "mms-all" ),
  401.                        var_CreateGetInteger( p_access, "audio" ),
  402.                        var_CreateGetInteger( p_access, "video" ) );
  403.     /* Check we have comptible asfh */
  404.     for( i = 1; i < 128; i++ )
  405.     {
  406.         asf_stream_t *p_old = &old_asfh.stream[i];
  407.         asf_stream_t *p_new = &p_sys->asfh.stream[i];
  408.         if( p_old->i_cat != p_new->i_cat || p_old->i_selected != p_new->i_selected )
  409.             break;
  410.     }
  411.     if( i < 128 )
  412.     {
  413.         msg_Warn( p_access, "incompatible asf header, restart" );
  414.         return Restart( p_access );
  415.     }
  416.     /* */
  417.     p_sys->i_packet_used = 0;
  418.     p_sys->i_packet_length = 0;
  419.     return VLC_SUCCESS;
  420. }
  421. static int OpenConnection( access_t *p_access )
  422. {
  423.     access_sys_t *p_sys = p_access->p_sys;
  424.     vlc_url_t    srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
  425.     if( ( p_sys->fd = net_ConnectTCP( p_access,
  426.                                       srv.psz_host, srv.i_port ) ) < 0 )
  427.     {
  428.         msg_Err( p_access, "cannot connect to %s:%d",
  429.                  srv.psz_host, srv.i_port );
  430.         return VLC_EGENERIC;
  431.     }
  432.     if( p_sys->b_proxy )
  433.     {
  434.         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  435.                     "GET http://%s:%d%s HTTP/1.0rn",
  436.                     p_sys->url.psz_host, p_sys->url.i_port,
  437.                     ( (p_sys->url.psz_path == NULL) ||
  438.                       (*p_sys->url.psz_path == '') ) ?
  439.                          "/" : p_sys->url.psz_path );
  440.         /* Proxy Authentication */
  441.         if( p_sys->proxy.psz_username && *p_sys->proxy.psz_username )
  442.         {
  443.             char *buf;
  444.             char *b64;
  445.             if( asprintf( &buf, "%s:%s", p_sys->proxy.psz_username,
  446.                        p_sys->proxy.psz_password ? p_sys->proxy.psz_password : "" ) == -1 )
  447.                 return VLC_ENOMEM;
  448.             b64 = vlc_b64_encode( buf );
  449.             free( buf );
  450.             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  451.                         "Proxy-Authorization: Basic %srn", b64 );
  452.             free( b64 );
  453.         }
  454.     }
  455.     else
  456.     {
  457.         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  458.                     "GET %s HTTP/1.0rn"
  459.                     "Host: %s:%drn",
  460.                     ( (p_sys->url.psz_path == NULL) ||
  461.                       (*p_sys->url.psz_path == '') ) ?
  462.                             "/" : p_sys->url.psz_path,
  463.                     p_sys->url.psz_host, p_sys->url.i_port );
  464.     }
  465.     return VLC_SUCCESS;
  466. }
  467. /*****************************************************************************
  468.  * Describe:
  469.  *****************************************************************************/
  470. static int Describe( access_t  *p_access, char **ppsz_location )
  471. {
  472.     access_sys_t *p_sys = p_access->p_sys;
  473.     char         *psz_location = NULL;
  474.     char         *psz;
  475.     int          i_code;
  476.     /* Reinit context */
  477.     p_sys->b_broadcast = true;
  478.     p_sys->i_request_context = 1;
  479.     p_sys->i_packet_sequence = 0;
  480.     p_sys->i_packet_used = 0;
  481.     p_sys->i_packet_length = 0;
  482.     p_sys->p_packet = NULL;
  483.     GenerateGuid ( &p_sys->guid );
  484.     if( OpenConnection( p_access ) )
  485.         return VLC_EGENERIC;
  486.     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  487.                 "Accept: */*rn"
  488.                 "User-Agent: "MMSH_USER_AGENT"rn"
  489.                 "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0rn"
  490.                 "Pragma: xClientGUID={"GUID_FMT"}rn"
  491.                 "Connection: Closern",
  492.                 p_sys->i_request_context++,
  493.                 GUID_PRINT( p_sys->guid ) );
  494.     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "rn" ) < 0 )
  495.     {
  496.         msg_Err( p_access, "failed to send request" );
  497.         goto error;
  498.     }
  499.     /* Receive the http header */
  500.     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL ) ) == NULL )
  501.     {
  502.         msg_Err( p_access, "failed to read answer" );
  503.         goto error;
  504.     }
  505.     if( strncmp( psz, "HTTP/1.", 7 ) )
  506.     {
  507.         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
  508.         free( psz );
  509.         goto error;
  510.     }
  511.     i_code = atoi( &psz[9] );
  512.     if( i_code >= 400 )
  513.     {
  514.         msg_Err( p_access, "error: %s", psz );
  515.         free( psz );
  516.         goto error;
  517.     }
  518.     msg_Dbg( p_access, "HTTP reply '%s'", psz );
  519.     free( psz );
  520.     for( ;; )
  521.     {
  522.         char *psz = net_Gets( p_access, p_sys->fd, NULL );
  523.         char *p;
  524.         if( psz == NULL )
  525.         {
  526.             msg_Err( p_access, "failed to read answer" );
  527.             goto error;
  528.         }
  529.         if( *psz == '' )
  530.         {
  531.             free( psz );
  532.             break;
  533.         }
  534.         if( ( p = strchr( psz, ':' ) ) == NULL )
  535.         {
  536.             msg_Err( p_access, "malformed header line: %s", psz );
  537.             free( psz );
  538.             goto error;
  539.         }
  540.         *p++ = '';
  541.         while( *p == ' ' ) p++;
  542.         /* FIXME FIXME test Content-Type to see if it's a plain stream or an
  543.          * asx FIXME */
  544.         if( !strcasecmp( psz, "Pragma" ) )
  545.         {
  546.             if( strstr( p, "features" ) )
  547.             {
  548.                 /* FIXME, it is a bit badly done here ..... */
  549.                 if( strstr( p, "broadcast" ) )
  550.                 {
  551.                     msg_Dbg( p_access, "stream type = broadcast" );
  552.                     p_sys->b_broadcast = true;
  553.                 }
  554.                 else if( strstr( p, "seekable" ) )
  555.                 {
  556.                     msg_Dbg( p_access, "stream type = seekable" );
  557.                     p_sys->b_broadcast = false;
  558.                 }
  559.                 else
  560.                 {
  561.                     msg_Warn( p_access, "unknow stream types (%s)", p );
  562.                     p_sys->b_broadcast = false;
  563.                 }
  564.             }
  565.         }
  566.         else if( !strcasecmp( psz, "Location" ) )
  567.         {
  568.             psz_location = strdup( p );
  569.         }
  570.         free( psz );
  571.     }
  572.     /* Handle the redirection */
  573.     if( ( (i_code == 301) || (i_code == 302) ||
  574.           (i_code == 303) || (i_code == 307) ) &&
  575.         psz_location && *psz_location )
  576.     {
  577.         msg_Dbg( p_access, "redirection to %s", psz_location );
  578.         net_Close( p_sys->fd ); p_sys->fd = -1;
  579.         *ppsz_location = psz_location;
  580.         return VLC_SUCCESS;
  581.     }
  582.     /* Read the asf header */
  583.     GetHeader( p_access );
  584.     if( p_sys->i_header <= 0 )
  585.     {
  586.         msg_Err( p_access, "header size == 0" );
  587.         goto error;
  588.     }
  589.     /* close this connection */
  590.     net_Close( p_sys->fd );
  591.     p_sys->fd = -1;
  592.     /* *** parse header and get stream and their id *** */
  593.     /* get all streams properties,
  594.      *
  595.      * TODO : stream bitrates properties(optional)
  596.      *        and bitrate mutual exclusion(optional) */
  597.     asf_HeaderParse ( &p_sys->asfh,
  598.                        p_sys->p_header, p_sys->i_header );
  599.     msg_Dbg( p_access, "packet count=%"PRId64" packet size=%d",
  600.              p_sys->asfh.i_data_packets_count,
  601.              p_sys->asfh.i_min_data_packet_size );
  602.     if( p_sys->asfh.i_min_data_packet_size <= 0 )
  603.         goto error;
  604.     asf_StreamSelect( &p_sys->asfh,
  605.                        var_CreateGetInteger( p_access, "mms-maxbitrate" ),
  606.                        var_CreateGetInteger( p_access, "mms-all" ),
  607.                        var_CreateGetInteger( p_access, "audio" ),
  608.                        var_CreateGetInteger( p_access, "video" ) );
  609.     return VLC_SUCCESS;
  610. error:
  611.     if( p_sys->fd > 0 )
  612.     {
  613.         net_Close( p_sys->fd  );
  614.         p_sys->fd = -1;
  615.     }
  616.     return VLC_EGENERIC;
  617. }
  618. static void GetHeader( access_t *p_access )
  619. {
  620.     access_sys_t *p_sys = p_access->p_sys;
  621.     /* Read the asf header */
  622.     p_sys->i_header = 0;
  623.     free( p_sys->p_header  );
  624.     p_sys->p_header = NULL;
  625.     for( ;; )
  626.     {
  627.         chunk_t ck;
  628.         if( GetPacket( p_access, &ck ) || ck.i_type != 0x4824 )
  629.             break;
  630.         if( ck.i_data > 0 )
  631.         {
  632.             p_sys->i_header += ck.i_data;
  633.             p_sys->p_header = realloc( p_sys->p_header, p_sys->i_header );
  634.             memcpy( &p_sys->p_header[p_sys->i_header - ck.i_data],
  635.                     ck.p_data, ck.i_data );
  636.         }
  637.     }
  638.     msg_Dbg( p_access, "complete header size=%d", p_sys->i_header );
  639. }
  640. /*****************************************************************************
  641.  * Start stream
  642.  ****************************************************************************/
  643. static int Start( access_t *p_access, int64_t i_pos )
  644. {
  645.     access_sys_t *p_sys = p_access->p_sys;
  646.     int  i_streams = 0;
  647.     int  i_streams_selected = 0;
  648.     int  i;
  649.     char *psz = NULL;
  650.     msg_Dbg( p_access, "starting stream" );
  651.     for( i = 1; i < 128; i++ )
  652.     {
  653.         if( p_sys->asfh.stream[i].i_cat == ASF_STREAM_UNKNOWN )
  654.             continue;
  655.         i_streams++;
  656.         if( p_sys->asfh.stream[i].i_selected )
  657.             i_streams_selected++;
  658.     }
  659.     if( i_streams_selected <= 0 )
  660.     {
  661.         msg_Err( p_access, "no stream selected" );
  662.         return VLC_EGENERIC;
  663.     }
  664.     if( OpenConnection( p_access ) )
  665.         return VLC_EGENERIC;
  666.     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  667.                 "Accept: */*rn"
  668.                 "User-Agent: "MMSH_USER_AGENT"rn" );
  669.     if( p_sys->b_broadcast )
  670.     {
  671.         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  672.                     "Pragma: no-cache,rate=1.000000,request-context=%drn",
  673.                     p_sys->i_request_context++ );
  674.     }
  675.     else
  676.     {
  677.         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  678.                     "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0rn",
  679.                     (uint32_t)((i_pos >> 32)&0xffffffff),
  680.                     (uint32_t)(i_pos&0xffffffff),
  681.                     p_sys->i_request_context++ );
  682.     }
  683.     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  684.                 "Pragma: xPlayStrm=1rn"
  685.                 "Pragma: xClientGUID={"GUID_FMT"}rn"
  686.                 "Pragma: stream-switch-count=%drn"
  687.                 "Pragma: stream-switch-entry=",
  688.                 GUID_PRINT( p_sys->guid ),
  689.                 i_streams);
  690.     for( i = 1; i < 128; i++ )
  691.     {
  692.         if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN )
  693.         {
  694.             int i_select = 2;
  695.             if( p_sys->asfh.stream[i].i_selected )
  696.             {
  697.                 i_select = 0;
  698.             }
  699.             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  700.                         "ffff:%d:%d ", i, i_select );
  701.         }
  702.     }
  703.     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "rn" );
  704.     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
  705.                 "Connection: Closern" );
  706.     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "rn" ) < 0 )
  707.     {
  708.         msg_Err( p_access, "failed to send request" );
  709.         return VLC_EGENERIC;
  710.     }
  711.     psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
  712.     if( psz == NULL )
  713.     {
  714.         msg_Err( p_access, "cannot read data 0" );
  715.         return VLC_EGENERIC;
  716.     }
  717.     if( atoi( &psz[9] ) >= 400 )
  718.     {
  719.         msg_Err( p_access, "error: %s", psz );
  720.         free( psz );
  721.         return VLC_EGENERIC;
  722.     }
  723.     msg_Dbg( p_access, "HTTP reply '%s'", psz );
  724.     free( psz );
  725.     /* FIXME check HTTP code */
  726.     for( ;; )
  727.     {
  728.         char *psz = net_Gets( p_access, p_sys->fd, NULL );
  729.         if( psz == NULL )
  730.         {
  731.             msg_Err( p_access, "cannot read data 1" );
  732.             return VLC_EGENERIC;
  733.         }
  734.         if( *psz == '' )
  735.         {
  736.             free( psz );
  737.             break;
  738.         }
  739.         msg_Dbg( p_access, "%s", psz );
  740.         free( psz );
  741.     }
  742.     p_sys->i_packet_used   = 0;
  743.     p_sys->i_packet_length = 0;
  744.     return VLC_SUCCESS;
  745. }
  746. /*****************************************************************************
  747.  * closing stream
  748.  *****************************************************************************/
  749. static void Stop( access_t *p_access )
  750. {
  751.     access_sys_t *p_sys = p_access->p_sys;
  752.     msg_Dbg( p_access, "closing stream" );
  753.     if( p_sys->fd > 0 )
  754.     {
  755.         net_Close( p_sys->fd );
  756.         p_sys->fd = -1;
  757.     }
  758. }
  759. /*****************************************************************************
  760.  * get packet
  761.  *****************************************************************************/
  762. static int GetPacket( access_t * p_access, chunk_t *p_ck )
  763. {
  764.     access_sys_t *p_sys = p_access->p_sys;
  765.     int restsize;
  766.     /* chunk_t */
  767.     memset( p_ck, 0, sizeof( chunk_t ) );
  768.     /* Read the chunk header */
  769.     /* Some headers are short, like 0x4324. Reading 12 bytes will cause us
  770.      * to lose synchronization with the stream. Just read to the length
  771.      * (4 bytes), decode and then read up to 8 additional bytes to get the
  772.      * entire header.
  773.      */
  774.     if( net_Read( p_access, p_sys->fd, NULL, p_sys->buffer, 4, true ) < 4 )
  775.     {
  776.        msg_Err( p_access, "cannot read data 2" );
  777.        return VLC_EGENERIC;
  778.     }
  779.     p_ck->i_type = GetWLE( p_sys->buffer);
  780.     p_ck->i_size = GetWLE( p_sys->buffer + 2);
  781.     restsize = p_ck->i_size;
  782.     if( restsize > 8 )
  783.         restsize = 8;
  784.     if( net_Read( p_access, p_sys->fd, NULL, p_sys->buffer + 4, restsize, true ) < restsize )
  785.     {
  786.         msg_Err( p_access, "cannot read data 3" );
  787.         return VLC_EGENERIC;
  788.     }
  789.     p_ck->i_sequence  = GetDWLE( p_sys->buffer + 4);
  790.     p_ck->i_unknown   = GetWLE( p_sys->buffer + 8);
  791.     /* Set i_size2 to 8 if this header was short, since a real value won't be
  792.      * present in the buffer. Using 8 avoid reading additional data for the
  793.      * packet.
  794.      */
  795.     if( restsize < 8 )
  796.         p_ck->i_size2 = 8;
  797.     else
  798.         p_ck->i_size2 = GetWLE( p_sys->buffer + 10);
  799.     p_ck->p_data      = p_sys->buffer + 12;
  800.     p_ck->i_data      = p_ck->i_size2 - 8;
  801.     if( p_ck->i_type == 0x4524 )   // Transfer complete
  802.     {
  803.         if( p_ck->i_sequence == 0 )
  804.         {
  805.             msg_Warn( p_access, "EOF" );
  806.             return VLC_EGENERIC;
  807.         }
  808.         else
  809.         {
  810.             msg_Warn( p_access, "next stream following" );
  811.             return VLC_EGENERIC;
  812.         }
  813.     }
  814.     else if( p_ck->i_type == 0x4324 )
  815.     {
  816.         /* 0x4324 is CHUNK_TYPE_RESET: a new stream will follow with a sequence of 0 */
  817.         msg_Warn( p_access, "next stream following (reset) seq=%d", p_ck->i_sequence  );
  818.         return VLC_EGENERIC;
  819.     }
  820.     else if( (p_ck->i_type != 0x4824) && (p_ck->i_type != 0x4424) )
  821.     {
  822.         msg_Err( p_access, "invalid chunk FATAL (0x%x)", p_ck->i_type );
  823.         return VLC_EGENERIC;
  824.     }
  825.     if( (p_ck->i_data > 0) &&
  826.         (net_Read( p_access, p_sys->fd, NULL, &p_sys->buffer[12],
  827.                    p_ck->i_data, true ) < p_ck->i_data) )
  828.     {
  829.         msg_Err( p_access, "cannot read data 4" );
  830.         return VLC_EGENERIC;
  831.     }
  832. #if 0
  833.     if( (p_sys->i_packet_sequence != 0) &&
  834.         (p_ck->i_sequence != p_sys->i_packet_sequence) )
  835.     {
  836.         msg_Warn( p_access, "packet lost ? (%d != %d)", p_ck->i_sequence, p_sys->i_packet_sequence );
  837.     }
  838. #endif
  839.     p_sys->i_packet_sequence = p_ck->i_sequence + 1;
  840.     p_sys->i_packet_used   = 0;
  841.     p_sys->i_packet_length = p_ck->i_data;
  842.     p_sys->p_packet        = p_ck->p_data;
  843.     return VLC_SUCCESS;
  844. }