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