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