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