1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: mmsh.c,v 1.6 2003/08/26 00:51:19 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
30 /*****************************************************************************
32 *****************************************************************************/
36 #include <vlc/input.h>
44 #ifdef HAVE_SYS_TIME_H
45 # include <sys/time.h>
52 #if defined( UNDER_CE )
54 #elif defined( WIN32 )
55 # include <winsock2.h>
56 # include <ws2tcpip.h>
58 # define IN_MULTICAST(a) IN_CLASSD(a)
61 # include <sys/socket.h>
71 /*****************************************************************************
73 *****************************************************************************/
74 int E_( MMSHOpen ) ( input_thread_t * );
75 void E_( MMSHClose ) ( input_thread_t * );
77 static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer,
79 static void Seek ( input_thread_t *, off_t );
81 /****************************************************************************
82 ****************************************************************************
83 ******************* *******************
84 ******************* Main functions *******************
85 ******************* *******************
86 ****************************************************************************
87 ****************************************************************************/
89 /****************************************************************************
90 * Open: connect to ftp server and ask for file
91 ****************************************************************************/
92 int E_( MMSHOpen ) ( input_thread_t *p_input )
98 http_field_t *p_field;
102 p_input->p_access_data = p_sys = malloc( sizeof( access_sys_t ) );
103 p_sys->i_proto = MMS_PROTO_HTTP;
105 p_sys->p_socket = NULL;
106 p_sys->i_request_context = 1;
108 p_sys->i_buffer_pos = 0;
109 p_sys->b_broadcast = VLC_TRUE;
110 p_sys->p_packet = NULL;
111 p_sys->i_packet_sequence = 0;
112 p_sys->i_packet_used = 0;
113 p_sys->i_packet_length = 0;
115 p_sys->i_request_context = 1;
116 E_( GenerateGuid )( &p_sys->guid );
118 /* open a tcp connection */
119 p_sys->p_url = E_( url_new )( p_input->psz_name );
121 if( *p_sys->p_url->psz_host == '\0' )
123 msg_Err( p_input, "invalid server addresse" );
126 if( p_sys->p_url->i_port <= 0 )
128 p_sys->p_url->i_port = 80;
131 if( ( p_sys->p_socket = NetOpenTCP( p_input, p_sys->p_url ) ) == NULL )
133 msg_Err( p_input, "cannot connect" );
137 /* *** send first request *** */
138 p = &p_sys->buffer[0];
139 p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path );
140 p += sprintf( p,"Accept: */*\r\n" );
141 p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" );
142 p += sprintf( p, "Host: %s:%d\r\n",
143 p_sys->p_url->psz_host, p_sys->p_url->i_port );
144 p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n",
145 p_sys->i_request_context++ );
146 p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n",
147 GUID_PRINT( p_sys->guid ) );
148 p += sprintf( p, "Connection: Close\r\n\r\n" );
149 NetWrite( p_input, p_sys->p_socket, p_sys->buffer, p - p_sys->buffer );
152 if( NetFill ( p_input, p_sys, BUFFER_SIZE ) <= 0 )
154 msg_Err( p_input, "cannot read answer" );
157 NetClose( p_input, p_sys->p_socket );
158 p_sys->p_socket = NULL;
160 p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer );
163 msg_Err( p_input, "cannot parse answer" );
167 if( p_ans->i_error >= 400 )
169 msg_Err( p_input, "error %d (server return=`%s')",
170 p_ans->i_error, p_ans->psz_answer );
171 http_answer_free( p_ans );
174 else if( p_ans->i_error >= 300 )
176 msg_Err( p_input, "FIXME redirect unsuported %d (server return=`%s')",
177 p_ans->i_error, p_ans->psz_answer );
178 http_answer_free( p_ans );
181 else if( p_ans->i_body <= 0 )
183 msg_Err( p_input, "empty answer" );
184 http_answer_free( p_ans );
188 /* now get features */
189 /* FIXME FIXME test Content-Type to see if it's a plain stream or an
191 for( p_field = p_ans->p_fields;
193 p_field = http_field_find( p_field->p_next, "Pragma" ) )
195 if( !strncasecmp( p_field->psz_value, "features", 8 ) )
197 if( strstr( p_field->psz_value, "broadcast" ) )
199 msg_Dbg( p_input, "stream type = broadcast" );
200 p_sys->b_broadcast = VLC_TRUE;
202 else if( strstr( p_field->psz_value, "seekable" ) )
204 msg_Dbg( p_input, "stream type = seekable" );
205 p_sys->b_broadcast = VLC_FALSE;
209 msg_Warn( p_input, "unknow stream types (%s)",
210 p_field->psz_value );
211 p_sys->b_broadcast = VLC_FALSE;
218 p_sys->p_header = malloc( p_ans->i_body );
221 if( chunk_parse( &ck, p_ans->p_body, p_ans->i_body ) )
223 msg_Err( p_input, "invalid chunk answer" );
226 if( ck.i_type != 0x4824 )
228 msg_Err( p_input, "invalid chunk (0x%x)", ck.i_type );
233 memcpy( &p_sys->p_header[p_sys->i_header],
237 p_sys->i_header += ck.i_data;
241 p_ans->p_body += 12 + ck.i_data;
242 p_ans->i_body -= 12 + ck.i_data;
244 } while( p_ans->i_body > 12 );
246 http_answer_free( p_ans );
248 msg_Dbg( p_input, "complete header size=%d", p_sys->i_header );
249 if( p_sys->i_header <= 0 )
251 msg_Err( p_input, "header size == 0" );
254 /* *** parse header and get stream and their id *** */
255 /* get all streams properties,
257 * TODO : stream bitrates properties(optional)
258 * and bitrate mutual exclusion(optional) */
259 E_( asf_HeaderParse )( &p_sys->asfh,
260 p_sys->p_header, p_sys->i_header );
261 msg_Dbg( p_input, "packet count=%lld packet size=%d",
262 p_sys->asfh.i_data_packets_count,
263 p_sys->asfh.i_min_data_packet_size );
265 E_( asf_StreamSelect)( &p_sys->asfh,
266 config_GetInt( p_input, "mms-maxbitrate" ),
267 config_GetInt( p_input, "mms-all" ),
268 config_GetInt( p_input, "audio" ),
269 config_GetInt( p_input, "video" ) );
271 if( mmsh_start( p_input, 0 ) )
273 msg_Err( p_input, "cannot start stream" );
277 /* *** set exported functions *** */
278 p_input->pf_read = Read;
279 p_input->pf_seek = Seek;
280 p_input->pf_set_program = input_SetProgram;
281 p_input->pf_set_area = NULL;
283 p_input->p_private = NULL;
284 p_input->i_mtu = 3 * p_sys->asfh.i_min_data_packet_size;
286 /* *** finished to set some variable *** */
287 vlc_mutex_lock( &p_input->stream.stream_lock );
288 p_input->stream.b_pace_control = 0;
289 if( p_sys->b_broadcast )
291 p_input->stream.p_selected_area->i_size = 0;
292 p_input->stream.b_seekable = 0;
296 p_input->stream.p_selected_area->i_size = p_sys->asfh.i_file_size;
297 p_input->stream.b_seekable = 1;
299 p_input->stream.p_selected_area->i_tell = 0;
300 p_input->stream.i_method = INPUT_METHOD_NETWORK;
301 vlc_mutex_unlock( &p_input->stream.stream_lock );
303 /* Update default_pts to a suitable value for ftp access */
304 p_input->i_pts_delay = config_GetInt( p_input, "mms-caching" ) * 1000;
307 return( VLC_SUCCESS );
310 E_( url_free )( p_sys->p_url );
312 if( p_sys->p_socket )
314 NetClose( p_input, p_sys->p_socket );
317 return( VLC_EGENERIC );
320 /*****************************************************************************
321 * Close: free unused data structures
322 *****************************************************************************/
323 void E_( MMSHClose ) ( input_thread_t *p_input )
325 access_sys_t *p_sys = p_input->p_access_data;
327 msg_Dbg( p_input, "stopping stream" );
329 mmsh_stop( p_input );
334 static int mmsh_get_packet( input_thread_t * p_input,
337 access_sys_t *p_sys = p_input->p_access_data;
339 int i_mov = p_sys->i_buffer - p_sys->i_buffer_pos;
341 if( p_sys->i_buffer_pos > BUFFER_SIZE / 2 )
345 memmove( &p_sys->buffer[0],
346 &p_sys->buffer[p_sys->i_buffer_pos],
350 p_sys->i_buffer = i_mov;
351 p_sys->i_buffer_pos = 0;
354 if( NetFill( p_input, p_sys, 12 ) < 12 )
356 msg_Warn( p_input, "cannot fill buffer" );
360 chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos],
361 p_sys->i_buffer - p_sys->i_buffer_pos );
363 if( p_ck->i_type == 0x4524 ) // Transfer complete
365 msg_Warn( p_input, "EOF" );
368 else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 )
370 msg_Err( p_input, "invalid chunk FATAL" );
374 if( p_ck->i_data < p_ck->i_size2 - 8 )
376 if( NetFill( p_input, p_sys, p_ck->i_size2 - 8 - p_ck->i_data ) <= 0 )
378 msg_Warn( p_input, "cannot fill buffer" );
381 chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos],
382 p_sys->i_buffer - p_sys->i_buffer_pos );
385 if( p_sys->i_packet_sequence != 0 &&
386 p_ck->i_sequence != p_sys->i_packet_sequence )
388 msg_Warn( p_input, "packet lost ?" );
391 p_sys->i_packet_sequence = p_ck->i_sequence + 1;
392 p_sys->i_packet_used = 0;
393 p_sys->i_packet_length = p_ck->i_data;
394 p_sys->p_packet = p_ck->p_data;
396 p_sys->i_buffer_pos += 12 + p_ck->i_data;
402 /*****************************************************************************
403 * Seek: try to go at the right place
404 *****************************************************************************/
405 static void Seek( input_thread_t * p_input, off_t i_pos )
407 access_sys_t *p_sys = p_input->p_access_data;
412 i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
413 i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
415 msg_Err( p_input, "seeking to "I64Fd, i_pos );
417 vlc_mutex_lock( &p_input->stream.stream_lock );
419 mmsh_stop( p_input );
420 mmsh_start( p_input, i_packet * p_sys->asfh.i_min_data_packet_size );
424 if( mmsh_get_packet( p_input, &ck ) )
430 if( ck.i_type != 0x4824 )
434 msg_Warn( p_input, "skipping header" );
437 p_sys->i_pos = i_pos;
438 p_sys->i_packet_used += i_offset;
441 p_input->stream.p_selected_area->i_tell = i_pos;
442 vlc_mutex_unlock( &p_input->stream.stream_lock );
446 /*****************************************************************************
448 *****************************************************************************/
449 static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer,
452 access_sys_t *p_sys = p_input->p_access_data;
456 while( i_data < i_len )
458 if( p_sys->i_packet_used < p_sys->i_packet_length )
460 i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used,
463 memcpy( &p_buffer[i_data],
464 &p_sys->p_packet[p_sys->i_packet_used],
468 p_sys->i_packet_used += i_copy;
470 else if( p_sys->i_pos + i_data > p_sys->i_header &&
471 (int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size )
473 i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used,
476 memset( &p_buffer[i_data], 0, i_copy );
479 p_sys->i_packet_used += i_copy;
484 /* get a new packet */
485 /* fill enought data (>12) */
486 msg_Dbg( p_input, "waiting data (buffer = %d bytes)",
489 if( mmsh_get_packet( p_input, &ck ) )
496 p_sys->i_pos += i_data;
502 /****************************************************************************/
503 /****************************************************************************/
504 /****************************************************************************/
505 /****************************************************************************/
506 /****************************************************************************/
508 static int mmsh_start( input_thread_t *p_input,
511 access_sys_t *p_sys = p_input->p_access_data;
515 http_answer_t *p_ans;
517 msg_Dbg( p_input, "starting stream" );
519 if( ( p_sys->p_socket = NetOpenTCP( p_input, p_sys->p_url ) ) == NULL )
521 /* should not occur */
522 msg_Err( p_input, "cannot connect to the server" );
526 for( i = 1; i < 128; i++ )
528 if( p_sys->asfh.stream[i].i_selected )
536 msg_Err( p_input, "no stream selected" );
540 p = &p_sys->buffer[0];
541 p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path );
542 p += sprintf( p,"Accept: */*\r\n" );
543 p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" );
544 p += sprintf( p, "Host: %s:%d\r\n",
545 p_sys->p_url->psz_host, p_sys->p_url->i_port );
546 if( p_sys->b_broadcast )
548 p += sprintf( p,"Pragma: no-cache,rate=1.000000,request-context=%d\r\n",
549 p_sys->i_request_context++ );
553 p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n",
554 (uint32_t)((i_pos >> 32)&0xffffffff),
555 (uint32_t)(i_pos&0xffffffff),
556 p_sys->i_request_context++ );
558 p += sprintf( p, "Pragma: xPlayStrm=1\r\n" );
559 p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n",
560 GUID_PRINT( p_sys->guid ) );
561 p += sprintf( p, "Pragma: stream-switch-count=%d\r\n", i_streams );
562 p += sprintf( p, "Pragma: stream-switch-entry=" );
563 for( i = 0; i < i_streams; i++ )
565 if( p_sys->asfh.stream[i].i_selected )
567 p += sprintf( p, "ffff:%d:0 ", p_sys->asfh.stream[i].i_id );
571 p += sprintf( p, "ffff:%d:2 ", p_sys->asfh.stream[i].i_id );
574 p += sprintf( p, "\r\n" );
575 p += sprintf( p, "Connection: Close\r\n\r\n" );
578 NetWrite( p_input, p_sys->p_socket, p_sys->buffer, p - p_sys->buffer );
580 msg_Dbg( p_input, "filling buffer" );
581 /* we read until we found a \r\n\r\n or \n\n */
583 p_sys->i_buffer_pos = 0;
590 p = &p_sys->buffer[p_sys->i_buffer];
592 NetRead( p_input, p_sys->p_socket,
593 &p_sys->buffer[p_sys->i_buffer],
602 msg_Dbg( p_input, "another try (%d/12)", i_try );
606 if( i_read <= 0 || p_input->b_die || p_input->b_error )
610 p_sys->i_buffer += i_read;
611 p_sys->buffer[p_sys->i_buffer] = '\0';
613 if( strstr( p, "\r\n\r\n" ) || strstr( p, "\n\n" ) )
615 msg_Dbg( p_input, "body found" );
618 if( p_sys->i_buffer >= BUFFER_SIZE - 1024 )
620 msg_Dbg( p_input, "buffer size exeded" );
625 p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer );
628 msg_Err( p_input, "cannot parse answer" );
632 if( p_ans->i_error < 200 || p_ans->i_error >= 300 )
634 msg_Err( p_input, "error %d (server return=`%s')",
635 p_ans->i_error, p_ans->psz_answer );
636 http_answer_free( p_ans );
642 p_sys->i_buffer_pos = 0;
647 p_sys->i_buffer_pos = p_ans->p_body - p_sys->buffer;
649 http_answer_free( p_ans );
654 static void mmsh_stop( input_thread_t *p_input )
656 access_sys_t *p_sys = p_input->p_access_data;
658 msg_Dbg( p_input, "closing stream" );
659 NetClose( p_input, p_sys->p_socket );
660 p_sys->p_socket = NULL;
663 static ssize_t NetFill( input_thread_t *p_input,
664 access_sys_t *p_sys, int i_size )
669 i_size = __MIN( i_size, BUFFER_SIZE - p_sys->i_buffer );
679 i_read = NetRead( p_input, p_sys->p_socket,
680 &p_sys->buffer[p_sys->i_buffer], i_size );
688 msg_Dbg( p_input, "another try %d/2", i_try );
692 if( i_read < 0 || p_input->b_die || p_input->b_error )
698 p_sys->i_buffer += i_read;
699 if( i_total >= i_size )
705 p_sys->buffer[p_sys->i_buffer] = '\0';
710 /****************************************************************************
712 ****************************************************************************/
713 static input_socket_t * NetOpenTCP( input_thread_t *p_input, url_t *p_url )
715 input_socket_t *p_socket;
718 network_socket_t socket_desc;
721 p_socket = malloc( sizeof( input_socket_t ) );
722 memset( p_socket, 0, sizeof( input_socket_t ) );
725 if( config_GetInt( p_input, "ipv4" ) )
727 psz_network = "ipv4";
729 else if( config_GetInt( p_input, "ipv6" ) )
731 psz_network = "ipv6";
734 msg_Dbg( p_input, "waiting for connection..." );
736 socket_desc.i_type = NETWORK_TCP;
737 socket_desc.psz_server_addr = p_url->psz_host;
738 socket_desc.i_server_port = p_url->i_port;
739 socket_desc.psz_bind_addr = "";
740 socket_desc.i_bind_port = 0;
741 socket_desc.i_ttl = 0;
742 p_input->p_private = (void*)&socket_desc;
743 if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
745 msg_Err( p_input, "failed to connect with server" );
748 module_Unneed( p_input, p_network );
749 p_socket->i_handle = socket_desc.i_handle;
750 p_input->i_mtu = socket_desc.i_mtu;
753 "connection with \"%s:%d\" successful",
760 /*****************************************************************************
761 * Read: read on a file descriptor, checking b_die periodically
762 *****************************************************************************/
763 static ssize_t NetRead( input_thread_t *p_input,
764 input_socket_t *p_socket,
765 byte_t *p_buffer, size_t i_len )
767 struct timeval timeout;
772 /* Initialize file descriptor set */
774 FD_SET( p_socket->i_handle, &fds );
776 /* We'll wait 1 second if nothing happens */
780 /* Find if some data is available */
781 while( ( i_ret = select( p_socket->i_handle + 1, &fds,
782 NULL, NULL, &timeout )) == 0 ||
784 ( i_ret < 0 && errno == EINTR )
789 FD_SET( p_socket->i_handle, &fds );
793 if( p_input->b_die || p_input->b_error )
801 msg_Err( p_input, "network select error (%s)", strerror(errno) );
805 i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
809 msg_Err( p_input, "recv failed (%s)", strerror(errno) );
815 static ssize_t NetWrite( input_thread_t *p_input,
816 input_socket_t *p_socket,
817 byte_t *p_buffer, size_t i_len )
819 struct timeval timeout;
824 /* Initialize file descriptor set */
826 FD_SET( p_socket->i_handle, &fds );
828 /* We'll wait 1 second if nothing happens */
832 /* Find if some data is available */
833 while( ( i_ret = select( p_socket->i_handle + 1, NULL, &fds, NULL, &timeout ) ) == 0 ||
835 ( i_ret < 0 && errno == EINTR )
840 FD_SET( p_socket->i_handle, &fds );
844 if( p_input->b_die || p_input->b_error )
852 msg_Err( p_input, "network select error (%s)", strerror(errno) );
856 i_send = send( p_socket->i_handle, p_buffer, i_len, 0 );
860 msg_Err( p_input, "send failed (%s)", strerror(errno) );
866 static void NetClose( input_thread_t *p_input, input_socket_t *p_socket )
868 #if defined( WIN32 ) || defined( UNDER_CE )
869 closesocket( p_socket->i_handle );
871 close( p_socket->i_handle );
877 static int http_next_line( uint8_t **pp_data, int *pi_data )
879 char *p, *p_end = *pp_data + *pi_data;
881 for( p = *pp_data; p < p_end; p++ )
883 if( p + 1 < p_end && *p == '\n' )
885 *pi_data = p_end - p - 1;
889 if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
891 *pi_data = p_end - p - 2;
901 static http_answer_t *http_answer_parse( uint8_t *p_data, int i_data )
903 http_answer_t *ans = malloc( sizeof( http_answer_t ) );
904 http_field_t **pp_last;
907 if( strncmp( p_data, "HTTP/1.", 7 ) )
912 ans->i_version = atoi( &p_data[7] );
913 ans->i_error = strtol( p_data + 8, &p, 0 );
918 if( ( ( end = strchr( p, '\r' ) ) == NULL )&&
919 ( ( end = strchr( p, '\n' ) ) == NULL ) )
921 end = &p_data[i_data];
924 ans->psz_answer = strndup( p, end - p );
926 fprintf( stderr, "version=%d error=%d answer=%s\n",
927 ans->i_version, ans->i_error, ans->psz_answer );
928 ans->p_fields = NULL;
932 pp_last = &ans->p_fields;
936 http_field_t *p_field;
939 if( http_next_line( &p_data, &i_data ) )
943 if( !strncmp( p_data, "\r\n", 2 ) || !strncmp( p_data, "\n", 1 ) )
948 colon = strstr( p_data, ": " );
953 end = strstr( colon, "\n" ) - 1;
959 p_field = malloc( sizeof( http_field_t ) );
960 p_field->psz_name = strndup( p_data, colon - p_data );
961 p_field->psz_value = strndup( colon + 2, end - colon - 2 );
962 p_field->p_next = NULL;
965 pp_last = &p_field->p_next;
967 fprintf( stderr, "field name=`%s' value=`%s'\n",
968 p_field->psz_name, p_field->psz_value );
972 if( http_next_line( &p_data, &i_data ) )
977 ans->p_body = p_data;
978 ans->i_body = i_data;
979 fprintf( stderr, "body size=%d\n", i_data );
984 static void http_answer_free( http_answer_t *ans )
986 http_field_t *p_field = ans->p_fields;
990 http_field_t *p_next;
992 p_next = p_field->p_next;
993 free( p_field->psz_name );
994 free( p_field->psz_value );
1000 free( ans->psz_answer );
1004 static http_field_t *http_field_find( http_field_t *p_field, char *psz_name )
1009 if( !strcasecmp( p_field->psz_name, psz_name ) )
1014 p_field = p_field->p_next;
1020 static int chunk_parse( chunk_t *ck, uint8_t *p_data, int i_data )
1024 return VLC_EGENERIC;
1027 ck->i_type = GetWLE( p_data );
1028 ck->i_size = GetWLE( p_data + 2);
1029 ck->i_sequence = GetDWLE( p_data + 4);
1030 ck->i_unknown = GetWLE( p_data + 8);
1031 ck->i_size2 = GetWLE( p_data + 10);
1033 ck->p_data = p_data + 12;
1034 ck->i_data = __MIN( i_data - 12, ck->i_size2 - 8 );