]> git.sesse.net Git - vlc/blob - modules/access/mms/mmstu.c
Move intf_EJect() out of core
[vlc] / modules / access / mms / mmstu.c
1 /*****************************************************************************
2  * mms.c: MMS access plug-in
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 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_access.h>
34
35 #include <errno.h>
36 #include <assert.h>
37
38 #ifdef HAVE_UNISTD_H
39 #   include <unistd.h>
40 #endif
41 #include <sys/types.h>
42 #ifdef HAVE_POLL
43 #   include <poll.h>
44 #endif
45
46 #include <vlc_network.h>
47 #include <vlc_url.h>
48 #include "asf.h"
49 #include "buffer.h"
50
51 #include "mms.h"
52 #include "mmstu.h"
53
54 #undef MMS_DEBUG
55
56 /****************************************************************************
57  * NOTES:
58  *  MMSProtocole documentation found at http://get.to/sdp
59  ****************************************************************************/
60
61 /*****************************************************************************
62  * Local prototypes
63  *****************************************************************************/
64 int   MMSTUOpen   ( access_t * );
65 void  MMSTUClose  ( access_t * );
66
67
68 static block_t *Block( access_t * );
69 static int Seek( access_t *, uint64_t );
70 static int Control( access_t *, int, va_list );
71
72 static int  MMSOpen ( access_t *, vlc_url_t *, int );
73 static int  MMSStart( access_t *, uint32_t );
74 static int  MMSStop ( access_t * );
75 static void MMSClose( access_t * );
76
77
78 static int  mms_CommandRead( access_t *p_access, int i_command1, int i_command2 );
79 static int  mms_CommandSend( access_t *, int, uint32_t, uint32_t, uint8_t *, int );
80
81 static int  mms_HeaderMediaRead( access_t *, int );
82
83 static int  mms_ReceivePacket( access_t * );
84
85 static void KeepAliveStart( access_t * );
86 static void KeepAliveStop( access_t * );
87
88 int  MMSTUOpen( access_t *p_access )
89 {
90     access_sys_t   *p_sys;
91     int             i_proto;
92     int             i_status;
93
94     /* Set up p_access */
95     access_InitFields( p_access );
96     p_access->pf_read = NULL;
97     p_access->pf_block = Block;
98     p_access->pf_control = Control;
99     p_access->pf_seek = Seek;
100
101     p_access->p_sys = p_sys = calloc( 1, sizeof( access_sys_t ) );
102     if( !p_sys ) return VLC_ENOMEM;
103
104     p_sys->i_timeout = var_CreateGetInteger( p_access, "mms-timeout" );
105
106     vlc_mutex_init( &p_sys->lock_netwrite );
107
108     /* *** Parse URL and get server addr/port and path *** */
109     vlc_UrlParse( &p_sys->url, p_access->psz_location, 0 );
110     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
111     {
112         msg_Err( p_access, "invalid server name" );
113         vlc_UrlClean( &p_sys->url );
114         vlc_mutex_destroy( &p_sys->lock_netwrite );
115         free( p_sys );
116         return VLC_EGENERIC;
117     }
118     if( p_sys->url.i_port <= 0 )
119     {
120         p_sys->url.i_port = 1755;
121     }
122
123     /* *** connect to this server *** */
124     /* look at  requested protocol (udp/tcp) */
125     i_proto = MMS_PROTO_AUTO;
126     if( *p_access->psz_access )
127     {
128         if( !strncmp( p_access->psz_access, "mmsu", 4 ) )
129         {
130             i_proto = MMS_PROTO_UDP;
131         }
132         else if( !strncmp( p_access->psz_access, "mmst", 4 ) )
133         {
134             i_proto = MMS_PROTO_TCP;
135         }
136     }
137
138     /* connect */
139     if( i_proto == MMS_PROTO_AUTO )
140     {   /* first try with TCP and then UDP*/
141         if( ( i_status = MMSOpen( p_access, &p_sys->url, MMS_PROTO_TCP ) ) )
142         {
143             if( !p_access->b_die )
144                 i_status = MMSOpen( p_access, &p_sys->url, MMS_PROTO_UDP );
145         }
146     }
147     else
148     {
149         i_status = MMSOpen( p_access, &p_sys->url, i_proto );
150     }
151
152     if( i_status )
153     {
154         msg_Err( p_access, "cannot connect to server" );
155         vlc_UrlClean( &p_sys->url );
156         vlc_mutex_destroy( &p_sys->lock_netwrite );
157         free( p_sys );
158         return VLC_EGENERIC;
159     }
160
161     msg_Dbg( p_access, "connected to %s:%d", p_sys->url.psz_host, p_sys->url.i_port );
162     /*
163      * i_flags_broadcast
164      *  yy xx ?? ??
165      *  broadcast    yy=0x02, xx= 0x00
166      *  pre-recorded yy=0x01, xx= 0x80 if video, 0x00 no video
167      */
168     if( p_sys->i_packet_count <= 0 && p_sys->asfh.i_data_packets_count > 0 )
169     {
170         p_sys->i_packet_count = p_sys->asfh.i_data_packets_count;
171     }
172     if( p_sys->i_packet_count <= 0 || ( p_sys->i_flags_broadcast >> 24 ) == 0x02 )
173     {
174         p_sys->b_seekable = false;
175     }
176     else
177     {
178         p_sys->b_seekable = true;
179         p_access->info.i_size =
180             (uint64_t)p_sys->i_header +
181             (uint64_t)p_sys->i_packet_count * (uint64_t)p_sys->i_packet_length;
182     }
183     p_sys->b_keep_alive = false;
184
185     /* *** Start stream *** */
186     if( MMSStart( p_access, 0xffffffff ) < 0 )
187     {
188         msg_Err( p_access, "cannot start stream" );
189         MMSTUClose ( p_access );
190         return VLC_EGENERIC;
191     }
192
193     return VLC_SUCCESS;
194 }
195
196 /*****************************************************************************
197  * Close: free unused data structures
198  *****************************************************************************/
199 void MMSTUClose( access_t *p_access )
200 {
201     access_sys_t *p_sys = p_access->p_sys;
202
203     KeepAliveStop( p_access );
204
205     /* close connection with server */
206     MMSClose( p_access );
207
208     /* free memory */
209     vlc_UrlClean( &p_sys->url );
210
211     free( p_sys );
212 }
213
214 /*****************************************************************************
215  * Control:
216  *****************************************************************************/
217 static int Control( access_t *p_access, int i_query, va_list args )
218 {
219     access_sys_t *p_sys = p_access->p_sys;
220     bool   *pb_bool;
221     bool    b_bool;
222     int64_t      *pi_64;
223     int           i_int;
224
225     switch( i_query )
226     {
227         /* */
228         case ACCESS_CAN_SEEK:
229             pb_bool = (bool*)va_arg( args, bool* );
230             *pb_bool = p_sys->b_seekable;
231             break;
232
233         case ACCESS_CAN_FASTSEEK:
234             pb_bool = (bool*)va_arg( args, bool* );
235             *pb_bool = false;
236             break;
237
238         case ACCESS_CAN_PAUSE:
239             pb_bool = (bool*)va_arg( args, bool* );
240             *pb_bool = true;
241             break;
242
243         case ACCESS_CAN_CONTROL_PACE:
244             pb_bool = (bool*)va_arg( args, bool* );
245
246 #if 0       /* Disable for now until we have a clock synchro algo
247              * which works with something else than MPEG over UDP */
248             *pb_bool = false;
249 #endif
250             *pb_bool = true;
251             break;
252
253         /* */
254         case ACCESS_GET_PTS_DELAY:
255             pi_64 = (int64_t*)va_arg( args, int64_t * );
256             *pi_64 = INT64_C(1000)
257                    * var_InheritInteger( p_access, "network-caching" );
258             break;
259
260         case ACCESS_GET_PRIVATE_ID_STATE:
261             i_int = (int)va_arg( args, int );
262             pb_bool = (bool *)va_arg( args, bool * );
263
264             if( i_int < 0 || i_int > 127 )
265                 return VLC_EGENERIC;
266             *pb_bool =  p_sys->asfh.stream[i_int].i_selected ? true : false;
267             break;
268
269         /* */
270         case ACCESS_SET_PAUSE_STATE:
271             b_bool = (bool)va_arg( args, int );
272             if( b_bool )
273             {
274                 MMSStop( p_access );
275                 KeepAliveStart( p_access );
276             }
277             else
278             {
279                 KeepAliveStop( p_access );
280                 Seek( p_access, p_access->info.i_pos );
281             }
282             break;
283
284         case ACCESS_GET_TITLE_INFO:
285         case ACCESS_SET_TITLE:
286         case ACCESS_SET_SEEKPOINT:
287         case ACCESS_SET_PRIVATE_ID_STATE:
288         case ACCESS_GET_CONTENT_TYPE:
289             return VLC_EGENERIC;
290
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, uint64_t i_pos )
304 {
305     access_sys_t *p_sys = p_access->p_sys;
306     uint32_t    i_packet;
307     uint32_t    i_offset;
308     var_buffer_t buffer;
309
310     if( i_pos < p_sys->i_header)
311     {
312         if( p_access->info.i_pos < p_sys->i_header )
313         {
314             /* no need to restart stream, it was already one
315              * or no stream was yet read */
316             p_access->info.i_pos = i_pos;
317             return VLC_SUCCESS;
318         }
319         else
320         {
321             i_packet = 0xffffffff;
322             i_offset = 0;
323         }
324     }
325     else
326     {
327         i_packet = ( i_pos - p_sys->i_header ) / p_sys->i_packet_length;
328         i_offset = ( i_pos - p_sys->i_header ) % p_sys->i_packet_length;
329     }
330     if( p_sys->b_seekable && i_packet >= p_sys->i_packet_count )
331         return VLC_EGENERIC;
332
333     msg_Dbg( p_access, "seeking to %"PRIu64 " (packet:%u)", i_pos, i_packet );
334
335     MMSStop( p_access );
336     msg_Dbg( p_access, "stream stopped (seek)" );
337
338     /* *** restart stream *** */
339     var_buffer_initwrite( &buffer, 0 );
340     var_buffer_add64( &buffer, 0 ); /* seek point in second */
341     var_buffer_add32( &buffer, 0xffffffff );
342     var_buffer_add32( &buffer, i_packet ); // begin from start
343     var_buffer_add8( &buffer, 0xff ); // stream time limit
344     var_buffer_add8( &buffer, 0xff ); //  on 3bytes ...
345     var_buffer_add8( &buffer, 0xff ); //
346     var_buffer_add8( &buffer, 0x00 ); // don't use limit
347     var_buffer_add32( &buffer, p_sys->i_media_packet_id_type );
348
349     mms_CommandSend( p_access, 0x07, p_sys->i_command_level, 0x0001ffff,
350                      buffer.p_data, buffer.i_data );
351
352     var_buffer_free( &buffer );
353
354
355     while( vlc_object_alive (p_access) )
356     {
357         if( mms_HeaderMediaRead( p_access, MMS_PACKET_CMD ) < 0 )
358         {
359             p_access->info.b_eof = true;
360             return VLC_EGENERIC;
361         }
362
363         if( p_sys->i_command == 0x1e )
364         {
365             msg_Dbg( p_access, "received 0x1e (seek)" );
366             break;
367         }
368     }
369
370     while( vlc_object_alive (p_access) )
371     {
372         if( mms_HeaderMediaRead( p_access, MMS_PACKET_CMD ) < 0 )
373         {
374             p_access->info.b_eof = true;
375             return VLC_EGENERIC;
376         }
377         if( p_sys->i_command == 0x05 )
378         {
379             msg_Dbg( p_access, "received 0x05 (seek)" );
380             break;
381         }
382     }
383
384     /* get a packet */
385     if( mms_HeaderMediaRead( p_access, MMS_PACKET_MEDIA ) < 0 )
386     {
387         p_access->info.b_eof = true;
388         return VLC_EGENERIC;
389     }
390
391     msg_Dbg( p_access, "Streaming restarted" );
392
393     p_sys->i_media_used += i_offset;
394     p_access->info.i_pos = i_pos;
395     p_access->info.b_eof = false;
396
397     return VLC_SUCCESS;
398 }
399
400 /*****************************************************************************
401  * Block:
402  *****************************************************************************/
403 static block_t *Block( access_t *p_access )
404 {
405     access_sys_t *p_sys = p_access->p_sys;
406
407     if( p_access->info.b_eof )
408         return NULL;
409
410     if( p_access->info.i_pos < p_sys->i_header )
411     {
412         const size_t i_copy = p_sys->i_header - p_access->info.i_pos;
413
414         block_t *p_block = block_New( p_access, i_copy );
415         if( !p_block )
416             return NULL;
417
418         memcpy( p_block->p_buffer, &p_sys->p_header[p_access->info.i_pos], i_copy );
419         p_access->info.i_pos += i_copy;
420         return p_block;
421     }
422     else if( p_sys->p_media && p_sys->i_media_used < __MAX( p_sys->i_media, p_sys->i_packet_length ) )
423     {
424         size_t i_copy = 0;
425         size_t i_padding = 0;
426
427         if( p_sys->i_media_used < p_sys->i_media )
428             i_copy = p_sys->i_media - p_sys->i_media_used;
429         if( __MAX( p_sys->i_media, p_sys->i_media_used ) < p_sys->i_packet_length )
430             i_padding = p_sys->i_packet_length - __MAX( p_sys->i_media, p_sys->i_media_used );
431
432         block_t *p_block = block_New( p_access, i_copy + i_padding );
433         if( !p_block )
434             return NULL;
435
436         if( i_copy > 0 )
437             memcpy( &p_block->p_buffer[0], &p_sys->p_media[p_sys->i_media_used], i_copy );
438         if( i_padding > 0 )
439             memset( &p_block->p_buffer[i_copy], 0, i_padding );
440
441         p_sys->i_media_used += i_copy + i_padding;
442         p_access->info.i_pos += i_copy + i_padding;
443         return p_block;
444     }
445
446     mms_HeaderMediaRead( p_access, MMS_PACKET_MEDIA );
447     return NULL;
448 }
449
450 /****************************************************************************
451  * MMSOpen : Open a connection with the server over mmst or mmsu
452  ****************************************************************************/
453 static int MMSOpen( access_t  *p_access, vlc_url_t *p_url, int  i_proto )
454 {
455     access_sys_t *p_sys = p_access->p_sys;
456     int           b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
457
458     var_buffer_t buffer;
459     char         tmp[4096];
460     uint16_t     *p;
461     int          i_server_version;
462     int          i_tool_version;
463     int          i_update_player_url;
464     int          i_encryption_type;
465     int          i;
466     int          i_streams;
467     int          i_first;
468     char         *mediapath;
469
470
471     /* *** Open a TCP connection with server *** */
472     msg_Dbg( p_access, "waiting for connection..." );
473     p_sys->i_handle_tcp = net_ConnectTCP( p_access, p_url->psz_host, p_url->i_port );
474     if( p_sys->i_handle_tcp < 0 )
475     {
476         msg_Err( p_access, "failed to open a connection (tcp)" );
477         return VLC_EGENERIC;
478     }
479     msg_Dbg( p_access,
480              "connection(tcp) with \"%s:%d\" successful",
481              p_url->psz_host,
482              p_url->i_port );
483
484     /* *** Bind port if UDP protocol is selected *** */
485     if( b_udp )
486     {
487         if( net_GetSockAddress( p_sys->i_handle_tcp, p_sys->sz_bind_addr,
488                                 NULL ) )
489         {
490             net_Close( p_sys->i_handle_tcp );
491             return VLC_EGENERIC;
492         }
493
494         p_sys->i_handle_udp = net_ListenUDP1( (vlc_object_t *)p_access, p_sys->sz_bind_addr,
495                                               7000 );
496         if( p_sys->i_handle_udp < 0 )
497         {
498             msg_Err( p_access, "failed to open a connection (udp)" );
499             net_Close( p_sys->i_handle_tcp );
500             return VLC_EGENERIC;
501         }
502         msg_Dbg( p_access,
503                  "connection(udp) at \"%s:%d\" successful",
504                  p_sys->sz_bind_addr, 7000 );
505     }
506
507     /* *** Init context for mms prototcol *** */
508      GenerateGuid ( &p_sys->guid );    /* used to identify client by server */
509     msg_Dbg( p_access,
510              "generated guid: "GUID_FMT,
511              GUID_PRINT( p_sys->guid ) );
512     p_sys->i_command_level = 1;          /* updated after 0x1A command */
513     p_sys->i_seq_num = 0;
514     p_sys->i_media_packet_id_type  = 0x04;
515     p_sys->i_header_packet_id_type = 0x02;
516     p_sys->i_proto = i_proto;
517     p_sys->i_packet_seq_num = 0;
518     p_sys->p_header = NULL;
519     p_sys->i_header = 0;
520     p_sys->p_media = NULL;
521     p_sys->i_media = 0;
522     p_sys->i_media_used = 0;
523
524     p_access->info.i_pos = 0;
525     p_sys->i_buffer_tcp = 0;
526     p_sys->i_buffer_udp = 0;
527     p_sys->p_cmd = NULL;
528     p_sys->i_cmd = 0;
529     p_access->info.b_eof = false;
530
531     /* *** send command 1 : connection request *** */
532     var_buffer_initwrite( &buffer, 0 );
533     var_buffer_add16( &buffer, 0x001c );
534     var_buffer_add16( &buffer, 0x0003 );
535     sprintf( tmp,
536              "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
537              GUID_PRINT( p_sys->guid ),
538              p_url->psz_host );
539     var_buffer_addUTF16( &buffer, tmp );
540
541     mms_CommandSend( p_access,
542                      0x01,          /* connexion request */
543                      0x00000000,    /* flags, FIXME */
544                      0x0004000b,    /* ???? */
545                      buffer.p_data,
546                      buffer.i_data );
547
548     if( mms_CommandRead( p_access, 0x01, 0 ) < 0 )
549     {
550         var_buffer_free( &buffer );
551         MMSClose( p_access );
552         return VLC_EGENERIC;
553     }
554
555     i_server_version = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 32 );
556     i_tool_version = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 36 );
557     i_update_player_url = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 40 );
558     i_encryption_type = GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 44 );
559     p = (uint16_t*)( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 48 );
560 #define GETUTF16( psz, size ) \
561     { \
562         int i; \
563         psz = xmalloc( size + 1); \
564         for( i = 0; i < size; i++ ) \
565         { \
566             psz[i] = p[i]; \
567         } \
568         psz[size] = '\0'; \
569         p += ( size ); \
570     }
571     GETUTF16( p_sys->psz_server_version, i_server_version );
572     GETUTF16( p_sys->psz_tool_version, i_tool_version );
573     GETUTF16( p_sys->psz_update_player_url, i_update_player_url );
574     GETUTF16( p_sys->psz_encryption_type, i_encryption_type );
575 #undef GETUTF16
576     msg_Dbg( p_access,
577              "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
578              p_sys->psz_server_version,
579              p_sys->psz_tool_version,
580              p_sys->psz_update_player_url,
581              p_sys->psz_encryption_type );
582
583     /* *** should make an 18 command to make data timing *** */
584
585     /* *** send command 2 : transport protocol selection *** */
586     var_buffer_reinitwrite( &buffer, 0 );
587     var_buffer_add32( &buffer, 0x00000000 );
588     var_buffer_add32( &buffer, 0x000a0000 );
589     var_buffer_add32( &buffer, 0x00000002 );
590     if( b_udp )
591     {
592         sprintf( tmp,
593                  "\\\\%s\\UDP\\%d",
594                  p_sys->sz_bind_addr,
595                  7000 ); // FIXME
596     }
597     else
598     {
599         sprintf( tmp, "\\\\192.168.0.1\\TCP\\1242"  );
600     }
601     var_buffer_addUTF16( &buffer, tmp );
602     var_buffer_add16( &buffer, '0' );
603
604     mms_CommandSend( p_access,
605                      0x02,          /* connexion request */
606                      0x00000000,    /* flags, FIXME */
607                      0xffffffff,    /* ???? */
608                      buffer.p_data,
609                      buffer.i_data );
610
611     /* *** response from server, should be 0x02 or 0x03 *** */
612     mms_CommandRead( p_access, 0x02, 0x03 );
613     if( p_sys->i_command == 0x03 )
614     {
615         msg_Err( p_access,
616                  "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
617         var_buffer_free( &buffer );
618         MMSClose( p_access );
619         return VLC_EGENERIC;
620     }
621     else if( p_sys->i_command != 0x02 )
622     {
623         msg_Warn( p_access, "received command isn't 0x02 in reponse to 0x02" );
624     }
625
626     /* *** send command 5 : media file name/path requested *** */
627     var_buffer_reinitwrite( &buffer, 0 );
628     var_buffer_add64( &buffer, 0 );
629
630     /* media file path shouldn't start with / character */
631     mediapath = p_url->psz_path;
632     if ( mediapath && *mediapath == '/' )
633     {
634         mediapath++;
635     }
636     var_buffer_addUTF16( &buffer, mediapath );
637
638     mms_CommandSend( p_access,
639                      0x05,
640                      p_sys->i_command_level,
641                      0xffffffff,
642                      buffer.p_data,
643                      buffer.i_data );
644
645     /* *** wait for reponse *** */
646     mms_CommandRead( p_access, 0x1a, 0x06 );
647
648     /* test if server send 0x1A answer */
649     if( p_sys->i_command == 0x1A )
650     {
651         msg_Err( p_access, "id/password requested (not yet supported)" );
652         /*  FIXME */
653         var_buffer_free( &buffer );
654         MMSClose( p_access );
655         return VLC_EGENERIC;
656     }
657     if( p_sys->i_command != 0x06 )
658     {
659         msg_Err( p_access,
660                  "unknown answer (0x%x instead of 0x06)",
661                  p_sys->i_command );
662         var_buffer_free( &buffer );
663         MMSClose( p_access );
664         return( -1 );
665     }
666
667     /*  1 for file ok, 2 for authen ok */
668     switch( GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE ) )
669     {
670         case 0x0001:
671             msg_Dbg( p_access, "media file name/path accepted" );
672             break;
673         case 0x0002:
674             msg_Dbg( p_access, "authentication accepted" );
675             break;
676         case -1:
677         default:
678         msg_Err( p_access, "error while asking for file %d",
679                  GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE ) );
680         var_buffer_free( &buffer );
681         MMSClose( p_access );
682         return VLC_EGENERIC;
683     }
684
685     p_sys->i_flags_broadcast =
686         GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 12 );
687     p_sys->i_media_length =
688         GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 24 );
689     p_sys->i_packet_length =
690         GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 44 );
691     p_sys->i_packet_count =
692         GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 48 );
693     p_sys->i_max_bit_rate =
694         GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 56 );
695     p_sys->i_header_size =
696         GetDWLE( p_sys->p_cmd + MMS_CMD_HEADERSIZE + 60 );
697
698     msg_Dbg( p_access,
699              "answer 0x06 flags:0x%8.8"PRIx32" media_length:%"PRIu32"s "
700              "packet_length:%zu packet_count:%"PRIu32" max_bit_rate:%d "
701              "header_size:%zu",
702              p_sys->i_flags_broadcast,
703              p_sys->i_media_length,
704              p_sys->i_packet_length,
705              p_sys->i_packet_count,
706              p_sys->i_max_bit_rate,
707              p_sys->i_header_size );
708
709     /* *** send command 15 *** */
710
711     var_buffer_reinitwrite( &buffer, 0 );
712     var_buffer_add32( &buffer, 0 );
713     var_buffer_add32( &buffer, 0x8000 );
714     var_buffer_add32( &buffer, 0xffffffff );
715     var_buffer_add32( &buffer, 0x00 );
716     var_buffer_add32( &buffer, 0x00 );
717     var_buffer_add32( &buffer, 0x00 );
718     var_buffer_add64( &buffer, (((uint64_t)0x40ac2000)<<32) );
719     var_buffer_add32( &buffer, p_sys->i_header_packet_id_type );
720     var_buffer_add32( &buffer, 0x00 );
721     mms_CommandSend( p_access, 0x15, p_sys->i_command_level, 0x00,
722                      buffer.p_data, buffer.i_data );
723
724     /* *** wait for reponse *** */
725     /* Commented out because it fails on some stream (no 0x11 answer) */
726 #if 0
727     mms_CommandRead( p_access, 0x11, 0 );
728
729     if( p_sys->i_command != 0x11 )
730     {
731         msg_Err( p_access,
732                  "unknown answer (0x%x instead of 0x11)",
733                  p_sys->i_command );
734         var_buffer_free( &buffer );
735         MMSClose( p_access );
736         return( -1 );
737     }
738 #endif
739
740     /* *** now read header packet *** */
741     /* XXX could be split over multiples packets */
742     msg_Dbg( p_access, "reading header" );
743     for( ;; )
744     {
745         if( mms_HeaderMediaRead( p_access, MMS_PACKET_HEADER ) < 0 )
746         {
747             msg_Err( p_access, "cannot receive header" );
748             var_buffer_free( &buffer );
749             MMSClose( p_access );
750             return VLC_EGENERIC;
751         }
752         if( p_sys->i_header >= p_sys->i_header_size )
753         {
754             msg_Dbg( p_access,
755                      "header complete(%zu)",
756                      p_sys->i_header );
757             break;
758         }
759         msg_Dbg( p_access,
760                  "header incomplete (%zu/%zu), reading more",
761                  p_sys->i_header,
762                  p_sys->i_header_size );
763     }
764
765     /* *** parse header and get stream and their id *** */
766     /* get all streams properties,
767      *
768      * TODO : stream bitrates properties(optional)
769      *        and bitrate mutual exclusion(optional) */
770      asf_HeaderParse ( &p_sys->asfh,
771                            p_sys->p_header, p_sys->i_header );
772      asf_StreamSelect( &p_sys->asfh,
773                            var_InheritInteger( p_access, "mms-maxbitrate" ),
774                            var_InheritBool( p_access, "mms-all" ),
775                            var_InheritBool( p_access, "audio" ),
776                            var_InheritBool( p_access, "video" ) );
777
778     /* *** now select stream we want to receive *** */
779     /* TODO take care of stream bitrate TODO */
780     i_streams = 0;
781     i_first = -1;
782     var_buffer_reinitwrite( &buffer, 0 );
783     /* for now, select first audio and video stream */
784     for( i = 1; i < 128; i++ )
785     {
786
787         if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN )
788         {
789             i_streams++;
790             if( i_first != -1 )
791             {
792                 var_buffer_add16( &buffer, 0xffff );
793                 var_buffer_add16( &buffer, i );
794             }
795             else
796             {
797                 i_first = i;
798             }
799             if( p_sys->asfh.stream[i].i_selected )
800             {
801                 var_buffer_add16( &buffer, 0x0000 );
802                 msg_Info( p_access,
803                           "selecting stream[0x%x] %s (%d Kib/s)",
804                           i,
805                           ( p_sys->asfh.stream[i].i_cat == ASF_STREAM_AUDIO  ) ?
806                                                   "audio" : "video" ,
807                           p_sys->asfh.stream[i].i_bitrate / 1024);
808             }
809             else
810             {
811                 var_buffer_add16( &buffer, 0x0002 );
812                 msg_Info( p_access,
813                           "ignoring stream[0x%x] %s (%d Kib/s)",
814                           i,
815                           ( p_sys->asfh.stream[i].i_cat == ASF_STREAM_AUDIO  ) ?
816                                     "audio" : "video" ,
817                           p_sys->asfh.stream[i].i_bitrate / 1024);
818
819             }
820         }
821     }
822
823     if( i_streams == 0 )
824     {
825         msg_Err( p_access, "cannot find any stream" );
826         var_buffer_free( &buffer );
827         MMSClose( p_access );
828         return VLC_EGENERIC;
829     }
830     mms_CommandSend( p_access, 0x33,
831                      i_streams,
832                      0xffff | ( i_first << 16 ),
833                      buffer.p_data, buffer.i_data );
834
835     mms_CommandRead( p_access, 0x21, 0 );
836     if( p_sys->i_command != 0x21 )
837     {
838         msg_Err( p_access,
839                  "unknown answer (0x%x instead of 0x21)",
840                  p_sys->i_command );
841         var_buffer_free( &buffer );
842         MMSClose( p_access );
843         return VLC_EGENERIC;
844     }
845
846
847     var_buffer_free( &buffer );
848
849     msg_Info( p_access, "connection successful" );
850
851     return VLC_SUCCESS;
852 }
853
854 /****************************************************************************
855  * MMSStart : Start streaming
856  ****************************************************************************/
857 static int MMSStart( access_t  *p_access, uint32_t i_packet )
858 {
859     access_sys_t        *p_sys = p_access->p_sys;
860     var_buffer_t    buffer;
861
862     /* *** start stream from packet 0 *** */
863     var_buffer_initwrite( &buffer, 0 );
864     var_buffer_add64( &buffer, 0 ); /* seek point in second */
865     var_buffer_add32( &buffer, 0xffffffff );
866     var_buffer_add32( &buffer, i_packet ); // begin from start
867     var_buffer_add8( &buffer, 0xff ); // stream time limit
868     var_buffer_add8( &buffer, 0xff ); //  on 3bytes ...
869     var_buffer_add8( &buffer, 0xff ); //
870     var_buffer_add8( &buffer, 0x00 ); // don't use limit
871     var_buffer_add32( &buffer, p_sys->i_media_packet_id_type );
872
873     mms_CommandSend( p_access, 0x07, p_sys->i_command_level, 0x0001ffff,
874                      buffer.p_data, buffer.i_data );
875
876     var_buffer_free( &buffer );
877
878     mms_CommandRead( p_access, 0x05, 0 );
879
880     if( p_sys->i_command != 0x05 )
881     {
882         msg_Err( p_access,
883                  "unknown answer (0x%x instead of 0x05)",
884                  p_sys->i_command );
885         return -1;
886     }
887     else
888     {
889         /* get a packet */
890         if( mms_HeaderMediaRead( p_access, MMS_PACKET_MEDIA ) < 0 )
891             return -1;
892         msg_Dbg( p_access, "streaming started" );
893         return 0;
894     }
895 }
896
897 /****************************************************************************
898  * MMSStop : Stop streaming
899  ****************************************************************************/
900 static int MMSStop( access_t  *p_access )
901 {
902     access_sys_t *p_sys = p_access->p_sys;
903
904     /* *** stop stream but keep connection alive *** */
905     mms_CommandSend( p_access,
906                      0x09,
907                      p_sys->i_command_level,
908                      0x001fffff,
909                      NULL, 0 );
910     return( 0 );
911 }
912
913 /****************************************************************************
914  * MMSClose : Close streaming and connection
915  ****************************************************************************/
916 static void MMSClose( access_t  *p_access )
917 {
918     access_sys_t        *p_sys = p_access->p_sys;
919
920     msg_Dbg( p_access, "Connection closed" );
921
922     /* *** tell server that we will disconnect *** */
923     mms_CommandSend( p_access,
924                      0x0d,
925                      p_sys->i_command_level,
926                      0x00000001,
927                      NULL, 0 );
928
929     /* *** close sockets *** */
930     net_Close( p_sys->i_handle_tcp );
931     if( p_sys->i_proto == MMS_PROTO_UDP )
932     {
933         net_Close( p_sys->i_handle_udp );
934     }
935
936     FREENULL( p_sys->p_cmd );
937     FREENULL( p_sys->p_media );
938     FREENULL( p_sys->p_header );
939
940     FREENULL( p_sys->psz_server_version );
941     FREENULL( p_sys->psz_tool_version );
942     FREENULL( p_sys->psz_update_player_url );
943     FREENULL( p_sys->psz_encryption_type );
944 }
945
946 /****************************************************************************
947  *
948  * MMS specific functions
949  *
950  ****************************************************************************/
951 static int mms_CommandSend( access_t *p_access, int i_command,
952                             uint32_t i_prefix1, uint32_t i_prefix2,
953                             uint8_t *p_data, int i_data_old )
954 {
955     var_buffer_t buffer;
956     access_sys_t *p_sys = p_access->p_sys;
957     int i_data_by8, i_ret;
958     int i_data = i_data_old;
959
960     while( i_data & 0x7 ) i_data++;
961     i_data_by8 = i_data >> 3;
962
963     /* first init buffer */
964     var_buffer_initwrite( &buffer, 0 );
965
966     var_buffer_add32( &buffer, 0x00000001 );    /* start sequence */
967     var_buffer_add32( &buffer, 0xB00BFACE );
968     /* size after protocol type */
969     var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );
970     var_buffer_add32( &buffer, 0x20534d4d );    /* protocol "MMS " */
971     var_buffer_add32( &buffer, i_data_by8 + 4 );
972     var_buffer_add32( &buffer, p_sys->i_seq_num ); p_sys->i_seq_num++;
973     var_buffer_add64( &buffer, 0 );
974     var_buffer_add32( &buffer, i_data_by8 + 2 );
975     var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
976     var_buffer_add32( &buffer, i_prefix1 );    /* command specific */
977     var_buffer_add32( &buffer, i_prefix2 );    /* command specific */
978
979     /* specific command data */
980     if( p_data && i_data > 0 )
981     {
982         var_buffer_addmemory( &buffer, p_data, i_data_old );
983     }
984
985     /* Append padding to the command data */
986     var_buffer_add64( &buffer, 0 );
987
988     /* send it */
989     vlc_mutex_lock( &p_sys->lock_netwrite );
990     i_ret = net_Write( p_access, p_sys->i_handle_tcp, NULL, buffer.p_data,
991                        buffer.i_data - ( 8 - ( i_data - i_data_old ) ) );
992     vlc_mutex_unlock( &p_sys->lock_netwrite );
993
994     if( i_ret != buffer.i_data - ( 8 - ( i_data - i_data_old ) ) )
995     {
996         var_buffer_free( &buffer );
997         msg_Err( p_access, "failed to send command" );
998         return VLC_EGENERIC;
999     }
1000
1001     var_buffer_free( &buffer );
1002     return VLC_SUCCESS;
1003 }
1004
1005 static int NetFillBuffer( access_t *p_access )
1006 {
1007 #ifdef UNDER_CE
1008     return -1;
1009
1010 #else
1011     access_sys_t    *p_sys = p_access->p_sys;
1012     int             i_ret;
1013     struct pollfd   ufd[2];
1014     unsigned        timeout, nfd;
1015
1016     /* FIXME when using udp */
1017     ssize_t i_tcp, i_udp;
1018     ssize_t i_tcp_read, i_udp_read;
1019     int i_try = 0;
1020
1021     i_tcp = MMS_BUFFER_SIZE/2 - p_sys->i_buffer_tcp;
1022
1023     if( p_sys->i_proto == MMS_PROTO_UDP )
1024     {
1025         i_udp = MMS_BUFFER_SIZE/2 - p_sys->i_buffer_udp;
1026     }
1027     else
1028     {
1029         i_udp = 0;  /* there isn't udp socket */
1030     }
1031
1032     if( ( i_udp <= 0 ) && ( i_tcp <= 0 ) )
1033     {
1034         msg_Warn( p_access, "nothing to read %d:%d", (int)i_tcp, (int)i_udp );
1035         return 0;
1036     }
1037     else
1038     {
1039         /* msg_Warn( p_access, "ask for tcp:%d udp:%d", i_tcp, i_udp ); */
1040     }
1041
1042     /* Find if some data is available */
1043     do
1044     {
1045         i_try++;
1046
1047         /* Initialize file descriptor set */
1048         memset (ufd, 0, sizeof (ufd));
1049         nfd = 0;
1050
1051         if( i_tcp > 0 )
1052         {
1053             ufd[nfd].fd = p_sys->i_handle_tcp;
1054             ufd[nfd].events = POLLIN;
1055             nfd++;
1056         }
1057         if( i_udp > 0 )
1058         {
1059             ufd[nfd].fd = p_sys->i_handle_udp;
1060             ufd[nfd].events = POLLIN;
1061             nfd++;
1062         }
1063
1064         /* We'll wait 0.5 second if nothing happens */
1065         timeout = __MIN( 500, p_sys->i_timeout );
1066
1067         if( i_try * timeout > p_sys->i_timeout )
1068         {
1069             msg_Err(p_access, "no data received");
1070             return -1;
1071         }
1072
1073         if( i_try > 3 && (p_sys->i_buffer_tcp > 0 || p_sys->i_buffer_udp > 0) )
1074         {
1075             return -1;
1076         }
1077
1078         if( !vlc_object_alive (p_access) )
1079             return -1;
1080
1081         //msg_Dbg( p_access, "NetFillBuffer: trying again (select)" );
1082
1083     } while( !(i_ret = poll( ufd, nfd, timeout)) ||
1084              (i_ret < 0 && errno == EINTR) );
1085
1086     if( i_ret < 0 )
1087     {
1088         msg_Err( p_access, "network poll error (%m)" );
1089         return -1;
1090     }
1091
1092     i_tcp_read = i_udp_read = 0;
1093
1094     if( ( i_tcp > 0 ) && ufd[0].revents )
1095     {
1096         i_tcp_read =
1097             recv( p_sys->i_handle_tcp,
1098                   p_sys->buffer_tcp + p_sys->i_buffer_tcp,
1099                   i_tcp + MMS_BUFFER_SIZE/2, 0 );
1100     }
1101
1102     if( i_udp > 0 && ufd[i_tcp > 0].revents )
1103     {
1104         i_udp_read = recv( p_sys->i_handle_udp,
1105                            p_sys->buffer_udp + p_sys->i_buffer_udp,
1106                            i_udp + MMS_BUFFER_SIZE/2, 0 );
1107     }
1108
1109 #ifdef MMS_DEBUG
1110     if( p_sys->i_proto == MMS_PROTO_UDP )
1111     {
1112         msg_Dbg( p_access, "filling buffer TCP:%d+%d UDP:%d+%d",
1113                  p_sys->i_buffer_tcp, i_tcp_read,
1114                  p_sys->i_buffer_udp, i_udp_read );
1115     }
1116     else
1117     {
1118         msg_Dbg( p_access, "filling buffer TCP:%d+%d",
1119                  p_sys->i_buffer_tcp, i_tcp_read );
1120     }
1121 #endif
1122
1123     if( i_tcp_read > 0 ) p_sys->i_buffer_tcp += i_tcp_read;
1124     if( i_udp_read > 0 ) p_sys->i_buffer_udp += i_udp_read;
1125
1126     return i_tcp_read + i_udp_read;
1127 #endif
1128 }
1129
1130 static int  mms_ParseCommand( access_t *p_access,
1131                               uint8_t *p_data,
1132                               size_t i_data,
1133                               int *pi_used )
1134 {
1135  #define GET32( i_pos ) \
1136     ( p_sys->p_cmd[i_pos] + ( p_sys->p_cmd[i_pos +1] << 8 ) + \
1137       ( p_sys->p_cmd[i_pos + 2] << 16 ) + \
1138       ( p_sys->p_cmd[i_pos + 3] << 24 ) )
1139
1140     access_sys_t        *p_sys = p_access->p_sys;
1141     uint32_t    i_length;
1142     uint32_t    i_id;
1143
1144     free( p_sys->p_cmd );
1145     p_sys->i_cmd = i_data;
1146     p_sys->p_cmd = xmalloc( i_data );
1147     memcpy( p_sys->p_cmd, p_data, i_data );
1148
1149     *pi_used = i_data; /* by default */
1150
1151     if( i_data < MMS_CMD_HEADERSIZE )
1152     {
1153         msg_Warn( p_access, "truncated command (header incomplete)" );
1154         p_sys->i_command = 0;
1155         return -1;
1156     }
1157     i_id =  GetDWLE( p_data + 4 );
1158     i_length = GetDWLE( p_data + 8 ) + 16;
1159
1160     if( i_id != 0xb00bface || i_length < 16 )
1161     {
1162         msg_Err( p_access,
1163                  "incorrect command header (0x%"PRIx32")", i_id );
1164         p_sys->i_command = 0;
1165         return -1;
1166     }
1167
1168     if( i_length > p_sys->i_cmd )
1169     {
1170         msg_Warn( p_access,
1171                   "truncated command (missing %zu bytes)",
1172                    (size_t)i_length - i_data  );
1173         p_sys->i_command = 0;
1174         return -1;
1175     }
1176     else if( i_length < p_sys->i_cmd )
1177     {
1178         p_sys->i_cmd = i_length;
1179         *pi_used = i_length;
1180     }
1181
1182     msg_Dbg( p_access,
1183              "recv command start_sequence:0x%8.8x command_id:0x%8.8x length:%d len8:%d sequence 0x%8.8x len8_II:%d dir_comm:0x%8.8x",
1184              GET32( 0 ),
1185              GET32( 4 ),
1186              GET32( 8 ),
1187              /* 12: protocol type "MMS " */
1188              GET32( 16 ),
1189              GET32( 20 ),
1190              /* 24: unknown (0) */
1191              /* 28: unknown (0) */
1192              GET32( 32 ),
1193              GET32( 36 )
1194              /* 40: switches */
1195              /* 44: extra */ );
1196
1197     p_sys->i_command = GET32( 36 ) & 0xffff;
1198 #undef GET32
1199
1200     return MMS_PACKET_CMD;
1201 }
1202
1203 static int  mms_ParsePacket( access_t *p_access,
1204                              uint8_t *p_data, size_t i_data,
1205                              int *pi_used )
1206 {
1207     access_sys_t        *p_sys = p_access->p_sys;
1208     int i_packet_seq_num;
1209     size_t i_packet_length;
1210     uint32_t i_packet_id;
1211
1212     *pi_used = i_data; /* default */
1213     if( i_data <= 8 )
1214     {
1215         msg_Warn( p_access, "truncated packet (header incomplete)" );
1216         return -1;
1217     }
1218
1219     i_packet_id = p_data[4];
1220     i_packet_seq_num = GetDWLE( p_data );
1221     i_packet_length = GetWLE( p_data + 6 );
1222
1223     //msg_Warn( p_access, "------->i_packet_length=%d, i_data=%d", i_packet_length, i_data );
1224
1225     if( i_packet_length > i_data || i_packet_length <= 8)
1226     {
1227      /*   msg_Dbg( p_access,
1228                  "truncated packet (Declared %d bytes, Actual %d bytes)",
1229                  i_packet_length, i_data  ); */
1230         *pi_used = 0;
1231         return -1;
1232     }
1233     else if( i_packet_length < i_data )
1234     {
1235         *pi_used = i_packet_length;
1236     }
1237
1238     if( i_packet_id == 0xff )
1239     {
1240         msg_Warn( p_access,
1241                   "receive MMS UDP pair timing" );
1242         return( MMS_PACKET_UDP_TIMING );
1243     }
1244
1245     if( i_packet_id != p_sys->i_header_packet_id_type &&
1246         i_packet_id != p_sys->i_media_packet_id_type )
1247     {
1248         msg_Warn( p_access, "incorrect Packet Id Type (0x%x)", i_packet_id );
1249         return -1;
1250     }
1251
1252     /* we now have a media or a header packet */
1253     if( i_packet_seq_num != p_sys->i_packet_seq_num )
1254     {
1255 #if 0
1256         /* FIXME for udp could be just wrong order ? */
1257         msg_Warn( p_access,
1258                   "detected packet lost (%d != %d)",
1259                   i_packet_seq_num,
1260                   p_sys->i_packet_seq_num );
1261 #endif
1262     }
1263     p_sys->i_packet_seq_num = i_packet_seq_num + 1;
1264
1265     if( i_packet_id == p_sys->i_header_packet_id_type )
1266     {
1267         if( p_sys->p_header )
1268         {
1269             p_sys->p_header = xrealloc( p_sys->p_header,
1270                                       p_sys->i_header + i_packet_length - 8 );
1271             memcpy( &p_sys->p_header[p_sys->i_header],
1272                     p_data + 8, i_packet_length - 8 );
1273             p_sys->i_header += i_packet_length - 8;
1274
1275         }
1276         else
1277         {
1278             uint8_t* p_packet = xmalloc( i_packet_length - 8 ); // don't bother with preheader
1279             memcpy( p_packet, p_data + 8, i_packet_length - 8 );
1280             p_sys->p_header = p_packet;
1281             p_sys->i_header = i_packet_length - 8;
1282         }
1283 /*        msg_Dbg( p_access,
1284                  "receive header packet (%d bytes)",
1285                  i_packet_length - 8 ); */
1286
1287         return MMS_PACKET_HEADER;
1288     }
1289     else
1290     {
1291         uint8_t* p_packet = xmalloc( i_packet_length - 8 ); // don't bother with preheader
1292         memcpy( p_packet, p_data + 8, i_packet_length - 8 );
1293         FREENULL( p_sys->p_media );
1294         p_sys->p_media = p_packet;
1295         p_sys->i_media = i_packet_length - 8;
1296         p_sys->i_media_used = 0;
1297 /*        msg_Dbg( p_access,
1298                  "receive media packet (%d bytes)",
1299                  i_packet_length - 8 ); */
1300
1301         return MMS_PACKET_MEDIA;
1302     }
1303 }
1304
1305 static int mms_ReceivePacket( access_t *p_access )
1306 {
1307     access_sys_t *p_sys = p_access->p_sys;
1308     int i_packet_tcp_type;
1309     int i_packet_udp_type;
1310
1311     for( ;; )
1312     {
1313         bool b_refill = true;
1314
1315         /* first if we need to refill buffer */
1316         if( p_sys->i_buffer_tcp >= MMS_CMD_HEADERSIZE )
1317         {
1318             if( GetDWLE( p_sys->buffer_tcp + 4 ) == 0xb00bface  )
1319             {
1320                 if( GetDWLE( p_sys->buffer_tcp + 8 ) + 16 <=
1321                     (uint32_t)p_sys->i_buffer_tcp )
1322                 {
1323                     b_refill = false;
1324                 }
1325             }
1326             else if( GetWLE( p_sys->buffer_tcp + 6 ) <= p_sys->i_buffer_tcp )
1327             {
1328                 b_refill = false;
1329             }
1330         }
1331         if( p_sys->i_proto == MMS_PROTO_UDP && p_sys->i_buffer_udp >= 8 &&
1332             GetWLE( p_sys->buffer_udp + 6 ) <= p_sys->i_buffer_udp )
1333         {
1334             b_refill = false;
1335         }
1336
1337         if( b_refill && NetFillBuffer( p_access ) < 0 )
1338         {
1339             msg_Warn( p_access, "cannot fill buffer" );
1340             return -1;
1341         }
1342
1343         i_packet_tcp_type = -1;
1344         i_packet_udp_type = -1;
1345
1346         if( p_sys->i_buffer_tcp > 0 )
1347         {
1348             int i_used;
1349
1350             if( GetDWLE( p_sys->buffer_tcp + 4 ) == 0xb00bface )
1351             {
1352                 i_packet_tcp_type =
1353                     mms_ParseCommand( p_access, p_sys->buffer_tcp,
1354                                       p_sys->i_buffer_tcp, &i_used );
1355
1356             }
1357             else
1358             {
1359                 i_packet_tcp_type =
1360                     mms_ParsePacket( p_access, p_sys->buffer_tcp,
1361                                      p_sys->i_buffer_tcp, &i_used );
1362             }
1363             if( i_used > 0 && i_used < MMS_BUFFER_SIZE )
1364             {
1365                 memmove( p_sys->buffer_tcp, p_sys->buffer_tcp + i_used,
1366                          MMS_BUFFER_SIZE - i_used );
1367             }
1368             p_sys->i_buffer_tcp -= i_used;
1369         }
1370         else if( p_sys->i_buffer_udp > 0 )
1371         {
1372             int i_used;
1373
1374             i_packet_udp_type =
1375                 mms_ParsePacket( p_access, p_sys->buffer_udp,
1376                                  p_sys->i_buffer_udp, &i_used );
1377
1378             if( i_used > 0 && i_used < MMS_BUFFER_SIZE )
1379             {
1380                 memmove( p_sys->buffer_udp, p_sys->buffer_udp + i_used,
1381                          MMS_BUFFER_SIZE - i_used );
1382             }
1383             p_sys->i_buffer_udp -= i_used;
1384         }
1385
1386         if( i_packet_tcp_type == MMS_PACKET_CMD && p_sys->i_command == 0x1b )
1387         {
1388             mms_CommandSend( p_access, 0x1b, 0, 0, NULL, 0 );
1389             i_packet_tcp_type = -1;
1390         }
1391
1392         if( i_packet_tcp_type != -1 )
1393         {
1394             return i_packet_tcp_type;
1395         }
1396         else if( i_packet_udp_type != -1 )
1397         {
1398             return i_packet_udp_type;
1399         }
1400     }
1401 }
1402
1403 static int mms_ReceiveCommand( access_t *p_access )
1404 {
1405     access_sys_t *p_sys = p_access->p_sys;
1406
1407     for( ;; )
1408     {
1409         int i_used;
1410         int i_status;
1411
1412         if( NetFillBuffer( p_access ) < 0 )
1413         {
1414             msg_Warn( p_access, "cannot fill buffer" );
1415             return VLC_EGENERIC;
1416         }
1417         if( p_sys->i_buffer_tcp > 0 )
1418         {
1419             i_status = mms_ParseCommand( p_access, p_sys->buffer_tcp,
1420                                          p_sys->i_buffer_tcp, &i_used );
1421             if( i_used < MMS_BUFFER_SIZE )
1422             {
1423                 memmove( p_sys->buffer_tcp, p_sys->buffer_tcp + i_used,
1424                          MMS_BUFFER_SIZE - i_used );
1425             }
1426             p_sys->i_buffer_tcp -= i_used;
1427
1428             if( i_status < 0 )
1429             {
1430                 return VLC_EGENERIC;
1431             }
1432
1433             if( p_sys->i_command == 0x1b )
1434             {
1435                 mms_CommandSend( p_access, 0x1b, 0, 0, NULL, 0 );
1436             }
1437             else
1438             {
1439                 break;
1440             }
1441         }
1442         else
1443         {
1444             return VLC_EGENERIC;
1445         }
1446     }
1447
1448     return VLC_SUCCESS;
1449 }
1450
1451 #define MMS_RETRY_MAX       10
1452 #define MMS_RETRY_SLEEP     50000
1453
1454 static int mms_CommandRead( access_t *p_access, int i_command1,
1455                             int i_command2 )
1456 {
1457     access_sys_t *p_sys = p_access->p_sys;
1458     int i_count;
1459     int i_status;
1460
1461     for( i_count = 0; i_count < MMS_RETRY_MAX; )
1462     {
1463         i_status = mms_ReceiveCommand( p_access );
1464         if( i_status < 0 || p_sys->i_command == 0 )
1465         {
1466             i_count++;
1467             msleep( MMS_RETRY_SLEEP );
1468         }
1469         else if( i_command1 == 0 && i_command2 == 0)
1470         {
1471             return VLC_SUCCESS;
1472         }
1473         else if( p_sys->i_command == i_command1 ||
1474                  p_sys->i_command == i_command2 )
1475         {
1476             return VLC_SUCCESS;
1477         }
1478         else
1479         {
1480             switch( p_sys->i_command )
1481             {
1482                 case 0x03:
1483                     msg_Warn( p_access, "socket closed by server" );
1484                     p_access->info.b_eof = true;
1485                     return VLC_EGENERIC;
1486                 case 0x1e:
1487                     msg_Warn( p_access, "end of media stream" );
1488                     p_access->info.b_eof = true;
1489                     return VLC_EGENERIC;
1490                 default:
1491                     break;
1492             }
1493         }
1494     }
1495     p_access->info.b_eof = true;
1496     msg_Warn( p_access, "failed to receive command (aborting)" );
1497
1498     return VLC_EGENERIC;
1499 }
1500
1501
1502 static int mms_HeaderMediaRead( access_t *p_access, int i_type )
1503 {
1504     access_sys_t *p_sys = p_access->p_sys;
1505     int          i_count;
1506
1507     for( i_count = 0; i_count < MMS_RETRY_MAX; )
1508     {
1509         int i_status;
1510
1511         if( !vlc_object_alive (p_access) )
1512             return -1;
1513
1514         i_status = mms_ReceivePacket( p_access );
1515         if( i_status < 0 )
1516         {
1517             i_count++;
1518             msg_Warn( p_access, "cannot receive header (%d/%d)",
1519                       i_count, MMS_RETRY_MAX );
1520             msleep( MMS_RETRY_SLEEP );
1521         }
1522         else if( i_status == i_type || i_type == MMS_PACKET_ANY )
1523         {
1524             return i_type;
1525         }
1526         else if( i_status == MMS_PACKET_CMD )
1527         {
1528             switch( p_sys->i_command )
1529             {
1530                 case 0x03:
1531                     msg_Warn( p_access, "socket closed by server" );
1532                     p_access->info.b_eof = true;
1533                     return -1;
1534                 case 0x1e:
1535                     msg_Warn( p_access, "end of media stream" );
1536                     p_access->info.b_eof = true;
1537                     return -1;
1538                 case 0x20:
1539                     /* XXX not too dificult to be done EXCEPT that we
1540                      * need to restart demuxer... and I don't see how we
1541                      * could do that :p */
1542                     msg_Err( p_access,
1543                              "reinitialization needed --> unsupported" );
1544                     p_access->info.b_eof = true;
1545                     return -1;
1546                 default:
1547                     break;
1548             }
1549         }
1550     }
1551
1552     msg_Err( p_access, "cannot receive %s (aborting)",
1553              ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );
1554     p_access->info.b_eof = true;
1555     return -1;
1556 }
1557
1558 VLC_NORETURN
1559 static void *KeepAliveThread( void *p_data )
1560 {
1561     access_t *p_access = p_data;
1562
1563     for( ;; )
1564     {
1565         /* Send keep-alive every ten seconds */
1566         int canc = vlc_savecancel();
1567
1568         mms_CommandSend( p_access, 0x1b, 0, 0, NULL, 0 );
1569
1570         vlc_restorecancel( canc );
1571
1572         msleep( 10 * CLOCK_FREQ );
1573     }
1574     assert(0);
1575 }
1576
1577 static void KeepAliveStart( access_t *p_access )
1578 {
1579     access_sys_t *p_sys = p_access->p_sys;
1580     if( p_sys->b_keep_alive )
1581         return;
1582
1583     p_sys->b_keep_alive = !vlc_clone( &p_sys->keep_alive,
1584                                       KeepAliveThread, p_access,
1585                                       VLC_THREAD_PRIORITY_LOW );
1586 }
1587
1588 static void KeepAliveStop( access_t *p_access )
1589 {
1590     access_sys_t *p_sys = p_access->p_sys;
1591     if( !p_sys->b_keep_alive )
1592         return;
1593
1594     vlc_cancel( p_sys->keep_alive );
1595     vlc_join( p_sys->keep_alive, NULL );
1596     p_sys->b_keep_alive = false;
1597 }