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