Mercurial > mplayer.hg
comparison libmpdemux/pnm.c @ 8570:16af93c01dbf
pnm:// support (ported from xine)
author | arpi |
---|---|
date | Thu, 26 Dec 2002 17:25:22 +0000 |
parents | |
children | 2d4328af7ea9 |
comparison
equal
deleted
inserted
replaced
8569:a34876f047d9 | 8570:16af93c01dbf |
---|---|
1 /* | |
2 * Copyright (C) 2000-2002 the xine project | |
3 * | |
4 * This file is part of xine, a free video player. | |
5 * | |
6 * xine is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * xine is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | |
19 * | |
20 * $Id$ | |
21 * | |
22 * pnm protocol implementation | |
23 * based upon code from joschka | |
24 */ | |
25 | |
26 #include <unistd.h> | |
27 #include <stdio.h> | |
28 #include <assert.h> | |
29 //#include <sys/socket.h> | |
30 //#include <netinet/in.h> | |
31 //#include <netdb.h> | |
32 #include <string.h> | |
33 #include <sys/stat.h> | |
34 #include <fcntl.h> | |
35 #include <errno.h> | |
36 #include <stdlib.h> | |
37 #include <sys/time.h> | |
38 #include <sys/types.h> | |
39 | |
40 #include "pnm.h" | |
41 //#include "libreal/rmff.h" | |
42 | |
43 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \ | |
44 (((long)(unsigned char)(ch3) ) | \ | |
45 ( (long)(unsigned char)(ch2) << 8 ) | \ | |
46 ( (long)(unsigned char)(ch1) << 16 ) | \ | |
47 ( (long)(unsigned char)(ch0) << 24 ) ) | |
48 | |
49 | |
50 #define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F') | |
51 #define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P') | |
52 #define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R') | |
53 #define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T') | |
54 #define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A') | |
55 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X') | |
56 #define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 ) | |
57 | |
58 /* | |
59 #define LOG | |
60 */ | |
61 | |
62 #define BUF_SIZE 1024 | |
63 #define HEADER_SIZE 1024 | |
64 | |
65 struct pnm_s { | |
66 | |
67 int s; | |
68 | |
69 // char *host; | |
70 // int port; | |
71 char *path; | |
72 // char *url; | |
73 | |
74 char buffer[BUF_SIZE]; /* scratch buffer */ | |
75 | |
76 /* receive buffer */ | |
77 uint8_t recv[BUF_SIZE]; | |
78 int recv_size; | |
79 int recv_read; | |
80 | |
81 uint8_t header[HEADER_SIZE]; | |
82 int header_len; | |
83 int header_read; | |
84 unsigned int seq_num[4]; /* two streams with two indices */ | |
85 unsigned int seq_current[2]; /* seqs of last stream chunk read */ | |
86 uint32_t ts_current; /* timestamp of current chunk */ | |
87 uint32_t ts_last[2]; /* timestamps of last chunks */ | |
88 unsigned int packet; /* number of last recieved packet */ | |
89 }; | |
90 | |
91 /* | |
92 * utility macros | |
93 */ | |
94 | |
95 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1]) | |
96 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \ | |
97 (((uint8_t*)(x))[1] << 16) | \ | |
98 (((uint8_t*)(x))[2] << 8) | \ | |
99 ((uint8_t*)(x))[3]) | |
100 | |
101 /* D means direct (no pointer) */ | |
102 #define BE_16D(x) ((x & 0xff00) >> 8)|((x & 0x00ff) << 8) | |
103 | |
104 /* sizes */ | |
105 #define PREAMBLE_SIZE 8 | |
106 #define CHECKSUM_SIZE 3 | |
107 | |
108 | |
109 /* header of rm files */ | |
110 #define RM_HEADER_SIZE 0x12 | |
111 const unsigned char rm_header[]={ | |
112 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */ | |
113 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */ | |
114 0x00, 0x00, /* object_version 0x00 */ | |
115 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */ | |
116 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */ | |
117 }; | |
118 | |
119 /* data chunk header */ | |
120 #define PNM_DATA_HEADER_SIZE 18 | |
121 const unsigned char pnm_data_header[]={ | |
122 'D','A','T','A', | |
123 0,0,0,0, /* data chunk size */ | |
124 0,0, /* object version */ | |
125 0,0,0,0, /* num packets */ | |
126 0,0,0,0}; /* next data header */ | |
127 | |
128 /* pnm request chunk ids */ | |
129 | |
130 #define PNA_CLIENT_CAPS 0x03 | |
131 #define PNA_CLIENT_CHALLANGE 0x04 | |
132 #define PNA_BANDWIDTH 0x05 | |
133 #define PNA_GUID 0x13 | |
134 #define PNA_TIMESTAMP 0x17 | |
135 #define PNA_TWENTYFOUR 0x18 | |
136 | |
137 #define PNA_CLIENT_STRING 0x63 | |
138 #define PNA_PATH_REQUEST 0x52 | |
139 | |
140 const unsigned char pnm_challenge[] = "0990f6b4508b51e801bd6da011ad7b56"; | |
141 const unsigned char pnm_timestamp[] = "[15/06/1999:22:22:49 00:00]"; | |
142 const unsigned char pnm_guid[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0"; | |
143 const unsigned char pnm_response[] = "97715a899cbe41cee00dd434851535bf"; | |
144 const unsigned char client_string[] = "WinNT_4.0_6.0.6.45_plus32_MP60_en-US_686l"; | |
145 | |
146 #define PNM_HEADER_SIZE 11 | |
147 const unsigned char pnm_header[] = { | |
148 'P','N','A', | |
149 0x00, 0x0a, | |
150 0x00, 0x14, | |
151 0x00, 0x02, | |
152 0x00, 0x01 }; | |
153 | |
154 #define PNM_CLIENT_CAPS_SIZE 126 | |
155 const unsigned char pnm_client_caps[] = { | |
156 0x07, 0x8a, 'p','n','r','v', | |
157 0, 0x90, 'p','n','r','v', | |
158 0, 0x64, 'd','n','e','t', | |
159 0, 0x46, 'p','n','r','v', | |
160 0, 0x32, 'd','n','e','t', | |
161 0, 0x2b, 'p','n','r','v', | |
162 0, 0x28, 'd','n','e','t', | |
163 0, 0x24, 'p','n','r','v', | |
164 0, 0x19, 'd','n','e','t', | |
165 0, 0x18, 'p','n','r','v', | |
166 0, 0x14, 's','i','p','r', | |
167 0, 0x14, 'd','n','e','t', | |
168 0, 0x24, '2','8','_','8', | |
169 0, 0x12, 'p','n','r','v', | |
170 0, 0x0f, 'd','n','e','t', | |
171 0, 0x0a, 's','i','p','r', | |
172 0, 0x0a, 'd','n','e','t', | |
173 0, 0x08, 's','i','p','r', | |
174 0, 0x06, 's','i','p','r', | |
175 0, 0x12, 'l','p','c','J', | |
176 0, 0x07, '0','5','_','6' }; | |
177 | |
178 const uint32_t pnm_default_bandwidth=10485800; | |
179 const uint32_t pnm_available_bandwidths[]={14400,19200,28800,33600,34430,57600, | |
180 115200,262200,393216,524300,1544000,10485800}; | |
181 | |
182 #define PNM_TWENTYFOUR_SIZE 16 | |
183 unsigned char pnm_twentyfour[]={ | |
184 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24, | |
185 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 }; | |
186 | |
187 /* now other data follows. marked with 0x0000 at the beginning */ | |
188 int after_chunks_length=6; | |
189 unsigned char after_chunks[]={ | |
190 0x00, 0x00, /* mark */ | |
191 | |
192 0x50, 0x84, /* seems to be fixated */ | |
193 0x1f, 0x3a /* varies on each request (checksum ?)*/ | |
194 }; | |
195 | |
196 static void hexdump (char *buf, int length); | |
197 | |
198 static int rm_write(int s, const char *buf, int len) { | |
199 int total, timeout; | |
200 | |
201 total = 0; timeout = 30; | |
202 while (total < len){ | |
203 int n; | |
204 | |
205 n = write (s, &buf[total], len - total); | |
206 | |
207 if (n > 0) | |
208 total += n; | |
209 else if (n < 0) { | |
210 if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) { | |
211 sleep (1); timeout--; | |
212 } else | |
213 return -1; | |
214 } | |
215 } | |
216 | |
217 return total; | |
218 } | |
219 | |
220 static ssize_t rm_read(int fd, void *buf, size_t count) { | |
221 | |
222 ssize_t ret, total; | |
223 | |
224 total = 0; | |
225 | |
226 while (total < count) { | |
227 | |
228 fd_set rset; | |
229 struct timeval timeout; | |
230 | |
231 FD_ZERO (&rset); | |
232 FD_SET (fd, &rset); | |
233 | |
234 timeout.tv_sec = 3; | |
235 timeout.tv_usec = 0; | |
236 | |
237 if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) { | |
238 return -1; | |
239 } | |
240 | |
241 ret=read (fd, ((uint8_t*)buf)+total, count-total); | |
242 | |
243 if (ret<=0) { | |
244 printf ("input_pnm: read error.\n"); | |
245 return ret; | |
246 } else | |
247 total += ret; | |
248 } | |
249 | |
250 return total; | |
251 } | |
252 | |
253 /* | |
254 * debugging utilities | |
255 */ | |
256 | |
257 static void hexdump (char *buf, int length) { | |
258 | |
259 int i; | |
260 | |
261 printf ("input_pnm: ascii>"); | |
262 for (i = 0; i < length; i++) { | |
263 unsigned char c = buf[i]; | |
264 | |
265 if ((c >= 32) && (c <= 128)) | |
266 printf ("%c", c); | |
267 else | |
268 printf ("."); | |
269 } | |
270 printf ("\n"); | |
271 | |
272 printf ("input_pnm: hexdump> "); | |
273 for (i = 0; i < length; i++) { | |
274 unsigned char c = buf[i]; | |
275 | |
276 printf ("%02x", c); | |
277 | |
278 if ((i % 16) == 15) | |
279 printf ("\npnm: "); | |
280 | |
281 if ((i % 2) == 1) | |
282 printf (" "); | |
283 | |
284 } | |
285 printf ("\n"); | |
286 } | |
287 | |
288 /* | |
289 * pnm_get_chunk gets a chunk from stream | |
290 * and returns number of bytes read | |
291 */ | |
292 | |
293 static unsigned int pnm_get_chunk(pnm_t *p, | |
294 unsigned int max, | |
295 unsigned int *chunk_type, | |
296 char *data, int *need_response) { | |
297 | |
298 unsigned int chunk_size; | |
299 int n; | |
300 char *ptr; | |
301 | |
302 /* get first PREAMBLE_SIZE bytes and ignore checksum */ | |
303 rm_read (p->s, data, CHECKSUM_SIZE); | |
304 if (data[0] == 0x72) | |
305 rm_read (p->s, data, PREAMBLE_SIZE); | |
306 else | |
307 rm_read (p->s, data+CHECKSUM_SIZE, PREAMBLE_SIZE-CHECKSUM_SIZE); | |
308 | |
309 *chunk_type = BE_32(data); | |
310 chunk_size = BE_32(data+4); | |
311 | |
312 switch (*chunk_type) { | |
313 case PNA_TAG: | |
314 *need_response=0; | |
315 ptr=data+PREAMBLE_SIZE; | |
316 rm_read (p->s, ptr++, 1); | |
317 | |
318 while(1) { | |
319 /* expecting following chunk format: 0x4f <chunk size> <data...> */ | |
320 | |
321 rm_read (p->s, ptr, 2); | |
322 if (*ptr == 'X') /* checking for server message */ | |
323 { | |
324 printf("input_pnm: got a message from server:\n"); | |
325 rm_read (p->s, ptr+2, 1); | |
326 n=BE_16(ptr+1); | |
327 rm_read (p->s, ptr+3, n); | |
328 ptr[3+n]=0; | |
329 printf("%s\n",ptr+3); | |
330 return -1; | |
331 } | |
332 | |
333 if (*ptr == 'F') /* checking for server error */ | |
334 { | |
335 printf("input_pnm: server error.\n"); | |
336 return -1; | |
337 } | |
338 if (*ptr == 'i') | |
339 { | |
340 ptr+=2; | |
341 *need_response=1; | |
342 continue; | |
343 } | |
344 if (*ptr != 0x4f) break; | |
345 n=ptr[1]; | |
346 rm_read (p->s, ptr+2, n); | |
347 ptr+=(n+2); | |
348 } | |
349 /* the checksum of the next chunk is ignored here */ | |
350 rm_read (p->s, ptr+2, 1); | |
351 ptr+=3; | |
352 chunk_size=ptr-data; | |
353 break; | |
354 case RMF_TAG: | |
355 case DATA_TAG: | |
356 case PROP_TAG: | |
357 case MDPR_TAG: | |
358 case CONT_TAG: | |
359 if (chunk_size > max) { | |
360 printf("error: max chunk size exeeded (max was 0x%04x)\n", max); | |
361 n=rm_read (p->s, &data[PREAMBLE_SIZE], 0x100 - PREAMBLE_SIZE); | |
362 hexdump(data,n+PREAMBLE_SIZE); | |
363 return -1; | |
364 } | |
365 rm_read (p->s, &data[PREAMBLE_SIZE], chunk_size-PREAMBLE_SIZE); | |
366 break; | |
367 default: | |
368 *chunk_type = 0; | |
369 chunk_size = PREAMBLE_SIZE; | |
370 break; | |
371 } | |
372 | |
373 return chunk_size; | |
374 } | |
375 | |
376 /* | |
377 * writes a chunk to a buffer, returns number of bytes written | |
378 */ | |
379 | |
380 static int pnm_write_chunk(uint16_t chunk_id, uint16_t length, | |
381 const char *chunk, char *data) { | |
382 | |
383 data[0]=(chunk_id>>8)%0xff; | |
384 data[1]=chunk_id%0xff; | |
385 data[2]=(length>>8)%0xff; | |
386 data[3]=length%0xff; | |
387 memcpy(&data[4],chunk,length); | |
388 | |
389 return length+4; | |
390 } | |
391 | |
392 /* | |
393 * constructs a request and sends it | |
394 */ | |
395 | |
396 static void pnm_send_request(pnm_t *p, uint32_t bandwidth) { | |
397 | |
398 uint16_t i16; | |
399 int c=PNM_HEADER_SIZE; | |
400 char fixme[]={0,1}; | |
401 | |
402 memcpy(p->buffer,pnm_header,PNM_HEADER_SIZE); | |
403 c+=pnm_write_chunk(PNA_CLIENT_CHALLANGE,strlen(pnm_challenge), | |
404 pnm_challenge,&p->buffer[c]); | |
405 c+=pnm_write_chunk(PNA_CLIENT_CAPS,PNM_CLIENT_CAPS_SIZE, | |
406 pnm_client_caps,&p->buffer[c]); | |
407 c+=pnm_write_chunk(0x0a,0,NULL,&p->buffer[c]); | |
408 c+=pnm_write_chunk(0x0c,0,NULL,&p->buffer[c]); | |
409 c+=pnm_write_chunk(0x0d,0,NULL,&p->buffer[c]); | |
410 c+=pnm_write_chunk(0x16,2,fixme,&p->buffer[c]); | |
411 c+=pnm_write_chunk(PNA_TIMESTAMP,strlen(pnm_timestamp), | |
412 pnm_timestamp,&p->buffer[c]); | |
413 c+=pnm_write_chunk(PNA_BANDWIDTH,4, | |
414 (const char *)&pnm_default_bandwidth,&p->buffer[c]); | |
415 c+=pnm_write_chunk(0x08,0,NULL,&p->buffer[c]); | |
416 c+=pnm_write_chunk(0x0e,0,NULL,&p->buffer[c]); | |
417 c+=pnm_write_chunk(0x0f,0,NULL,&p->buffer[c]); | |
418 c+=pnm_write_chunk(0x11,0,NULL,&p->buffer[c]); | |
419 c+=pnm_write_chunk(0x10,0,NULL,&p->buffer[c]); | |
420 c+=pnm_write_chunk(0x15,0,NULL,&p->buffer[c]); | |
421 c+=pnm_write_chunk(0x12,0,NULL,&p->buffer[c]); | |
422 c+=pnm_write_chunk(PNA_GUID,strlen(pnm_guid), | |
423 pnm_guid,&p->buffer[c]); | |
424 c+=pnm_write_chunk(PNA_TWENTYFOUR,PNM_TWENTYFOUR_SIZE, | |
425 pnm_twentyfour,&p->buffer[c]); | |
426 | |
427 /* data after chunks */ | |
428 memcpy(&p->buffer[c],after_chunks,after_chunks_length); | |
429 c+=after_chunks_length; | |
430 | |
431 /* client id string */ | |
432 p->buffer[c]=PNA_CLIENT_STRING; | |
433 i16=BE_16D((strlen(client_string)-1)); /* dont know why do we have -1 here */ | |
434 memcpy(&p->buffer[c+1],&i16,2); | |
435 memcpy(&p->buffer[c+3],client_string,strlen(client_string)+1); | |
436 c=c+3+strlen(client_string)+1; | |
437 | |
438 /* file path */ | |
439 p->buffer[c]=0; | |
440 p->buffer[c+1]=PNA_PATH_REQUEST; | |
441 i16=BE_16D(strlen(p->path)); | |
442 memcpy(&p->buffer[c+2],&i16,2); | |
443 memcpy(&p->buffer[c+4],p->path,strlen(p->path)); | |
444 c=c+4+strlen(p->path); | |
445 | |
446 /* some trailing bytes */ | |
447 p->buffer[c]='y'; | |
448 p->buffer[c+1]='B'; | |
449 | |
450 rm_write(p->s,p->buffer,c+2); | |
451 } | |
452 | |
453 /* | |
454 * pnm_send_response sends a response of a challenge | |
455 */ | |
456 | |
457 static void pnm_send_response(pnm_t *p, const char *response) { | |
458 | |
459 int size=strlen(response); | |
460 | |
461 p->buffer[0]=0x23; | |
462 p->buffer[1]=0; | |
463 p->buffer[2]=(unsigned char) size; | |
464 | |
465 memcpy(&p->buffer[3], response, size); | |
466 | |
467 rm_write (p->s, p->buffer, size+3); | |
468 | |
469 } | |
470 | |
471 /* | |
472 * get headers and challenge and fix headers | |
473 * write headers to p->header | |
474 * write challenge to p->buffer | |
475 * | |
476 * return 0 on error. != 0 on success | |
477 */ | |
478 | |
479 static int pnm_get_headers(pnm_t *p, int *need_response) { | |
480 | |
481 uint32_t chunk_type; | |
482 uint8_t *ptr=p->header; | |
483 uint8_t *prop_hdr=NULL; | |
484 int chunk_size,size=0; | |
485 int nr; | |
486 /* rmff_header_t *h; */ | |
487 | |
488 *need_response=0; | |
489 | |
490 while(1) { | |
491 if (HEADER_SIZE-size<=0) | |
492 { | |
493 printf("input_pnm: header buffer overflow. exiting\n"); | |
494 return 0; | |
495 } | |
496 chunk_size=pnm_get_chunk(p,HEADER_SIZE-size,&chunk_type,ptr,&nr); | |
497 if (chunk_size < 0) return 0; | |
498 if (chunk_type == 0) break; | |
499 if (chunk_type == PNA_TAG) | |
500 { | |
501 memcpy(ptr, rm_header, RM_HEADER_SIZE); | |
502 chunk_size=RM_HEADER_SIZE; | |
503 *need_response=nr; | |
504 } | |
505 if (chunk_type == DATA_TAG) | |
506 chunk_size=0; | |
507 if (chunk_type == RMF_TAG) | |
508 chunk_size=0; | |
509 if (chunk_type == PROP_TAG) | |
510 prop_hdr=ptr; | |
511 size+=chunk_size; | |
512 ptr+=chunk_size; | |
513 } | |
514 | |
515 /* set pre-buffer to a low number */ | |
516 /* prop_hdr[36]=0x01; | |
517 prop_hdr[37]=0xd6; */ | |
518 | |
519 /* set data offset */ | |
520 size--; | |
521 prop_hdr[42]=(size>>24)%0xff; | |
522 prop_hdr[43]=(size>>16)%0xff; | |
523 prop_hdr[44]=(size>>8)%0xff; | |
524 prop_hdr[45]=(size)%0xff; | |
525 size++; | |
526 | |
527 /* read challenge */ | |
528 memcpy (p->buffer, ptr, PREAMBLE_SIZE); | |
529 rm_read (p->s, &p->buffer[PREAMBLE_SIZE], 64); | |
530 | |
531 /* now write a data header */ | |
532 memcpy(ptr, pnm_data_header, PNM_DATA_HEADER_SIZE); | |
533 size+=PNM_DATA_HEADER_SIZE; | |
534 /* | |
535 h=rmff_scan_header(p->header); | |
536 rmff_fix_header(h); | |
537 p->header_len=rmff_get_header_size(h); | |
538 rmff_dump_header(h, p->header, HEADER_SIZE); | |
539 */ | |
540 p->header_len=size; | |
541 | |
542 return 1; | |
543 } | |
544 | |
545 /* | |
546 * determine correct stream number by looking at indices | |
547 */ | |
548 | |
549 static int pnm_calc_stream(pnm_t *p) { | |
550 | |
551 char str0=0,str1=0; | |
552 | |
553 /* looking at the first index to | |
554 * find possible stream types | |
555 */ | |
556 if (p->seq_current[0]==p->seq_num[0]) str0=1; | |
557 if (p->seq_current[0]==p->seq_num[2]) str1=1; | |
558 | |
559 switch (str0+str1) { | |
560 case 1: /* one is possible, good. */ | |
561 if (str0) | |
562 { | |
563 p->seq_num[0]++; | |
564 p->seq_num[1]=p->seq_current[1]+1; | |
565 return 0; | |
566 } else | |
567 { | |
568 p->seq_num[2]++; | |
569 p->seq_num[3]=p->seq_current[1]+1; | |
570 return 1; | |
571 } | |
572 break; | |
573 case 0: | |
574 case 2: /* both types or none possible, not so good */ | |
575 /* try to figure out by second index */ | |
576 if ( (p->seq_current[1] == p->seq_num[1]) | |
577 &&(p->seq_current[1] != p->seq_num[3])) | |
578 { | |
579 /* ok, only stream0 matches */ | |
580 p->seq_num[0]=p->seq_current[0]+1; | |
581 p->seq_num[1]++; | |
582 return 0; | |
583 } | |
584 if ( (p->seq_current[1] == p->seq_num[3]) | |
585 &&(p->seq_current[1] != p->seq_num[1])) | |
586 { | |
587 /* ok, only stream1 matches */ | |
588 p->seq_num[2]=p->seq_current[0]+1; | |
589 p->seq_num[3]++; | |
590 return 1; | |
591 } | |
592 /* wow, both streams match, or not. */ | |
593 /* now we try to decide by timestamps */ | |
594 if (p->ts_current < p->ts_last[1]) | |
595 return 0; | |
596 if (p->ts_current < p->ts_last[0]) | |
597 return 1; | |
598 /* does not help, we guess type 0 */ | |
599 #ifdef LOG | |
600 printf("guessing stream# 0\n"); | |
601 #endif | |
602 p->seq_num[0]=p->seq_current[0]+1; | |
603 p->seq_num[1]=p->seq_current[1]+1; | |
604 return 0; | |
605 break; | |
606 } | |
607 printf("input_pnm: wow, something very nasty happened in pnm_calc_stream\n"); | |
608 return 2; | |
609 } | |
610 | |
611 /* | |
612 * gets a stream chunk and writes it to a recieve buffer | |
613 */ | |
614 | |
615 static int pnm_get_stream_chunk(pnm_t *p) { | |
616 | |
617 int n; | |
618 char keepalive='!'; | |
619 unsigned int fof1, fof2, stream; | |
620 | |
621 /* send a keepalive */ | |
622 /* realplayer seems to do that every 43th package */ | |
623 if ((p->packet%43) == 42) | |
624 { | |
625 rm_write(p->s,&keepalive,1); | |
626 } | |
627 | |
628 /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2> | |
629 * where <o> is the offset to next stream chunk, | |
630 * <i1> is a 16 bit index | |
631 * <i2> is a 8 bit index which counts from 0x10 to somewhere | |
632 */ | |
633 | |
634 n = rm_read (p->s, p->buffer, 8); | |
635 if (n<8) return 0; | |
636 | |
637 /* skip 8 bytes if 0x62 is read */ | |
638 if (p->buffer[0] == 0x62) | |
639 { | |
640 n = rm_read (p->s, p->buffer, 8); | |
641 if (n<8) return 0; | |
642 #ifdef LOG | |
643 printf("input_pnm: had to seek 8 bytes on 0x62\n"); | |
644 #endif | |
645 } | |
646 | |
647 /* a server message */ | |
648 if (p->buffer[0] == 'X') | |
649 { | |
650 int size=BE_16(&p->buffer[1]); | |
651 | |
652 rm_read (p->s, &p->buffer[8], size-5); | |
653 p->buffer[size+3]=0; | |
654 printf("input_pnm: got message from server while reading stream:\n%s\n", &p->buffer[3]); | |
655 return 0; | |
656 } | |
657 if (p->buffer[0] == 'F') | |
658 { | |
659 printf("input_pnm: server error.\n"); | |
660 return 0; | |
661 } | |
662 | |
663 /* skip bytewise to next chunk. | |
664 * seems, that we dont need that, if we send enough | |
665 * keepalives | |
666 */ | |
667 n=0; | |
668 while (p->buffer[0] != 0x5a) { | |
669 int i; | |
670 for (i=1; i<8; i++) { | |
671 p->buffer[i-1]=p->buffer[i]; | |
672 } | |
673 rm_read (p->s, &p->buffer[7], 1); | |
674 n++; | |
675 } | |
676 | |
677 #ifdef LOG | |
678 if (n) printf("input_pnm: had to seek %i bytes to next chunk\n", n); | |
679 #endif | |
680 | |
681 /* check for 'Z's */ | |
682 if ((p->buffer[0] != 0x5a)||(p->buffer[7] != 0x5a)) | |
683 { | |
684 printf("input_pnm: bad boundaries\n"); | |
685 hexdump(p->buffer, 8); | |
686 return 0; | |
687 } | |
688 | |
689 /* check offsets */ | |
690 fof1=BE_16(&p->buffer[1]); | |
691 fof2=BE_16(&p->buffer[3]); | |
692 if (fof1 != fof2) | |
693 { | |
694 printf("input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1,fof2); | |
695 return 0; | |
696 } | |
697 | |
698 /* get first index */ | |
699 p->seq_current[0]=BE_16(&p->buffer[5]); | |
700 | |
701 /* now read the rest of stream chunk */ | |
702 n = rm_read (p->s, &p->recv[5], fof1-5); | |
703 if (n<(fof1-5)) return 0; | |
704 | |
705 /* get second index */ | |
706 p->seq_current[1]=p->recv[5]; | |
707 | |
708 /* get timestamp */ | |
709 p->ts_current=BE_32(&p->recv[6]); | |
710 | |
711 /* get stream number */ | |
712 stream=pnm_calc_stream(p); | |
713 | |
714 /* saving timestamp */ | |
715 p->ts_last[stream]=p->ts_current; | |
716 | |
717 /* constructing a data packet header */ | |
718 | |
719 p->recv[0]=0; /* object version */ | |
720 p->recv[1]=0; | |
721 | |
722 fof2=BE_16(&fof2); | |
723 memcpy(&p->recv[2], &fof2, 2); | |
724 /*p->recv[2]=(fof2>>8)%0xff;*/ /* length */ | |
725 /*p->recv[3]=(fof2)%0xff;*/ | |
726 | |
727 p->recv[4]=0; /* stream number */ | |
728 p->recv[5]=stream; | |
729 | |
730 p->recv[10]=p->recv[10] & 0xfe; /* streambox seems to do that... */ | |
731 | |
732 p->packet++; | |
733 | |
734 p->recv_size=fof1; | |
735 | |
736 return fof1; | |
737 } | |
738 | |
739 // pnm_t *pnm_connect(const char *mrl) { | |
740 pnm_t *pnm_connect(int fd, char *path) { | |
741 | |
742 pnm_t *p=malloc(sizeof(pnm_t)); | |
743 int need_response; | |
744 | |
745 p->path=strdup(path); | |
746 p->s=fd; | |
747 | |
748 pnm_send_request(p,pnm_available_bandwidths[10]); | |
749 if (!pnm_get_headers(p, &need_response)) { | |
750 printf ("input_pnm: failed to set up stream\n"); | |
751 free(p->path); | |
752 free(p); | |
753 return NULL; | |
754 } | |
755 if (need_response) | |
756 pnm_send_response(p, pnm_response); | |
757 p->ts_last[0]=0; | |
758 p->ts_last[1]=0; | |
759 | |
760 /* copy header to recv */ | |
761 | |
762 memcpy(p->recv, p->header, p->header_len); | |
763 p->recv_size = p->header_len; | |
764 p->recv_read = 0; | |
765 | |
766 return p; | |
767 } | |
768 | |
769 int pnm_read (pnm_t *this, char *data, int len) { | |
770 | |
771 int to_copy=len; | |
772 char *dest=data; | |
773 char *source=this->recv + this->recv_read; | |
774 int fill=this->recv_size - this->recv_read; | |
775 | |
776 if (len < 0) return 0; | |
777 while (to_copy > fill) { | |
778 | |
779 memcpy(dest, source, fill); | |
780 to_copy -= fill; | |
781 dest += fill; | |
782 this->recv_read=0; | |
783 | |
784 if (!pnm_get_stream_chunk (this)) { | |
785 #ifdef LOG | |
786 printf ("input_pnm: %d of %d bytes provided\n", len-to_copy, len); | |
787 #endif | |
788 return len-to_copy; | |
789 } | |
790 source = this->recv; | |
791 fill = this->recv_size - this->recv_read; | |
792 } | |
793 | |
794 memcpy(dest, source, to_copy); | |
795 this->recv_read += to_copy; | |
796 | |
797 #ifdef LOG | |
798 printf ("input_pnm: %d bytes provided\n", len); | |
799 #endif | |
800 | |
801 return len; | |
802 } | |
803 | |
804 int pnm_peek_header (pnm_t *this, char *data) { | |
805 | |
806 memcpy (data, this->header, this->header_len); | |
807 return this->header_len; | |
808 } | |
809 | |
810 void pnm_close(pnm_t *p) { | |
811 | |
812 if (p->s >= 0) close(p->s); | |
813 free(p->path); | |
814 free(p); | |
815 } | |
816 |