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
22 * pnm protocol implementation
23 * based upon code from joschka
39 #define closesocket close
40 #include <sys/socket.h>
41 //#include <netinet/in.h>
50 #include "osdep/timer.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 )
77 #define HEADER_SIZE 4096
88 char buffer
[BUF_SIZE
]; /* scratch buffer */
91 uint8_t recv
[BUF_SIZE
];
95 uint8_t header
[HEADER_SIZE
];
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 */
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) | \
115 /* D means direct (no pointer) */
116 #define BE_16D(x) ((x & 0xff00) >> 8)|((x & 0x00ff) << 8)
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
[]={
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
[] = {
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
) {
215 total
= 0; timeout
= 30;
219 n
= send (s
, &buf
[total
], len
- total
, 0);
224 #ifndef HAVE_WINSOCK2
225 if ((timeout
>0) && ((errno
== EAGAIN
) || (errno
== EINPROGRESS
))) {
227 if ((timeout
>0) && ((errno
== EAGAIN
) || (WSAGetLastError() == WSAEINPROGRESS
))) {
229 usec_sleep (1000000); timeout
--;
238 static ssize_t
rm_read(int fd
, void *buf
, size_t count
) {
244 while (total
< count
) {
247 struct timeval timeout
;
255 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
259 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
262 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: read error.\n");
272 * debugging utilities
275 static void hexdump (char *buf
, int length
) {
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
);
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
);
297 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\npnm: ");
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
,
313 unsigned int *chunk_type
,
314 char *data
, int *need_response
) {
316 unsigned int chunk_size
;
320 if (max
< PREAMBLE_SIZE
)
323 /* get first PREAMBLE_SIZE bytes and ignore checksum */
324 rm_read (p
->s
, data
, CHECKSUM_SIZE
);
326 rm_read (p
->s
, data
, PREAMBLE_SIZE
);
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
) {
338 ptr
=data
+PREAMBLE_SIZE
;
341 rm_read (p
->s
, ptr
++, 1);
345 /* expecting following chunk format: 0x4f <chunk size> <data...> */
349 rm_read (p
->s
, ptr
, 2);
351 if (*ptr
== 'X') /* checking for server message */
353 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got a message from server:\n");
356 rm_read (p
->s
, ptr
+2, 1);
361 rm_read (p
->s
, ptr
+3, n
);
364 mp_msg(MSGT_OPEN
, MSGL_WARN
, "%s\n",ptr
+3);
368 if (*ptr
== 'F') /* checking for server error */
370 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
379 if (*ptr
!= 0x4f) break;
383 rm_read (p
->s
, ptr
+2, n
);
387 /* the checksum of the next chunk is ignored here */
390 rm_read (p
->s
, ptr
+2, 1);
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
);
402 n
=rm_read (p
->s
, &data
[PREAMBLE_SIZE
], 0x100 - PREAMBLE_SIZE
);
403 hexdump(data
,n
+PREAMBLE_SIZE
);
407 rm_read (p
->s
, &data
[PREAMBLE_SIZE
], chunk_size
-PREAMBLE_SIZE
);
411 chunk_size
= PREAMBLE_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;
429 memcpy(&data
[4],chunk
,length
);
435 * constructs a request and sends it
438 static void pnm_send_request(pnm_t
*p
, uint32_t bandwidth
) {
441 int c
=PNM_HEADER_SIZE
;
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;
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 */
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
);
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
) {
524 uint8_t *ptr
=p
->header
;
525 uint8_t *prop_hdr
=NULL
;
526 int chunk_size
,size
=0;
528 /* rmff_header_t *h; */
533 if (HEADER_SIZE
-size
<=0)
535 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: header buffer overflow. exiting\n");
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
;
547 if (chunk_type
== DATA_TAG
)
549 if (chunk_type
== RMF_TAG
)
551 if (chunk_type
== PROP_TAG
)
558 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: error while parsing headers.\n");
562 /* set data offset */
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;
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);
580 p->header_len=rmff_get_header_size(h);
581 rmff_dump_header(h, p->header, HEADER_SIZE);
589 * determine correct stream number by looking at indices
592 static int pnm_calc_stream(pnm_t
*p
) {
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;
603 case 1: /* one is possible, good. */
607 p
->seq_num
[1]=p
->seq_current
[1]+1;
612 p
->seq_num
[3]=p
->seq_current
[1]+1;
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;
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;
635 /* wow, both streams match, or not. */
636 /* now we try to decide by timestamps */
637 if (p
->ts_current
< p
->ts_last
[1])
639 if (p
->ts_current
< p
->ts_last
[0])
641 /* does not help, we guess type 0 */
643 mp_msg(MSGT_OPEN
, MSGL_INFO
, "guessing stream# 0\n");
645 p
->seq_num
[0]=p
->seq_current
[0]+1;
646 p
->seq_num
[1]=p
->seq_current
[1]+1;
650 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: wow, something very nasty happened in pnm_calc_stream\n");
655 * gets a stream chunk and writes it to a recieve buffer
658 static int pnm_get_stream_chunk(pnm_t
*p
) {
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);
681 /* skip 8 bytes if 0x62 is read */
682 if (p
->buffer
[0] == 0x62)
684 n
= rm_read (p
->s
, p
->buffer
, 8);
687 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek 8 bytes on 0x62\n");
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);
698 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got message from server while reading stream:\n%s\n", &p
->buffer
[3]);
701 if (p
->buffer
[0] == 'F')
703 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
707 /* skip bytewise to next chunk.
708 * seems, that we don't need that, if we send enough
712 while (p
->buffer
[0] != 0x5a) {
714 for (i
=1; i
<8; i
++) {
715 p
->buffer
[i
-1]=p
->buffer
[i
];
717 rm_read (p
->s
, &p
->buffer
[7], 1);
722 if (n
) mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek %i bytes to next chunk\n", n
);
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);
734 fof1
=BE_16(&p
->buffer
[1]);
735 fof2
=BE_16(&p
->buffer
[3]);
738 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1
,fof2
);
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];
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 */
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 */
774 p
->recv
[10]=p
->recv
[10] & 0xfe; /* streambox seems to do that... */
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
));
789 p
->path
=strdup(path
);
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");
800 pnm_send_response(p
, pnm_response
);
804 /* copy header to recv */
806 memcpy(p
->recv
, p
->header
, p
->header_len
);
807 p
->recv_size
= p
->header_len
;
813 static int pnm_read (pnm_t
*this, char *data
, int len
) {
817 char *source
=this->recv
+ this->recv_read
;
818 int fill
=this->recv_size
- this->recv_read
;
821 if (len
< 0) return 0;
822 while (to_copy
> fill
) {
824 memcpy(dest
, source
, fill
);
829 if ((retval
= pnm_get_stream_chunk (this)) <= 0) {
831 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d of %d bytes provided\n", len
-to_copy
, len
);
839 fill
= this->recv_size
- this->recv_read
;
842 memcpy(dest
, source
, to_copy
);
843 this->recv_read
+= to_copy
;
846 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d bytes provided\n", 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
);
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
) {
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
) {
879 stream
->streaming_ctrl
->bandwidth
= network_bandwidth
;
880 url
= url_new(stream
->url
);
881 stream
->streaming_ctrl
->url
= check4proxies(url
);
884 fd
= connect2Server( stream
->streaming_ctrl
->url
->hostname
,
885 stream
->streaming_ctrl
->url
->port
? stream
->streaming_ctrl
->url
->port
: 7070,1 );
890 pnm
= pnm_connect(fd
,stream
->streaming_ctrl
->url
->file
);
893 stream
->type
= STREAMTYPE_STREAM
;
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
);
906 streaming_ctrl_free(stream
->streaming_ctrl
);
907 stream
->streaming_ctrl
= NULL
;
908 return STREAM_UNSUPORTED
;
912 stream_info_t stream_info_pnm
= {
918 {"pnm", NULL
}, //pnm as fallback
920 0 // Urls are an option string