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