fix
[mplayer/glamo.git] / libmpdemux / pnm.c
blob033772d8d900b18021818c5d7e413d3a993fef1e
1 /*
2 * Copyright (C) 2000-2002 the xine project
4 * This file is part of xine, a free video player.
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.
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.
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
20 * $Id$
22 * pnm protocol implementation
23 * based upon code from joschka
26 #include "config.h"
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <assert.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <sys/time.h>
37 #include <inttypes.h>
38 #ifndef HAVE_WINSOCK2
39 #define closesocket close
40 #include <sys/socket.h>
41 //#include <netinet/in.h>
42 //#include <netdb.h>
43 #else
44 #include <winsock2.h>
45 #endif
47 #include "stream.h"
48 #include "demuxer.h"
49 #include "help_mp.h"
50 #include "osdep/timer.h"
52 #include "pnm.h"
53 //#include "libreal/rmff.h"
55 extern int network_bandwidth;
57 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
58 (((long)(unsigned char)(ch3) ) | \
59 ( (long)(unsigned char)(ch2) << 8 ) | \
60 ( (long)(unsigned char)(ch1) << 16 ) | \
61 ( (long)(unsigned char)(ch0) << 24 ) )
64 #define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F')
65 #define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P')
66 #define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R')
67 #define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T')
68 #define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A')
69 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X')
70 #define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 )
73 #define LOG
76 #define BUF_SIZE 4096
77 #define HEADER_SIZE 4096
79 struct pnm_s {
81 int s;
83 // char *host;
84 // int port;
85 char *path;
86 // char *url;
88 char buffer[BUF_SIZE]; /* scratch buffer */
90 /* receive buffer */
91 uint8_t recv[BUF_SIZE];
92 int recv_size;
93 int recv_read;
95 uint8_t header[HEADER_SIZE];
96 int header_len;
97 int header_read;
98 unsigned int seq_num[4]; /* two streams with two indices */
99 unsigned int seq_current[2]; /* seqs of last stream chunk read */
100 uint32_t ts_current; /* timestamp of current chunk */
101 uint32_t ts_last[2]; /* timestamps of last chunks */
102 unsigned int packet; /* number of last recieved packet */
106 * utility macros
109 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
110 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \
111 (((uint8_t*)(x))[1] << 16) | \
112 (((uint8_t*)(x))[2] << 8) | \
113 ((uint8_t*)(x))[3])
115 /* D means direct (no pointer) */
116 #define BE_16D(x) ((x & 0xff00) >> 8)|((x & 0x00ff) << 8)
118 /* sizes */
119 #define PREAMBLE_SIZE 8
120 #define CHECKSUM_SIZE 3
123 /* header of rm files */
124 #define RM_HEADER_SIZE 0x12
125 static const unsigned char rm_header[]={
126 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */
127 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */
128 0x00, 0x00, /* object_version 0x00 */
129 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */
130 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */
133 /* data chunk header */
134 #define PNM_DATA_HEADER_SIZE 18
135 static const unsigned char pnm_data_header[]={
136 'D','A','T','A',
137 0,0,0,0, /* data chunk size */
138 0,0, /* object version */
139 0,0,0,0, /* num packets */
140 0,0,0,0}; /* next data header */
142 /* pnm request chunk ids */
144 #define PNA_CLIENT_CAPS 0x03
145 #define PNA_CLIENT_CHALLANGE 0x04
146 #define PNA_BANDWIDTH 0x05
147 #define PNA_GUID 0x13
148 #define PNA_TIMESTAMP 0x17
149 #define PNA_TWENTYFOUR 0x18
151 #define PNA_CLIENT_STRING 0x63
152 #define PNA_PATH_REQUEST 0x52
154 static const unsigned char pnm_challenge[] = "0990f6b4508b51e801bd6da011ad7b56";
155 static const unsigned char pnm_timestamp[] = "[15/06/1999:22:22:49 00:00]";
156 static const unsigned char pnm_guid[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0";
157 static const unsigned char pnm_response[] = "97715a899cbe41cee00dd434851535bf";
158 static const unsigned char client_string[] = "WinNT_9.0_6.0.6.45_plus32_MP60_en-US_686l";
160 #define PNM_HEADER_SIZE 11
161 static const unsigned char pnm_header[] = {
162 'P','N','A',
163 0x00, 0x0a,
164 0x00, 0x14,
165 0x00, 0x02,
166 0x00, 0x01 };
168 #define PNM_CLIENT_CAPS_SIZE 126
169 static const unsigned char pnm_client_caps[] = {
170 0x07, 0x8a, 'p','n','r','v',
171 0, 0x90, 'p','n','r','v',
172 0, 0x64, 'd','n','e','t',
173 0, 0x46, 'p','n','r','v',
174 0, 0x32, 'd','n','e','t',
175 0, 0x2b, 'p','n','r','v',
176 0, 0x28, 'd','n','e','t',
177 0, 0x24, 'p','n','r','v',
178 0, 0x19, 'd','n','e','t',
179 0, 0x18, 'p','n','r','v',
180 0, 0x14, 's','i','p','r',
181 0, 0x14, 'd','n','e','t',
182 0, 0x24, '2','8','_','8',
183 0, 0x12, 'p','n','r','v',
184 0, 0x0f, 'd','n','e','t',
185 0, 0x0a, 's','i','p','r',
186 0, 0x0a, 'd','n','e','t',
187 0, 0x08, 's','i','p','r',
188 0, 0x06, 's','i','p','r',
189 0, 0x12, 'l','p','c','J',
190 0, 0x07, '0','5','_','6' };
192 static const uint32_t pnm_default_bandwidth=10485800;
193 static const uint32_t pnm_available_bandwidths[]={14400,19200,28800,33600,34430,57600,
194 115200,262200,393216,524300,1544000,10485800};
196 #define PNM_TWENTYFOUR_SIZE 16
197 static unsigned char pnm_twentyfour[]={
198 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24,
199 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 };
201 /* now other data follows. marked with 0x0000 at the beginning */
202 static int after_chunks_length=6;
203 static unsigned char after_chunks[]={
204 0x00, 0x00, /* mark */
206 0x50, 0x84, /* seems to be fixated */
207 0x1f, 0x3a /* varies on each request (checksum ?)*/
210 static void hexdump (char *buf, int length);
212 static int rm_write(int s, const char *buf, int len) {
213 int total, timeout;
215 total = 0; timeout = 30;
216 while (total < len){
217 int n;
219 n = send (s, &buf[total], len - total, 0);
221 if (n > 0)
222 total += n;
223 else if (n < 0) {
224 #ifndef HAVE_WINSOCK2
225 if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) {
226 #else
227 if ((timeout>0) && ((errno == EAGAIN) || (WSAGetLastError() == WSAEINPROGRESS))) {
228 #endif
229 usec_sleep (1000000); timeout--;
230 } else
231 return -1;
235 return total;
238 static ssize_t rm_read(int fd, void *buf, size_t count) {
240 ssize_t ret, total;
242 total = 0;
244 while (total < count) {
246 fd_set rset;
247 struct timeval timeout;
249 FD_ZERO (&rset);
250 FD_SET (fd, &rset);
252 timeout.tv_sec = 3;
253 timeout.tv_usec = 0;
255 if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) {
256 return -1;
259 ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0);
261 if (ret<=0) {
262 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: read error.\n");
263 return ret;
264 } else
265 total += ret;
268 return total;
272 * debugging utilities
275 static void hexdump (char *buf, int length) {
277 int i;
279 mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: ascii>");
280 for (i = 0; i < length; i++) {
281 unsigned char c = buf[i];
283 if ((c >= 32) && (c <= 128))
284 mp_msg(MSGT_OPEN, MSGL_INFO, "%c", c);
285 else
286 mp_msg(MSGT_OPEN, MSGL_INFO, ".");
288 mp_msg(MSGT_OPEN, MSGL_INFO, "\n");
290 mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: hexdump> ");
291 for (i = 0; i < length; i++) {
292 unsigned char c = buf[i];
294 mp_msg(MSGT_OPEN, MSGL_INFO, "%02x", c);
296 if ((i % 16) == 15)
297 mp_msg(MSGT_OPEN, MSGL_INFO, "\npnm: ");
299 if ((i % 2) == 1)
300 mp_msg(MSGT_OPEN, MSGL_INFO, " ");
303 mp_msg(MSGT_OPEN, MSGL_INFO, "\n");
307 * pnm_get_chunk gets a chunk from stream
308 * and returns number of bytes read
311 static int pnm_get_chunk(pnm_t *p,
312 unsigned int max,
313 unsigned int *chunk_type,
314 char *data, int *need_response) {
316 unsigned int chunk_size;
317 unsigned int n;
318 char *ptr;
320 if (max < PREAMBLE_SIZE)
321 return -1;
323 /* get first PREAMBLE_SIZE bytes and ignore checksum */
324 rm_read (p->s, data, CHECKSUM_SIZE);
325 if (data[0] == 0x72)
326 rm_read (p->s, data, PREAMBLE_SIZE);
327 else
328 rm_read (p->s, data+CHECKSUM_SIZE, PREAMBLE_SIZE-CHECKSUM_SIZE);
330 max -= PREAMBLE_SIZE;
332 *chunk_type = BE_32(data);
333 chunk_size = BE_32(data+4);
335 switch (*chunk_type) {
336 case PNA_TAG:
337 *need_response=0;
338 ptr=data+PREAMBLE_SIZE;
339 if (max < 1)
340 return -1;
341 rm_read (p->s, ptr++, 1);
342 max -= 1;
344 while(1) {
345 /* expecting following chunk format: 0x4f <chunk size> <data...> */
347 if (max < 2)
348 return -1;
349 rm_read (p->s, ptr, 2);
350 max -= 2;
351 if (*ptr == 'X') /* checking for server message */
353 mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: got a message from server:\n");
354 if (max < 1)
355 return -1;
356 rm_read (p->s, ptr+2, 1);
357 max = -1;
358 n=BE_16(ptr+1);
359 if (max < n)
360 return -1;
361 rm_read (p->s, ptr+3, n);
362 max -= n;
363 ptr[3+n]=0;
364 mp_msg(MSGT_OPEN, MSGL_WARN, "%s\n",ptr+3);
365 return -1;
368 if (*ptr == 'F') /* checking for server error */
370 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: server error.\n");
371 return -1;
373 if (*ptr == 'i')
375 ptr+=2;
376 *need_response=1;
377 continue;
379 if (*ptr != 0x4f) break;
380 n=ptr[1];
381 if (max < n)
382 return -1;
383 rm_read (p->s, ptr+2, n);
384 max -= n;
385 ptr+=(n+2);
387 /* the checksum of the next chunk is ignored here */
388 if (max < 1)
389 return -1;
390 rm_read (p->s, ptr+2, 1);
391 ptr+=3;
392 chunk_size=ptr-data;
393 break;
394 case RMF_TAG:
395 case DATA_TAG:
396 case PROP_TAG:
397 case MDPR_TAG:
398 case CONT_TAG:
399 if (chunk_size > max || chunk_size < PREAMBLE_SIZE) {
400 mp_msg(MSGT_OPEN, MSGL_ERR, "error: max chunk size exceded (max was 0x%04x)\n", max);
401 #ifdef LOG
402 n=rm_read (p->s, &data[PREAMBLE_SIZE], 0x100 - PREAMBLE_SIZE);
403 hexdump(data,n+PREAMBLE_SIZE);
404 #endif
405 return -1;
407 rm_read (p->s, &data[PREAMBLE_SIZE], chunk_size-PREAMBLE_SIZE);
408 break;
409 default:
410 *chunk_type = 0;
411 chunk_size = PREAMBLE_SIZE;
412 break;
415 return chunk_size;
419 * writes a chunk to a buffer, returns number of bytes written
422 static int pnm_write_chunk(uint16_t chunk_id, uint16_t length,
423 const char *chunk, char *data) {
425 data[0]=(chunk_id>>8)%0xff;
426 data[1]=chunk_id%0xff;
427 data[2]=(length>>8)%0xff;
428 data[3]=length%0xff;
429 memcpy(&data[4],chunk,length);
431 return length+4;
435 * constructs a request and sends it
438 static void pnm_send_request(pnm_t *p, uint32_t bandwidth) {
440 uint16_t i16;
441 int c=PNM_HEADER_SIZE;
442 char fixme[]={0,1};
444 memcpy(p->buffer,pnm_header,PNM_HEADER_SIZE);
445 c+=pnm_write_chunk(PNA_CLIENT_CHALLANGE,strlen(pnm_challenge),
446 pnm_challenge,&p->buffer[c]);
447 c+=pnm_write_chunk(PNA_CLIENT_CAPS,PNM_CLIENT_CAPS_SIZE,
448 pnm_client_caps,&p->buffer[c]);
449 c+=pnm_write_chunk(0x0a,0,NULL,&p->buffer[c]);
450 c+=pnm_write_chunk(0x0c,0,NULL,&p->buffer[c]);
451 c+=pnm_write_chunk(0x0d,0,NULL,&p->buffer[c]);
452 c+=pnm_write_chunk(0x16,2,fixme,&p->buffer[c]);
453 c+=pnm_write_chunk(PNA_TIMESTAMP,strlen(pnm_timestamp),
454 pnm_timestamp,&p->buffer[c]);
455 c+=pnm_write_chunk(PNA_BANDWIDTH,4,
456 (const char *)&pnm_default_bandwidth,&p->buffer[c]);
457 c+=pnm_write_chunk(0x08,0,NULL,&p->buffer[c]);
458 c+=pnm_write_chunk(0x0e,0,NULL,&p->buffer[c]);
459 c+=pnm_write_chunk(0x0f,0,NULL,&p->buffer[c]);
460 c+=pnm_write_chunk(0x11,0,NULL,&p->buffer[c]);
461 c+=pnm_write_chunk(0x10,0,NULL,&p->buffer[c]);
462 c+=pnm_write_chunk(0x15,0,NULL,&p->buffer[c]);
463 c+=pnm_write_chunk(0x12,0,NULL,&p->buffer[c]);
464 c+=pnm_write_chunk(PNA_GUID,strlen(pnm_guid),
465 pnm_guid,&p->buffer[c]);
466 c+=pnm_write_chunk(PNA_TWENTYFOUR,PNM_TWENTYFOUR_SIZE,
467 pnm_twentyfour,&p->buffer[c]);
469 /* data after chunks */
470 memcpy(&p->buffer[c],after_chunks,after_chunks_length);
471 c+=after_chunks_length;
473 /* client id string */
474 p->buffer[c]=PNA_CLIENT_STRING;
475 i16=BE_16D((strlen(client_string)-1)); /* don't know why do we have -1 here */
476 memcpy(&p->buffer[c+1],&i16,2);
477 memcpy(&p->buffer[c+3],client_string,strlen(client_string)+1);
478 c=c+3+strlen(client_string)+1;
480 /* file path */
481 p->buffer[c]=0;
482 p->buffer[c+1]=PNA_PATH_REQUEST;
483 i16=BE_16D(strlen(p->path));
484 memcpy(&p->buffer[c+2],&i16,2);
485 memcpy(&p->buffer[c+4],p->path,strlen(p->path));
486 c=c+4+strlen(p->path);
488 /* some trailing bytes */
489 p->buffer[c]='y';
490 p->buffer[c+1]='B';
492 rm_write(p->s,p->buffer,c+2);
496 * pnm_send_response sends a response of a challenge
499 static void pnm_send_response(pnm_t *p, const char *response) {
501 int size=strlen(response);
503 p->buffer[0]=0x23;
504 p->buffer[1]=0;
505 p->buffer[2]=(unsigned char) size;
507 memcpy(&p->buffer[3], response, size);
509 rm_write (p->s, p->buffer, size+3);
514 * get headers and challenge and fix headers
515 * write headers to p->header
516 * write challenge to p->buffer
518 * return 0 on error. != 0 on success
521 static int pnm_get_headers(pnm_t *p, int *need_response) {
523 uint32_t chunk_type;
524 uint8_t *ptr=p->header;
525 uint8_t *prop_hdr=NULL;
526 int chunk_size,size=0;
527 int nr;
528 /* rmff_header_t *h; */
530 *need_response=0;
532 while(1) {
533 if (HEADER_SIZE-size<=0)
535 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: header buffer overflow. exiting\n");
536 return 0;
538 chunk_size=pnm_get_chunk(p,HEADER_SIZE-size,&chunk_type,ptr,&nr);
539 if (chunk_size < 0) return 0;
540 if (chunk_type == 0) break;
541 if (chunk_type == PNA_TAG)
543 memcpy(ptr, rm_header, RM_HEADER_SIZE);
544 chunk_size=RM_HEADER_SIZE;
545 *need_response=nr;
547 if (chunk_type == DATA_TAG)
548 chunk_size=0;
549 if (chunk_type == RMF_TAG)
550 chunk_size=0;
551 if (chunk_type == PROP_TAG)
552 prop_hdr=ptr;
553 size+=chunk_size;
554 ptr+=chunk_size;
557 if (!prop_hdr) {
558 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: error while parsing headers.\n");
559 return 0;
562 /* set data offset */
563 size--;
564 prop_hdr[42]=(size>>24)%0xff;
565 prop_hdr[43]=(size>>16)%0xff;
566 prop_hdr[44]=(size>>8)%0xff;
567 prop_hdr[45]=(size)%0xff;
568 size++;
570 /* read challenge */
571 memcpy (p->buffer, ptr, PREAMBLE_SIZE);
572 rm_read (p->s, &p->buffer[PREAMBLE_SIZE], 64);
574 /* now write a data header */
575 memcpy(ptr, pnm_data_header, PNM_DATA_HEADER_SIZE);
576 size+=PNM_DATA_HEADER_SIZE;
578 h=rmff_scan_header(p->header);
579 rmff_fix_header(h);
580 p->header_len=rmff_get_header_size(h);
581 rmff_dump_header(h, p->header, HEADER_SIZE);
583 p->header_len=size;
585 return 1;
589 * determine correct stream number by looking at indices
592 static int pnm_calc_stream(pnm_t *p) {
594 char str0=0,str1=0;
596 /* looking at the first index to
597 * find possible stream types
599 if (p->seq_current[0]==p->seq_num[0]) str0=1;
600 if (p->seq_current[0]==p->seq_num[2]) str1=1;
602 switch (str0+str1) {
603 case 1: /* one is possible, good. */
604 if (str0)
606 p->seq_num[0]++;
607 p->seq_num[1]=p->seq_current[1]+1;
608 return 0;
609 } else
611 p->seq_num[2]++;
612 p->seq_num[3]=p->seq_current[1]+1;
613 return 1;
615 break;
616 case 0:
617 case 2: /* both types or none possible, not so good */
618 /* try to figure out by second index */
619 if ( (p->seq_current[1] == p->seq_num[1])
620 &&(p->seq_current[1] != p->seq_num[3]))
622 /* ok, only stream0 matches */
623 p->seq_num[0]=p->seq_current[0]+1;
624 p->seq_num[1]++;
625 return 0;
627 if ( (p->seq_current[1] == p->seq_num[3])
628 &&(p->seq_current[1] != p->seq_num[1]))
630 /* ok, only stream1 matches */
631 p->seq_num[2]=p->seq_current[0]+1;
632 p->seq_num[3]++;
633 return 1;
635 /* wow, both streams match, or not. */
636 /* now we try to decide by timestamps */
637 if (p->ts_current < p->ts_last[1])
638 return 0;
639 if (p->ts_current < p->ts_last[0])
640 return 1;
641 /* does not help, we guess type 0 */
642 #ifdef LOG
643 mp_msg(MSGT_OPEN, MSGL_INFO, "guessing stream# 0\n");
644 #endif
645 p->seq_num[0]=p->seq_current[0]+1;
646 p->seq_num[1]=p->seq_current[1]+1;
647 return 0;
648 break;
650 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: wow, something very nasty happened in pnm_calc_stream\n");
651 return 2;
655 * gets a stream chunk and writes it to a recieve buffer
658 static int pnm_get_stream_chunk(pnm_t *p) {
660 int n;
661 char keepalive='!';
662 unsigned int fof1, fof2, stream;
664 /* send a keepalive */
665 /* realplayer seems to do that every 43th package */
666 if ((p->packet%43) == 42)
668 rm_write(p->s,&keepalive,1);
671 /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2>
672 * where <o> is the offset to next stream chunk,
673 * <i1> is a 16 bit index
674 * <i2> is a 8 bit index which counts from 0x10 to somewhere
677 n = rm_read (p->s, p->buffer, 8);
678 if (n<0) return -1;
679 if (n<8) return 0;
681 /* skip 8 bytes if 0x62 is read */
682 if (p->buffer[0] == 0x62)
684 n = rm_read (p->s, p->buffer, 8);
685 if (n<8) return 0;
686 #ifdef LOG
687 mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: had to seek 8 bytes on 0x62\n");
688 #endif
691 /* a server message */
692 if (p->buffer[0] == 'X')
694 int size=BE_16(&p->buffer[1]);
696 rm_read (p->s, &p->buffer[8], size-5);
697 p->buffer[size+3]=0;
698 mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: got message from server while reading stream:\n%s\n", &p->buffer[3]);
699 return -1;
701 if (p->buffer[0] == 'F')
703 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: server error.\n");
704 return -1;
707 /* skip bytewise to next chunk.
708 * seems, that we don't need that, if we send enough
709 * keepalives
711 n=0;
712 while (p->buffer[0] != 0x5a) {
713 int i;
714 for (i=1; i<8; i++) {
715 p->buffer[i-1]=p->buffer[i];
717 rm_read (p->s, &p->buffer[7], 1);
718 n++;
721 #ifdef LOG
722 if (n) mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: had to seek %i bytes to next chunk\n", n);
723 #endif
725 /* check for 'Z's */
726 if ((p->buffer[0] != 0x5a)||(p->buffer[7] != 0x5a))
728 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: bad boundaries\n");
729 hexdump(p->buffer, 8);
730 return 0;
733 /* check offsets */
734 fof1=BE_16(&p->buffer[1]);
735 fof2=BE_16(&p->buffer[3]);
736 if (fof1 != fof2)
738 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1,fof2);
739 return 0;
742 /* get first index */
743 p->seq_current[0]=BE_16(&p->buffer[5]);
745 /* now read the rest of stream chunk */
746 n = rm_read (p->s, &p->recv[5], fof1-5);
747 if (n<(fof1-5)) return 0;
749 /* get second index */
750 p->seq_current[1]=p->recv[5];
752 /* get timestamp */
753 p->ts_current=BE_32(&p->recv[6]);
755 /* get stream number */
756 stream=pnm_calc_stream(p);
758 /* saving timestamp */
759 p->ts_last[stream]=p->ts_current;
761 /* constructing a data packet header */
763 p->recv[0]=0; /* object version */
764 p->recv[1]=0;
766 fof2=BE_16(&fof2);
767 memcpy(&p->recv[2], &fof2, 2);
768 /*p->recv[2]=(fof2>>8)%0xff;*/ /* length */
769 /*p->recv[3]=(fof2)%0xff;*/
771 p->recv[4]=0; /* stream number */
772 p->recv[5]=stream;
774 p->recv[10]=p->recv[10] & 0xfe; /* streambox seems to do that... */
776 p->packet++;
778 p->recv_size=fof1;
780 return fof1;
783 // pnm_t *pnm_connect(const char *mrl) {
784 static pnm_t *pnm_connect(int fd, char *path) {
786 pnm_t *p=malloc(sizeof(pnm_t));
787 int need_response=0;
789 p->path=strdup(path);
790 p->s=fd;
792 pnm_send_request(p,pnm_available_bandwidths[10]);
793 if (!pnm_get_headers(p, &need_response)) {
794 mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: failed to set up stream\n");
795 free(p->path);
796 free(p);
797 return NULL;
799 if (need_response)
800 pnm_send_response(p, pnm_response);
801 p->ts_last[0]=0;
802 p->ts_last[1]=0;
804 /* copy header to recv */
806 memcpy(p->recv, p->header, p->header_len);
807 p->recv_size = p->header_len;
808 p->recv_read = 0;
810 return p;
813 static int pnm_read (pnm_t *this, char *data, int len) {
815 int to_copy=len;
816 char *dest=data;
817 char *source=this->recv + this->recv_read;
818 int fill=this->recv_size - this->recv_read;
819 int retval;
821 if (len < 0) return 0;
822 while (to_copy > fill) {
824 memcpy(dest, source, fill);
825 to_copy -= fill;
826 dest += fill;
827 this->recv_read=0;
829 if ((retval = pnm_get_stream_chunk (this)) <= 0) {
830 #ifdef LOG
831 mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: %d of %d bytes provided\n", len-to_copy, len);
832 #endif
833 if (retval < 0)
834 return retval;
835 else
836 return len-to_copy;
838 source = this->recv;
839 fill = this->recv_size - this->recv_read;
842 memcpy(dest, source, to_copy);
843 this->recv_read += to_copy;
845 #ifdef LOG
846 mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: %d bytes provided\n", len);
847 #endif
849 return len;
852 static int pnm_peek_header (pnm_t *this, char *data) {
854 memcpy (data, this->header, this->header_len);
855 return this->header_len;
858 static void pnm_close(pnm_t *p) {
860 if (p->s >= 0) closesocket(p->s);
861 free(p->path);
862 free(p);
865 static int pnm_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl ) {
866 return pnm_read(stream_ctrl->data, buffer, size);
869 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
870 int fd;
871 pnm_t *pnm;
872 URL_t *url;
874 mp_msg(MSGT_OPEN, MSGL_INFO, "STREAM_PNM, URL: %s\n", stream->url);
875 stream->streaming_ctrl = streaming_ctrl_new();
876 if(stream->streaming_ctrl==NULL) {
877 return STREAM_ERROR;
879 stream->streaming_ctrl->bandwidth = network_bandwidth;
880 url = url_new(stream->url);
881 stream->streaming_ctrl->url = check4proxies(url);
882 //url_free(url);
884 fd = connect2Server( stream->streaming_ctrl->url->hostname,
885 stream->streaming_ctrl->url->port ? stream->streaming_ctrl->url->port : 7070,1 );
887 if(fd<0)
888 goto fail;
890 pnm = pnm_connect(fd,stream->streaming_ctrl->url->file);
891 if(!pnm)
892 goto fail;
893 stream->type = STREAMTYPE_STREAM;
894 stream->fd=fd;
895 stream->streaming_ctrl->data=pnm;
896 stream->streaming_ctrl->streaming_read = pnm_streaming_read;
897 //stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
898 stream->streaming_ctrl->prebuffer_size = 8*1024; // 8 KBytes
899 stream->streaming_ctrl->buffering = 1;
900 stream->streaming_ctrl->status = streaming_playing_e;
901 *file_format = DEMUXER_TYPE_REAL;
902 fixup_network_stream_cache(stream);
903 return STREAM_OK;
905 fail:
906 streaming_ctrl_free(stream->streaming_ctrl);
907 stream->streaming_ctrl = NULL;
908 return STREAM_UNSUPORTED;
912 stream_info_t stream_info_pnm = {
913 "RealNetworks pnm",
914 "pnm",
915 "Arpi, xine team",
916 "ported from xine",
917 open_s,
918 {"pnm", NULL}, //pnm as fallback
919 NULL,
920 0 // Urls are an option string