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