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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * pnm protocol implementation
23 * based upon code from joschka
39 #define closesocket close
40 #include <sys/socket.h>
41 //#include <netinet/in.h>
48 #include "libmpdemux/demuxer.h"
50 #include "osdep/timer.h"
54 //#include "libreal/rmff.h"
56 extern int network_bandwidth
;
58 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
59 (((long)(unsigned char)(ch3) ) | \
60 ( (long)(unsigned char)(ch2) << 8 ) | \
61 ( (long)(unsigned char)(ch1) << 16 ) | \
62 ( (long)(unsigned char)(ch0) << 24 ) )
65 #define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F')
66 #define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P')
67 #define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R')
68 #define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T')
69 #define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A')
70 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X')
71 #define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 )
78 #define HEADER_SIZE 4096
89 char buffer
[BUF_SIZE
]; /* scratch buffer */
92 uint8_t recv
[BUF_SIZE
];
96 uint8_t header
[HEADER_SIZE
];
99 unsigned int seq_num
[4]; /* two streams with two indices */
100 unsigned int seq_current
[2]; /* seqs of last stream chunk read */
101 uint32_t ts_current
; /* timestamp of current chunk */
102 uint32_t ts_last
[2]; /* timestamps of last chunks */
103 unsigned int packet
; /* number of last recieved packet */
110 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
111 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \
112 (((uint8_t*)(x))[1] << 16) | \
113 (((uint8_t*)(x))[2] << 8) | \
116 /* D means direct (no pointer) */
117 #define BE_16D(x) ((x & 0xff00) >> 8)|((x & 0x00ff) << 8)
120 #define PREAMBLE_SIZE 8
121 #define CHECKSUM_SIZE 3
124 /* header of rm files */
125 #define RM_HEADER_SIZE 0x12
126 static const unsigned char rm_header
[]={
127 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */
128 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */
129 0x00, 0x00, /* object_version 0x00 */
130 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */
131 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */
134 /* data chunk header */
135 #define PNM_DATA_HEADER_SIZE 18
136 static const unsigned char pnm_data_header
[]={
138 0,0,0,0, /* data chunk size */
139 0,0, /* object version */
140 0,0,0,0, /* num packets */
141 0,0,0,0}; /* next data header */
143 /* pnm request chunk ids */
145 #define PNA_CLIENT_CAPS 0x03
146 #define PNA_CLIENT_CHALLANGE 0x04
147 #define PNA_BANDWIDTH 0x05
148 #define PNA_GUID 0x13
149 #define PNA_TIMESTAMP 0x17
150 #define PNA_TWENTYFOUR 0x18
152 #define PNA_CLIENT_STRING 0x63
153 #define PNA_PATH_REQUEST 0x52
155 static const unsigned char pnm_challenge
[] = "0990f6b4508b51e801bd6da011ad7b56";
156 static const unsigned char pnm_timestamp
[] = "[15/06/1999:22:22:49 00:00]";
157 static const unsigned char pnm_guid
[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0";
158 static const unsigned char pnm_response
[] = "97715a899cbe41cee00dd434851535bf";
159 static const unsigned char client_string
[] = "WinNT_9.0_6.0.6.45_plus32_MP60_en-US_686l";
161 #define PNM_HEADER_SIZE 11
162 static const unsigned char pnm_header
[] = {
169 #define PNM_CLIENT_CAPS_SIZE 126
170 static const unsigned char pnm_client_caps
[] = {
171 0x07, 0x8a, 'p','n','r','v',
172 0, 0x90, 'p','n','r','v',
173 0, 0x64, 'd','n','e','t',
174 0, 0x46, 'p','n','r','v',
175 0, 0x32, 'd','n','e','t',
176 0, 0x2b, 'p','n','r','v',
177 0, 0x28, 'd','n','e','t',
178 0, 0x24, 'p','n','r','v',
179 0, 0x19, 'd','n','e','t',
180 0, 0x18, 'p','n','r','v',
181 0, 0x14, 's','i','p','r',
182 0, 0x14, 'd','n','e','t',
183 0, 0x24, '2','8','_','8',
184 0, 0x12, 'p','n','r','v',
185 0, 0x0f, 'd','n','e','t',
186 0, 0x0a, 's','i','p','r',
187 0, 0x0a, 'd','n','e','t',
188 0, 0x08, 's','i','p','r',
189 0, 0x06, 's','i','p','r',
190 0, 0x12, 'l','p','c','J',
191 0, 0x07, '0','5','_','6' };
193 static const uint32_t pnm_default_bandwidth
=10485800;
194 static const uint32_t pnm_available_bandwidths
[]={14400,19200,28800,33600,34430,57600,
195 115200,262200,393216,524300,1544000,10485800};
197 #define PNM_TWENTYFOUR_SIZE 16
198 static unsigned char pnm_twentyfour
[]={
199 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24,
200 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 };
202 /* now other data follows. marked with 0x0000 at the beginning */
203 static int after_chunks_length
=6;
204 static unsigned char after_chunks
[]={
205 0x00, 0x00, /* mark */
207 0x50, 0x84, /* seems to be fixated */
208 0x1f, 0x3a /* varies on each request (checksum ?)*/
211 static void hexdump (char *buf
, int length
);
213 static int rm_write(int s
, const char *buf
, int len
) {
216 total
= 0; timeout
= 30;
220 n
= send (s
, &buf
[total
], len
- total
, 0);
225 #ifndef HAVE_WINSOCK2
226 if ((timeout
>0) && ((errno
== EAGAIN
) || (errno
== EINPROGRESS
))) {
228 if ((timeout
>0) && ((errno
== EAGAIN
) || (WSAGetLastError() == WSAEINPROGRESS
))) {
230 usec_sleep (1000000); timeout
--;
239 static ssize_t
rm_read(int fd
, void *buf
, size_t count
) {
245 while (total
< count
) {
248 struct timeval timeout
;
256 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
260 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
263 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: read error.\n");
273 * debugging utilities
276 static void hexdump (char *buf
, int length
) {
280 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: ascii>");
281 for (i
= 0; i
< length
; i
++) {
282 unsigned char c
= buf
[i
];
284 if ((c
>= 32) && (c
<= 128))
285 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%c", c
);
287 mp_msg(MSGT_OPEN
, MSGL_INFO
, ".");
289 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
291 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: hexdump> ");
292 for (i
= 0; i
< length
; i
++) {
293 unsigned char c
= buf
[i
];
295 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%02x", c
);
298 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\npnm: ");
301 mp_msg(MSGT_OPEN
, MSGL_INFO
, " ");
304 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
308 * pnm_get_chunk gets a chunk from stream
309 * and returns number of bytes read
312 static int pnm_get_chunk(pnm_t
*p
,
314 unsigned int *chunk_type
,
315 char *data
, int *need_response
) {
317 unsigned int chunk_size
;
321 if (max
< PREAMBLE_SIZE
)
324 /* get first PREAMBLE_SIZE bytes and ignore checksum */
325 rm_read (p
->s
, data
, CHECKSUM_SIZE
);
327 rm_read (p
->s
, data
, PREAMBLE_SIZE
);
329 rm_read (p
->s
, data
+CHECKSUM_SIZE
, PREAMBLE_SIZE
-CHECKSUM_SIZE
);
331 max
-= PREAMBLE_SIZE
;
333 *chunk_type
= BE_32(data
);
334 chunk_size
= BE_32(data
+4);
336 switch (*chunk_type
) {
339 ptr
=data
+PREAMBLE_SIZE
;
342 rm_read (p
->s
, ptr
++, 1);
346 /* expecting following chunk format: 0x4f <chunk size> <data...> */
350 rm_read (p
->s
, ptr
, 2);
352 if (*ptr
== 'X') /* checking for server message */
354 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got a message from server:\n");
357 rm_read (p
->s
, ptr
+2, 1);
362 rm_read (p
->s
, ptr
+3, n
);
365 mp_msg(MSGT_OPEN
, MSGL_WARN
, "%s\n",ptr
+3);
369 if (*ptr
== 'F') /* checking for server error */
371 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
380 if (*ptr
!= 0x4f) break;
384 rm_read (p
->s
, ptr
+2, n
);
388 /* the checksum of the next chunk is ignored here */
391 rm_read (p
->s
, ptr
+2, 1);
400 if (chunk_size
> max
|| chunk_size
< PREAMBLE_SIZE
) {
401 mp_msg(MSGT_OPEN
, MSGL_ERR
, "error: max chunk size exceded (max was 0x%04x)\n", max
);
403 n
=rm_read (p
->s
, &data
[PREAMBLE_SIZE
], 0x100 - PREAMBLE_SIZE
);
404 hexdump(data
,n
+PREAMBLE_SIZE
);
408 rm_read (p
->s
, &data
[PREAMBLE_SIZE
], chunk_size
-PREAMBLE_SIZE
);
412 chunk_size
= PREAMBLE_SIZE
;
420 * writes a chunk to a buffer, returns number of bytes written
423 static int pnm_write_chunk(uint16_t chunk_id
, uint16_t length
,
424 const char *chunk
, char *data
) {
426 data
[0]=(chunk_id
>>8)%0xff;
427 data
[1]=chunk_id
%0xff;
428 data
[2]=(length
>>8)%0xff;
430 memcpy(&data
[4],chunk
,length
);
436 * constructs a request and sends it
439 static void pnm_send_request(pnm_t
*p
, uint32_t bandwidth
) {
442 int c
=PNM_HEADER_SIZE
;
445 memcpy(p
->buffer
,pnm_header
,PNM_HEADER_SIZE
);
446 c
+=pnm_write_chunk(PNA_CLIENT_CHALLANGE
,strlen(pnm_challenge
),
447 pnm_challenge
,&p
->buffer
[c
]);
448 c
+=pnm_write_chunk(PNA_CLIENT_CAPS
,PNM_CLIENT_CAPS_SIZE
,
449 pnm_client_caps
,&p
->buffer
[c
]);
450 c
+=pnm_write_chunk(0x0a,0,NULL
,&p
->buffer
[c
]);
451 c
+=pnm_write_chunk(0x0c,0,NULL
,&p
->buffer
[c
]);
452 c
+=pnm_write_chunk(0x0d,0,NULL
,&p
->buffer
[c
]);
453 c
+=pnm_write_chunk(0x16,2,fixme
,&p
->buffer
[c
]);
454 c
+=pnm_write_chunk(PNA_TIMESTAMP
,strlen(pnm_timestamp
),
455 pnm_timestamp
,&p
->buffer
[c
]);
456 c
+=pnm_write_chunk(PNA_BANDWIDTH
,4,
457 (const char *)&pnm_default_bandwidth
,&p
->buffer
[c
]);
458 c
+=pnm_write_chunk(0x08,0,NULL
,&p
->buffer
[c
]);
459 c
+=pnm_write_chunk(0x0e,0,NULL
,&p
->buffer
[c
]);
460 c
+=pnm_write_chunk(0x0f,0,NULL
,&p
->buffer
[c
]);
461 c
+=pnm_write_chunk(0x11,0,NULL
,&p
->buffer
[c
]);
462 c
+=pnm_write_chunk(0x10,0,NULL
,&p
->buffer
[c
]);
463 c
+=pnm_write_chunk(0x15,0,NULL
,&p
->buffer
[c
]);
464 c
+=pnm_write_chunk(0x12,0,NULL
,&p
->buffer
[c
]);
465 c
+=pnm_write_chunk(PNA_GUID
,strlen(pnm_guid
),
466 pnm_guid
,&p
->buffer
[c
]);
467 c
+=pnm_write_chunk(PNA_TWENTYFOUR
,PNM_TWENTYFOUR_SIZE
,
468 pnm_twentyfour
,&p
->buffer
[c
]);
470 /* data after chunks */
471 memcpy(&p
->buffer
[c
],after_chunks
,after_chunks_length
);
472 c
+=after_chunks_length
;
474 /* client id string */
475 p
->buffer
[c
]=PNA_CLIENT_STRING
;
476 i16
=BE_16D((strlen(client_string
)-1)); /* don't know why do we have -1 here */
477 memcpy(&p
->buffer
[c
+1],&i16
,2);
478 memcpy(&p
->buffer
[c
+3],client_string
,strlen(client_string
)+1);
479 c
=c
+3+strlen(client_string
)+1;
483 p
->buffer
[c
+1]=PNA_PATH_REQUEST
;
484 i16
=BE_16D(strlen(p
->path
));
485 memcpy(&p
->buffer
[c
+2],&i16
,2);
486 memcpy(&p
->buffer
[c
+4],p
->path
,strlen(p
->path
));
487 c
=c
+4+strlen(p
->path
);
489 /* some trailing bytes */
493 rm_write(p
->s
,p
->buffer
,c
+2);
497 * pnm_send_response sends a response of a challenge
500 static void pnm_send_response(pnm_t
*p
, const char *response
) {
502 int size
=strlen(response
);
506 p
->buffer
[2]=(unsigned char) size
;
508 memcpy(&p
->buffer
[3], response
, size
);
510 rm_write (p
->s
, p
->buffer
, size
+3);
515 * get headers and challenge and fix headers
516 * write headers to p->header
517 * write challenge to p->buffer
519 * return 0 on error. != 0 on success
522 static int pnm_get_headers(pnm_t
*p
, int *need_response
) {
525 uint8_t *ptr
=p
->header
;
526 uint8_t *prop_hdr
=NULL
;
527 int chunk_size
,size
=0;
529 /* rmff_header_t *h; */
534 if (HEADER_SIZE
-size
<=0)
536 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: header buffer overflow. exiting\n");
539 chunk_size
=pnm_get_chunk(p
,HEADER_SIZE
-size
,&chunk_type
,ptr
,&nr
);
540 if (chunk_size
< 0) return 0;
541 if (chunk_type
== 0) break;
542 if (chunk_type
== PNA_TAG
)
544 memcpy(ptr
, rm_header
, RM_HEADER_SIZE
);
545 chunk_size
=RM_HEADER_SIZE
;
548 if (chunk_type
== DATA_TAG
)
550 if (chunk_type
== RMF_TAG
)
552 if (chunk_type
== PROP_TAG
)
559 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: error while parsing headers.\n");
563 /* set data offset */
565 prop_hdr
[42]=(size
>>24)%0xff;
566 prop_hdr
[43]=(size
>>16)%0xff;
567 prop_hdr
[44]=(size
>>8)%0xff;
568 prop_hdr
[45]=(size
)%0xff;
572 memcpy (p
->buffer
, ptr
, PREAMBLE_SIZE
);
573 rm_read (p
->s
, &p
->buffer
[PREAMBLE_SIZE
], 64);
575 /* now write a data header */
576 memcpy(ptr
, pnm_data_header
, PNM_DATA_HEADER_SIZE
);
577 size
+=PNM_DATA_HEADER_SIZE
;
579 h=rmff_scan_header(p->header);
581 p->header_len=rmff_get_header_size(h);
582 rmff_dump_header(h, p->header, HEADER_SIZE);
590 * determine correct stream number by looking at indices
593 static int pnm_calc_stream(pnm_t
*p
) {
597 /* looking at the first index to
598 * find possible stream types
600 if (p
->seq_current
[0]==p
->seq_num
[0]) str0
=1;
601 if (p
->seq_current
[0]==p
->seq_num
[2]) str1
=1;
604 case 1: /* one is possible, good. */
608 p
->seq_num
[1]=p
->seq_current
[1]+1;
613 p
->seq_num
[3]=p
->seq_current
[1]+1;
618 case 2: /* both types or none possible, not so good */
619 /* try to figure out by second index */
620 if ( (p
->seq_current
[1] == p
->seq_num
[1])
621 &&(p
->seq_current
[1] != p
->seq_num
[3]))
623 /* ok, only stream0 matches */
624 p
->seq_num
[0]=p
->seq_current
[0]+1;
628 if ( (p
->seq_current
[1] == p
->seq_num
[3])
629 &&(p
->seq_current
[1] != p
->seq_num
[1]))
631 /* ok, only stream1 matches */
632 p
->seq_num
[2]=p
->seq_current
[0]+1;
636 /* wow, both streams match, or not. */
637 /* now we try to decide by timestamps */
638 if (p
->ts_current
< p
->ts_last
[1])
640 if (p
->ts_current
< p
->ts_last
[0])
642 /* does not help, we guess type 0 */
644 mp_msg(MSGT_OPEN
, MSGL_INFO
, "guessing stream# 0\n");
646 p
->seq_num
[0]=p
->seq_current
[0]+1;
647 p
->seq_num
[1]=p
->seq_current
[1]+1;
651 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: wow, something very nasty happened in pnm_calc_stream\n");
656 * gets a stream chunk and writes it to a recieve buffer
659 static int pnm_get_stream_chunk(pnm_t
*p
) {
663 unsigned int fof1
, fof2
, stream
;
665 /* send a keepalive */
666 /* realplayer seems to do that every 43th package */
667 if ((p
->packet
%43) == 42)
669 rm_write(p
->s
,&keepalive
,1);
672 /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2>
673 * where <o> is the offset to next stream chunk,
674 * <i1> is a 16 bit index
675 * <i2> is a 8 bit index which counts from 0x10 to somewhere
678 n
= rm_read (p
->s
, p
->buffer
, 8);
682 /* skip 8 bytes if 0x62 is read */
683 if (p
->buffer
[0] == 0x62)
685 n
= rm_read (p
->s
, p
->buffer
, 8);
688 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek 8 bytes on 0x62\n");
692 /* a server message */
693 if (p
->buffer
[0] == 'X')
695 int size
=BE_16(&p
->buffer
[1]);
697 rm_read (p
->s
, &p
->buffer
[8], size
-5);
699 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got message from server while reading stream:\n%s\n", &p
->buffer
[3]);
702 if (p
->buffer
[0] == 'F')
704 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
708 /* skip bytewise to next chunk.
709 * seems, that we don't need that, if we send enough
713 while (p
->buffer
[0] != 0x5a) {
715 for (i
=1; i
<8; i
++) {
716 p
->buffer
[i
-1]=p
->buffer
[i
];
718 rm_read (p
->s
, &p
->buffer
[7], 1);
723 if (n
) mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek %i bytes to next chunk\n", n
);
727 if ((p
->buffer
[0] != 0x5a)||(p
->buffer
[7] != 0x5a))
729 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: bad boundaries\n");
730 hexdump(p
->buffer
, 8);
735 fof1
=BE_16(&p
->buffer
[1]);
736 fof2
=BE_16(&p
->buffer
[3]);
739 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1
,fof2
);
743 /* get first index */
744 p
->seq_current
[0]=BE_16(&p
->buffer
[5]);
746 /* now read the rest of stream chunk */
747 n
= rm_read (p
->s
, &p
->recv
[5], fof1
-5);
748 if (n
<(fof1
-5)) return 0;
750 /* get second index */
751 p
->seq_current
[1]=p
->recv
[5];
754 p
->ts_current
=BE_32(&p
->recv
[6]);
756 /* get stream number */
757 stream
=pnm_calc_stream(p
);
759 /* saving timestamp */
760 p
->ts_last
[stream
]=p
->ts_current
;
762 /* constructing a data packet header */
764 p
->recv
[0]=0; /* object version */
768 memcpy(&p
->recv
[2], &fof2
, 2);
769 /*p->recv[2]=(fof2>>8)%0xff;*/ /* length */
770 /*p->recv[3]=(fof2)%0xff;*/
772 p
->recv
[4]=0; /* stream number */
775 p
->recv
[10]=p
->recv
[10] & 0xfe; /* streambox seems to do that... */
784 // pnm_t *pnm_connect(const char *mrl) {
785 static pnm_t
*pnm_connect(int fd
, char *path
) {
787 pnm_t
*p
=malloc(sizeof(pnm_t
));
790 p
->path
=strdup(path
);
793 pnm_send_request(p
,pnm_available_bandwidths
[10]);
794 if (!pnm_get_headers(p
, &need_response
)) {
795 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: failed to set up stream\n");
801 pnm_send_response(p
, pnm_response
);
805 /* copy header to recv */
807 memcpy(p
->recv
, p
->header
, p
->header_len
);
808 p
->recv_size
= p
->header_len
;
814 static int pnm_read (pnm_t
*this, char *data
, int len
) {
818 char *source
=this->recv
+ this->recv_read
;
819 int fill
=this->recv_size
- this->recv_read
;
822 if (len
< 0) return 0;
823 while (to_copy
> fill
) {
825 memcpy(dest
, source
, fill
);
830 if ((retval
= pnm_get_stream_chunk (this)) <= 0) {
832 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d of %d bytes provided\n", len
-to_copy
, len
);
840 fill
= this->recv_size
- this->recv_read
;
843 memcpy(dest
, source
, to_copy
);
844 this->recv_read
+= to_copy
;
847 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d bytes provided\n", len
);
853 static int pnm_peek_header (pnm_t
*this, char *data
) {
855 memcpy (data
, this->header
, this->header_len
);
856 return this->header_len
;
859 static void pnm_close(pnm_t
*p
) {
861 if (p
->s
>= 0) closesocket(p
->s
);
866 static int pnm_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
) {
867 return pnm_read(stream_ctrl
->data
, buffer
, size
);
870 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
875 mp_msg(MSGT_OPEN
, MSGL_INFO
, "STREAM_PNM, URL: %s\n", stream
->url
);
876 stream
->streaming_ctrl
= streaming_ctrl_new();
877 if(stream
->streaming_ctrl
==NULL
) {
880 stream
->streaming_ctrl
->bandwidth
= network_bandwidth
;
881 url
= url_new(stream
->url
);
882 stream
->streaming_ctrl
->url
= check4proxies(url
);
885 fd
= connect2Server( stream
->streaming_ctrl
->url
->hostname
,
886 stream
->streaming_ctrl
->url
->port
? stream
->streaming_ctrl
->url
->port
: 7070,1 );
891 pnm
= pnm_connect(fd
,stream
->streaming_ctrl
->url
->file
);
894 stream
->type
= STREAMTYPE_STREAM
;
896 stream
->streaming_ctrl
->data
=pnm
;
897 stream
->streaming_ctrl
->streaming_read
= pnm_streaming_read
;
898 //stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
899 stream
->streaming_ctrl
->prebuffer_size
= 8*1024; // 8 KBytes
900 stream
->streaming_ctrl
->buffering
= 1;
901 stream
->streaming_ctrl
->status
= streaming_playing_e
;
902 *file_format
= DEMUXER_TYPE_REAL
;
903 fixup_network_stream_cache(stream
);
907 streaming_ctrl_free(stream
->streaming_ctrl
);
908 stream
->streaming_ctrl
= NULL
;
909 return STREAM_UNSUPORTED
;
913 stream_info_t stream_info_pnm
= {
919 {"pnm", NULL
}, //pnm as fallback
921 0 // Urls are an option string