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