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