]> git.sesse.net Git - vlc/blob - modules/access/mms/mmsh.c
* asf: fixed unintialised variable, cleaned up some stuffs.
[vlc] / modules / access / mms / mmsh.c
1 /*****************************************************************************
2  * mmsh.c:
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id$
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  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31
32 #include "vlc_playlist.h"
33
34 #include "network.h"
35 #include "asf.h"
36 #include "buffer.h"
37
38 #include "mms.h"
39 #include "mmsh.h"
40
41 /* TODO:
42  *  - http_proxy
43  *  - authentication
44  */
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 int  E_(MMSHOpen)  ( input_thread_t * );
50 void E_(MMSHClose) ( input_thread_t * );
51
52 static ssize_t  Read( input_thread_t *, byte_t *, size_t );
53 static ssize_t  ReadRedirect( input_thread_t *, byte_t *, size_t );
54 static void     Seek( input_thread_t *, off_t );
55
56 static int      Start( input_thread_t *, off_t );
57 static void     Stop( input_thread_t * );
58 static int      GetPacket( input_thread_t *, chunk_t * );
59
60 /****************************************************************************
61  * Open: connect to ftp server and ask for file
62  ****************************************************************************/
63 int  E_( MMSHOpen )  ( input_thread_t *p_input )
64 {
65     access_sys_t    *p_sys;
66
67     char            *psz;
68     char            *psz_location = NULL;
69     int             i_code;
70
71     vlc_value_t     val;
72
73     /* init p_sys */
74     p_input->p_access_data = p_sys = malloc( sizeof( access_sys_t ) );
75     p_sys->i_proto = MMS_PROTO_HTTP;
76
77     p_sys->fd       = -1;
78     p_sys->i_request_context = 1;
79     p_sys->b_broadcast = VLC_TRUE;
80     p_sys->p_packet = NULL;
81     p_sys->i_packet_sequence = 0;
82     p_sys->i_packet_used = 0;
83     p_sys->i_packet_length = 0;
84     p_sys->i_pos = 0;
85     p_sys->i_request_context = 1;
86     E_( GenerateGuid )( &p_sys->guid );
87
88     /* open a tcp connection */
89     vlc_UrlParse( &p_sys->url, p_input->psz_name, 0 );
90     if( p_sys->url.psz_host == NULL && *p_sys->url.psz_host == '\0' )
91     {
92         msg_Err( p_input, "invalid host" );
93         goto error;
94     }
95     if( p_sys->url.i_port <= 0 )
96     {
97         p_sys->url.i_port = 80;
98     }
99
100     if( ( p_sys->fd = net_OpenTCP( p_input, p_sys->url.psz_host,
101                                             p_sys->url.i_port ) ) < 0 )
102     {
103         msg_Err( p_input, "cannot connect to%s:%d", p_sys->url.psz_host, p_sys->url.i_port );
104         goto error;
105     }
106
107     /* send first request */
108     net_Printf( VLC_OBJECT(p_input), p_sys->fd,
109                 "GET %s HTTP/1.0\r\n"
110                 "Accept: */*\r\n"
111                 "User-Agent: NSPlayer/4.1.0.3856\r\n"
112                 "Host: %s:%d\r\n"
113                 "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n"
114                 "Pragma: xClientGUID={"GUID_FMT"}\r\n"
115                 "Connection: Close\r\n",
116                 ( p_sys->url.psz_path == NULL || *p_sys->url.psz_path == '\0' ) ? "/" : p_sys->url.psz_path,
117                 p_sys->url.psz_host, p_sys->url.i_port,
118                 p_sys->i_request_context++,
119                 GUID_PRINT( p_sys->guid ) );
120
121     if( net_Printf( VLC_OBJECT(p_input), p_sys->fd, "\r\n" ) < 0 )
122     {
123         msg_Err( p_input, "failed to send request" );
124         goto error;
125     }
126
127     /* Receive the http header */
128     if( ( psz = net_Gets( VLC_OBJECT(p_input), p_sys->fd ) ) == NULL )
129     {
130         msg_Err( p_input, "failed to read answer" );
131         goto error;
132     }
133     if( strncmp( psz, "HTTP/1.", 7 ) )
134     {
135         msg_Err( p_input, "invalid HTTP reply '%s'", psz );
136         free( psz );
137         goto error;
138     }
139     i_code = atoi( &psz[9] );
140     if( i_code >= 400 )
141     {
142         msg_Err( p_input, "error: %s", psz );
143         free( psz );
144         goto error;
145     }
146
147     msg_Dbg( p_input, "HTTP reply '%s'", psz );
148     free( psz );
149     for( ;; )
150     {
151         char *psz = net_Gets( p_input, p_sys->fd );
152         char *p;
153
154         if( psz == NULL )
155         {
156             msg_Err( p_input, "failed to read answer" );
157             goto error;
158         }
159
160         if( *psz == '\0' )
161         {
162             free( psz );
163             break;
164         }
165
166         if( ( p = strchr( psz, ':' ) ) == NULL )
167         {
168             msg_Err( p_input, "malformed header line: %s", psz );
169             free( psz );
170             goto error;
171         }
172         *p++ = '\0';
173         while( *p == ' ' ) p++;
174
175         /* FIXME FIXME test Content-Type to see if it's a plain stream or an
176          * asx FIXME */
177         if( !strcasecmp( psz, "Pragma" ) )
178         {
179             if( !strncasecmp( p, "features", 8 ) )
180             {
181                 if( strstr( p, "broadcast" ) )
182                 {
183                     msg_Dbg( p_input, "stream type = broadcast" );
184                     p_sys->b_broadcast = VLC_TRUE;
185                 }
186                 else if( strstr( p, "seekable" ) )
187                 {
188                     msg_Dbg( p_input, "stream type = seekable" );
189                     p_sys->b_broadcast = VLC_FALSE;
190                 }
191                 else
192                 {
193                     msg_Warn( p_input, "unknow stream types (%s)", p );
194                     p_sys->b_broadcast = VLC_FALSE;
195                 }
196             }
197         }
198         else if( !strcasecmp( psz, "Location" ) )
199         {
200             psz_location = strdup( p );
201         }
202
203         free( psz );
204     }
205
206     /* Handle the redirection */
207     if( ( i_code == 301 || i_code == 302 ||
208           i_code == 303 || i_code == 307 ) &&
209         psz_location && *psz_location )
210     {
211         playlist_t * p_playlist;
212
213         msg_Dbg( p_input, "redirection to %s", psz_location );
214         net_Close( p_sys->fd ); p_sys->fd = -1;
215
216         p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
217         if( !p_playlist )
218         {
219             msg_Err( p_input, "redirection failed: can't find playlist" );
220             goto error;
221         }
222         p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
223         playlist_Add( p_playlist, psz_location, psz_location,
224                       PLAYLIST_INSERT | PLAYLIST_GO,
225                       p_playlist->i_index + 1 );
226         vlc_object_release( p_playlist );
227
228         p_input->pf_read = ReadRedirect;
229         p_input->pf_seek = NULL;
230         p_input->pf_set_program = input_SetProgram;
231         p_input->pf_set_area = NULL;
232
233         /* *** finished to set some variable *** */
234         vlc_mutex_lock( &p_input->stream.stream_lock );
235         p_input->stream.b_pace_control = 0;
236         p_input->stream.p_selected_area->i_size = 0;
237         p_input->stream.b_seekable = 0;
238         p_input->stream.p_selected_area->i_tell = 0;
239         p_input->stream.i_method = INPUT_METHOD_NETWORK;
240         vlc_mutex_unlock( &p_input->stream.stream_lock );
241
242         return VLC_SUCCESS;
243     }
244
245     /* Read the asf header */
246     p_sys->i_header = 0;
247     p_sys->p_header = NULL;
248     for( ;; )
249     {
250         chunk_t ck;
251         if( GetPacket( p_input, &ck ) ||
252             ck.i_type != 0x4824 )
253         {
254             break;
255         }
256
257         if( ck.i_data > 0 )
258         {
259             p_sys->i_header += ck.i_data;
260             p_sys->p_header = realloc( p_sys->p_header, p_sys->i_header );
261             memcpy( &p_sys->p_header[p_sys->i_header - ck.i_data],
262                     ck.p_data, ck.i_data );
263         }
264     }
265     msg_Dbg( p_input, "complete header size=%d", p_sys->i_header );
266     if( p_sys->i_header <= 0 )
267     {
268         msg_Err( p_input, "header size == 0" );
269         goto error;
270     }
271     /* close this connection */
272     net_Close( p_sys->fd ); p_sys->fd = -1;
273
274     /* *** parse header and get stream and their id *** */
275     /* get all streams properties,
276      *
277      * TODO : stream bitrates properties(optional)
278      *        and bitrate mutual exclusion(optional) */
279     E_( asf_HeaderParse )( &p_sys->asfh,
280                            p_sys->p_header, p_sys->i_header );
281     msg_Dbg( p_input, "packet count=%lld packet size=%d",
282              p_sys->asfh.i_data_packets_count,
283              p_sys->asfh.i_min_data_packet_size );
284
285     E_( asf_StreamSelect)( &p_sys->asfh,
286                            config_GetInt( p_input, "mms-maxbitrate" ),
287                            config_GetInt( p_input, "mms-all" ),
288                            config_GetInt( p_input, "audio" ),
289                            config_GetInt( p_input, "video" ) );
290
291     if( Start( p_input, 0 ) )
292     {
293         msg_Err( p_input, "cannot start stream" );
294         goto error;
295     }
296
297     /* *** set exported functions *** */
298     p_input->pf_read = Read;
299     p_input->pf_seek = Seek;
300     p_input->pf_set_program = input_SetProgram;
301     p_input->pf_set_area = NULL;
302
303     p_input->p_private = NULL;
304     p_input->i_mtu = 3 * p_sys->asfh.i_min_data_packet_size;
305
306     /* *** finished to set some variable *** */
307     vlc_mutex_lock( &p_input->stream.stream_lock );
308     p_input->stream.b_pace_control = 0;
309     if( p_sys->b_broadcast )
310     {
311         p_input->stream.p_selected_area->i_size = 0;
312         p_input->stream.b_seekable = 0;
313     }
314     else
315     {
316         p_input->stream.p_selected_area->i_size = p_sys->asfh.i_file_size;
317         p_input->stream.b_seekable = 1;
318     }
319     p_input->stream.p_selected_area->i_tell = 0;
320     p_input->stream.i_method = INPUT_METHOD_NETWORK;
321     vlc_mutex_unlock( &p_input->stream.stream_lock );
322
323     /* Update default_pts to a suitable value for mms access */
324     var_Get( p_input, "mms-caching", &val );
325     p_input->i_pts_delay = val.i_int * 1000;
326
327     return VLC_SUCCESS;
328
329 error:
330     vlc_UrlClean( &p_sys->url );
331     if( p_sys->fd > 0 )
332     {
333         net_Close( p_sys->fd  );
334     }
335     free( p_sys );
336     return VLC_EGENERIC;
337 }
338
339 /*****************************************************************************
340  * Close: free unused data structures
341  *****************************************************************************/
342 void E_( MMSHClose )( input_thread_t *p_input )
343 {
344     access_sys_t    *p_sys   = p_input->p_access_data;
345
346     msg_Dbg( p_input, "stopping stream" );
347
348     Stop( p_input );
349
350     free( p_sys );
351 }
352
353 /*****************************************************************************
354  * Seek: try to go at the right place
355  *****************************************************************************/
356 static void Seek( input_thread_t * p_input, off_t i_pos )
357 {
358     access_sys_t *p_sys = p_input->p_access_data;
359     chunk_t      ck;
360     off_t        i_offset;
361     off_t        i_packet;
362
363     i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
364     i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
365
366     msg_Dbg( p_input, "seeking to "I64Fd, i_pos );
367
368     vlc_mutex_lock( &p_input->stream.stream_lock );
369
370     Stop( p_input );
371     Start( p_input, i_packet * p_sys->asfh.i_min_data_packet_size );
372
373     for( ;; )
374     {
375         if( GetPacket( p_input, &ck ) )
376         {
377             break;
378         }
379
380         /* skip headers */
381         if( ck.i_type != 0x4824 )
382         {
383             break;
384         }
385         msg_Warn( p_input, "skipping header" );
386     }
387
388     p_sys->i_pos = i_pos;
389     p_sys->i_packet_used += i_offset;
390
391     p_input->stream.p_selected_area->i_tell = i_pos;
392     vlc_mutex_unlock( &p_input->stream.stream_lock );
393 }
394
395 /*****************************************************************************
396  * Read:
397  *****************************************************************************/
398 static ssize_t ReadRedirect( input_thread_t *p_input, byte_t *p, size_t i )
399 {
400     return 0;
401 }
402
403 /*****************************************************************************
404  * Read:
405  *****************************************************************************/
406 static ssize_t Read        ( input_thread_t * p_input, byte_t * p_buffer,
407                              size_t i_len )
408 {
409     access_sys_t *p_sys = p_input->p_access_data;
410     size_t       i_copy;
411     size_t       i_data = 0;
412
413     while( i_data < i_len )
414     {
415         if( p_sys->i_packet_used < p_sys->i_packet_length )
416         {
417             i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used,
418                             i_len - i_data );
419
420             memcpy( &p_buffer[i_data],
421                     &p_sys->p_packet[p_sys->i_packet_used],
422                     i_copy );
423
424             i_data += i_copy;
425             p_sys->i_packet_used += i_copy;
426         }
427         else if( p_sys->i_pos + i_data > p_sys->i_header &&
428                  (int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size )
429         {
430             i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used,
431                             i_len - i_data );
432
433             memset( &p_buffer[i_data], 0, i_copy );
434
435             i_data += i_copy;
436             p_sys->i_packet_used += i_copy;
437         }
438         else
439         {
440             chunk_t ck;
441             if( GetPacket( p_input, &ck ) )
442             {
443                 return -1;
444             }
445         }
446     }
447
448     p_sys->i_pos += i_data;
449
450     return( i_data );
451 }
452
453 /*****************************************************************************
454  *
455  *****************************************************************************/
456 static int Start( input_thread_t *p_input, off_t i_pos )
457 {
458     access_sys_t *p_sys = p_input->p_access_data;
459     int  i_streams = 0;
460     int  i;
461     char *psz;
462
463     msg_Dbg( p_input, "starting stream" );
464
465     if( ( p_sys->fd = net_OpenTCP( p_input, p_sys->url.psz_host,
466                                             p_sys->url.i_port ) ) < 0 )
467     {
468         /* should not occur */
469         msg_Err( p_input, "cannot connect to the server" );
470         return VLC_EGENERIC;
471     }
472
473     for( i = 1; i < 128; i++ )
474     {
475         if( p_sys->asfh.stream[i].i_selected )
476         {
477             i_streams++;
478         }
479     }
480
481     if( i_streams <= 0 )
482     {
483         msg_Err( p_input, "no stream selected" );
484         return VLC_EGENERIC;
485     }
486     net_Printf( VLC_OBJECT(p_input), p_sys->fd,
487                 "GET %s HTTP/1.0\r\n"
488                 "Accept: */*\r\n"
489                 "User-Agent: NSPlayer/4.1.0.3856\r\n"
490                 "Host: %s:%d\r\n",
491                 ( p_sys->url.psz_path == NULL || *p_sys->url.psz_path == '\0' ) ? "/" : p_sys->url.psz_path,
492                 p_sys->url.psz_host, p_sys->url.i_port );
493     if( p_sys->b_broadcast )
494     {
495         net_Printf( VLC_OBJECT(p_input), p_sys->fd,
496                     "Pragma: no-cache,rate=1.000000,request-context=%d\r\n",
497                     p_sys->i_request_context++ );
498     }
499     else
500     {
501         net_Printf( VLC_OBJECT(p_input), p_sys->fd,
502                     "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n",
503                     (uint32_t)((i_pos >> 32)&0xffffffff),
504                     (uint32_t)(i_pos&0xffffffff),
505                     p_sys->i_request_context++ );
506     }
507     net_Printf( VLC_OBJECT(p_input), p_sys->fd,
508                 "Pragma: xPlayStrm=1\r\n"
509                 "Pragma: xClientGUID={"GUID_FMT"}\r\n"
510                 "Pragma: stream-switch-count=%d\r\n"
511                 "Pragma: stream-switch-entry=",
512                 GUID_PRINT( p_sys->guid ),
513                 i_streams);
514
515     for( i = 1; i < 128; i++ )
516     {
517         if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN )
518         {
519             int i_select = 2;
520             if( p_sys->asfh.stream[i].i_selected )
521             {
522                 i_select = 0;
523             }
524
525             net_Printf( VLC_OBJECT(p_input), p_sys->fd,
526                         "ffff:%d:%d ", i, i_select );
527         }
528     }
529     net_Printf( VLC_OBJECT(p_input), p_sys->fd, "\r\n" );
530     net_Printf( VLC_OBJECT(p_input), p_sys->fd, "Connection: Close\r\n" );
531
532     if( net_Printf( VLC_OBJECT(p_input), p_sys->fd, "\r\n" ) < 0 )
533     {
534         msg_Err( p_input, "failed to send request" );
535         return VLC_EGENERIC;
536     }
537
538     if( ( psz = net_Gets( VLC_OBJECT(p_input), p_sys->fd ) ) == NULL )
539     {
540         msg_Err( p_input, "cannot read data" );
541         return VLC_EGENERIC;
542     }
543     if( atoi( &psz[9] ) >= 400 )
544     {
545         msg_Err( p_input, "error: %s", psz );
546         free( psz );
547         return VLC_EGENERIC;
548     }
549     msg_Dbg( p_input, "HTTP reply '%s'", psz );
550     free( psz );
551
552     /* FIXME check HTTP code */
553     for( ;; )
554     {
555         char *psz = net_Gets( p_input, p_sys->fd );
556         if( psz == NULL )
557         {
558             msg_Err( p_input, "cannot read data" );
559             return VLC_EGENERIC;
560         }
561         if( *psz == '\0' )
562         {
563             free( psz );
564             break;
565         }
566         msg_Dbg( p_input, "%s", psz );
567         free( psz );
568     }
569
570     return VLC_SUCCESS;
571 }
572
573 /*****************************************************************************
574  *
575  *****************************************************************************/
576 static void Stop( input_thread_t *p_input )
577 {
578     access_sys_t *p_sys = p_input->p_access_data;
579
580     msg_Dbg( p_input, "closing stream" );
581     net_Close( p_sys->fd ); p_sys->fd = -1;
582 }
583
584 /*****************************************************************************
585  *
586  *****************************************************************************/
587 static int GetPacket( input_thread_t * p_input, chunk_t *p_ck )
588 {
589     access_sys_t *p_sys = p_input->p_access_data;
590
591     /* Read the chunk header */
592     if( net_Read( p_input, p_sys->fd, p_sys->buffer, 12, VLC_TRUE ) < 12 )
593     {
594         /* msg_Err( p_input, "cannot read data" ); */
595         return VLC_EGENERIC;
596     }
597
598     p_ck->i_type      = GetWLE( p_sys->buffer);
599     p_ck->i_size      = GetWLE( p_sys->buffer + 2);
600     p_ck->i_sequence  = GetDWLE( p_sys->buffer + 4);
601     p_ck->i_unknown   = GetWLE( p_sys->buffer + 8);
602     p_ck->i_size2     = GetWLE( p_sys->buffer + 10);
603     p_ck->p_data      = p_sys->buffer + 12;
604     p_ck->i_data      = p_ck->i_size2 - 8;
605
606     if( p_ck->i_type == 0x4524 )   // Transfer complete
607     {
608         msg_Warn( p_input, "EOF" );
609         return VLC_EGENERIC;
610     }
611     else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 )
612     {
613         msg_Err( p_input, "invalid chunk FATAL" );
614         return VLC_EGENERIC;
615     }
616
617     if( net_Read( p_input, p_sys->fd, &p_sys->buffer[12], p_ck->i_data, VLC_TRUE ) < p_ck->i_data )
618     {
619         msg_Err( p_input, "cannot read data" );
620         return VLC_EGENERIC;
621     }
622
623     if( p_sys->i_packet_sequence != 0 &&
624         p_ck->i_sequence != p_sys->i_packet_sequence )
625     {
626         msg_Warn( p_input, "packet lost ?" );
627     }
628
629     p_sys->i_packet_sequence = p_ck->i_sequence + 1;
630     p_sys->i_packet_used   = 0;
631     p_sys->i_packet_length = p_ck->i_data;
632     p_sys->p_packet        = p_ck->p_data;
633
634     return VLC_SUCCESS;
635 }