]> git.sesse.net Git - vlc/blob - modules/access/mms/mms.c
* ./Makefile.am: fixed rc compilation under mingw32/cygwin.
[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.2 2002/11/12 13:57:12 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 #endif
60
61 #include "network.h"
62 #include "asf.h"
63 #include "var_buffer.h"
64 #include "mms.h"
65 /****************************************************************************
66  * NOTES: 
67  *  MMSProtocole documentation found at http://get.to/sdp
68  ****************************************************************************/
69
70 /*****************************************************************************
71  * Local prototypes
72  *****************************************************************************/
73 static int  Open        ( vlc_object_t * );
74 static void Close       ( vlc_object_t * );
75
76 static int  Read        ( input_thread_t * p_input, byte_t * p_buffer,
77                           size_t i_len );
78 static ssize_t NetRead  ( input_thread_t * p_input, input_socket_t * p_socket,
79                           byte_t * p_buffer, size_t i_len );
80 static void Seek        ( input_thread_t *, off_t );
81 static int  SetProgram  ( input_thread_t *, pgrm_descriptor_t * );  
82
83
84 static int MMSOpen( input_thread_t  *, url_t *, int, char * );
85
86 static int MMSStart  ( input_thread_t  *, uint32_t );
87 static int MMSStop  ( input_thread_t  *p_input );   // Not used
88
89 static int MMSClose  ( input_thread_t  * );
90
91
92 static int  mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 );
93 static int  mms_CommandSend( input_thread_t *, int, uint32_t, uint32_t, uint8_t *, int );
94
95 static int  mms_HeaderMediaRead( input_thread_t *, int );
96
97 static int  mms_ReceivePacket( input_thread_t * );
98     
99 static void mms_ParseURL( url_t *p_url, char *psz_url );
100
101
102
103 /* 
104  * XXX DON'T FREE MY MEMORY !!! XXX
105  * non mais :P
106  */
107
108 /* 
109  * Ok, ok, j'le ferai plus...
110  */
111
112
113 /*****************************************************************************
114  * Module descriptor
115  *****************************************************************************/
116 vlc_module_begin();
117     set_description( _("MMS access module") );
118     set_capability( "access", 0 );
119     add_shortcut( "mms" );
120     add_shortcut( "mmsu" );
121     add_shortcut( "mmst" );
122     set_callbacks( Open, Close );
123 vlc_module_end();
124
125 #define BUF_SIZE 200000
126
127 static int Open( vlc_object_t *p_this )
128 {
129     access_t    *p_access;
130     int         i_proto;
131     char        *psz_network;
132     int         i_status;
133
134     input_thread_t  *p_input = (input_thread_t*)p_this;
135
136     /* *** allocate p_access_data *** */
137     p_input->p_access_data = 
138         (void*)p_access = malloc( sizeof( access_t ) );
139     memset( p_access, 0, sizeof( access_t ) );
140
141     p_access->p_cmd = malloc( BUF_SIZE );
142
143
144     /* *** Parse URL and get server addr/port and path *** */
145     mms_ParseURL( &p_access->url, p_input->psz_name );
146     if( p_access->url.psz_server_addr == NULL || 
147         !( *p_access->url.psz_server_addr ) )
148     {
149         FREE( p_access->url.psz_private );
150         FREE( p_access->p_cmd );
151         msg_Err( p_input, "invalid server name" );
152         return( -1 );
153     }
154     if( p_access->url.i_server_port == 0 )
155     {   
156         p_access->url.i_server_port = 1755; // default port
157     }
158     
159
160     /* *** connect to this server *** */
161     /* 1: look at  requested protocol (udp/tcp) */
162     i_proto = MMS_PROTO_AUTO;
163     if( *p_input->psz_access )
164     {
165         if( !strncmp( p_input->psz_access, "mmsu", 4 ) )
166         {
167             i_proto = MMS_PROTO_UDP;
168         }
169         else if( !strncmp( p_input->psz_access, "mmst", 4 ) )
170         {
171             i_proto = MMS_PROTO_TCP;
172         }
173     }
174     /* 2: look at ip version ipv4/ipv6 */
175     psz_network = "";
176     if( config_GetInt( p_input, "ipv4" ) )
177     {
178         psz_network = "ipv4";
179     }
180     else if( config_GetInt( p_input, "ipv6" ) )
181     {
182         psz_network = "ipv6";
183     }
184     /* 3: connect */
185     if( i_proto == MMS_PROTO_AUTO )
186     {   // first try with TCP
187         i_status = 
188             MMSOpen( p_input, &p_access->url, MMS_PROTO_TCP, psz_network );
189         if( i_status < 0 )
190         {   // then with UDP
191             i_status = 
192              MMSOpen( p_input, &p_access->url, MMS_PROTO_UDP, psz_network );
193         }
194     }
195     else
196     {
197
198         i_status = 
199             MMSOpen( p_input, &p_access->url, i_proto, psz_network );
200     }
201
202     if( i_status < 0 )
203     {
204         // all sockets are closed
205         msg_Err( p_input, "cannot connect to server" );
206         FREE( p_access->url.psz_private );
207         FREE( p_access->p_cmd );
208         return( -1 );
209     }
210     msg_Dbg( p_input, "connected to %s", p_access->url.psz_server_addr );
211
212     // all sockets are open
213     
214     
215     /* *** set exported functions *** */
216     p_input->pf_read = Read;
217     p_input->pf_seek = Seek;
218     p_input->pf_set_program = SetProgram;
219     p_input->pf_set_area = NULL;
220     
221     p_input->p_private = NULL; // XXX ??
222
223     /* *** finished to set some variable *** */
224     vlc_mutex_lock( &p_input->stream.stream_lock );
225     /* those data could be different for UDP/TCP */
226     p_input->stream.b_pace_control = 0;
227     p_input->stream.p_selected_area->i_tell = 0;
228     if( p_access->i_packet_count <= 0 )
229     {
230         p_input->stream.b_seekable = 0;
231         p_input->stream.p_selected_area->i_size = 0;
232     }
233     else
234     {
235         p_input->stream.b_seekable = 0;
236         p_input->stream.p_selected_area->i_size = 
237             p_access->i_header + 
238             p_access->i_packet_count * p_access->i_packet_length;
239     }
240     
241     p_input->stream.i_method = INPUT_METHOD_NETWORK;
242     vlc_mutex_unlock( &p_input->stream.stream_lock );
243
244     /* *** Start stream *** */
245     if( MMSStart( p_input, 0xffffffff ) < 0 )
246     {
247         msg_Err( p_input, "cannot start stream" );
248         MMSClose( p_input );
249         FREE( p_access->url.psz_private );
250         FREE( p_access->p_cmd );
251         return( -1 );
252     }
253                            
254     return( 0 );
255 }
256
257 /*****************************************************************************
258  * Close: free unused data structures
259  *****************************************************************************/
260 static void Close( vlc_object_t *p_this )
261 {
262     input_thread_t *  p_input = (input_thread_t *)p_this;
263     access_t    *p_access = (access_t*)p_input->p_access_data;
264     
265     /* close connection with server */
266     MMSClose( p_input );
267     
268     /* free memory */
269     FREE( p_access->url.psz_private );
270     FREE( p_access->p_cmd );
271 }
272
273 /*****************************************************************************
274  * SetProgram: do nothing
275  *****************************************************************************/
276 static int SetProgram( input_thread_t * p_input,
277                        pgrm_descriptor_t * p_program )
278 {
279     return( 0 );
280 }
281
282 /*****************************************************************************
283  * Seek: try to go at the right place
284  *****************************************************************************/
285 static void Seek( input_thread_t * p_input, off_t i_pos )
286 {
287     /* 
288      * FIXME  
289      * Don't work
290      * Probably some bad or missing command
291      *
292      * 
293      */
294 #if 0
295
296     access_t    *p_access = (access_t*)p_input->p_access_data;
297     uint32_t    i_packet;
298     uint32_t    i_offset;
299     
300     if( i_pos < 0 )
301     {
302         return;
303     }    
304     msg_Dbg( p_input, "seeking to %lld, header size:%d", i_pos, p_access->i_header );
305     if( i_pos < p_access->i_header)
306     {
307
308         if( p_access->i_pos < p_access->i_header )
309         {
310             /* no need to restart stream, it was already one 
311              * or no stream was yet read */
312             p_access->i_pos = i_pos;
313             return;
314         }
315         else
316         {
317             i_packet = 0xffffffff;
318             i_offset = 0;
319         }
320     }
321     else
322     {
323         i_packet = ( i_pos - p_access->i_header ) / p_access->i_packet_length;
324         i_offset = ( i_pos - p_access->i_header ) % p_access->i_packet_length;
325     }
326
327     MMSStop( p_input );
328     MMSStart( p_input, i_packet );
329     p_access->i_media_used += i_offset;
330     p_access->i_pos = i_pos;
331 #endif
332 }
333
334 static int  Read        ( input_thread_t * p_input, byte_t * p_buffer,
335                           size_t i_len )
336 {
337     access_t    *p_access = (access_t*)p_input->p_access_data;
338     size_t      i_data;
339     size_t      i_copy;
340
341     i_data = 0;
342     
343     /* *** send header if needed ** */
344     if( p_access->i_pos < p_access->i_header )
345     { 
346         i_copy = __MIN( i_len, p_access->i_header - p_access->i_pos );
347         if( i_copy > 0 )
348         {
349             memcpy( p_buffer, 
350                     p_access->p_header + p_access->i_pos,
351                     i_copy );
352         }
353         i_data += i_copy;
354     }
355
356     /* *** now send data if needed *** */
357     while( i_data < i_len )
358     {
359         if( p_access->i_media_used < p_access->i_media )
360         {
361             i_copy = __MIN( i_len - i_data , 
362                             p_access->i_media - p_access->i_media_used );
363             memcpy( p_buffer + i_data, 
364                     p_access->p_media + p_access->i_media_used,
365                     i_copy );
366             i_data += i_copy;
367             p_access->i_media_used += i_copy;
368         }
369         else if( p_access->p_media != NULL && 
370                  p_access->i_media_used < p_access->i_packet_length )
371         {
372             i_copy = __MIN( i_len - i_data,
373                             p_access->i_packet_length - p_access->i_media_used);
374             memset( p_buffer + i_data, 0, i_copy );
375
376             i_data += i_copy;
377             p_access->i_media_used += i_copy;
378         }
379         else
380         {
381             if( mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA ) < 0 );
382             {
383                 p_access->i_pos += i_data;
384                 return( i_data );
385             }
386         }
387     }
388
389     p_access->i_pos += i_data;
390     return( i_data );
391 }
392
393 /*****************************************************************************
394  * NetRead: read on a file descriptor, checking b_die periodically
395  *****************************************************************************/
396 static ssize_t NetRead( input_thread_t * p_input, input_socket_t * p_socket,
397                         byte_t * p_buffer, size_t i_len )
398 {
399 #ifdef UNDER_CE
400     return -1;
401
402 #else
403     struct timeval  timeout;
404     fd_set          fds;
405     int             i_ret;
406
407     /* Initialize file descriptor set */
408     FD_ZERO( &fds );
409     FD_SET( p_socket->i_handle, &fds );
410
411     /* We'll wait 0.5 second if nothing happens */
412     timeout.tv_sec = 0;
413     timeout.tv_usec = 500000;
414
415     /* Find if some data is available */
416     i_ret = select( p_socket->i_handle + 1, &fds,
417                     NULL, NULL, &timeout );
418
419     if( i_ret == -1 && errno != EINTR )
420     {
421         msg_Err( p_input, "network select error (%s)", strerror(errno) );
422     }
423     else if( i_ret > 0 )
424     {
425         ssize_t i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
426
427         if( i_recv > 0 )
428         {
429             vlc_mutex_lock( &p_input->stream.stream_lock );
430             p_input->stream.p_selected_area->i_tell += i_recv;
431             vlc_mutex_unlock( &p_input->stream.stream_lock );
432         }
433
434         if( i_recv < 0 )
435         {
436             msg_Err( p_input, "recv failed (%s)", strerror(errno) );
437         }
438
439         return i_recv;
440     }
441
442     return 0;
443
444 #endif
445 }
446
447 static void asf_HeaderParse( mms_stream_t stream[128],
448                              uint8_t *p_header, int i_header )
449 {
450     var_buffer_t buffer;
451     guid_t      guid;
452     uint64_t    i_size;
453     int         i;
454     
455     for( i = 0; i < 128; i++ )
456     {
457         stream[i].i_cat = MMS_STREAM_UNKNOWN;
458     }
459
460     var_buffer_initread( &buffer, p_header, i_header );
461     
462     var_buffer_getguid( &buffer, &guid );
463     if( !CmpGuid( &guid, &asf_object_header_guid ) )
464     {
465 //        XXX Error
466     }
467     var_buffer_getmemory( &buffer, NULL, 30 - 16 );
468     
469     for( ;; )
470     {
471         if( var_buffer_readempty( &buffer ) )
472         {
473             return;
474         }
475
476         var_buffer_getguid( &buffer, &guid );
477         i_size = var_buffer_get64( &buffer );
478         if( CmpGuid( &guid, &asf_object_stream_properties_guid ) )
479         {
480             int     i_stream_id;
481             guid_t  stream_type;
482
483 //            msg_Dbg( p_input, "found stream_properties" );
484             
485             var_buffer_getguid( &buffer, &stream_type );
486             var_buffer_getmemory( &buffer, NULL, 32 );
487             i_stream_id = var_buffer_get8( &buffer ) & 0x7f;
488             var_buffer_getmemory( &buffer, NULL, i_size - 24 - 32 - 16 - 1 );
489             
490             if( CmpGuid( &stream_type, &asf_object_stream_type_video ) )
491             {
492 //                msg_Dbg( p_input, "video stream[%d] found", i_stream_id );
493                 stream[i_stream_id].i_cat = MMS_STREAM_VIDEO;
494             }
495             else if( CmpGuid( &stream_type, &asf_object_stream_type_audio ) )
496             {
497 //                msg_Dbg( p_input, "audio stream[%d] found", i_stream_id );
498                 stream[i_stream_id].i_cat = MMS_STREAM_AUDIO;
499             }
500             else
501             {
502 //                msg_Dbg( p_input, "unknown stream[%d] found", i_stream_id );
503                 stream[i_stream_id].i_cat = MMS_STREAM_UNKNOWN;
504             }
505         }
506         else if ( CmpGuid( &guid, &asf_object_bitrate_properties_guid ) )
507         {
508             int     i_count;
509             uint8_t i_stream_id;
510             
511             i_count = var_buffer_get16( &buffer );
512             i_size -= 2;
513             while( i_count > 0 )
514             {
515                 i_stream_id = var_buffer_get16( &buffer )&0x7f;
516                 stream[i_stream_id].i_bitrate =  var_buffer_get32( &buffer );
517                 i_count--;
518                 i_size -= 6;
519             }
520             var_buffer_getmemory( &buffer, NULL, i_size - 24 );
521         }
522         else            
523         {
524             // skip unknown guid
525             var_buffer_getmemory( &buffer, NULL, i_size - 24 );
526         }
527     }
528 }
529
530 /****************************************************************************
531  * MMSOpen : Open a connection with the server over mmst or mmsu(not yet)
532  ****************************************************************************/
533 static int MMSOpen( input_thread_t  *p_input,
534                        url_t *p_url,
535                        int  i_proto,
536                        char *psz_network ) /* "", "ipv4", "ipv6" */
537 {
538     module_t    *p_network;
539     access_t    *p_access = (access_t*)p_input->p_access_data;
540
541     network_socket_t    socket_desc;
542     int b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;
543
544     var_buffer_t buffer;
545     char         tmp[4096];
546     uint16_t     *p;
547     int          i_server_version;
548     int          i_tool_version;
549     int          i_update_player_url;
550     int          i_encryption_type;
551     int          i;
552     int          i_streams;
553     int          i_first;
554     int          b_audio;
555     int          b_video;
556
557
558     /* *** Open a TCP connection with server *** */
559     msg_Dbg( p_input, "waiting for connection..." );
560     socket_desc.i_type = NETWORK_TCP;
561     socket_desc.psz_server_addr = p_url->psz_server_addr;
562     socket_desc.i_server_port   = p_url->i_server_port;
563     socket_desc.psz_bind_addr   = "";
564     socket_desc.i_bind_port     = 0;
565     p_input->p_private = (void*)&socket_desc;
566     if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
567     {
568         msg_Err( p_input, "failed to open a connection" );
569         return( -1 );
570     }
571     module_Unneed( p_input, p_network );
572     p_access->socket_server.i_handle = socket_desc.i_handle;
573     p_input->i_mtu    = socket_desc.i_mtu;  // FIXME
574     msg_Dbg( p_input, 
575              "connection with \"%s:%d\" successful",
576              p_url->psz_server_addr,
577              p_url->i_server_port );
578
579     /* *** Bind port if UDP protocol is selected *** */
580     // TODO
581     if( b_udp )
582     {
583         msg_Err( p_input,
584                  "MMS/UDP not yet implemented" );
585         // close socket
586 #if defined( UNDER_CE )
587         CloseHandle( (HANDLE)p_access->socket_server.i_handle );
588 #elif defined( WIN32 )
589         closesocket( p_access->socket_server.i_handle );
590 #else
591         close( p_access->socket_server.i_handle );
592 #endif
593         return( -1 );
594     }
595
596     /* *** Init context for mms prototcol *** */
597     GenerateGuid( &p_access->guid );    // used to identify client by server
598     msg_Dbg( p_input, 
599              "generated guid: "GUID_FMT, 
600              GUID_PRINT( p_access->guid ) );
601     p_access->i_command_level = 1;          // updated after 0x1A command 
602     p_access->i_seq_num = 0;
603     p_access->i_media_packet_id_type  = 0x04;
604     p_access->i_header_packet_id_type = 0x02;
605     p_access->i_proto = i_proto;
606     p_access->i_packet_seq_num = 0;
607     p_access->p_header = NULL;
608     p_access->i_header = 0;
609     p_access->p_media = NULL;
610     p_access->i_media = 0;
611     p_access->i_media_used = 0;
612
613     p_access->i_pos = 0;
614
615     /* *** send command 1 : connection request *** */
616     var_buffer_initwrite( &buffer, 0 );
617     var_buffer_add16( &buffer, 0x001c );
618     var_buffer_add16( &buffer, 0x0003 );
619     sprintf( tmp, 
620              "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
621              GUID_PRINT( p_access->guid ),
622              p_url->psz_server_addr );
623     var_buffer_addUTF16( &buffer, tmp );
624     
625     mms_CommandSend( p_input,
626                      0x01,          /* connexion request */
627                      0x00000000,    /* flags, FIXME */
628                      0x0004000b,    /* ???? */
629                      buffer.p_data,
630                      buffer.i_data );
631
632     mms_CommandRead( p_input, 0x01, 0 );
633     i_server_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 32 );
634     i_tool_version = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 36 );
635     i_update_player_url = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 40 );
636     i_encryption_type = GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
637     p = (uint16_t*)( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
638 #define GETUTF16( psz, size ) \
639     { \
640         int i; \
641         psz = malloc( size + 1); \
642         for( i = 0; i < size; i++ ) \
643         { \
644             psz[i] = p[i]; \
645         } \
646         psz[size] = '\0'; \
647         p += 2 * ( size ); \
648     }
649     GETUTF16( p_access->psz_server_version, i_server_version );
650     GETUTF16( p_access->psz_tool_version, i_tool_version );
651     GETUTF16( p_access->psz_update_player_url, i_update_player_url );
652     GETUTF16( p_access->psz_encryption_type, i_encryption_type );
653 #undef GETUTF16
654     msg_Dbg( p_input, 
655              "0x01 --> server_version:\"%s\" tool_version:\"%s\" update_player_url:\"%s\" encryption_type:\"%s\"",
656              p_access->psz_server_version,
657              p_access->psz_tool_version,
658              p_access->psz_update_player_url,
659              p_access->psz_encryption_type );
660
661     /* *** should make an 18 command to make data timing *** */
662     
663     /* *** send command 2 : transport protocol selection *** */
664     var_buffer_reinitwrite( &buffer, 0 );
665     var_buffer_add32( &buffer, 0x00000000 );
666     var_buffer_add32( &buffer, 0x000a0000 );
667     var_buffer_add32( &buffer, 0x00000002 );
668     // FIXME wrong for UDP FIXME
669     sprintf( tmp, "\\\\127.0.0.1\\%s\\1242", b_udp ? "UDP" : "TCP" );
670     var_buffer_addUTF16( &buffer, tmp );
671     var_buffer_add16( &buffer, '0' );
672     
673     mms_CommandSend( p_input,
674                      0x02,          /* connexion request */
675                      0x00000000,    /* flags, FIXME */
676                      0xffffffff,    /* ???? */
677                      buffer.p_data,
678                      buffer.i_data );
679
680     /* *** response from server, should be 0x02 or 0x03 *** */
681     mms_CommandRead( p_input, 0x02, 0 );
682     if( p_access->i_command == 0x03 )
683     {
684         msg_Err( p_input, 
685                  "%s protocol selection failed", b_udp ? "UDP" : "TCP" );
686         var_buffer_free( &buffer );
687         MMSClose( p_input );
688         return( -1 );
689     }
690     else if( p_access->i_command != 0x02 )
691     {
692         msg_Warn( p_input, "received command isn't 0x02 in reponse to 0x02" );
693     }
694     
695     /* *** send command 5 : media file name/path requested *** */
696     var_buffer_reinitwrite( &buffer, 0 );
697     var_buffer_add64( &buffer, 0 );
698 //    var_buffer_addUTF16( &buffer, "/" );
699     var_buffer_addUTF16( &buffer, p_url->psz_path );
700     
701     mms_CommandSend( p_input, 
702                      0x05,
703                      p_access->i_command_level,
704                      0xffffffff,
705                      buffer.p_data,
706                      buffer.i_data );
707
708     /* *** wait for reponse *** */
709     mms_CommandRead( p_input, 0x1a, 0x06 );
710     
711     /* test if server send 0x1A answer */
712     if( p_access->i_command == 0x1A )
713     {
714         msg_Err( p_input, "id/password requested (not yet supported)" );
715         // FIXME
716         var_buffer_free( &buffer );
717         MMSClose( p_input );
718         return( -1 );
719     }
720     if( p_access->i_command != 0x06 )
721     {
722         msg_Err( p_input, 
723                  "unknown answer (0x%x instead of 0x06)",
724                  p_access->i_command );
725         var_buffer_free( &buffer );
726         MMSClose( p_input );
727         return( -1 );
728     }
729
730     // 1 for file ok, 2 for authen ok
731     switch( GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) )
732     {
733         case 0x0001:
734             msg_Dbg( p_input, "Media file name/path accepted" );
735             break;
736         case 0x0002:
737             msg_Dbg( p_input, "Authentication accepted" );
738             break;
739         case -1:
740         default:
741         msg_Err( p_input, "error while asking for file %d",
742                  GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE ) );
743         var_buffer_free( &buffer );
744         MMSClose( p_input );
745         return( -1 );
746     }
747
748     p_access->i_flags_broadcast = 
749         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 12 );
750     p_access->i_media_length =
751         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 24 );
752     p_access->i_packet_length =
753         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 44 );
754     p_access->i_packet_count =
755         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 48 );
756     p_access->i_max_bit_rate =
757         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 56 );
758     p_access->i_header_size =
759         GetDWLE( p_access->p_cmd + MMS_CMD_HEADERSIZE + 60 );
760
761     msg_Dbg( p_input,
762              "answer 0x06 flags:0x%8.8x media_length:%ds packet_length:%d packet_count:%d max_bit_rate:%d header_size:%d",
763              p_access->i_flags_broadcast,
764              p_access->i_media_length,
765              p_access->i_packet_length,
766              p_access->i_packet_count,
767              p_access->i_max_bit_rate,
768              p_access->i_header_size );
769     
770     /* *** send command 15 *** */
771     
772     var_buffer_reinitwrite( &buffer, 0 );
773     var_buffer_add32( &buffer, 0 );
774     var_buffer_add32( &buffer, 0x8000 );
775     var_buffer_add32( &buffer, 0xffffffff );
776     var_buffer_add32( &buffer, 0x00 );
777     var_buffer_add32( &buffer, 0x00 );
778     var_buffer_add32( &buffer, 0x00 );
779     var_buffer_add64( &buffer, 0x40ac200000000000 );
780     var_buffer_add32( &buffer, p_access->i_header_packet_id_type );
781     mms_CommandSend( p_input, 0x15, p_access->i_command_level, 0x00, 
782                      buffer.p_data, buffer.i_data );
783
784     /* *** wait for reponse *** */
785     mms_CommandRead( p_input, 0x11, 0 );
786
787     if( p_access->i_command != 0x11 )
788     {
789         msg_Err( p_input, 
790                  "unknown answer (0x%x instead of 0x11)",
791                  p_access->i_command );
792         var_buffer_free( &buffer );
793         MMSClose( p_input );
794         return( -1 );
795     }
796     /* *** now read header packet *** */
797     if( mms_HeaderMediaRead( p_input, MMS_PACKET_HEADER ) < 0 )
798     {
799         msg_Err( p_input, "cannot receive header" );
800         var_buffer_free( &buffer );
801         MMSClose( p_input );
802         return( -1 );
803     }
804     /* *** parse header and get stream and their id *** */
805     // get all streams properties, 
806     // 
807     // TODO : stream bitrates properties(optional)
808     //        and bitrate mutual exclusion(optional)
809     asf_HeaderParse( p_access->stream, 
810                      p_access->p_header, p_access->i_header );
811     
812     /* *** now select stream we want to receive *** */
813     // TODO take care of stream bitrate TODO
814     i_streams = 0;
815     i_first = -1;
816     var_buffer_reinitwrite( &buffer, 0 );
817     /* for now, select first audio and video stream */
818     b_audio = 0;
819     b_video = 0;
820     for( i = 1; i < 128; i++ )
821     {
822
823         if( ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO && !b_audio )||
824             ( p_access->stream[i].i_cat == MMS_STREAM_VIDEO && !b_video ) )
825         {
826             i_streams++;
827             if( i_first == -1 )
828             {   
829                 i_first = i;
830                 var_buffer_add16( &buffer, 0x0000 ); // on
831             }   
832             else
833             {
834                 var_buffer_add16( &buffer, 0xffff );
835                 var_buffer_add16( &buffer, i );
836                 var_buffer_add16( &buffer, 0x0000 );
837             }
838             msg_Info( p_input, 
839                       "selecting stream[0x%x] %s (%d kb/s)",
840                       i,
841                       ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO  ) ? 
842                         "audio" : "video" ,
843                         p_access->stream[i].i_bitrate / 1024);
844             if( p_access->stream[i].i_cat == MMS_STREAM_AUDIO )
845             {
846                 b_audio = 1;
847             }
848             if( p_access->stream[i].i_cat == MMS_STREAM_VIDEO )
849             {
850                 b_video = 1;
851             }
852
853         }
854         else if( p_access->stream[i].i_cat != MMS_STREAM_UNKNOWN )
855         {
856             msg_Info( p_input, 
857                       "ignoring stream[0x%x] %s (%d kb/s)",
858                       i,
859                       ( p_access->stream[i].i_cat == MMS_STREAM_AUDIO  ) ? 
860                         "audio" : "video" ,
861                        p_access->stream[i].i_bitrate / 1024);
862         }
863     } 
864
865     if( i_streams == 0 )
866     {
867         msg_Err( p_input, "cannot find any stream" );
868         var_buffer_free( &buffer );
869         MMSClose( p_input );
870         return( -1 );
871     }
872     mms_CommandSend( p_input, 0x33,
873                      i_streams,
874                      0xffff | ( i_first << 16 ),
875                      buffer.p_data, buffer.i_data );
876     
877     mms_CommandRead( p_input, 0x21, 0 );
878     if( p_access->i_command != 0x21 )
879     {
880         msg_Err( p_input, 
881                  "unknown answer (0x%x instead of 0x21)", 
882                  p_access->i_command );
883         var_buffer_free( &buffer );
884         MMSClose( p_input );
885         return( -1 );
886     }
887
888
889     var_buffer_free( &buffer );
890
891     msg_Info( p_input, "connection sucessful" );
892
893     return( 0 );
894 }
895
896 /****************************************************************************
897  * MMSStart : Start streaming
898  ****************************************************************************/
899 static int MMSStart  ( input_thread_t  *p_input, uint32_t i_packet )
900 {
901     access_t        *p_access = (access_t*)p_input->p_access_data;
902     var_buffer_t    buffer;
903
904     /* *** start stream from packet 0 *** */
905     var_buffer_initwrite( &buffer, 0 );
906     var_buffer_add64( &buffer, 0 ); // seek point in second
907     var_buffer_add32( &buffer, 0xffffffff ); 
908 //    var_buffer_add32( &buffer, 0xffffffff ); // begin from start
909     var_buffer_add32( &buffer, i_packet ); // begin from start
910     var_buffer_add8( &buffer, 0xff ); // stream time limit
911     var_buffer_add8( &buffer, 0xff ); //  on 3bytes ...
912     var_buffer_add8( &buffer, 0xff ); //
913     var_buffer_add8( &buffer, 0x00 ); // don't use limit
914     var_buffer_add32( &buffer, p_access->i_media_packet_id_type ); 
915     
916     mms_CommandSend( p_input, 0x07, p_access->i_command_level, 0x0001ffff, 
917                      buffer.p_data, buffer.i_data );
918     
919     var_buffer_free( &buffer );
920     
921     mms_CommandRead( p_input, 0x05, 0 );
922
923     if( p_access->i_command != 0x05 )
924     {
925         msg_Err( p_input, 
926                  "unknown answer (0x%x instead of 0x05)", 
927                  p_access->i_command );
928         return( -1 );
929     }
930     else
931     {   
932         /* get a packet */
933         mms_HeaderMediaRead( p_input, MMS_PACKET_MEDIA );
934         msg_Dbg( p_input, "Streaming started" );
935         return( 0 );
936     }
937 }
938
939 /****************************************************************************
940  * MMSStop : Stop streaming
941  ****************************************************************************/
942 static int MMSStop  ( input_thread_t  *p_input )
943 {
944     access_t        *p_access = (access_t*)p_input->p_access_data;
945
946     /* *** stop stream but keep connection alive *** */    
947     mms_CommandSend( p_input,
948                      0x09,
949                      p_access->i_command_level,
950                      0x001fffff,
951                      NULL, 0 );
952     return( 0 );
953 }
954
955 /****************************************************************************
956  * MMSClose : Close streaming and connection
957  ****************************************************************************/
958 static int MMSClose  ( input_thread_t  *p_input )
959 {
960     access_t        *p_access = (access_t*)p_input->p_access_data;
961
962     msg_Dbg( p_input, "Connection closed" );
963
964     /* *** tell server that we will disconnect *** */    
965     mms_CommandSend( p_input,
966                      0x0d,
967                      p_access->i_command_level,
968                      0x00000001,
969                      NULL, 0 );
970     /* *** close sockets *** */
971 #if defined( UNDER_CE )
972     CloseHandle( (HANDLE)p_access->socket_server.i_handle );
973 #elif defined( WIN32 )
974     closesocket( p_access->socket_server.i_handle );
975 #else
976     close( p_access->socket_server.i_handle );
977 #endif
978
979     if( p_access->i_proto == MMS_PROTO_UDP )
980     {
981 #if defined( UNDER_CE )
982         CloseHandle( (HANDLE)p_access->socket_data.i_handle );
983 #elif defined( WIN32 )
984         closesocket( p_access->socket_data.i_handle );
985 #else
986         close( p_access->socket_data.i_handle );
987 #endif
988     }
989     
990     FREE( p_access->p_media );
991     FREE( p_access->p_header );
992
993     FREE( p_access->psz_server_version );
994     FREE( p_access->psz_tool_version );
995     FREE( p_access->psz_update_player_url );
996     FREE( p_access->psz_encryption_type );
997
998     return( 0 );
999 }
1000
1001 /*****************************************************************************
1002  * mms_ParseURL : parse an url string and fill an url_t 
1003  *****************************************************************************/
1004 static void mms_ParseURL( url_t *p_url, char *psz_url )
1005 {
1006     char *psz_parser;
1007     char *psz_server_port;
1008
1009     p_url->psz_private = strdup( psz_url );
1010     
1011     psz_parser = p_url->psz_private;
1012     
1013     while( *psz_parser == '/' )
1014     {
1015         psz_parser++;
1016     }
1017     p_url->psz_server_addr = psz_parser;
1018
1019     while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
1020     {
1021         psz_parser++;
1022     } 
1023
1024     if( *psz_parser == ':' )
1025     {
1026         *psz_parser = '\0';
1027         psz_parser++;
1028         psz_server_port = psz_parser;
1029
1030         while( *psz_parser && *psz_parser != '/' )
1031         {
1032             psz_parser++;
1033         }
1034     }
1035     else
1036     {
1037         psz_server_port = "";
1038     }
1039     if( *psz_parser == '/' )
1040     {
1041         *psz_parser = '\0';
1042         psz_parser++;
1043         p_url->psz_path = psz_parser;
1044     }
1045     
1046     if( *psz_server_port )
1047     {
1048         p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
1049     }
1050     else
1051     {
1052         p_url->i_server_port = 0;
1053     }
1054 }
1055
1056 static int mms_ReadData( input_thread_t *p_input,
1057                          uint8_t  *p_data,
1058                          int i_data )
1059 {
1060     access_t *p_access = (access_t*)p_input->p_access_data;
1061
1062     int i_read;
1063
1064     while( i_data > 0 )
1065     {
1066         i_read = NetRead( p_input, &p_access->socket_server, p_data, i_data );
1067         if( i_read < 0 )
1068         {
1069             msg_Err( p_input, "failed to read data" );
1070             return( -1 );
1071         }
1072         i_data -= i_read;
1073         p_data += i_read;
1074     }
1075     return( 0 );
1076 }
1077
1078
1079 /****************************************************************************
1080  *
1081  * MMS specific functions
1082  *
1083  ****************************************************************************/
1084
1085 static int mms_CommandSend( input_thread_t *p_input, 
1086                              int i_command, 
1087                              uint32_t i_prefix1, uint32_t i_prefix2, 
1088                              uint8_t *p_data, int i_data )
1089 {
1090     var_buffer_t buffer;
1091
1092     access_t    *p_access = (access_t*)p_input->p_access_data;
1093     int i_data_by8;
1094     
1095     i_data_by8 = ( i_data + 7 ) / 8;
1096
1097     /* first init uffer */
1098     var_buffer_initwrite( &buffer, 0 );
1099
1100     var_buffer_add32( &buffer, 0x00000001 );    // start sequence
1101     var_buffer_add32( &buffer, 0xB00BFACE );    // ...
1102     // size after protocol type
1103     var_buffer_add32( &buffer, i_data + MMS_CMD_HEADERSIZE - 16 );     
1104     var_buffer_add32( &buffer, 0x20534d4d );    // protocol "MMS "
1105     var_buffer_add32( &buffer, i_data_by8 + 4 );
1106     var_buffer_add32( &buffer, p_access->i_seq_num ); p_access->i_seq_num++;
1107     var_buffer_add64( &buffer, 0 );
1108     var_buffer_add32( &buffer, i_data_by8 + 2 );
1109     var_buffer_add32( &buffer, 0x00030000 | i_command ); /* dir | command */
1110     var_buffer_add32( &buffer, i_prefix1 );    /* command specific */
1111     var_buffer_add32( &buffer, i_prefix2 );    /* command specific */
1112
1113     /* specific command data */
1114     if( p_data && i_data > 0 )
1115     {
1116         var_buffer_addmemory( &buffer, p_data, i_data );
1117     }
1118
1119     /* send it */
1120     if( send( p_access->socket_server.i_handle, 
1121               buffer.p_data, 
1122               buffer.i_data,
1123               0 ) == -1 )
1124     {
1125         msg_Err( p_input, "failed to send command" );
1126         return( -1 );
1127     }
1128
1129     var_buffer_free( &buffer );
1130     return( 0 );
1131 }   
1132
1133 static int  mms_ReceiveCommand( input_thread_t *p_input )
1134 {
1135 #define GET32( i_pos ) \
1136     ( p_access->p_cmd[i_pos] + ( p_access->p_cmd[i_pos +1] << 8 ) + \
1137       ( p_access->p_cmd[i_pos + 2] << 16 ) + \
1138       ( p_access->p_cmd[i_pos + 3] << 24 ) )
1139
1140     access_t    *p_access = (access_t*)p_input->p_access_data;
1141
1142     do
1143     {
1144         int i_length;
1145         // see for UDP mode
1146
1147         /* *** Read complete command *** */
1148         p_access->i_cmd = NetRead( p_input, &p_access->socket_server,
1149                                    p_access->p_cmd, BUF_SIZE );
1150         if( p_access->i_cmd < 12 )
1151         {
1152             msg_Warn( p_input, "failed to receive command" );
1153             p_access->i_command = 0;
1154             return( -1 );
1155         }
1156         i_length = GetDWLE( p_access->p_cmd + 8 ) + 16;
1157         if( i_length > p_access->i_cmd )
1158         {
1159             if( mms_ReadData( p_input, 
1160                               p_access->p_cmd + p_access->i_cmd,
1161                               i_length - p_access->i_cmd ) < 0 )
1162             {
1163                 msg_Warn( p_input, "failed to receive command" );
1164                 p_access->i_command = 0;
1165                 return( -1 );
1166             }
1167         }
1168
1169         msg_Dbg( p_input, "received %d bytes", p_access->i_cmd );
1170
1171         p_access->i_command = GET32( 36 ) & 0xffff;
1172         msg_Dbg( p_input, 
1173                  "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",
1174                  GET32( 0 ),
1175                  GET32( 4 ),
1176                  GET32( 8 ),
1177                  GET32( 16 ),
1178                  GET32( 20 ),
1179                  GET32( 32 ),
1180                  GET32( 36 ),
1181                  GET32( 40 ) );
1182
1183         if( p_access->i_command == 0x1b )
1184         {
1185             mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1186         }
1187
1188     } while( p_access->i_command == 0x1b );
1189
1190     return( 0 );
1191 }
1192
1193 #define MMS_RETRY_MAX       10
1194 #define MMS_RETRY_SLEEP     50000
1195
1196 static int mms_CommandRead( input_thread_t *p_input, int i_command1, int i_command2 )
1197 {
1198     access_t    *p_access = (access_t*)p_input->p_access_data;
1199     int i_count;
1200     int i_status;
1201
1202     for( i_count = 0; i_count < MMS_RETRY_MAX; )
1203     {
1204
1205         i_status = mms_ReceiveCommand( p_input );
1206         if( i_status < 0 || p_access->i_command == 0 )
1207         {
1208             i_count++;
1209             msleep( MMS_RETRY_SLEEP );
1210         }
1211         else if( i_command1 == 0 && i_command2 == 0)
1212         {
1213             return( 0 );
1214         }
1215         else if( p_access->i_command == i_command1 || p_access->i_command == i_command2 )
1216         {
1217             return( 0 );   
1218         }
1219         else
1220         {
1221             switch( p_access->i_command )
1222             {
1223                 case 0x03:
1224                     msg_Warn( p_input, "socket closed by server" );
1225                     return( -1 );
1226                 case 0x1e:
1227                     msg_Warn( p_input, "end of media stream" );
1228                     return( -1 );
1229                 default:
1230                     break;
1231             }
1232         }
1233     }
1234     msg_Warn( p_input, "failed to receive command (abording)" );
1235
1236     return( -1 );
1237 }
1238
1239
1240
1241 static int mms_ReceivePacket( input_thread_t *p_input )
1242 {
1243     access_t    *p_access = (access_t*)p_input->p_access_data;
1244     uint8_t     preheader[8];
1245     int         i_read;
1246
1247     if( p_access->i_proto == MMS_PROTO_UDP )
1248     {
1249         return( -1 );
1250     }
1251     else
1252     {
1253         for( ;; )
1254         {
1255             i_read = NetRead( p_input, &p_access->socket_server, preheader, 8 );
1256             if( i_read < 8 )
1257             {
1258                 msg_Warn( p_input, "cannot read preheader" );
1259                 return( -1 );
1260             }
1261             /* preheader format :
1262              * u32 i_sequence_number
1263              * u8  i_packet_id
1264              * u8  i_udp_sequence/i_tcp_flags
1265              * u16 i_length
1266              */
1267             if( preheader[4] == p_access->i_header_packet_id_type ||
1268                 preheader[4] == p_access->i_media_packet_id_type ||
1269                 preheader[4] == 0xff )// udp timing pair
1270             {
1271                 int i_packet_seq_num;
1272                 int i_packet_length;
1273                 int i_packet_id;
1274
1275                 uint8_t  *p_packet;
1276                 
1277                 i_packet_seq_num = GetDWLE( preheader );
1278                 i_packet_length = GetWLE( preheader + 6 );
1279                 i_packet_id = preheader[4];
1280                 
1281                 /* *** read complete packet *** */
1282                 if( i_packet_length <= 8 )
1283                 {
1284                     msg_Err( p_input,
1285                              "empty or broken packet" );
1286                     return( -1 );
1287                 }
1288                 p_packet = malloc( i_packet_length - 8 );
1289                 if( mms_ReadData( p_input, 
1290                                   p_packet,
1291                                   i_packet_length - 8 ) < 0 )
1292                 {
1293                     msg_Err( p_input,
1294                              "cannot read data" );
1295                 }
1296
1297                 
1298                 if( i_packet_id == 0xff )
1299                 {
1300                     msg_Warn( p_input,
1301                               "receive MMS UDP pair timing" );
1302                     free( p_packet );
1303                     return( MMS_PACKET_UDP_TIMING );
1304                 }
1305                 else
1306                 {
1307                     if( i_packet_seq_num != p_access->i_packet_seq_num )
1308                     {
1309                         // FIXME for udp could be just wrong order ?
1310                         msg_Warn( p_input, 
1311                                   "detected packet lost (%d != %d)",
1312                                   i_packet_seq_num,
1313                                   p_access->i_packet_seq_num );
1314                         p_access->i_packet_seq_num = i_packet_seq_num;
1315                     }
1316                     p_access->i_packet_seq_num++;
1317
1318                     if( i_packet_id == p_access->i_header_packet_id_type )
1319                     {
1320                         FREE( p_access->p_header );
1321                         p_access->p_header = p_packet;
1322                         p_access->i_header = i_packet_length - 8;
1323                         return( MMS_PACKET_HEADER );
1324                     }
1325                     else
1326                     {
1327                         FREE( p_access->p_media );
1328                         p_access->p_media = p_packet;
1329                         p_access->i_media = i_packet_length - 8;
1330                         p_access->i_media_used = 0;
1331                         return( MMS_PACKET_MEDIA );
1332                     }
1333                 }
1334             }
1335             else
1336             {
1337                 int i_packet_length;
1338                 // command ?
1339                 if( GetDWLE( preheader + 4 ) != 0xb00bface )
1340                 {
1341                     msg_Err( p_input, 
1342                              "incorrect command header (0x%x)",
1343                               GetDWLE( preheader + 4 ) );
1344                 }
1345                 memcpy( p_access->p_cmd, preheader, 8 );
1346                 if( mms_ReadData( p_input, 
1347                                   p_access->p_cmd + 8,
1348                                   8 ) < 0 )
1349                 {
1350                     msg_Err( p_input,
1351                              "cannot read data" );
1352                 }
1353                 p_access->i_cmd = 16;
1354                 i_packet_length = GetDWLE( p_access->p_cmd + 8 );
1355                 if( mms_ReadData( p_input, 
1356                                   p_access->p_cmd + 16,
1357                                   i_packet_length ) < 0 )
1358                 {
1359                     msg_Err( p_input,
1360                              "cannot read data" );
1361                 }
1362                 p_access->i_cmd += i_packet_length;
1363                 p_access->i_command = GetDWLE( p_access->p_cmd + 36 ) & 0xffff;
1364                 if( p_access->i_command == 0x1b )
1365                 {
1366                     mms_CommandSend( p_input, 0x1b, 0, 0, NULL, 0 );
1367                 }
1368                 else
1369                 {
1370                     return( MMS_PACKET_CMD );
1371                 }
1372
1373             }
1374         }
1375     }
1376 }
1377
1378
1379 static int mms_HeaderMediaRead( input_thread_t *p_input, int i_type )
1380 {
1381     access_t    *p_access = (access_t*)p_input->p_access_data;
1382     int         i_count;
1383     
1384     for( i_count = 0; i_count < MMS_RETRY_MAX; )
1385     {
1386         int i_status;
1387
1388         i_status = mms_ReceivePacket( p_input );
1389         if( i_status < 0 )
1390         {
1391             i_count++;
1392             msg_Warn( p_input, 
1393                       "cannot receive header (%d/%d)", i_count, MMS_RETRY_MAX );
1394             msleep( MMS_RETRY_SLEEP );
1395         }
1396         else if( i_status == i_type )
1397         {
1398             return( 0 );
1399         }
1400         else if( i_status == MMS_PACKET_CMD )
1401         {
1402             switch( p_access->i_command )
1403             {
1404                 case 0x03:
1405                     msg_Warn( p_input, "socket closed by server" );
1406                     return( -1 );
1407                 case 0x1e:
1408                     msg_Warn( p_input, "end of media stream" );
1409                     return( -1 );
1410                 case 0x20:
1411                     /* XXX not too dificult to be done EXCEPT that we 
1412                      * need to restart demuxer... and I don't see how we 
1413                      * could do that :p */ 
1414                     msg_Err( p_input, 
1415                              "reinitialization needed --> unsupported" );
1416                     return( -1 );
1417                 default:
1418                     break;
1419             }
1420         }
1421     }
1422     msg_Err( p_input, 
1423              "cannot receive %s (abording)",
1424                ( i_type == MMS_PACKET_HEADER ) ? "header" : "media data" );
1425     return( -1 );
1426 }
1427
1428
1429
1430