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