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