* mms/* : converted to access2. It should be reworked to use pf_block, but
[vlc.git] / 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)  ( access_t * );
50 void E_(MMSHClose) ( access_t * );
51
52 static int  Read( access_t *, uint8_t *, int );
53 static int  ReadRedirect( access_t *, uint8_t *, int );
54 static int  Seek( access_t *, int64_t );
55 static int  Control( access_t *, int, va_list );
56
57 static int  Describe( access_t  *, char **ppsz_location );
58 static int  Start( access_t *, int64_t );
59 static void Stop( access_t * );
60 static int  GetPacket( access_t *, chunk_t * );
61
62 /****************************************************************************
63  * Open: connect to ftp server and ask for file
64  ****************************************************************************/
65 int E_(MMSHOpen)( access_t *p_access )
66 {
67     access_sys_t    *p_sys;
68     char            *psz_location = NULL;
69     vlc_value_t     val;
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     /* Update default_pts to a suitable value for mms access */
149     var_Get( p_access, "mms-caching", &val );
150
151     return VLC_SUCCESS;
152 }
153
154 /*****************************************************************************
155  * Close: free unused data structures
156  *****************************************************************************/
157 void E_( MMSHClose )( access_t *p_access )
158 {
159     access_sys_t *p_sys = p_access->p_sys;
160
161     Stop( p_access );
162     free( p_sys );
163 }
164
165 /*****************************************************************************
166  * Control:
167  *****************************************************************************/
168 static int Control( access_t *p_access, int i_query, va_list args )
169 {
170     access_sys_t *p_sys = p_access->p_sys;
171     vlc_bool_t   *pb_bool;
172     int          *pi_int;
173     int64_t      *pi_64;
174     vlc_value_t  val;
175
176     switch( i_query )
177     {
178         /* */
179         case ACCESS_CAN_SEEK:
180             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
181             *pb_bool = !p_sys->b_broadcast;
182             break;
183         case ACCESS_CAN_FASTSEEK:
184         case ACCESS_CAN_PAUSE:
185         case ACCESS_CAN_CONTROL_PACE:
186             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
187             *pb_bool = VLC_FALSE;
188             break;
189
190         /* */
191         case ACCESS_GET_MTU:
192             pi_int = (int*)va_arg( args, int * );
193             *pi_int = 3 * p_sys->asfh.i_min_data_packet_size;
194             break;
195
196         case ACCESS_GET_PTS_DELAY:
197             pi_64 = (int64_t*)va_arg( args, int64_t * );
198             var_Get( p_access, "mms-caching", &val );
199             *pi_64 = val.i_int * 1000;
200             break;
201
202         /* */
203         case ACCESS_SET_PAUSE_STATE:
204         case ACCESS_GET_TITLE_INFO:
205         case ACCESS_SET_TITLE:
206         case ACCESS_SET_SEEKPOINT:
207             return VLC_EGENERIC;
208
209         default:
210             msg_Err( p_access, "unimplemented query in control" );
211             return VLC_EGENERIC;
212
213     }
214     return VLC_SUCCESS;
215 }
216
217 /*****************************************************************************
218  * Seek: try to go at the right place
219  *****************************************************************************/
220 static int Seek( access_t *p_access, int64_t i_pos )
221 {
222     access_sys_t *p_sys = p_access->p_sys;
223     chunk_t      ck;
224     off_t        i_offset;
225     off_t        i_packet;
226
227     msg_Dbg( p_access, "seeking to "I64Fd, i_pos );
228
229     i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
230     i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
231
232     Stop( p_access );
233     Start( p_access, i_packet * p_sys->asfh.i_min_data_packet_size );
234
235     while( !p_access->b_die )
236     {
237         if( GetPacket( p_access, &ck ) )
238             break;
239
240         /* skip headers */
241         if( ck.i_type != 0x4824 )
242             break;
243
244         msg_Warn( p_access, "skipping header" );
245     }
246
247     p_access->info.i_pos = i_pos;
248     p_access->info.b_eof = VLC_FALSE;
249     p_sys->i_packet_used += i_offset;
250
251     return VLC_SUCCESS;
252 }
253
254 /*****************************************************************************
255  * Read:
256  *****************************************************************************/
257 static int ReadRedirect( access_t *p_access, uint8_t *p, int i_len )
258 {
259     return 0;
260 }
261
262 /*****************************************************************************
263  * Read:
264  *****************************************************************************/
265 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
266 {
267     access_sys_t *p_sys = p_access->p_sys;
268     size_t       i_copy;
269     size_t       i_data = 0;
270
271     if( p_access->info.b_eof )
272         return 0;
273
274     while( i_data < i_len )
275     {
276         if( p_access->info.i_pos < p_sys->i_start + p_sys->i_header )
277         {
278             int i_offset = p_access->info.i_pos - p_sys->i_start;
279             i_copy = __MIN( p_sys->i_header - i_offset, i_len - i_data );
280             memcpy( &p_buffer[i_data], &p_sys->p_header[i_offset], i_copy );
281
282             i_data += i_copy;
283             p_access->info.i_pos += i_copy;
284         }
285         else if( p_sys->i_packet_used < p_sys->i_packet_length )
286         {
287             i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used,
288                             i_len - i_data );
289
290             memcpy( &p_buffer[i_data],
291                     &p_sys->p_packet[p_sys->i_packet_used],
292                     i_copy );
293
294             i_data += i_copy;
295             p_sys->i_packet_used += i_copy;
296             p_access->info.i_pos += i_copy;
297         }
298         else if( p_sys->i_packet_length > 0 &&
299                  (int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size )
300         {
301             i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used,
302                             i_len - i_data );
303
304             memset( &p_buffer[i_data], 0, i_copy );
305
306             i_data += i_copy;
307             p_sys->i_packet_used += i_copy;
308             p_access->info.i_pos += i_copy;
309         }
310         else
311         {
312             chunk_t ck;
313             if( GetPacket( p_access, &ck ) )
314             {
315                 if( ck.i_type == 0x4524 && ck.i_sequence != 0 && p_sys->b_broadcast )
316                 {
317                     char *psz_location = NULL;
318
319                     p_sys->i_start = p_access->info.i_pos;
320
321                     msg_Dbg( p_access, "stoping the stream" );
322                     Stop( p_access );
323
324                     msg_Dbg( p_access, "describe the stream" );
325                     if( Describe( p_access, &psz_location ) )
326                     {
327                         msg_Err( p_access, "describe failed" );
328                         p_access->info.b_eof = VLC_TRUE;
329                         return 0;
330                     }
331                     if( Start( p_access, 0 ) )
332                     {
333                         msg_Err( p_access, "Start failed" );
334                         p_access->info.b_eof = VLC_TRUE;
335                         return 0;
336                     }
337                 }
338                 else
339                 {
340                     p_access->info.b_eof = VLC_TRUE;
341                     return 0;
342                 }
343             }
344             if( ck.i_type != 0x4424 )
345             {
346                 p_sys->i_packet_used = 0;
347                 p_sys->i_packet_length = 0;
348             }
349         }
350     }
351
352     return( i_data );
353 }
354
355 /*****************************************************************************
356  * Describe:
357  *****************************************************************************/
358 static int Describe( access_t  *p_access, char **ppsz_location )
359 {
360     access_sys_t *p_sys = p_access->p_sys;
361     char         *psz_location = NULL;
362     char         *psz;
363     int          i_code;
364
365     /* Reinit context */
366     p_sys->b_broadcast = VLC_TRUE;
367     p_sys->i_request_context = 1;
368     p_sys->i_packet_sequence = 0;
369     p_sys->i_packet_used = 0;
370     p_sys->i_packet_length = 0;
371     p_sys->p_packet = NULL;
372     E_( GenerateGuid )( &p_sys->guid );
373
374     if( ( p_sys->fd = net_OpenTCP( p_access, p_sys->url.psz_host,
375                                             p_sys->url.i_port ) ) < 0 )
376     {
377         msg_Err( p_access, "cannot connect to%s:%d", p_sys->url.psz_host, p_sys->url.i_port );
378         goto error;
379     }
380
381     /* send first request */
382     net_Printf( VLC_OBJECT(p_access), p_sys->fd,
383                 "GET %s HTTP/1.0\r\n"
384                 "Accept: */*\r\n"
385                 "User-Agent: NSPlayer/4.1.0.3856\r\n"
386                 "Host: %s:%d\r\n"
387                 "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n"
388                 "Pragma: xClientGUID={"GUID_FMT"}\r\n"
389                 "Connection: Close\r\n",
390                 ( p_sys->url.psz_path == NULL || *p_sys->url.psz_path == '\0' ) ? "/" : p_sys->url.psz_path,
391                 p_sys->url.psz_host, p_sys->url.i_port,
392                 p_sys->i_request_context++,
393                 GUID_PRINT( p_sys->guid ) );
394
395     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, "\r\n" ) < 0 )
396     {
397         msg_Err( p_access, "failed to send request" );
398         goto error;
399     }
400
401     /* Receive the http header */
402     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd ) ) == NULL )
403     {
404         msg_Err( p_access, "failed to read answer" );
405         goto error;
406     }
407     if( strncmp( psz, "HTTP/1.", 7 ) )
408     {
409         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
410         free( psz );
411         goto error;
412     }
413     i_code = atoi( &psz[9] );
414     if( i_code >= 400 )
415     {
416         msg_Err( p_access, "error: %s", psz );
417         free( psz );
418         goto error;
419     }
420
421     msg_Dbg( p_access, "HTTP reply '%s'", psz );
422     free( psz );
423     for( ;; )
424     {
425         char *psz = net_Gets( p_access, p_sys->fd );
426         char *p;
427
428         if( psz == NULL )
429         {
430             msg_Err( p_access, "failed to read answer" );
431             goto error;
432         }
433
434         if( *psz == '\0' )
435         {
436             free( psz );
437             break;
438         }
439
440         if( ( p = strchr( psz, ':' ) ) == NULL )
441         {
442             msg_Err( p_access, "malformed header line: %s", psz );
443             free( psz );
444             goto error;
445         }
446         *p++ = '\0';
447         while( *p == ' ' ) p++;
448
449         /* FIXME FIXME test Content-Type to see if it's a plain stream or an
450          * asx FIXME */
451         if( !strcasecmp( psz, "Pragma" ) )
452         {
453             if( strstr( p, "features" ) )
454             {
455                 /* FIXME, it is a bit badly done here ..... */
456                 if( strstr( p, "broadcast" ) )
457                 {
458                     msg_Dbg( p_access, "stream type = broadcast" );
459                     p_sys->b_broadcast = VLC_TRUE;
460                 }
461                 else if( strstr( p, "seekable" ) )
462                 {
463                     msg_Dbg( p_access, "stream type = seekable" );
464                     p_sys->b_broadcast = VLC_FALSE;
465                 }
466                 else
467                 {
468                     msg_Warn( p_access, "unknow stream types (%s)", p );
469                     p_sys->b_broadcast = VLC_FALSE;
470                 }
471             }
472         }
473         else if( !strcasecmp( psz, "Location" ) )
474         {
475             psz_location = strdup( p );
476         }
477
478         free( psz );
479     }
480
481     /* Handle the redirection */
482     if( ( i_code == 301 || i_code == 302 ||
483           i_code == 303 || i_code == 307 ) &&
484         psz_location && *psz_location )
485     {
486         msg_Dbg( p_access, "redirection to %s", psz_location );
487         net_Close( p_sys->fd ); p_sys->fd = -1;
488
489         *ppsz_location = psz_location;
490         return VLC_SUCCESS;
491     }
492
493     /* Read the asf header */
494     p_sys->i_header = 0;
495     p_sys->p_header = NULL;
496     for( ;; )
497     {
498         chunk_t ck;
499         if( GetPacket( p_access, &ck ) ||
500             ck.i_type != 0x4824 )
501         {
502             break;
503         }
504
505         if( ck.i_data > 0 )
506         {
507             p_sys->i_header += ck.i_data;
508             p_sys->p_header = realloc( p_sys->p_header, p_sys->i_header );
509             memcpy( &p_sys->p_header[p_sys->i_header - ck.i_data],
510                     ck.p_data, ck.i_data );
511         }
512     }
513     msg_Dbg( p_access, "complete header size=%d", p_sys->i_header );
514     if( p_sys->i_header <= 0 )
515     {
516         msg_Err( p_access, "header size == 0" );
517         goto error;
518     }
519     /* close this connection */
520     net_Close( p_sys->fd ); p_sys->fd = -1;
521
522     /* *** parse header and get stream and their id *** */
523     /* get all streams properties,
524      *
525      * TODO : stream bitrates properties(optional)
526      *        and bitrate mutual exclusion(optional) */
527     E_( asf_HeaderParse )( &p_sys->asfh,
528                            p_sys->p_header, p_sys->i_header );
529     msg_Dbg( p_access, "packet count=%lld packet size=%d",
530              p_sys->asfh.i_data_packets_count,
531              p_sys->asfh.i_min_data_packet_size );
532
533     E_( asf_StreamSelect)( &p_sys->asfh,
534                            config_GetInt( p_access, "mms-maxbitrate" ),
535                            config_GetInt( p_access, "mms-all" ),
536                            config_GetInt( p_access, "audio" ),
537                            config_GetInt( p_access, "video" ) );
538
539     return VLC_SUCCESS;
540
541 error:
542     if( p_sys->fd > 0 )
543     {
544         net_Close( p_sys->fd  );
545         p_sys->fd = -1;
546     }
547     return VLC_EGENERIC;
548 }
549
550 /*****************************************************************************
551  *
552  *****************************************************************************/
553 static int Start( access_t *p_access, off_t i_pos )
554 {
555     access_sys_t *p_sys = p_access->p_sys;
556     int  i_streams = 0;
557     int  i;
558     char *psz;
559
560     msg_Dbg( p_access, "starting stream" );
561
562     if( ( p_sys->fd = net_OpenTCP( p_access, p_sys->url.psz_host,
563                                             p_sys->url.i_port ) ) < 0 )
564     {
565         /* should not occur */
566         msg_Err( p_access, "cannot connect to the server" );
567         return VLC_EGENERIC;
568     }
569
570     for( i = 1; i < 128; i++ )
571     {
572         if( p_sys->asfh.stream[i].i_selected )
573         {
574             i_streams++;
575         }
576     }
577
578     if( i_streams <= 0 )
579     {
580         msg_Err( p_access, "no stream selected" );
581         return VLC_EGENERIC;
582     }
583     net_Printf( VLC_OBJECT(p_access), p_sys->fd,
584                 "GET %s HTTP/1.0\r\n"
585                 "Accept: */*\r\n"
586                 "User-Agent: NSPlayer/4.1.0.3856\r\n"
587                 "Host: %s:%d\r\n",
588                 ( p_sys->url.psz_path == NULL || *p_sys->url.psz_path == '\0' ) ? "/" : p_sys->url.psz_path,
589                 p_sys->url.psz_host, p_sys->url.i_port );
590     if( p_sys->b_broadcast )
591     {
592         net_Printf( VLC_OBJECT(p_access), p_sys->fd,
593                     "Pragma: no-cache,rate=1.000000,request-context=%d\r\n",
594                     p_sys->i_request_context++ );
595     }
596     else
597     {
598         net_Printf( VLC_OBJECT(p_access), p_sys->fd,
599                     "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n",
600                     (uint32_t)((i_pos >> 32)&0xffffffff),
601                     (uint32_t)(i_pos&0xffffffff),
602                     p_sys->i_request_context++ );
603     }
604     net_Printf( VLC_OBJECT(p_access), p_sys->fd,
605                 "Pragma: xPlayStrm=1\r\n"
606                 "Pragma: xClientGUID={"GUID_FMT"}\r\n"
607                 "Pragma: stream-switch-count=%d\r\n"
608                 "Pragma: stream-switch-entry=",
609                 GUID_PRINT( p_sys->guid ),
610                 i_streams);
611
612     for( i = 1; i < 128; i++ )
613     {
614         if( p_sys->asfh.stream[i].i_cat != ASF_STREAM_UNKNOWN )
615         {
616             int i_select = 2;
617             if( p_sys->asfh.stream[i].i_selected )
618             {
619                 i_select = 0;
620             }
621
622             net_Printf( VLC_OBJECT(p_access), p_sys->fd,
623                         "ffff:%d:%d ", i, i_select );
624         }
625     }
626     net_Printf( VLC_OBJECT(p_access), p_sys->fd, "\r\n" );
627     net_Printf( VLC_OBJECT(p_access), p_sys->fd, "Connection: Close\r\n" );
628
629     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, "\r\n" ) < 0 )
630     {
631         msg_Err( p_access, "failed to send request" );
632         return VLC_EGENERIC;
633     }
634
635     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd ) ) == NULL )
636     {
637         msg_Err( p_access, "cannot read data" );
638         return VLC_EGENERIC;
639     }
640     if( atoi( &psz[9] ) >= 400 )
641     {
642         msg_Err( p_access, "error: %s", psz );
643         free( psz );
644         return VLC_EGENERIC;
645     }
646     msg_Dbg( p_access, "HTTP reply '%s'", psz );
647     free( psz );
648
649     /* FIXME check HTTP code */
650     for( ;; )
651     {
652         char *psz = net_Gets( p_access, p_sys->fd );
653         if( psz == NULL )
654         {
655             msg_Err( p_access, "cannot read data" );
656             return VLC_EGENERIC;
657         }
658         if( *psz == '\0' )
659         {
660             free( psz );
661             break;
662         }
663         msg_Dbg( p_access, "%s", psz );
664         free( psz );
665     }
666
667     p_sys->i_packet_used   = 0;
668     p_sys->i_packet_length = 0;
669
670     return VLC_SUCCESS;
671 }
672
673 /*****************************************************************************
674  *
675  *****************************************************************************/
676 static void Stop( access_t *p_access )
677 {
678     access_sys_t *p_sys = p_access->p_sys;
679
680     msg_Dbg( p_access, "closing stream" );
681     if( p_sys->fd > 0 )
682     {
683         net_Close( p_sys->fd );
684         p_sys->fd = -1;
685     }
686 }
687
688 /*****************************************************************************
689  *
690  *****************************************************************************/
691 static int GetPacket( access_t * p_access, chunk_t *p_ck )
692 {
693     access_sys_t *p_sys = p_access->p_sys;
694
695     /* chunk_t */
696     memset( p_ck, 0, sizeof( chunk_t ) );
697
698     /* Read the chunk header */
699     if( net_Read( p_access, p_sys->fd, p_sys->buffer, 12, VLC_TRUE ) < 12 )
700     {
701         /* msg_Err( p_access, "cannot read data" ); */
702         return VLC_EGENERIC;
703     }
704
705     p_ck->i_type      = GetWLE( p_sys->buffer);
706     p_ck->i_size      = GetWLE( p_sys->buffer + 2);
707     p_ck->i_sequence  = GetDWLE( p_sys->buffer + 4);
708     p_ck->i_unknown   = GetWLE( p_sys->buffer + 8);
709     p_ck->i_size2     = GetWLE( p_sys->buffer + 10);
710     p_ck->p_data      = p_sys->buffer + 12;
711     p_ck->i_data      = p_ck->i_size2 - 8;
712
713     if( p_ck->i_type == 0x4524 )   // Transfer complete
714     {
715         if( p_ck->i_sequence == 0 )
716         {
717             msg_Warn( p_access, "EOF" );
718             return VLC_EGENERIC;
719         }
720         else
721         {
722             msg_Warn( p_access, "Next stream follow but not supported" );
723             return VLC_EGENERIC;
724         }
725     }
726     else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 )
727     {
728         msg_Err( p_access, "invalid chunk FATAL (0x%x)", p_ck->i_type );
729         return VLC_EGENERIC;
730     }
731
732     if( p_ck->i_data > 0 &&
733         net_Read( p_access, p_sys->fd, &p_sys->buffer[12], p_ck->i_data, VLC_TRUE ) < p_ck->i_data )
734     {
735         msg_Err( p_access, "cannot read data" );
736         return VLC_EGENERIC;
737     }
738
739     if( p_sys->i_packet_sequence != 0 &&
740         p_ck->i_sequence != p_sys->i_packet_sequence )
741     {
742         msg_Warn( p_access, "packet lost ? (%d != %d)", p_ck->i_sequence, p_sys->i_packet_sequence );
743     }
744
745     p_sys->i_packet_sequence = p_ck->i_sequence + 1;
746     p_sys->i_packet_used   = 0;
747     p_sys->i_packet_length = p_ck->i_data;
748     p_sys->p_packet        = p_ck->p_data;
749
750     return VLC_SUCCESS;
751 }