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