]> git.sesse.net Git - vlc/blob - modules/access/mms/mmsh.c
Access/*: Second lecture (refs #438)
[vlc] / modules / access / mms / mmsh.c
1 /*****************************************************************************
2  * mmsh.c:
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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 "vlc_url.h"
36 #include "asf.h"
37 #include "buffer.h"
38
39 #include "mms.h"
40 #include "mmsh.h"
41
42 /* TODO:
43  *  - http_proxy
44  *  - authentication
45  */
46
47 /*****************************************************************************
48  * Local prototypes
49  *****************************************************************************/
50 int  E_(MMSHOpen)  ( access_t * );
51 void E_(MMSHClose) ( access_t * );
52
53 static int  Read( access_t *, uint8_t *, int );
54 static int  ReadRedirect( access_t *, uint8_t *, int );
55 static int  Seek( access_t *, int64_t );
56 static int  Control( access_t *, int, va_list );
57
58 static int  Describe( access_t  *, char **ppsz_location );
59 static int  Start( access_t *, int64_t );
60 static void Stop( access_t * );
61 static int  GetPacket( access_t *, chunk_t * );
62
63 /****************************************************************************
64  * Open: connect to ftp server and ask for file
65  ****************************************************************************/
66 int E_(MMSHOpen)( access_t *p_access )
67 {
68     access_sys_t    *p_sys;
69     char            *psz_location = NULL;
70
71     /* init p_sys */
72
73     /* Set up p_access */
74     p_access->pf_read = Read;
75     p_access->pf_block = NULL;
76     p_access->pf_control = Control;
77     p_access->pf_seek = Seek;
78     p_access->info.i_update = 0;
79     p_access->info.i_size = 0;
80     p_access->info.i_pos = 0;
81     p_access->info.b_eof = VLC_FALSE;
82     p_access->info.i_title = 0;
83     p_access->info.i_seekpoint = 0;
84     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
85     memset( p_sys, 0, sizeof( access_sys_t ) );
86     p_sys->i_proto= MMS_PROTO_HTTP;
87     p_sys->fd     = -1;
88     p_sys->i_start= 0;
89
90     /* open a tcp connection */
91     vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
92     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
93     {
94         msg_Err( p_access, "invalid host" );
95         vlc_UrlClean( &p_sys->url );
96         free( p_sys );
97         return VLC_EGENERIC;
98     }
99     if( p_sys->url.i_port <= 0 )
100         p_sys->url.i_port = 80;
101
102     if( Describe( p_access, &psz_location ) )
103     {
104         vlc_UrlClean( &p_sys->url );
105         free( p_sys );
106         return VLC_EGENERIC;
107     }
108     /* Handle redirection */
109     if( psz_location && *psz_location )
110     {
111         playlist_t * p_playlist = vlc_object_find( p_access, VLC_OBJECT_PLAYLIST, FIND_PARENT );
112
113         msg_Dbg( p_access, "redirection to %s", psz_location );
114
115         if( !p_playlist )
116         {
117             msg_Err( p_access, "redirection failed: can't find playlist" );
118             free( psz_location );
119             return VLC_EGENERIC;
120         }
121         p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
122         playlist_Add( p_playlist, psz_location, psz_location,
123                       PLAYLIST_INSERT | PLAYLIST_GO,
124                       p_playlist->i_index + 1 );
125         vlc_object_release( p_playlist );
126
127         free( psz_location );
128
129         p_access->pf_read = ReadRedirect;
130         return VLC_SUCCESS;
131     }
132
133     /* Start playing */
134     if( Start( p_access, 0 ) )
135     {
136         msg_Err( p_access, "cannot start stream" );
137         free( p_sys->p_header );
138         vlc_UrlClean( &p_sys->url );
139         free( p_sys );
140         return VLC_EGENERIC;
141     }
142
143     if( !p_sys->b_broadcast )
144     {
145         p_access->info.i_size = p_sys->asfh.i_file_size;
146     }
147
148     return VLC_SUCCESS;
149 }
150
151 /*****************************************************************************
152  * Close: free unused data structures
153  *****************************************************************************/
154 void E_( MMSHClose )( access_t *p_access )
155 {
156     access_sys_t *p_sys = p_access->p_sys;
157
158     Stop( p_access );
159     free( p_sys );
160 }
161
162 /*****************************************************************************
163  * Control:
164  *****************************************************************************/
165 static int Control( access_t *p_access, int i_query, va_list args )
166 {
167     access_sys_t *p_sys = p_access->p_sys;
168     vlc_bool_t   *pb_bool;
169     int          *pi_int;
170     int64_t      *pi_64;
171     int          i_int;
172
173     switch( i_query )
174     {
175         /* */
176         case ACCESS_CAN_SEEK:
177             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
178             *pb_bool = !p_sys->b_broadcast;
179             break;
180
181         case ACCESS_CAN_FASTSEEK:
182         case ACCESS_CAN_PAUSE:
183             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
184             *pb_bool = VLC_FALSE;
185             break;
186
187         case ACCESS_CAN_CONTROL_PACE:
188             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
189
190 #if 0       /* Disable for now until we have a clock synchro algo
191              * which works with something else than MPEG over UDP */
192             *pb_bool = VLC_FALSE;
193 #endif
194             *pb_bool = VLC_TRUE;
195             break;
196
197         /* */
198         case ACCESS_GET_MTU:
199             pi_int = (int*)va_arg( args, int * );
200             *pi_int = 3 * p_sys->asfh.i_min_data_packet_size;
201             break;
202
203         case ACCESS_GET_PTS_DELAY:
204             pi_64 = (int64_t*)va_arg( args, int64_t * );
205             *pi_64 = (int64_t)var_GetInteger( p_access, "mms-caching" ) * I64C(1000);
206             break;
207
208         case ACCESS_GET_PRIVATE_ID_STATE:
209             i_int = (int)va_arg( args, int );
210             pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t * );
211
212             if( i_int < 0 || i_int > 127 )
213                 return VLC_EGENERIC;
214             *pb_bool =  p_sys->asfh.stream[i_int].i_selected ? VLC_TRUE : VLC_FALSE;
215             break;
216
217         /* */
218         case ACCESS_SET_PAUSE_STATE:
219         case ACCESS_GET_TITLE_INFO:
220         case ACCESS_SET_TITLE:
221         case ACCESS_SET_SEEKPOINT:
222         case ACCESS_SET_PRIVATE_ID_STATE:
223             return VLC_EGENERIC;
224
225         default:
226             msg_Warn( p_access, "unimplemented query in control" );
227             return VLC_EGENERIC;
228
229     }
230     return VLC_SUCCESS;
231 }
232
233 /*****************************************************************************
234  * Seek: try to go at the right place
235  *****************************************************************************/
236 static int Seek( access_t *p_access, int64_t i_pos )
237 {
238     access_sys_t *p_sys = p_access->p_sys;
239     chunk_t      ck;
240     off_t        i_offset;
241     off_t        i_packet;
242
243     msg_Dbg( p_access, "seeking to "I64Fd, i_pos );
244
245     i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
246     i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
247
248     Stop( p_access );
249     Start( p_access, i_packet * p_sys->asfh.i_min_data_packet_size );
250
251     while( !p_access->b_die )
252     {
253         if( GetPacket( p_access, &ck ) )
254             break;
255
256         /* skip headers */
257         if( ck.i_type != 0x4824 )
258             break;
259
260         msg_Warn( p_access, "skipping header" );
261     }
262
263     p_access->info.i_pos = i_pos;
264     p_access->info.b_eof = VLC_FALSE;
265     p_sys->i_packet_used += i_offset;
266
267     return VLC_SUCCESS;
268 }
269
270 /*****************************************************************************
271  * Read:
272  *****************************************************************************/
273 static int ReadRedirect( access_t *p_access, uint8_t *p, int i_len )
274 {
275     return 0;
276 }
277
278 /*****************************************************************************
279  * Read:
280  *****************************************************************************/
281 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
282 {
283     access_sys_t *p_sys = p_access->p_sys;
284     size_t       i_copy;
285     size_t       i_data = 0;
286
287     if( p_access->info.b_eof )
288         return 0;
289
290     while( i_data < (size_t) i_len )
291     {
292         if( p_access->info.i_pos < p_sys->i_start + p_sys->i_header )
293         {
294             int i_offset = p_access->info.i_pos - p_sys->i_start;
295             i_copy = __MIN( p_sys->i_header - i_offset, (int)((size_t)i_len - i_data) );
296             memcpy( &p_buffer[i_data], &p_sys->p_header[i_offset], i_copy );
297
298             i_data += i_copy;
299             p_access->info.i_pos += i_copy;
300         }
301         else if( p_sys->i_packet_used < p_sys->i_packet_length )
302         {
303             i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used,
304                             i_len - i_data );
305
306             memcpy( &p_buffer[i_data],
307                     &p_sys->p_packet[p_sys->i_packet_used],
308                     i_copy );
309
310             i_data += i_copy;
311             p_sys->i_packet_used += i_copy;
312             p_access->info.i_pos += i_copy;
313         }
314         else if( (p_sys->i_packet_length > 0) &&
315                  ((int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size) )
316         {
317             i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used,
318                             i_len - i_data );
319
320             memset( &p_buffer[i_data], 0, i_copy );
321
322             i_data += i_copy;
323             p_sys->i_packet_used += i_copy;
324             p_access->info.i_pos += i_copy;
325         }
326         else
327         {
328             chunk_t ck;
329             if( GetPacket( p_access, &ck ) )
330             {
331                 if( (ck.i_type == 0x4524) && (ck.i_sequence != 0) &&
332                     (p_sys->b_broadcast) )
333                 {
334                     char *psz_location = NULL;
335
336                     p_sys->i_start = p_access->info.i_pos;
337
338                     msg_Dbg( p_access, "stoping the stream" );
339                     Stop( p_access );
340
341                     msg_Dbg( p_access, "describe the stream" );
342                     if( Describe( p_access, &psz_location ) )
343                     {
344                         msg_Err( p_access, "describe failed" );
345                         p_access->info.b_eof = VLC_TRUE;
346                         return 0;
347                     }
348                     if( Start( p_access, 0 ) )
349                     {
350                         msg_Err( p_access, "Start failed" );
351                         p_access->info.b_eof = VLC_TRUE;
352                         return 0;
353                     }
354                 }
355                 else
356                 {
357                     p_access->info.b_eof = VLC_TRUE;
358                     return 0;
359                 }
360             }
361             if( ck.i_type != 0x4424 )
362             {
363                 p_sys->i_packet_used = 0;
364                 p_sys->i_packet_length = 0;
365             }
366         }
367     }
368
369     return( i_data );
370 }
371
372 /*****************************************************************************
373  * Describe:
374  *****************************************************************************/
375 static int Describe( access_t  *p_access, char **ppsz_location )
376 {
377     access_sys_t *p_sys = p_access->p_sys;
378     char         *psz_location = NULL;
379     char         *psz;
380     int          i_code;
381
382     /* Reinit context */
383     p_sys->b_broadcast = VLC_TRUE;
384     p_sys->i_request_context = 1;
385     p_sys->i_packet_sequence = 0;
386     p_sys->i_packet_used = 0;
387     p_sys->i_packet_length = 0;
388     p_sys->p_packet = NULL;
389     E_( GenerateGuid )( &p_sys->guid );
390
391     if( ( p_sys->fd = net_ConnectTCP( p_access, p_sys->url.psz_host,
392                                             p_sys->url.i_port ) ) < 0 )
393     {
394         msg_Err( p_access, "cannot connect to %s:%d", p_sys->url.psz_host,
395                  p_sys->url.i_port );
396         goto error;
397     }
398
399     /* send first request */
400     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
401                 "GET %s HTTP/1.0\r\n"
402                 "Accept: */*\r\n"
403                 "User-Agent: NSPlayer/4.1.0.3856\r\n"
404                 "Host: %s:%d\r\n"
405                 "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n"
406                 "Pragma: xClientGUID={"GUID_FMT"}\r\n"
407                 "Connection: Close\r\n",
408                 ( p_sys->url.psz_path == NULL || *p_sys->url.psz_path == '\0' ) ? "/" : p_sys->url.psz_path,
409                 p_sys->url.psz_host, p_sys->url.i_port,
410                 p_sys->i_request_context++,
411                 GUID_PRINT( p_sys->guid ) );
412
413     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" ) < 0 )
414     {
415         msg_Err( p_access, "failed to send request" );
416         goto error;
417     }
418
419     /* Receive the http header */
420     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL ) ) == NULL )
421     {
422         msg_Err( p_access, "failed to read answer" );
423         goto error;
424     }
425     if( strncmp( psz, "HTTP/1.", 7 ) )
426     {
427         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
428         free( psz );
429         goto error;
430     }
431     i_code = atoi( &psz[9] );
432     if( i_code >= 400 )
433     {
434         msg_Err( p_access, "error: %s", psz );
435         free( psz );
436         goto error;
437     }
438
439     msg_Dbg( p_access, "HTTP reply '%s'", psz );
440     free( psz );
441     for( ;; )
442     {
443         char *psz = net_Gets( p_access, p_sys->fd, NULL );
444         char *p;
445
446         if( psz == NULL )
447         {
448             msg_Err( p_access, "failed to read answer" );
449             goto error;
450         }
451
452         if( *psz == '\0' )
453         {
454             free( psz );
455             break;
456         }
457
458         if( ( p = strchr( psz, ':' ) ) == NULL )
459         {
460             msg_Err( p_access, "malformed header line: %s", psz );
461             free( psz );
462             goto error;
463         }
464         *p++ = '\0';
465         while( *p == ' ' ) p++;
466
467         /* FIXME FIXME test Content-Type to see if it's a plain stream or an
468          * asx FIXME */
469         if( !strcasecmp( psz, "Pragma" ) )
470         {
471             if( strstr( p, "features" ) )
472             {
473                 /* FIXME, it is a bit badly done here ..... */
474                 if( strstr( p, "broadcast" ) )
475                 {
476                     msg_Dbg( p_access, "stream type = broadcast" );
477                     p_sys->b_broadcast = VLC_TRUE;
478                 }
479                 else if( strstr( p, "seekable" ) )
480                 {
481                     msg_Dbg( p_access, "stream type = seekable" );
482                     p_sys->b_broadcast = VLC_FALSE;
483                 }
484                 else
485                 {
486                     msg_Warn( p_access, "unknow stream types (%s)", p );
487                     p_sys->b_broadcast = VLC_FALSE;
488                 }
489             }
490         }
491         else if( !strcasecmp( psz, "Location" ) )
492         {
493             psz_location = strdup( p );
494         }
495
496         free( psz );
497     }
498
499     /* Handle the redirection */
500     if( ( i_code == 301 || i_code == 302 ||
501           i_code == 303 || i_code == 307 ) &&
502         psz_location && *psz_location )
503     {
504         msg_Dbg( p_access, "redirection to %s", psz_location );
505         net_Close( p_sys->fd ); p_sys->fd = -1;
506
507         *ppsz_location = psz_location;
508         return VLC_SUCCESS;
509     }
510
511     /* Read the asf header */
512     p_sys->i_header = 0;
513     p_sys->p_header = NULL;
514     for( ;; )
515     {
516         chunk_t ck;
517         if( GetPacket( p_access, &ck ) ||
518             (ck.i_type != 0x4824) )
519         {
520             break;
521         }
522
523         if( ck.i_data > 0 )
524         {
525             p_sys->i_header += ck.i_data;
526             p_sys->p_header = realloc( p_sys->p_header, p_sys->i_header );
527             memcpy( &p_sys->p_header[p_sys->i_header - ck.i_data],
528                     ck.p_data, ck.i_data );
529         }
530     }
531     msg_Dbg( p_access, "complete header size=%d", p_sys->i_header );
532     if( p_sys->i_header <= 0 )
533     {
534         msg_Err( p_access, "header size == 0" );
535         goto error;
536     }
537     /* close this connection */
538     net_Close( p_sys->fd ); p_sys->fd = -1;
539
540     /* *** parse header and get stream and their id *** */
541     /* get all streams properties,
542      *
543      * TODO : stream bitrates properties(optional)
544      *        and bitrate mutual exclusion(optional) */
545     E_( asf_HeaderParse )( &p_sys->asfh,
546                            p_sys->p_header, p_sys->i_header );
547     msg_Dbg( p_access, "packet count="I64Fd" packet size=%d",
548              p_sys->asfh.i_data_packets_count,
549              p_sys->asfh.i_min_data_packet_size );
550
551     E_( asf_StreamSelect)( &p_sys->asfh,
552                            var_CreateGetInteger( p_access, "mms-maxbitrate" ),
553                            var_CreateGetInteger( p_access, "mms-all" ),
554                            var_CreateGetInteger( p_access, "audio" ),
555                            var_CreateGetInteger( p_access, "video" ) );
556     return VLC_SUCCESS;
557
558 error:
559     if( p_sys->fd > 0 )
560     {
561         net_Close( p_sys->fd  );
562         p_sys->fd = -1;
563     }
564     return VLC_EGENERIC;
565 }
566
567 /*****************************************************************************
568  *
569  *****************************************************************************/
570 static int Start( access_t *p_access, off_t i_pos )
571 {
572     access_sys_t *p_sys = p_access->p_sys;
573     int  i_streams = 0;
574     int  i;
575     char *psz;
576
577     msg_Dbg( p_access, "starting stream" );
578
579     if( ( p_sys->fd = net_ConnectTCP( p_access, p_sys->url.psz_host,
580                                       p_sys->url.i_port ) ) < 0 )
581     {
582         /* should not occur */
583         msg_Err( p_access, "cannot connect to the server" );
584         return VLC_EGENERIC;
585     }
586
587     for( i = 1; i < 128; i++ )
588     {
589         if( p_sys->asfh.stream[i].i_selected )
590         {
591             i_streams++;
592         }
593     }
594
595     if( i_streams <= 0 )
596     {
597         msg_Err( p_access, "no stream selected" );
598         return VLC_EGENERIC;
599     }
600     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
601                 "GET %s HTTP/1.0\r\n"
602                 "Accept: */*\r\n"
603                 "User-Agent: NSPlayer/4.1.0.3856\r\n"
604                 "Host: %s:%d\r\n",
605                 ( p_sys->url.psz_path == NULL || *p_sys->url.psz_path == '\0' ) ? "/" : p_sys->url.psz_path,
606                 p_sys->url.psz_host, p_sys->url.i_port );
607     if( p_sys->b_broadcast )
608     {
609         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
610                     "Pragma: no-cache,rate=1.000000,request-context=%d\r\n",
611                     p_sys->i_request_context++ );
612     }
613     else
614     {
615         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
616                     "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n",
617                     (uint32_t)((i_pos >> 32)&0xffffffff),
618                     (uint32_t)(i_pos&0xffffffff),
619                     p_sys->i_request_context++ );
620     }
621     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
622                 "Pragma: xPlayStrm=1\r\n"
623                 "Pragma: xClientGUID={"GUID_FMT"}\r\n"
624                 "Pragma: stream-switch-count=%d\r\n"
625                 "Pragma: stream-switch-entry=",
626                 GUID_PRINT( p_sys->guid ),
627                 i_streams);
628
629     for( i = 1; i < 128; i++ )
630     {
631         if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN )
632         {
633             int i_select = 2;
634             if( p_sys->asfh.stream[i].i_selected )
635             {
636                 i_select = 0;
637             }
638
639             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
640                         "ffff:%d:%d ", i, i_select );
641         }
642     }
643     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" );
644     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
645                 "Connection: Close\r\n" );
646
647     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" ) < 0 )
648     {
649         msg_Err( p_access, "failed to send request" );
650         return VLC_EGENERIC;
651     }
652
653     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL ) ) == NULL )
654     {
655         msg_Err( p_access, "cannot read data" );
656         return VLC_EGENERIC;
657     }
658     if( atoi( &psz[9] ) >= 400 )
659     {
660         msg_Err( p_access, "error: %s", psz );
661         free( psz );
662         return VLC_EGENERIC;
663     }
664     msg_Dbg( p_access, "HTTP reply '%s'", psz );
665     free( psz );
666
667     /* FIXME check HTTP code */
668     for( ;; )
669     {
670         char *psz = net_Gets( p_access, p_sys->fd, NULL );
671         if( psz == NULL )
672         {
673             msg_Err( p_access, "cannot read data" );
674             return VLC_EGENERIC;
675         }
676         if( *psz == '\0' )
677         {
678             free( psz );
679             break;
680         }
681         msg_Dbg( p_access, "%s", psz );
682         free( psz );
683     }
684
685     p_sys->i_packet_used   = 0;
686     p_sys->i_packet_length = 0;
687
688     return VLC_SUCCESS;
689 }
690
691 /*****************************************************************************
692  *
693  *****************************************************************************/
694 static void Stop( access_t *p_access )
695 {
696     access_sys_t *p_sys = p_access->p_sys;
697
698     msg_Dbg( p_access, "closing stream" );
699     if( p_sys->fd > 0 )
700     {
701         net_Close( p_sys->fd );
702         p_sys->fd = -1;
703     }
704 }
705
706 /*****************************************************************************
707  *
708  *****************************************************************************/
709 static int GetPacket( access_t * p_access, chunk_t *p_ck )
710 {
711     access_sys_t *p_sys = p_access->p_sys;
712     int restsize;
713
714     /* chunk_t */
715     memset( p_ck, 0, sizeof( chunk_t ) );
716
717     /* Read the chunk header */
718     /* Some headers are short, like 0x4324. Reading 12 bytes will cause us
719      * to lose synchronization with the stream. Just read to the length
720      * (4 bytes), decode and then read up to 8 additional bytes to get the
721      * entire header.
722      */
723     if( net_Read( p_access, p_sys->fd, NULL, p_sys->buffer, 4, VLC_TRUE ) < 4 )
724     {
725        msg_Err( p_access, "cannot read data" );
726        return VLC_EGENERIC;
727     }
728     p_ck->i_type = GetWLE( p_sys->buffer);
729     p_ck->i_size = GetWLE( p_sys->buffer + 2);
730
731     restsize = p_ck->i_size;
732     if( restsize > 8 )
733         restsize = 8;
734
735     if( net_Read( p_access, p_sys->fd, NULL, p_sys->buffer + 4, restsize, VLC_TRUE ) < restsize )
736     {
737         msg_Err( p_access, "cannot read data" );
738         return VLC_EGENERIC;
739     }
740     p_ck->i_sequence  = GetDWLE( p_sys->buffer + 4);
741     p_ck->i_unknown   = GetWLE( p_sys->buffer + 8);
742
743     /* Set i_size2 to 8 if this header was short, since a real value won't be
744      * present in the buffer. Using 8 avoid reading additional data for the
745      * packet.
746      */
747     if( restsize < 8 )
748         p_ck->i_size2 = 8;
749     else
750         p_ck->i_size2 = GetWLE( p_sys->buffer + 10);
751
752     p_ck->p_data      = p_sys->buffer + 12;
753     p_ck->i_data      = p_ck->i_size2 - 8;
754
755     if( p_ck->i_type == 0x4524 )   // Transfer complete
756     {
757         if( p_ck->i_sequence == 0 )
758         {
759             msg_Warn( p_access, "EOF" );
760             return VLC_EGENERIC;
761         }
762         else
763         {
764             msg_Warn( p_access, "next stream follow but not supported" );
765             return VLC_EGENERIC;
766         }
767     }
768     /* 0x4324 is CHUNK_TYPE_RESET. We can safely ignore it: a new stream will
769      * follow with a sequence of 0 */
770     else if( (p_ck->i_type != 0x4824) && (p_ck->i_type != 0x4424) &&
771              (p_ck->i_type != 0x4324) )
772     {
773         msg_Err( p_access, "invalid chunk FATAL (0x%x)", p_ck->i_type );
774         return VLC_EGENERIC;
775     }
776
777     if( (p_ck->i_data > 0) &&
778         (net_Read( p_access, p_sys->fd, NULL, &p_sys->buffer[12],
779                    p_ck->i_data, VLC_TRUE ) < p_ck->i_data) )
780     {
781         msg_Err( p_access, "cannot read data" );
782         return VLC_EGENERIC;
783     }
784
785     if( (p_sys->i_packet_sequence != 0) &&
786         (p_ck->i_sequence != p_sys->i_packet_sequence) )
787     {
788         msg_Warn( p_access, "packet lost ? (%d != %d)", p_ck->i_sequence, p_sys->i_packet_sequence );
789     }
790
791     p_sys->i_packet_sequence = p_ck->i_sequence + 1;
792     p_sys->i_packet_used   = 0;
793     p_sys->i_packet_length = p_ck->i_data;
794     p_sys->p_packet        = p_ck->p_data;
795
796     return VLC_SUCCESS;
797 }