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