]> git.sesse.net Git - vlc/blob - modules/access/mms/mms.c
eba32cd436e11717c57c8a87a1f6ac9e020c7f11
[vlc] / modules / access / mms / mms.c
1 /*****************************************************************************
2  * mms.c: MMS access plug-in
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: mms.c,v 1.7 2002/11/25 00:22:04 fenrir Exp $
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24
25 /*
26  * TODO:
27  *  - clean code, break huge code block
28  *  - fix memory leak
29  *  - begin udp support...
30  */
31
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <fcntl.h>
41
42 #include <vlc/vlc.h>
43 #include <vlc/input.h>
44
45 #ifdef HAVE_UNISTD_H
46 #   include <unistd.h>
47 #elif defined( _MSC_VER ) && defined( _WIN32 )
48 #   include <io.h>
49 #endif
50
51 #ifdef WIN32
52 #   include <winsock2.h>
53 #   include <ws2tcpip.h>
54 #   ifndef IN_MULTICAST
55 #       define IN_MULTICAST(a) IN_CLASSD(a)
56 #   endif
57 #else
58 #   include <sys/socket.h>
59 #endif
60
61 #include "network.h"
62 #include "asf.h"
63 #include "buffer.h"
64 #include "mms.h"
65
66 /****************************************************************************
67  * NOTES: 
68  *  MMSProtocole documentation found at http://get.to/sdp
69  ****************************************************************************/
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int  Open        ( vlc_object_t * );
75 static void Close       ( vlc_object_t * );
76
77 static int  Read        ( input_thread_t * p_input, byte_t * p_buffer,
78                           size_t i_len );
79 static void Seek        ( input_thread_t *, off_t );
80 static int  SetProgram  ( input_thread_t *, pgrm_descriptor_t * );  
81
82
83 static int MMSOpen( input_thread_t  *, url_t *, int, char * );
84
85 static int MMSStart  ( input_thread_t  *, uint32_t );
86 static int MMSStop  ( input_thread_t  *p_input );
87
88 static int MMSClose  ( input_thread_t  * );
89
90
91 static int  mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
92 static int  mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
93
94 static int  mms_HeaderMediaRead( input_thread_t *, int );
95
96 static int  mms_ReceivePacket( input_thread_t * );
97     
98 static void mms_ParseURL( url_t *p_url, char *psz_url );
99
100
101
102 /* 
103  * XXX DON'T FREE MY MEMORY !!! XXX
104  * non mais :P
105  */
106
107 /* 
108  * Ok, ok, j'le ferai plus...
109  */
110 /*
111  * Merci :)) 
112  */
113
114 /*****************************************************************************
115  * Module descriptor
116  *****************************************************************************/
117 vlc_module_begin();
118     set_description( _("MMS access module") );
119     set_capability( "access", 0 );
120     add_category_hint( "stream", NULL );
121         add_bool( "mms-all", 0, NULL, 
122                   "force selection of all streams",
123                   "force selection of all streams" );
124
125         add_string( "mms-stream", NULL, NULL,
126                     "streams selection",
127                     "force this stream selection" );
128         add_integer( "mms-maxbitrate", 0, NULL, 
129                      "max bitrate",
130                      "set max bitrate for auto streams selections" );
131     add_shortcut( "mms" );
132     add_shortcut( "mmsu" );
133     add_shortcut( "mmst" );
134     set_callbacks( Open, Close );
135 vlc_module_end();
136
137 #define BUF_SIZE 200000
138
139 static int Open( vlc_object_t *p_this )
140 {
141     access_t    *p_access;
142     int         i_proto;
143     char        *psz_network;
144     int         i_status;
145
146     input_thread_t  *p_input = (input_thread_t*)p_this;
147
148     /* *** allocate p_access_data *** */
149     p_input->p_access_data = 
150         (void*)p_access = malloc( sizeof( access_t ) );
151     memset( p_access, 0, sizeof( access_t ) );
152
153
154     /* *** Parse URL and get server addr/port and path *** */
155     mms_ParseURL( &p_access->url, p_input->psz_name );
156     if( p_access->url.psz_server_addr == NULL || 
157         !( *p_access->url.psz_server_addr ) )
158     {
159         FREE( p_access->url.psz_private );
160         msg_Err( p_input, "invalid server name" );
161         return( -1 );
162     }
163     if( p_access->url.i_server_port == 0 )
164     {   
165         p_access->url.i_server_port = 1755; /* default port */
166     }
167     if( p_access->url.i_bind_port == 0 )
168     {   
169         p_access->url.i_bind_port = 7000;   /* default port */
170     }
171    
172
173     /* *** connect to this server *** */
174     /* 1: look at  requested protocol (udp/tcp) */
175     i_proto = MMS_PROTO_AUTO;
176     if( *p_input->psz_access )
177     {
178         if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
179         {
180             i_proto = MMS_PROTO_UDP;
181         }
182         else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
183         {
184             i_proto = MMS_PROTO_TCP;
185         }
186     }
187     /* 2: look at ip version ipv4/ipv6 */
188     psz_network = "";
189     if( config_GetInt( p_input, "ipv4" ) )
190     {
191         psz_network = "ipv4";
192     }
193     else if( config_GetInt( p_input, "ipv6" ) )
194     {
195         psz_network = "ipv6";
196     }
197     /* 3: connect */
198     if( i_proto == MMS_PROTO_AUTO )
199     {   /* first try with TCP */
200         i_status = 
201             MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
202         if( i_status < 0 )
203         {   /* then with UDP */
204             i_status = 
205              MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
206         }
207     }
208     else
209     {
210
211         i_status = 
212             MMSOpen( p_input, &p_access->url, i_proto, psz_network );
213     }
214
215     if( i_status < 0 )
216     {
217         msg_Err( p_input, "cannot connect to server" );
218         FREE( p_access->url.psz_private );
219         return( -1 );
220     }
221     msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
222
223     
224     /* *** set exported functions *** */
225     p_input->pf_read = Read;
226     p_input->pf_seek = Seek;
227     p_input->pf_set_program = SetProgram;
228     p_input->pf_set_area = NULL;
229     
230     p_input->p_private = NULL;
231
232     /* *** finished to set some variable *** */
233     vlc_mutex_lock( &p_input->stream.stream_lock );
234     /* those data could be different for UDP/TCP */
235     p_input->stream.b_pace_control = 1;
236     p_input->stream.p_selected_area->i_tell = 0;
237     if( p_access->i_packet_count <= 0 )
238     {
239         p_input->stream.b_seekable = 0;
240         p_input->stream.p_selected_area->i_size = 0;
241     }
242     else
243     {
244         p_input->stream.b_seekable = 0;
245         p_input->stream.p_selected_area->i_size = 
246             p_access->i_header + 
247             p_access->i_packet_count * p_access->i_packet_length;
248     }
249     
250     p_input->stream.i_method = INPUT_METHOD_NETWORK;
251     vlc_mutex_unlock( &p_input->stream.stream_lock );
252
253     /* *** Start stream *** */
254     if( MMSStart( p_input, 0xffffffff ) < 0 )
255     {
256         msg_Err( p_input, "cannot start stream" );
257         MMSClose( p_input );
258         FREE( p_access->url.psz_private );
259         return( -1 );
260     }
261                            
262     return( 0 );
263 }
264
265 /*****************************************************************************
266  * Close: free unused data structures
267  *****************************************************************************/
268 static void Close( vlc_object_t *p_this )
269 {
270     input_thread_t *  p_input = (input_thread_t *)p_this;
271     access_t    *p_access = (access_t*)p_input->p_access_data;
272     
273     /* close connection with server */
274     MMSClose( p_input );
275     
276     /* free memory */
277     FREE( p_access->url.psz_private );
278 }
279
280 /*****************************************************************************
281  * SetProgram: do nothing
282  *****************************************************************************/
283 static int SetProgram( input_thread_t * p_input,
284                        pgrm_descriptor_t * p_program )
285 {
286     return( 0 );
287 }
288
289 /*****************************************************************************
290  * Seek: try to go at the right place
291  *****************************************************************************/
292 static void Seek( input_thread_t * p_input, off_t i_pos )
293 {
294     /* 
295      * FIXME  
296      * Don't work
297      * Probably some bad or missing command
298      *
299      * 
300      */
301 #if 0
302
303     access_t    *p_access = (access_t*)p_input->p_access_data;
304     uint32_t    i_packet;
305     uint32_t    i_offset;
306     
307     if( i_pos < 0 )
308     {
309         return;
310     }    
311     msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
312     if( i_pos < p_access->i_header)
313     {
314
315         if( p_access->i_pos < p_access->i_header )
316         {
317             /* no need to restart stream, it was already one 
318              * or no stream was yet read */
319             p_access->i_pos = i_pos;
320             return;
321         }
322         else
323         {
324             i_packet = 0xffffffff;
325             i_offset = 0;
326         }
327     }
328     else
329     {
330         i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
331         i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
332     }
333
334     MMSStop( p_input );
335     MMSStart( p_input, i_packet );
336     p_access->i_media_used += i_offset;
337     p_access->i_pos = i_pos;
338 #endif
339 }
340
341 static int  Read        ( input_thread_t * p_input, byte_t * p_buffer,
342                           size_t i_len )
343 {
344     access_t    *p_access = (access_t*)p_input->p_access_data;
345     size_t      i_data;
346     size_t      i_copy;
347
348     i_data = 0;
349     
350     /* *** send header if needed ** */
351     if( p_access->i_pos < p_access->i_header )
352     { 
353         i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
354         if( i_copy > 0 )
355         {
356             memcpy( p_buffer, 
357                     p_access->p_header + p_access->i_pos,
358                     i_copy );
359         }
360         i_data += i_copy;
361     }
362
363     /* *** now send data if needed *** */
364     while( i_data < i_len )
365     {
366         if( p_access->i_media_used < p_access->i_media )
367         {
368             i_copy = __MIN( i_len - i_data , 
369                             p_access->i_media - p_access->i_media_used );
370             memcpy( p_buffer + i_data, 
371                     p_access->p_media + p_access->i_media_used,
372                     i_copy );
373             i_data += i_copy;
374             p_access->i_media_used += i_copy;
375         }
376         else if( p_access->p_media != NULL && 
377                  p_access->i_media_used < p_access->i_packet_length )
378         {
379             i_copy = __MIN( i_len - i_data,
380                             p_access->i_packet_length - p_access->i_media_used);
381             memset( p_buffer + i_data, 0, i_copy );
382
383             i_data += i_copy;
384             p_access->i_media_used += i_copy;
385         }
386         else
387         {
388             if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
389             {
390                 p_access->i_pos += i_data;
391                 return( i_data );
392             }
393         }
394     }
395
396     p_access->i_pos += i_data;
397     return( i_data );
398 }
399
400 static void asf_HeaderParse( mms_stream_t stream[128],
401                              uint8_t *p_header, int i_header )
402 {
403     var_buffer_t buffer;
404     guid_t      guid;
405     uint64_t    i_size;
406     int         i;
407     
408     for( i = 0; i < 128; i++ )
409     {
410         stream[i].i_cat = MMS_STREAM_UNKNOWN;
411     }
412
413 //    fprintf( stderr, " ---------------------header:%d\n", i_header );
414     var_buffer_initread( &buffer, p_header, i_header );
415     
416     var_buffer_getguid( &buffer, &guid );
417
418     if( !CmpGuid( &guid, &asf_object_header_guid ) )
419     {
420 //        XXX Error
421 //        fprintf( stderr, " ---------------------ERROR------\n" );
422     }
423     var_buffer_getmemory( &buffer, NULL, 30 - 16 );
424     
425     for( ;; )
426     {
427 //    fprintf( stderr, " ---------------------data:%d\n", buffer.i_data );
428         if( var_buffer_readempty( &buffer ) )
429         {
430             return;
431         }
432
433         var_buffer_getguid( &buffer, &guid );
434         i_size = var_buffer_get64( &buffer );
435         if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
436         {
437             int     i_stream_id;
438             guid_t  stream_type;
439 //            msg_Dbg( p_input, "found stream_properties" );
440             
441             var_buffer_getguid( &buffer, &stream_type );
442             var_buffer_getmemory( &buffer, NULL, 32 );
443             i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
444
445  //           fprintf( stderr, " 1---------------------skip:%lld\n", i_size - 24 - 32 - 16 - 1 );
446             var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1);
447             
448             if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
449             {
450 //                msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
451                 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
452             }
453             else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
454             {
455 //                msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
456                 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
457             }
458             else
459             {
460 //                msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
461                 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
462             }
463         }
464         else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
465         {
466             int     i_count;
467             uint8_t i_stream_id;
468             
469             i_count = var_buffer_get16( &buffer );
470             i_size -= 2;
471             while( i_count > 0 )
472             {
473                 i_stream_id = var_buffer_get16( &buffer )&0x7f;
474                 stream[i_stream_id].i_bitrate =  var_buffer_get32( &buffer );
475                 i_count--;
476                 i_size -= 6;
477             }
478 //            fprintf( stderr, " 2---------------------skip:%lld\n", i_size - 24);
479             var_buffer_getmemory( &buffer, NULL, i_size - 24 );
480         }
481         else            
482         {
483             // skip unknown guid
484             var_buffer_getmemory( &buffer, NULL, i_size - 24 );
485 //            fprintf( stderr, " 3---------------------skip:%lld\n", i_size - 24);
486         }
487     }
488 }
489
490 static void mms_StreamSelect( input_thread_t * p_input, 
491                               mms_stream_t stream[128] )
492 {
493     /* XXX FIXME use mututal eclusion information */
494     int i;
495     int i_audio, i_video;
496     int b_audio, b_video;
497     int i_bitrate_total;
498     int i_bitrate_max;
499     char *psz_stream;
500
501     i_audio = 0;
502     i_video = 0;
503     i_bitrate_total = 0;
504     i_bitrate_max = config_GetInt( p_input, "mms-maxbitrate" );
505     b_audio = config_GetInt( p_input, "audio" );
506     b_video = config_GetInt( p_input, "video" );
507     if( config_GetInt( p_input, "mms-all" ) )
508     {
509         /* select all valid stream */
510         for( i = 1; i < 128; i++ )
511         {
512             if( stream[i].i_cat != MMS_STREAM_UNKNOWN )
513             {
514                 stream[i].i_selected = 1;
515             }
516         }
517         return;
518     }
519     else
520     {
521         for( i = 0; i < 128; i++ )
522         {
523             stream[i].i_selected = 0; /* by default, not selected */
524         }
525     }
526     psz_stream = config_GetPsz( p_input, "mms-stream" );
527
528     if( psz_stream && *psz_stream )
529     {
530         char *psz_tmp = psz_stream;
531         while( *psz_tmp )
532         {
533             if( *psz_tmp == ',' )
534             {
535                 psz_tmp++;
536             }
537             else
538             {
539                 int i_stream;
540                 i_stream = atoi( psz_tmp );
541                 while( *psz_tmp != '\0' && *psz_tmp != ',' )
542                 {
543                     psz_tmp++;
544                 }
545
546                 if( i_stream > 0 && i_stream < 128 &&
547                     stream[i_stream].i_cat != MMS_STREAM_UNKNOWN )
548                 {
549                     stream[i_stream].i_selected = 1;
550                 }
551             }
552         }
553         FREE( psz_stream );
554         return;
555     }
556     FREE( psz_stream );
557
558     /* big test:
559      * select a stream if 
560      *    - no audio nor video stream    
561      *    - or: 
562      *         - if i_bitrate_max not set keep the highest bitrate
563      *         - if i_bitrate_max is set, keep stream that make we used best
564      *           quality regarding i_bitrate_max
565      *
566      * XXX: little buggy:
567      *        - it doesn't use mutual exclusion info..
568      *        - when selecting a better stream we could select 
569      *        something that make i_bitrate_total> i_bitrate_max
570      */ 
571     for( i = 1; i < 128; i++ )
572     {
573         if( stream[i].i_cat == MMS_STREAM_UNKNOWN )
574         {
575             continue;
576         }
577         else if( stream[i].i_cat == MMS_STREAM_AUDIO && b_audio &&
578                  ( i_audio <= 0 || 
579                     ( ( ( stream[i].i_bitrate > stream[i_audio].i_bitrate && 
580                           ( i_bitrate_total + stream[i].i_bitrate - stream[i_audio].i_bitrate 
581                                             < i_bitrate_max || !i_bitrate_max) ) ||
582                         ( stream[i].i_bitrate < stream[i_audio].i_bitrate && 
583                               i_bitrate_max != 0 && i_bitrate_total > i_bitrate_max )
584                       ) )  ) )
585         {
586             /* unselect old stream */
587             if( i_audio > 0 )
588             {
589                 stream[i_audio].i_selected = 0;
590                 if( stream[i_audio].i_bitrate> 0 )
591                 {
592                     i_bitrate_total -= stream[i_audio].i_bitrate;
593                 }
594             }
595
596             stream[i].i_selected = 1;
597             if( stream[i].i_bitrate> 0 )
598             {
599                 i_bitrate_total += stream[i].i_bitrate;
600             }
601             i_audio = i;
602         }
603         else if( stream[i].i_cat == MMS_STREAM_VIDEO && b_video &&
604                  ( i_video <= 0 || 
605                     (
606                         ( ( stream[i].i_bitrate > stream[i_video].i_bitrate && 
607                             ( i_bitrate_total + stream[i].i_bitrate - stream[i_video].i_bitrate 
608                                             < i_bitrate_max || !i_bitrate_max) ) ||
609                           ( stream[i].i_bitrate < stream[i_video].i_bitrate && 
610                             i_bitrate_max != 0 && i_bitrate_total > i_bitrate_max )
611                         ) ) )  )
612         {
613             /* unselect old stream */
614
615             stream[i_video].i_selected = 0;
616             if( stream[i_video].i_bitrate> 0 )
617             {
618                 i_bitrate_total -= stream[i_video].i_bitrate;
619             }
620
621             stream[i].i_selected = 1;
622             if( stream[i].i_bitrate> 0 )
623             {
624                 i_bitrate_total += stream[i].i_bitrate;
625             }
626             i_video = i;
627         }
628
629     }
630     if( i_bitrate_max > 0 )
631     {
632         msg_Dbg( p_input, 
633                  "requested bitrate:%d real bitrate:%d", 
634                  i_bitrate_max, i_bitrate_total );
635     }
636     else
637     {
638         msg_Dbg( p_input, 
639                  "total bitrate:%d", 
640                  i_bitrate_total );
641     }
642 }
643
644 /****************************************************************************
645  * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
646  ****************************************************************************/
647 static int MMSOpen( input_thread_t  *p_input,
648                        url_t *p_url,
649                        int  i_proto,
650                        char *psz_network ) /* "", "ipv4", "ipv6" */
651 {
652     module_t    *p_network;
653     access_t    *p_access = (access_t*)p_input->p_access_data;
654
655     network_socket_t    socket_desc;
656     int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
657
658     var_buffer_t buffer;
659     char         tmp[4096];
660     uint16_t     *p;
661     int          i_server_version;
662     int          i_tool_version;
663     int          i_update_player_url;
664     int          i_encryption_type;
665     int          i;
666     int          i_streams;
667     int          i_first;
668
669
670     /* *** Open a TCP connection with server *** */
671     msg_Dbg( p_input, "waiting for connection..." );
672     socket_desc.i_type = NETWORK_TCP;
673     socket_desc.psz_server_addr = p_url->psz_server_addr;
674     socket_desc.i_server_port   = p_url->i_server_port;
675     socket_desc.psz_bind_addr   = "";
676     socket_desc.i_bind_port     = 0;
677     p_input->p_private = (void*)&socket_desc;
678     if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
679     {
680         msg_Err( p_input, "failed to open a connection (tcp)" );
681         return( -1 );
682     }
683     module_Unneed( p_input, p_network );
684     p_access->socket_tcp.i_handle = socket_desc.i_handle;
685     p_input->i_mtu    = 0; /*socket_desc.i_mtu;*/
686     msg_Dbg( p_input, 
687              "connection(tcp) with \"%s:%d\" successful",
688              p_url->psz_server_addr,
689              p_url->i_server_port );
690
691     /* *** Bind port if UDP protocol is selected *** */
692     if( b_udp )
693     {
694         msg_Err( p_input,
695                  "MMS/UDP not yet functionnal, anyway trying..." );
696         if( !p_url->psz_bind_addr || !*p_url->psz_bind_addr )
697         {
698             msg_Err( p_input, "for udp you have to provide bind address (mms://<server_addr>@<bind_addr/<path> (FIXME)" );
699 #if defined( UNDER_CE )
700             CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
701 #elif defined( WIN32 )
702             closesocket( p_access->socket_tcp.i_handle );
703 #else
704             close( p_access->socket_tcp.i_handle );
705 #endif
706             return( -1 );
707         }
708         socket_desc.i_type = NETWORK_UDP;
709         socket_desc.psz_server_addr = "";
710         socket_desc.i_server_port   = 0;
711         socket_desc.psz_bind_addr   = p_url->psz_bind_addr;
712         socket_desc.i_bind_port     = p_url->i_bind_port;
713         p_input->p_private = (void*)&socket_desc;
714         if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
715         {
716             msg_Err( p_input, "failed to open a connection (udp)" );
717 #if defined( UNDER_CE )
718             CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
719 #elif defined( WIN32 )
720             closesocket( p_access->socket_tcp.i_handle );
721 #else
722             close( p_access->socket_tcp.i_handle );
723 #endif
724             return( -1 );
725         }
726         module_Unneed( p_input, p_network );
727         p_access->socket_udp.i_handle = socket_desc.i_handle;
728         p_input->i_mtu    = 0;/*socket_desc.i_mtu;  FIXME */
729
730     }
731
732     /* *** Init context for mms prototcol *** */
733     GenerateGuid( &p_access->guid );    /* used to identify client by server */
734     msg_Dbg( p_input, 
735              "generated guid: "GUID_FMT, 
736              GUID_PRINT( p_access->guid ) );
737     p_access->i_command_level = 1;          /* updated after 0x1A command */
738     p_access->i_seq_num = 0;
739     p_access->i_media_packet_id_type  = 0x04;
740     p_access->i_header_packet_id_type = 0x02;
741     p_access->i_proto = i_proto;
742     p_access->i_packet_seq_num = 0;
743     p_access->p_header = NULL;
744     p_access->i_header = 0;
745     p_access->p_media = NULL;
746     p_access->i_media = 0;
747     p_access->i_media_used = 0;
748
749     p_access->i_pos = 0;
750     p_access->i_buffer_tcp = 0;
751     p_access->i_buffer_udp = 0;
752     p_access->p_cmd = NULL;
753     p_access->i_cmd = 0;
754
755     /* *** send command 1 : connection request *** */
756     var_buffer_initwrite( &buffer, 0 );
757     var_buffer_add16( &buffer, 0x001c );
758     var_buffer_add16( &buffer, 0x0003 );
759     sprintf( tmp, 
760              "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
761              GUID_PRINT( p_access->guid ),
762              p_url->psz_server_addr );
763     var_buffer_addUTF16( &buffer, tmp );
764     
765     mms_CommandSend( p_input,
766                      0x01,          /* connexion request */
767                      0x00000000,    /* flags, FIXME */
768                      0x0004000b,    /* ???? */
769                      buffer.p_data,
770                      buffer.i_data );
771
772     mms_CommandRead( p_input, 0x01, 0 );
773     i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
774     i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
775     i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
776     i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
777     p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
778 #define GETUTF16( psz, size ) \
779     { \
780         int i; \
781         psz = malloc( size + 1); \
782         for( i = 0; i < size; i++ ) \
783         { \
784             psz[i] = p[i]; \
785         } \
786         psz[size] = '\0'; \
787         p += 2 * ( size ); \
788     }
789     GETUTF16( p_access->psz_server_version, i_server_version );
790     GETUTF16( p_access->psz_tool_version, i_tool_version );
791     GETUTF16( p_access->psz_update_player_url, i_update_player_url );
792     GETUTF16( p_access->psz_encryption_type, i_encryption_type );
793 #undef GETUTF16
794     msg_Dbg( p_input, 
795              "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
796              p_access->psz_server_version,
797              p_access->psz_tool_version,
798              p_access->psz_update_player_url,
799              p_access->psz_encryption_type );
800
801     /* *** should make an 18 command to make data timing *** */
802     
803     /* *** send command 2 : transport protocol selection *** */
804     var_buffer_reinitwrite( &buffer, 0 );
805     var_buffer_add32( &buffer, 0x00000000 );
806     var_buffer_add32( &buffer, 0x000a0000 );
807     var_buffer_add32( &buffer, 0x00000002 );
808     if( b_udp )
809     {
810         sprintf( tmp, 
811                  "\\\\%s\\UDP\\%d",
812                  p_url->psz_bind_addr,
813                  p_url->i_bind_port );
814     }
815     else
816     {
817         sprintf( tmp, "\\\\127.0.0.1\\TCP\\1242"  );
818     }
819     var_buffer_addUTF16( &buffer, tmp );
820     var_buffer_add16( &buffer, '0' );
821     
822     mms_CommandSend( p_input,
823                      0x02,          /* connexion request */
824                      0x00000000,    /* flags, FIXME */
825                      0xffffffff,    /* ???? */
826                      buffer.p_data,
827                      buffer.i_data );
828
829     /* *** response from server, should be 0x02 or 0x03 *** */
830     mms_CommandRead( p_input, 0x02, 0x03 );
831     if( p_access->i_command == 0x03 )
832     {
833         msg_Err( p_input, 
834                  "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
835         var_buffer_free( &buffer );
836         MMSClose( p_input );
837         return( -1 );
838     }
839     else if( p_access->i_command != 0x02 )
840     {
841         msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
842     }
843     
844     /* *** send command 5 : media file name/path requested *** */
845     var_buffer_reinitwrite( &buffer, 0 );
846     var_buffer_add64( &buffer, 0 );
847     var_buffer_addUTF16( &buffer, p_url->psz_path );
848     
849     mms_CommandSend( p_input, 
850                      0x05,
851                      p_access->i_command_level,
852                      0xffffffff,
853                      buffer.p_data,
854                      buffer.i_data );
855
856     /* *** wait for reponse *** */
857     mms_CommandRead( p_input, 0x1a, 0x06 );
858     
859     /* test if server send 0x1A answer */
860     if( p_access->i_command == 0x1A )
861     {
862         msg_Err( p_input, "id/password requested (not yet supported)" );
863         /*  FIXME */
864         var_buffer_free( &buffer );
865         MMSClose( p_input );
866         return( -1 );
867     }
868     if( p_access->i_command != 0x06 )
869     {
870         msg_Err( p_input, 
871                  "unknown answer (0x%x instead of 0x06)",
872                  p_access->i_command );
873         var_buffer_free( &buffer );
874         MMSClose( p_input );
875         return( -1 );
876     }
877
878     /*  1 for file ok, 2 for authen ok */
879     switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
880     {
881         case 0x0001:
882             msg_Dbg( p_input, "Media file name/path accepted" );
883             break;
884         case 0x0002:
885             msg_Dbg( p_input, "Authentication accepted" );
886             break;
887         case -1:
888         default:
889         msg_Err( p_input, "error while asking for file %d",
890                  GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
891         var_buffer_free( &buffer );
892         MMSClose( p_input );
893         return( -1 );
894     }
895
896     p_access->i_flags_broadcast = 
897         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
898     p_access->i_media_length =
899         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
900     p_access->i_packet_length =
901         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
902     p_access->i_packet_count =
903         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
904     p_access->i_max_bit_rate =
905         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
906     p_access->i_header_size =
907         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
908
909     msg_Dbg( p_input,
910              "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
911              p_access->i_flags_broadcast,
912              p_access->i_media_length,
913              p_access->i_packet_length,
914              p_access->i_packet_count,
915              p_access->i_max_bit_rate,
916              p_access->i_header_size );
917     
918     /* XXX XXX dirty hack XXX XXX */
919     p_input->i_mtu    = 3 * p_access->i_packet_length;
920     
921     /* *** send command 15 *** */
922     
923     var_buffer_reinitwrite( &buffer, 0 );
924     var_buffer_add32( &buffer, 0 );
925     var_buffer_add32( &buffer, 0x8000 );
926     var_buffer_add32( &buffer, 0xffffffff );
927     var_buffer_add32( &buffer, 0x00 );
928     var_buffer_add32( &buffer, 0x00 );
929     var_buffer_add32( &buffer, 0x00 );
930     var_buffer_add64( &buffer, 0x40ac200000000000 );
931     var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
932     mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00, 
933                      buffer.p_data, buffer.i_data );
934
935     /* *** wait for reponse *** */
936     mms_CommandRead( p_input, 0x11, 0 );
937
938     if( p_access->i_command != 0x11 )
939     {
940         msg_Err( p_input, 
941                  "unknown answer (0x%x instead of 0x11)",
942                  p_access->i_command );
943         var_buffer_free( &buffer );
944         MMSClose( p_input );
945         return( -1 );
946     }
947     /* *** now read header packet *** */
948     if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
949     {
950         msg_Err( p_input, "cannot receive header" );
951         var_buffer_free( &buffer );
952         MMSClose( p_input );
953         return( -1 );
954     }
955     /* *** parse header and get stream and their id *** */
956     /* get all streams properties,
957      * 
958      * TODO : stream bitrates properties(optional)
959      *        and bitrate mutual exclusion(optional) */
960     asf_HeaderParse( p_access->stream, 
961                      p_access->p_header, p_access->i_header );
962     mms_StreamSelect( p_input, p_access->stream );
963     /* *** now select stream we want to receive *** */
964     /* TODO take care of stream bitrate TODO */
965     i_streams = 0;
966     i_first = -1;
967     var_buffer_reinitwrite( &buffer, 0 );
968     /* for now, select first audio and video stream */
969     for( i = 1; i < 128; i++ )
970     {
971         
972         if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
973         {
974             i_streams++;
975             if( i_first != -1 )
976             {
977                 var_buffer_add16( &buffer, 0xffff );
978                 var_buffer_add16( &buffer, i );
979             }
980             else
981             {
982                 i_first = i;
983             }
984             if( p_access->stream[i].i_selected )
985             {
986                 var_buffer_add16( &buffer, 0x0000 );
987                 msg_Info( p_input, 
988                           "selecting stream[0x%x] %s (%d kb/s)",
989                           i,
990                           ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO  ) ? 
991                                                   "audio" : "video" ,
992                           p_access->stream[i].i_bitrate / 1024);
993             }
994             else
995             {
996                 var_buffer_add16( &buffer, 0x0002 );
997                 msg_Info( p_input, 
998                           "ignoring stream[0x%x] %s (%d kb/s)",
999                           i,
1000                           ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO  ) ? 
1001                                     "audio" : "video" ,
1002                           p_access->stream[i].i_bitrate / 1024);
1003
1004             }
1005         }
1006     } 
1007
1008     if( i_streams == 0 )
1009     {
1010         msg_Err( p_input, "cannot find any stream" );
1011         var_buffer_free( &buffer );
1012         MMSClose( p_input );
1013         return( -1 );
1014     }
1015     mms_CommandSend( p_input, 0x33,
1016                      i_streams,
1017                      0xffff | ( i_first << 16 ),
1018                      buffer.p_data, buffer.i_data );
1019     
1020     mms_CommandRead( p_input, 0x21, 0 );
1021     if( p_access->i_command != 0x21 )
1022     {
1023         msg_Err( p_input, 
1024                  "unknown answer (0x%x instead of 0x21)", 
1025                  p_access->i_command );
1026         var_buffer_free( &buffer );
1027         MMSClose( p_input );
1028         return( -1 );
1029     }
1030
1031
1032     var_buffer_free( &buffer );
1033
1034     msg_Info( p_input, "connection sucessful" );
1035
1036     return( 0 );
1037 }
1038
1039 /****************************************************************************
1040  * MMSStart : Start streaming
1041  ****************************************************************************/
1042 static int MMSStart  ( input_thread_t  *p_input, uint32_t i_packet )
1043 {
1044     access_t        *p_access = (access_t*)p_input->p_access_data;
1045     var_buffer_t    buffer;
1046
1047     /* *** start stream from packet 0 *** */
1048     var_buffer_initwrite( &buffer, 0 );
1049     var_buffer_add64( &buffer, 0 ); /* seek point in second */
1050     var_buffer_add32( &buffer, 0xffffffff ); 
1051     var_buffer_add32( &buffer, i_packet ); // begin from start
1052     var_buffer_add8( &buffer, 0xff ); // stream time limit
1053     var_buffer_add8( &buffer, 0xff ); //  on 3bytes ...
1054     var_buffer_add8( &buffer, 0xff ); //
1055     var_buffer_add8( &buffer, 0x00 ); // don't use limit
1056     var_buffer_add32( &buffer, p_access->i_media_packet_id_type ); 
1057     
1058     mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff, 
1059                      buffer.p_data, buffer.i_data );
1060     
1061     var_buffer_free( &buffer );
1062     
1063     mms_CommandRead( p_input, 0x05, 0 );
1064
1065     if( p_access->i_command != 0x05 )
1066     {
1067         msg_Err( p_input, 
1068                  "unknown answer (0x%x instead of 0x05)", 
1069                  p_access->i_command );
1070         return( -1 );
1071     }
1072     else
1073     {   
1074         /* get a packet */
1075         mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
1076         msg_Dbg( p_input, "Streaming started" );
1077         return( 0 );
1078     }
1079 }
1080
1081 /****************************************************************************
1082  * MMSStop : Stop streaming
1083  ****************************************************************************/
1084 static int MMSStop  ( input_thread_t  *p_input )
1085 {
1086     access_t        *p_access = (access_t*)p_input->p_access_data;
1087
1088     /* *** stop stream but keep connection alive *** */    
1089     mms_CommandSend( p_input,
1090                      0x09,
1091                      p_access->i_command_level,
1092                      0x001fffff,
1093                      NULL, 0 );
1094     return( 0 );
1095 }
1096
1097 /****************************************************************************
1098  * MMSClose : Close streaming and connection
1099  ****************************************************************************/
1100 static int MMSClose  ( input_thread_t  *p_input )
1101 {
1102     access_t        *p_access = (access_t*)p_input->p_access_data;
1103
1104     msg_Dbg( p_input, "Connection closed" );
1105
1106     /* *** tell server that we will disconnect *** */    
1107     mms_CommandSend( p_input,
1108                      0x0d,
1109                      p_access->i_command_level,
1110                      0x00000001,
1111                      NULL, 0 );
1112     /* *** close sockets *** */
1113 #if defined( UNDER_CE )
1114     CloseHandle( (HANDLE)p_access->socket_tcp.i_handle );
1115 #elif defined( WIN32 )
1116     closesocket( p_access->socket_tcp.i_handle );
1117 #else
1118     close( p_access->socket_tcp.i_handle );
1119 #endif
1120
1121     if( p_access->i_proto == MMS_PROTO_UDP )
1122     {
1123 #if defined( UNDER_CE )
1124         CloseHandle( (HANDLE)p_access->socket_udp.i_handle );
1125 #elif defined( WIN32 )
1126         closesocket( p_access->socket_udp.i_handle );
1127 #else
1128         close( p_access->socket_udp.i_handle );
1129 #endif
1130     }
1131     
1132     FREE( p_access->p_cmd );
1133     FREE( p_access->p_media );
1134     FREE( p_access->p_header );
1135
1136     FREE( p_access->psz_server_version );
1137     FREE( p_access->psz_tool_version );
1138     FREE( p_access->psz_update_player_url );
1139     FREE( p_access->psz_encryption_type );
1140
1141     return( 0 );
1142 }
1143
1144 /*****************************************************************************
1145  * mms_ParseURL : parse an url string and fill an url_t 
1146  *****************************************************************************/
1147 static void mms_ParseURL( url_t *p_url, char *psz_url )
1148 {
1149     char *psz_parser;
1150     char *psz_server_port;
1151
1152     p_url->psz_private = strdup( psz_url );
1153     
1154     psz_parser = p_url->psz_private;
1155     
1156     while( *psz_parser == '/' )
1157     {
1158         psz_parser++;
1159     }
1160     p_url->psz_server_addr = psz_parser;
1161
1162     while( *psz_parser && 
1163            *psz_parser != ':' &&  *psz_parser != '/' && *psz_parser != '@' )
1164     {
1165         psz_parser++;
1166     } 
1167
1168     if( *psz_parser == ':' )
1169     {
1170         *psz_parser = '\0';
1171         psz_parser++;
1172         psz_server_port = psz_parser;
1173
1174         while( *psz_parser && *psz_parser != '/' )
1175         {
1176             psz_parser++;
1177         }
1178     }
1179     else
1180     {
1181         psz_server_port = "";
1182     }
1183
1184     if( *psz_parser == '@' )
1185     {
1186         char *psz_bind_port;
1187         
1188         *psz_parser = '\0';
1189         psz_parser++;
1190                         
1191         p_url->psz_bind_addr = psz_parser;
1192
1193         while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
1194         {
1195             psz_parser++;
1196         } 
1197
1198         if( *psz_parser == ':' )
1199         {
1200             *psz_parser = '\0';
1201             psz_parser++;
1202             psz_bind_port = psz_parser;
1203
1204             while( *psz_parser && *psz_parser != '/' )
1205             {
1206                 psz_parser++;
1207             }
1208         }
1209         else
1210         {
1211             psz_bind_port = "";
1212         }
1213         if( *psz_bind_port )
1214         {
1215             p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
1216         }
1217         else
1218         {
1219             p_url->i_bind_port = 0;
1220         }
1221     }
1222     else
1223     {
1224         p_url->psz_bind_addr = "";
1225         p_url->i_bind_port = 0;
1226     }
1227
1228     if( *psz_parser == '/' )
1229     {
1230         *psz_parser = '\0';
1231         psz_parser++;
1232         p_url->psz_path = psz_parser;
1233     }
1234     
1235     if( *psz_server_port )
1236     {
1237         p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
1238     }
1239     else
1240     {
1241         p_url->i_server_port = 0;
1242     }
1243 }
1244
1245 /****************************************************************************
1246  *
1247  * MMS specific functions
1248  *
1249  ****************************************************************************/
1250
1251 static int mms_CommandSend( input_thread_t *p_input, 
1252                              int i_command, 
1253                              uint32_t i_prefix1, uint32_t i_prefix2, 
1254                              uint8_t *p_data, int i_data )
1255 {
1256     var_buffer_t buffer;
1257
1258     access_t    *p_access = (access_t*)p_input->p_access_data;
1259     int i_data_by8;
1260     
1261     i_data_by8 = ( i_data + 7 ) / 8;
1262
1263     /* first init uffer */
1264     var_buffer_initwrite( &buffer, 0 );
1265
1266     var_buffer_add32( &buffer, 0x00000001 );    /* start sequence */
1267     var_buffer_add32( &buffer, 0xB00BFACE );
1268     /* size after protocol type */
1269     var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );     
1270     var_buffer_add32( &buffer, 0x20534d4d );    /* protocol "MMS " */
1271     var_buffer_add32( &buffer, i_data_by8 + 4 );
1272     var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1273     var_buffer_add64( &buffer, 0 );
1274     var_buffer_add32( &buffer, i_data_by8 + 2 );
1275     var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1276     var_buffer_add32( &buffer, i_prefix1 );    /* command specific */
1277     var_buffer_add32( &buffer, i_prefix2 );    /* command specific */
1278
1279     /* specific command data */
1280     if( p_data && i_data > 0 )
1281     {
1282         var_buffer_addmemory( &buffer, p_data, i_data );
1283     }
1284
1285     /* send it */
1286     if( send( p_access->socket_tcp.i_handle, 
1287               buffer.p_data, 
1288               buffer.i_data,
1289               0 ) == -1 )
1290     {
1291         msg_Err( p_input, "failed to send command" );
1292         return( -1 );
1293     }
1294
1295     var_buffer_free( &buffer );
1296     return( 0 );
1297 }   
1298
1299 static int  NetFillBuffer( input_thread_t *p_input )
1300 {
1301 #ifdef UNDER_CE
1302     return -1;
1303 #else
1304     access_t    *p_access = (access_t*)p_input->p_access_data;
1305     struct timeval  timeout;
1306     fd_set          fds;
1307     int             i_ret;
1308
1309     /* FIXME when using udp */
1310     ssize_t i_tcp, i_udp;
1311     ssize_t i_tcp_read, i_udp_read;
1312     int i_handle_max;
1313
1314     /* Initialize file descriptor set */
1315     FD_ZERO( &fds );
1316
1317     i_tcp = MMS_BUFFER_SIZE/2 - p_access->i_buffer_tcp;
1318     
1319     if( p_access->i_proto == MMS_PROTO_UDP )
1320     {
1321         i_udp = MMS_BUFFER_SIZE/2 - p_access->i_buffer_udp;
1322     }
1323     else
1324     {
1325         i_udp = 0;  /* there isn't udp socket */
1326     }
1327     
1328     i_handle_max = 0;
1329     if( i_tcp > 0 )
1330     {
1331         FD_SET( p_access->socket_tcp.i_handle, &fds );
1332         i_handle_max = __MAX( i_handle_max, p_access->socket_tcp.i_handle );
1333     }
1334     if( i_udp > 0 )
1335     {
1336         FD_SET( p_access->socket_udp.i_handle, &fds );
1337         i_handle_max = __MAX( i_handle_max, p_access->socket_udp.i_handle );
1338     }
1339    
1340     if( i_handle_max == 0 )
1341     {
1342         msg_Warn( p_input, "nothing to read %d:%d", i_tcp, i_udp );
1343         return( 0 );
1344     }
1345     else
1346     {
1347         msg_Warn( p_input, "ask for tcp:%d udp:%d", i_tcp, i_udp );
1348     }
1349
1350     /* We'll wait 0.5 second if nothing happens */
1351     timeout.tv_sec = 0;
1352     timeout.tv_usec = 500000;
1353
1354     /* Find if some data is available */
1355     i_ret = select( i_handle_max + 1, 
1356                     &fds,
1357                     NULL, NULL, &timeout );
1358
1359     if( i_ret == -1 && errno != EINTR )
1360     {
1361         msg_Err( p_input, "network select error (%s)", strerror(errno) );
1362         return -1;
1363     }
1364     
1365     if( i_tcp > 0 && FD_ISSET( p_access->socket_tcp.i_handle, &fds ) )
1366     {
1367         i_tcp_read = 
1368             recv( p_access->socket_tcp.i_handle, 
1369                   p_access->buffer_tcp + p_access->i_buffer_tcp, 
1370                   i_tcp + MMS_BUFFER_SIZE/2, 0 );
1371     }
1372     else
1373     {
1374         i_tcp_read = 0;
1375     }
1376     
1377     if( i_udp > 0 && FD_ISSET( p_access->socket_udp.i_handle, &fds ) )
1378     {
1379         i_udp_read = recv( p_access->socket_udp.i_handle, 
1380                            p_access->buffer_udp + p_access->i_buffer_udp, 
1381                            i_udp + MMS_BUFFER_SIZE/2, 0 );
1382     }
1383     else
1384     {
1385         i_udp_read = 0;
1386     }
1387
1388     
1389     p_access->i_buffer_tcp += i_tcp_read;
1390     p_access->i_buffer_udp += i_udp_read;
1391     
1392     msg_Dbg( p_input, 
1393              "filling TCP buffer with %d bytes (buffer:%d)", 
1394              i_tcp_read,
1395              p_access->i_buffer_tcp );
1396     if( p_access->i_proto == MMS_PROTO_UDP )
1397     {
1398         msg_Dbg( p_input, 
1399                  "filling UDP buffer with %d bytes (buffer:%d)", 
1400                  i_udp_read,
1401                  p_access->i_buffer_udp );
1402     }
1403
1404     return( i_tcp_read + i_udp_read);
1405 #endif
1406 }
1407
1408 static int  mms_ParseCommand( input_thread_t *p_input, 
1409                               uint8_t *p_data, 
1410                               int i_data,
1411                               int *pi_used )
1412 {
1413  #define GET32( i_pos ) \
1414     ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1415       ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1416       ( p_access->p_cmd[i_pos + 3] << 24 ) )
1417
1418     access_t    *p_access = (access_t*)p_input->p_access_data;
1419     int         i_length;
1420     uint32_t    i_id;
1421     
1422     if( p_access->p_cmd )
1423     {
1424         free( p_access->p_cmd );
1425     }
1426     p_access->i_cmd = i_data;
1427     p_access->p_cmd = malloc( i_data );
1428     memcpy( p_access->p_cmd, p_data, i_data );
1429     
1430     *pi_used = i_data; /* by default */
1431
1432     if( i_data < MMS_CMD_HEADERSIZE )
1433     {
1434         msg_Warn( p_input, "truncated command (header incomplete)" );
1435         p_access->i_command = 0;
1436         return( -1 );
1437     }
1438     i_id =  GetDWLE( p_data + 4 );
1439     i_length = GetDWLE( p_data + 8 ) + 16;
1440     
1441     if( i_id != 0xb00bface )
1442     {
1443         msg_Err( p_input, 
1444                  "incorrect command header (0x%x)", i_id );
1445         p_access->i_command = 0;
1446         return( -1 );
1447     }
1448    
1449     if( i_length > p_access->i_cmd )
1450     {
1451         msg_Warn( p_input, 
1452                   "truncated command (missing %d bytes)",
1453                    i_length - i_data  );
1454         p_access->i_command = 0;
1455         return( -1 );
1456     }
1457     else if( i_length < p_access->i_cmd )
1458     {
1459         p_access->i_cmd = i_length;
1460         *pi_used = i_length;
1461     }
1462
1463     msg_Dbg( p_input, 
1464              "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",
1465              GET32( 0 ),
1466              GET32( 4 ),
1467              GET32( 8 ),
1468              GET32( 16 ),
1469              GET32( 20 ),
1470              GET32( 32 ),
1471              GET32( 36 ),
1472              GET32( 40 ) );
1473
1474     p_access->i_command = GET32( 36 ) & 0xffff;
1475
1476     return( MMS_PACKET_CMD );
1477 }
1478
1479 static int  mms_ParsePacket( input_thread_t *p_input, 
1480                              uint8_t *p_data, int i_data,
1481                               int *pi_used )
1482 {
1483     access_t    *p_access = (access_t*)p_input->p_access_data;
1484     int i_packet_seq_num;
1485     int i_packet_length;
1486     int i_packet_id;
1487
1488     uint8_t  *p_packet;
1489
1490
1491     *pi_used = i_data; /* default */
1492     if( i_data <= 8 )
1493     {
1494         msg_Warn( p_input, "truncated packet (header incomplete)" );
1495         return( -1 );
1496     }
1497     
1498     i_packet_id = p_data[4];
1499     i_packet_seq_num = GetDWLE( p_data );
1500     i_packet_length = GetWLE( p_data + 6 );
1501    
1502
1503     if( i_packet_length > i_data || i_packet_length <= 8)
1504     {
1505         msg_Warn( p_input, 
1506                   "truncated packet (missing %d bytes)",
1507                   i_packet_length - i_data  );
1508         return( -1 );
1509     }
1510     else if( i_packet_length < i_data )
1511     {
1512         *pi_used = i_packet_length;
1513     }
1514
1515     if( i_packet_id == 0xff )
1516     {
1517         msg_Warn( p_input,
1518                   "receive MMS UDP pair timing" );
1519         return( MMS_PACKET_UDP_TIMING );
1520     }
1521     
1522     if( i_packet_id != p_access->i_header_packet_id_type &&
1523         i_packet_id != p_access->i_media_packet_id_type )
1524     {
1525         msg_Warn( p_input, "incorrect Packet Id Type (0x%x)", i_packet_id );
1526         return( -1 );
1527     }
1528
1529     /* we now have a media or a header packet */
1530     p_packet = malloc( i_packet_length - 8 ); // don't bother with preheader
1531     memcpy( p_packet, p_data + 8, i_packet_length - 8 );
1532     
1533     if( i_packet_seq_num != p_access->i_packet_seq_num )
1534     {
1535         /* FIXME for udp could be just wrong order ? */
1536         msg_Warn( p_input, 
1537                   "detected packet lost (%d != %d)",
1538                   i_packet_seq_num,
1539                   p_access->i_packet_seq_num );
1540         p_access->i_packet_seq_num = i_packet_seq_num;
1541     }
1542     p_access->i_packet_seq_num++;
1543
1544     if( i_packet_id == p_access->i_header_packet_id_type )
1545     {
1546         FREE( p_access->p_header );
1547         p_access->p_header = p_packet;
1548         p_access->i_header = i_packet_length - 8;
1549         msg_Dbg( p_input, 
1550                  "receive header packet (%d bytes)", 
1551                  i_packet_length - 8 );
1552
1553         return( MMS_PACKET_HEADER );
1554     }
1555     else
1556     {
1557         FREE( p_access->p_media );
1558         p_access->p_media = p_packet;
1559         p_access->i_media = i_packet_length - 8;
1560         p_access->i_media_used = 0;
1561         msg_Dbg( p_input, 
1562                  "receive media packet (%d bytes)",
1563                  i_packet_length - 8 );
1564         
1565         return( MMS_PACKET_MEDIA );
1566     }
1567 }
1568
1569 static int mms_ReceivePacket( input_thread_t *p_input )
1570 {
1571     access_t    *p_access = (access_t*)p_input->p_access_data;
1572     int i_packet_tcp_type;
1573     int i_packet_udp_type;
1574
1575     for( ;; )
1576     {
1577         if( NetFillBuffer( p_input ) < 0 )
1578         {
1579             msg_Warn( p_input, "cannot fill buffer" );
1580             continue;
1581         }
1582         /* TODO udp */
1583         
1584         i_packet_tcp_type = -1;
1585         i_packet_udp_type = -1;
1586         
1587         if( p_access->i_buffer_tcp > 0 )
1588         {
1589             int i_used;
1590             
1591             if( GetDWLE( p_access->buffer_tcp + 4 ) == 0xb00bface )
1592             {
1593                 i_packet_tcp_type = 
1594                     mms_ParseCommand( p_input, 
1595                                       p_access->buffer_tcp,
1596                                       p_access->i_buffer_tcp,
1597                                       &i_used );
1598
1599             }
1600             else
1601             {
1602                 i_packet_tcp_type = 
1603                     mms_ParsePacket( p_input,
1604                                      p_access->buffer_tcp,
1605                                      p_access->i_buffer_tcp,
1606                                      &i_used );
1607             }
1608             if( i_used < MMS_BUFFER_SIZE )
1609             {
1610                 memmove( p_access->buffer_tcp,
1611                          p_access->buffer_tcp + i_used,
1612                          MMS_BUFFER_SIZE - i_used );
1613             }
1614             p_access->i_buffer_tcp -= i_used;
1615         }
1616         else if( p_access->i_buffer_udp > 0 )
1617         {
1618             int i_used;
1619 #if 0
1620             if( GetDWLE( p_access->buffer_tcp + 4 ) == 0xb00bface )
1621             {
1622                 i_packet_tcp_type = 
1623                     mms_ParseCommand( p_input, 
1624                                       p_access->buffer_tcp,
1625                                       p_access->i_buffer_tcp,
1626                                       &i_used );
1627
1628             }
1629             else
1630 #endif
1631             {
1632                 i_packet_tcp_type = 
1633                     mms_ParsePacket( p_input,
1634                                      p_access->buffer_udp,
1635                                      p_access->i_buffer_udp,
1636                                      &i_used );
1637             }
1638             if( i_used < MMS_BUFFER_SIZE )
1639             {
1640                 memmove( p_access->buffer_udp,
1641                          p_access->buffer_udp + i_used,
1642                          MMS_BUFFER_SIZE - i_used );
1643             }
1644             p_access->i_buffer_udp -= i_used;
1645         }
1646         else
1647         {
1648             i_packet_udp_type = -1;
1649         }
1650
1651         if( i_packet_tcp_type == MMS_PACKET_CMD && 
1652                 p_access->i_command == 0x1b )
1653         {
1654             mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1655             i_packet_tcp_type = -1;
1656         }
1657
1658         if( i_packet_tcp_type != -1 )
1659         {
1660             return( i_packet_tcp_type );
1661         } 
1662         else if( i_packet_udp_type != -1 )
1663         {
1664             return( i_packet_udp_type );
1665         }   
1666
1667     }
1668 }
1669
1670 static int  mms_ReceiveCommand( input_thread_t *p_input )
1671 {
1672     access_t    *p_access = (access_t*)p_input->p_access_data;
1673
1674     for( ;; )
1675     {
1676         int i_used;
1677         int i_status;
1678         NetFillBuffer( p_input );
1679
1680         i_status = mms_ParseCommand( p_input, 
1681                               p_access->buffer_tcp, 
1682                               p_access->i_buffer_tcp,
1683                               &i_used );
1684         if( i_used < MMS_BUFFER_SIZE )
1685         {
1686             memmove( p_access->buffer_tcp,
1687                      p_access->buffer_tcp + i_used,
1688                      MMS_BUFFER_SIZE - i_used );
1689         }
1690         p_access->i_buffer_tcp -= i_used;
1691
1692         if( i_status < 0 )
1693         {
1694             return( -1 );
1695         }
1696         
1697         if( p_access->i_command == 0x1b )
1698         {
1699             mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1700         }
1701         else
1702         {
1703             break;
1704         }
1705     }
1706
1707     return( 0 );
1708 }
1709
1710 #define MMS_RETRY_MAX       10
1711 #define MMS_RETRY_SLEEP     50000
1712
1713 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1714 {
1715     access_t    *p_access = (access_t*)p_input->p_access_data;
1716     int i_count;
1717     int i_status;
1718
1719     for( i_count = 0; i_count < MMS_RETRY_MAX; )
1720     {
1721
1722         i_status = mms_ReceiveCommand( p_input );
1723         if( i_status < 0 || p_access->i_command == 0 )
1724         {
1725             i_count++;
1726             msleep( MMS_RETRY_SLEEP );
1727         }
1728         else if( i_command1 == 0 && i_command2 == 0)
1729         {
1730             return( 0 );
1731         }
1732         else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1733         {
1734             return( 0 );   
1735         }
1736         else
1737         {
1738             switch( p_access->i_command )
1739             {
1740                 case 0x03:
1741                     msg_Warn( p_input, "socket closed by server" );
1742                     return( -1 );
1743                 case 0x1e:
1744                     msg_Warn( p_input, "end of media stream" );
1745                     return( -1 );
1746                 default:
1747                     break;
1748             }
1749         }
1750     }
1751     msg_Warn( p_input, "failed to receive command (abording)" );
1752
1753     return( -1 );
1754 }
1755
1756
1757 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1758 {
1759     access_t    *p_access = (access_t*)p_input->p_access_data;
1760     int         i_count;
1761     
1762     for( i_count = 0; i_count < MMS_RETRY_MAX; )
1763     {
1764         int i_status;
1765
1766         i_status = mms_ReceivePacket( p_input );
1767         if( i_status < 0 )
1768         {
1769             i_count++;
1770             msg_Warn( p_input, 
1771                       "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1772             msleep( MMS_RETRY_SLEEP );
1773         }
1774         else if( i_status == i_type || i_type == MMS_PACKET_ANY )
1775         {
1776             return( 0 );
1777         }
1778         else if( i_status == MMS_PACKET_CMD )
1779         {
1780             switch( p_access->i_command )
1781             {
1782                 case 0x03:
1783                     msg_Warn( p_input, "socket closed by server" );
1784                     return( -1 );
1785                 case 0x1e:
1786                     msg_Warn( p_input, "end of media stream" );
1787                     return( -1 );
1788                 case 0x20:
1789                     /* XXX not too dificult to be done EXCEPT that we 
1790                      * need to restart demuxer... and I don't see how we 
1791                      * could do that :p */ 
1792                     msg_Err( p_input, 
1793                              "reinitialization needed --> unsupported" );
1794                     return( -1 );
1795                 default:
1796                     break;
1797             }
1798         }
1799     }
1800     msg_Err( p_input, 
1801              "cannot receive %s (abording)",
1802                ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );
1803     return( -1 );
1804 }
1805