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
38 #ifndef HAVE_WINSOCK2_H
39 #include <sys/socket.h>
40 //#include <netinet/in.h>
46 #include "libavutil/intreadwrite.h"
49 #include "libmpdemux/demuxer.h"
51 #include "osdep/timer.h"
55 //#include "libreal/rmff.h"
57 extern int network_bandwidth
;
59 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
60 (((long)(unsigned char)(ch3) ) | \
61 ( (long)(unsigned char)(ch2) << 8 ) | \
62 ( (long)(unsigned char)(ch1) << 16 ) | \
63 ( (long)(unsigned char)(ch0) << 24 ) )
66 #define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F')
67 #define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P')
68 #define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R')
69 #define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T')
70 #define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A')
71 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X')
72 #define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 )
79 #define HEADER_SIZE 4096
90 char buffer
[BUF_SIZE
]; /* scratch buffer */
93 uint8_t recv
[BUF_SIZE
];
97 uint8_t header
[HEADER_SIZE
];
100 unsigned int seq_num
[4]; /* two streams with two indices */
101 unsigned int seq_current
[2]; /* seqs of last stream chunk read */
102 uint32_t ts_current
; /* timestamp of current chunk */
103 uint32_t ts_last
[2]; /* timestamps of last chunks */
104 unsigned int packet
; /* number of last recieved packet */
108 #define PREAMBLE_SIZE 8
109 #define CHECKSUM_SIZE 3
112 /* header of rm files */
113 static const unsigned char rm_header
[]={
114 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */
115 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */
116 0x00, 0x00, /* object_version 0x00 */
117 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */
118 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */
121 /* data chunk header */
122 static const unsigned char pnm_data_header
[]={
124 0,0,0,0, /* data chunk size */
125 0,0, /* object version */
126 0,0,0,0, /* num packets */
127 0,0,0,0}; /* next data header */
129 /* pnm request chunk ids */
131 #define PNA_CLIENT_CAPS 0x03
132 #define PNA_CLIENT_CHALLANGE 0x04
133 #define PNA_BANDWIDTH 0x05
134 #define PNA_GUID 0x13
135 #define PNA_TIMESTAMP 0x17
136 #define PNA_TWENTYFOUR 0x18
138 #define PNA_CLIENT_STRING 0x63
139 #define PNA_PATH_REQUEST 0x52
141 static const unsigned char pnm_challenge
[] = "0990f6b4508b51e801bd6da011ad7b56";
142 static const unsigned char pnm_timestamp
[] = "[15/06/1999:22:22:49 00:00]";
143 static const unsigned char pnm_guid
[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0";
144 static const unsigned char pnm_response
[] = "97715a899cbe41cee00dd434851535bf";
145 static const unsigned char client_string
[] = "WinNT_9.0_6.0.6.45_plus32_MP60_en-US_686l";
147 static const unsigned char pnm_header
[] = {
154 static const unsigned char pnm_client_caps
[] = {
155 0x07, 0x8a, 'p','n','r','v',
156 0, 0x90, 'p','n','r','v',
157 0, 0x64, 'd','n','e','t',
158 0, 0x46, 'p','n','r','v',
159 0, 0x32, 'd','n','e','t',
160 0, 0x2b, 'p','n','r','v',
161 0, 0x28, 'd','n','e','t',
162 0, 0x24, 'p','n','r','v',
163 0, 0x19, 'd','n','e','t',
164 0, 0x18, 'p','n','r','v',
165 0, 0x14, 's','i','p','r',
166 0, 0x14, 'd','n','e','t',
167 0, 0x24, '2','8','_','8',
168 0, 0x12, 'p','n','r','v',
169 0, 0x0f, 'd','n','e','t',
170 0, 0x0a, 's','i','p','r',
171 0, 0x0a, 'd','n','e','t',
172 0, 0x08, 's','i','p','r',
173 0, 0x06, 's','i','p','r',
174 0, 0x12, 'l','p','c','J',
175 0, 0x07, '0','5','_','6' };
177 static const uint32_t pnm_default_bandwidth
=10485800;
178 static const uint32_t pnm_available_bandwidths
[]={14400,19200,28800,33600,34430,57600,
179 115200,262200,393216,524300,1544000,10485800};
181 static const unsigned char pnm_twentyfour
[]={
182 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24,
183 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 };
185 /* now other data follows. marked with 0x0000 at the beginning */
186 static const unsigned char after_chunks
[]={
187 0x00, 0x00, /* mark */
189 0x50, 0x84, /* seems to be fixated */
190 0x1f, 0x3a /* varies on each request (checksum ?)*/
193 static void hexdump (char *buf
, int length
);
195 static int rm_write(int s
, const char *buf
, int len
) {
198 total
= 0; timeout
= 30;
202 n
= send (s
, &buf
[total
], len
- total
, 0);
207 #ifndef HAVE_WINSOCK2_H
208 if (timeout
>0 && (errno
== EAGAIN
|| errno
== EINPROGRESS
)) {
210 if (timeout
>0 && (errno
== EAGAIN
|| WSAGetLastError() == WSAEINPROGRESS
)) {
212 usec_sleep (1000000); timeout
--;
221 static ssize_t
rm_read(int fd
, void *buf
, size_t count
) {
227 while (total
< count
) {
230 struct timeval timeout
;
238 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
242 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
245 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: read error.\n");
255 * debugging utilities
258 static void hexdump (char *buf
, int length
) {
262 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: ascii>");
263 for (i
= 0; i
< length
; i
++) {
264 unsigned char c
= buf
[i
];
266 if (c
>= 32 && c
<= 128)
267 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%c", c
);
269 mp_msg(MSGT_OPEN
, MSGL_INFO
, ".");
271 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
273 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: hexdump> ");
274 for (i
= 0; i
< length
; i
++) {
275 unsigned char c
= buf
[i
];
277 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%02x", c
);
280 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\npnm: ");
283 mp_msg(MSGT_OPEN
, MSGL_INFO
, " ");
286 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
290 * pnm_get_chunk gets a chunk from stream
291 * and returns number of bytes read
294 static int pnm_get_chunk(pnm_t
*p
,
296 unsigned int *chunk_type
,
297 char *data
, int *need_response
) {
299 unsigned int chunk_size
;
303 if (max
< PREAMBLE_SIZE
)
306 /* get first PREAMBLE_SIZE bytes and ignore checksum */
307 rm_read (p
->s
, data
, CHECKSUM_SIZE
);
309 rm_read (p
->s
, data
, PREAMBLE_SIZE
);
311 rm_read (p
->s
, data
+CHECKSUM_SIZE
, PREAMBLE_SIZE
-CHECKSUM_SIZE
);
313 max
-= PREAMBLE_SIZE
;
315 *chunk_type
= AV_RB32(data
);
316 chunk_size
= AV_RB32(data
+4);
318 switch (*chunk_type
) {
321 ptr
=data
+PREAMBLE_SIZE
;
324 rm_read (p
->s
, ptr
++, 1);
328 /* expecting following chunk format: 0x4f <chunk size> <data...> */
332 rm_read (p
->s
, ptr
, 2);
334 if (*ptr
== 'X') /* checking for server message */
336 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got a message from server:\n");
339 rm_read (p
->s
, ptr
+2, 1);
344 rm_read (p
->s
, ptr
+3, n
);
347 mp_msg(MSGT_OPEN
, MSGL_WARN
, "%s\n",ptr
+3);
351 if (*ptr
== 'F') /* checking for server error */
353 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
362 if (*ptr
!= 0x4f) break;
366 rm_read (p
->s
, ptr
+2, n
);
370 /* the checksum of the next chunk is ignored here */
373 rm_read (p
->s
, ptr
+2, 1);
382 if (chunk_size
> max
|| chunk_size
< PREAMBLE_SIZE
) {
383 mp_msg(MSGT_OPEN
, MSGL_ERR
, "error: max chunk size exceded (max was 0x%04x)\n", max
);
385 n
=rm_read (p
->s
, &data
[PREAMBLE_SIZE
], 0x100 - PREAMBLE_SIZE
);
386 hexdump(data
,n
+PREAMBLE_SIZE
);
390 rm_read (p
->s
, &data
[PREAMBLE_SIZE
], chunk_size
-PREAMBLE_SIZE
);
394 chunk_size
= PREAMBLE_SIZE
;
402 * writes a chunk to a buffer, returns number of bytes written
405 static int pnm_write_chunk(uint16_t chunk_id
, uint16_t length
,
406 const char *chunk
, char *data
) {
408 AV_WB16(&data
[0], chunk_id
);
409 AV_WB16(&data
[2], length
);
410 memcpy(&data
[4],chunk
,length
);
416 * constructs a request and sends it
419 static void pnm_send_request(pnm_t
*p
, uint32_t bandwidth
) {
422 int c
=sizeof(pnm_header
);
425 memcpy(p
->buffer
,pnm_header
,sizeof(pnm_header
));
426 c
+=pnm_write_chunk(PNA_CLIENT_CHALLANGE
,strlen(pnm_challenge
),
427 pnm_challenge
,&p
->buffer
[c
]);
428 c
+=pnm_write_chunk(PNA_CLIENT_CAPS
,sizeof(pnm_client_caps
),
429 pnm_client_caps
,&p
->buffer
[c
]);
430 c
+=pnm_write_chunk(0x0a,0,NULL
,&p
->buffer
[c
]);
431 c
+=pnm_write_chunk(0x0c,0,NULL
,&p
->buffer
[c
]);
432 c
+=pnm_write_chunk(0x0d,0,NULL
,&p
->buffer
[c
]);
433 c
+=pnm_write_chunk(0x16,2,fixme
,&p
->buffer
[c
]);
434 c
+=pnm_write_chunk(PNA_TIMESTAMP
,strlen(pnm_timestamp
),
435 pnm_timestamp
,&p
->buffer
[c
]);
436 c
+=pnm_write_chunk(PNA_BANDWIDTH
,4,
437 (const char *)&pnm_default_bandwidth
,&p
->buffer
[c
]);
438 c
+=pnm_write_chunk(0x08,0,NULL
,&p
->buffer
[c
]);
439 c
+=pnm_write_chunk(0x0e,0,NULL
,&p
->buffer
[c
]);
440 c
+=pnm_write_chunk(0x0f,0,NULL
,&p
->buffer
[c
]);
441 c
+=pnm_write_chunk(0x11,0,NULL
,&p
->buffer
[c
]);
442 c
+=pnm_write_chunk(0x10,0,NULL
,&p
->buffer
[c
]);
443 c
+=pnm_write_chunk(0x15,0,NULL
,&p
->buffer
[c
]);
444 c
+=pnm_write_chunk(0x12,0,NULL
,&p
->buffer
[c
]);
445 c
+=pnm_write_chunk(PNA_GUID
,strlen(pnm_guid
),
446 pnm_guid
,&p
->buffer
[c
]);
447 c
+=pnm_write_chunk(PNA_TWENTYFOUR
,sizeof(pnm_twentyfour
),
448 pnm_twentyfour
,&p
->buffer
[c
]);
450 /* data after chunks */
451 memcpy(&p
->buffer
[c
],after_chunks
,sizeof(after_chunks
));
452 c
+=sizeof(after_chunks
);
454 /* client id string */
455 p
->buffer
[c
]=PNA_CLIENT_STRING
;
456 AV_WB16(&p
->buffer
[c
+1], strlen(client_string
)-1); /* don't know why do we have -1 here */
457 memcpy(&p
->buffer
[c
+1],&i16
,2);
458 memcpy(&p
->buffer
[c
+3],client_string
,strlen(client_string
)+1);
459 c
=c
+3+strlen(client_string
)+1;
463 p
->buffer
[c
+1]=PNA_PATH_REQUEST
;
464 AV_WB16(&p
->buffer
[c
+2], strlen(p
->path
));
465 memcpy(&p
->buffer
[c
+4],p
->path
,strlen(p
->path
));
466 c
=c
+4+strlen(p
->path
);
468 /* some trailing bytes */
472 rm_write(p
->s
,p
->buffer
,c
+2);
476 * pnm_send_response sends a response of a challenge
479 static void pnm_send_response(pnm_t
*p
, const char *response
) {
481 int size
=strlen(response
);
485 p
->buffer
[2]=(unsigned char) size
;
487 memcpy(&p
->buffer
[3], response
, size
);
489 rm_write (p
->s
, p
->buffer
, size
+3);
494 * get headers and challenge and fix headers
495 * write headers to p->header
496 * write challenge to p->buffer
498 * return 0 on error. != 0 on success
501 static int pnm_get_headers(pnm_t
*p
, int *need_response
) {
504 uint8_t *ptr
=p
->header
;
505 uint8_t *prop_hdr
=NULL
;
506 int chunk_size
,size
=0;
508 /* rmff_header_t *h; */
513 if (HEADER_SIZE
-size
<=0)
515 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: header buffer overflow. exiting\n");
518 chunk_size
=pnm_get_chunk(p
,HEADER_SIZE
-size
,&chunk_type
,ptr
,&nr
);
519 if (chunk_size
< 0) return 0;
520 if (chunk_type
== 0) break;
521 if (chunk_type
== PNA_TAG
)
523 memcpy(ptr
, rm_header
, sizeof(rm_header
));
524 chunk_size
=sizeof(rm_header
);
527 if (chunk_type
== DATA_TAG
)
529 if (chunk_type
== RMF_TAG
)
531 if (chunk_type
== PROP_TAG
)
538 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: error while parsing headers.\n");
542 /* set data offset */
543 AV_WB32(&prop_hdr
[42], size
- 1);
546 memcpy (p
->buffer
, ptr
, PREAMBLE_SIZE
);
547 rm_read (p
->s
, &p
->buffer
[PREAMBLE_SIZE
], 64);
549 /* now write a data header */
550 memcpy(ptr
, pnm_data_header
, sizeof(pnm_data_header
));
551 size
+=sizeof(pnm_data_header
);
553 h=rmff_scan_header(p->header);
555 p->header_len=rmff_get_header_size(h);
556 rmff_dump_header(h, p->header, HEADER_SIZE);
564 * determine correct stream number by looking at indices
567 static int pnm_calc_stream(pnm_t
*p
) {
571 /* looking at the first index to
572 * find possible stream types
574 if (p
->seq_current
[0]==p
->seq_num
[0]) str0
=1;
575 if (p
->seq_current
[0]==p
->seq_num
[2]) str1
=1;
578 case 1: /* one is possible, good. */
582 p
->seq_num
[1]=p
->seq_current
[1]+1;
587 p
->seq_num
[3]=p
->seq_current
[1]+1;
592 case 2: /* both types or none possible, not so good */
593 /* try to figure out by second index */
594 if ( p
->seq_current
[1] == p
->seq_num
[1]
595 && p
->seq_current
[1] != p
->seq_num
[3])
597 /* ok, only stream0 matches */
598 p
->seq_num
[0]=p
->seq_current
[0]+1;
602 if ( p
->seq_current
[1] == p
->seq_num
[3]
603 && p
->seq_current
[1] != p
->seq_num
[1])
605 /* ok, only stream1 matches */
606 p
->seq_num
[2]=p
->seq_current
[0]+1;
610 /* wow, both streams match, or not. */
611 /* now we try to decide by timestamps */
612 if (p
->ts_current
< p
->ts_last
[1])
614 if (p
->ts_current
< p
->ts_last
[0])
616 /* does not help, we guess type 0 */
618 mp_msg(MSGT_OPEN
, MSGL_INFO
, "guessing stream# 0\n");
620 p
->seq_num
[0]=p
->seq_current
[0]+1;
621 p
->seq_num
[1]=p
->seq_current
[1]+1;
625 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: wow, something very nasty happened in pnm_calc_stream\n");
630 * gets a stream chunk and writes it to a recieve buffer
633 static int pnm_get_stream_chunk(pnm_t
*p
) {
637 unsigned int fof1
, fof2
, stream
;
639 /* send a keepalive */
640 /* realplayer seems to do that every 43th package */
641 if (p
->packet
%43 == 42)
642 rm_write(p
->s
,&keepalive
,1);
644 /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2>
645 * where <o> is the offset to next stream chunk,
646 * <i1> is a 16 bit index
647 * <i2> is a 8 bit index which counts from 0x10 to somewhere
650 n
= rm_read (p
->s
, p
->buffer
, 8);
654 /* skip 8 bytes if 0x62 is read */
655 if (p
->buffer
[0] == 0x62)
657 n
= rm_read (p
->s
, p
->buffer
, 8);
660 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek 8 bytes on 0x62\n");
664 /* a server message */
665 if (p
->buffer
[0] == 'X')
667 int size
=AV_RB16(&p
->buffer
[1]);
669 rm_read (p
->s
, &p
->buffer
[8], size
-5);
671 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got message from server while reading stream:\n%s\n", &p
->buffer
[3]);
674 if (p
->buffer
[0] == 'F')
676 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
680 /* skip bytewise to next chunk.
681 * seems, that we don't need that, if we send enough
685 while (p
->buffer
[0] != 0x5a) {
688 p
->buffer
[i
-1]=p
->buffer
[i
];
689 rm_read (p
->s
, &p
->buffer
[7], 1);
694 if (n
) mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek %i bytes to next chunk\n", n
);
698 if (p
->buffer
[0] != 0x5a || p
->buffer
[7] != 0x5a)
700 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: bad boundaries\n");
701 hexdump(p
->buffer
, 8);
706 fof1
=AV_RB16(&p
->buffer
[1]);
707 fof2
=AV_RB16(&p
->buffer
[3]);
710 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1
,fof2
);
714 /* get first index */
715 p
->seq_current
[0]=AV_RB16(&p
->buffer
[5]);
717 /* now read the rest of stream chunk */
718 n
= rm_read (p
->s
, &p
->recv
[5], fof1
-5);
719 if (n
<fof1
-5) return 0;
721 /* get second index */
722 p
->seq_current
[1]=p
->recv
[5];
725 p
->ts_current
=AV_RB32(&p
->recv
[6]);
727 /* get stream number */
728 stream
=pnm_calc_stream(p
);
730 /* saving timestamp */
731 p
->ts_last
[stream
]=p
->ts_current
;
733 /* constructing a data packet header */
735 p
->recv
[0]=0; /* object version */
738 AV_WB16(&p
->recv
[2], fof2
); /* length */
740 p
->recv
[4]=0; /* stream number */
743 p
->recv
[10] &= 0xfe; /* streambox seems to do that... */
752 // pnm_t *pnm_connect(const char *mrl) {
753 static pnm_t
*pnm_connect(int fd
, char *path
) {
755 pnm_t
*p
=malloc(sizeof(pnm_t
));
758 p
->path
=strdup(path
);
761 pnm_send_request(p
,pnm_available_bandwidths
[10]);
762 if (!pnm_get_headers(p
, &need_response
)) {
763 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: failed to set up stream\n");
769 pnm_send_response(p
, pnm_response
);
773 /* copy header to recv */
775 memcpy(p
->recv
, p
->header
, p
->header_len
);
776 p
->recv_size
= p
->header_len
;
782 static int pnm_read (pnm_t
*this, char *data
, int len
) {
786 char *source
=this->recv
+ this->recv_read
;
787 int fill
=this->recv_size
- this->recv_read
;
790 if (len
< 0) return 0;
791 while (to_copy
> fill
) {
793 memcpy(dest
, source
, fill
);
798 if ((retval
= pnm_get_stream_chunk (this)) <= 0) {
800 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d of %d bytes provided\n", len
-to_copy
, len
);
808 fill
= this->recv_size
- this->recv_read
;
811 memcpy(dest
, source
, to_copy
);
812 this->recv_read
+= to_copy
;
815 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d bytes provided\n", len
);
821 static int pnm_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
) {
822 return pnm_read(stream_ctrl
->data
, buffer
, size
);
825 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
830 mp_msg(MSGT_OPEN
, MSGL_INFO
, "STREAM_PNM, URL: %s\n", stream
->url
);
831 stream
->streaming_ctrl
= streaming_ctrl_new();
832 if(stream
->streaming_ctrl
==NULL
)
835 stream
->streaming_ctrl
->bandwidth
= network_bandwidth
;
836 url
= url_new(stream
->url
);
837 stream
->streaming_ctrl
->url
= check4proxies(url
);
840 fd
= connect2Server( stream
->streaming_ctrl
->url
->hostname
,
841 stream
->streaming_ctrl
->url
->port
? stream
->streaming_ctrl
->url
->port
: 7070,1 );
846 pnm
= pnm_connect(fd
,stream
->streaming_ctrl
->url
->file
);
849 stream
->type
= STREAMTYPE_STREAM
;
851 stream
->streaming_ctrl
->data
=pnm
;
852 stream
->streaming_ctrl
->streaming_read
= pnm_streaming_read
;
853 //stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
854 stream
->streaming_ctrl
->prebuffer_size
= 8*1024; // 8 KBytes
855 stream
->streaming_ctrl
->buffering
= 1;
856 stream
->streaming_ctrl
->status
= streaming_playing_e
;
857 *file_format
= DEMUXER_TYPE_REAL
;
858 fixup_network_stream_cache(stream
);
862 streaming_ctrl_free(stream
->streaming_ctrl
);
863 stream
->streaming_ctrl
= NULL
;
864 return STREAM_UNSUPPORTED
;
868 const stream_info_t stream_info_pnm
= {
874 {"pnm", NULL
}, //pnm as fallback
876 0 // Urls are an option string