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