1 /*
2 (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org)
3 (c) Copyright 2000-2004 Convergence (integrated media) GmbH
4
5 All rights reserved.
6
7 Written by Denis Oliver Kropp <dok@directfb.org>,
8 Andreas Hundt <andi@fischlustig.de>,
9 Sven Neumann <neo@directfb.org>,
10 Ville Syrjälä <syrjala@sci.fi> and
11 Claudio Ciccani <klan@users.sf.net>.
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the
25 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 Boston, MA 02111-1307, USA.
27 */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <errno.h>
37
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <netdb.h>
45
46 #include <direct/build.h>
47 #include <direct/types.h>
48 #include <direct/mem.h>
49 #include <direct/memcpy.h>
50 #include <direct/messages.h>
51 #include <direct/debug.h>
52 #include <direct/util.h>
53
54 #include <direct/stream.h>
55
56 struct __D_DirectStream {
57 int magic;
58 int ref;
59
60 int fd;
61 unsigned int offset;
62 int length;
63
64 char *mime;
65
66 /* cache for piped streams */
67 void *cache;
68 unsigned int cache_size;
69
70 #if DIRECT_BUILD_NETWORK
71 /* remote streams data */
72 struct {
73 int sd;
74
75 char *host;
76 int port;
77 struct addrinfo *addr;
78
79 char *user;
80 char *pass;
81 char *auth;
82
83 char *path;
84
85 int redirects;
86
87 void *data;
88
89 bool real_rtsp;
90 bool real_pack;
91 } remote;
92 #endif
93
94 DirectResult (*wait) ( DirectStream *stream,
95 unsigned int length,
96 struct timeval *tv );
97 DirectResult (*peek) ( DirectStream *stream,
98 unsigned int length,
99 int offset,
100 void *buf,
101 unsigned int *read_out );
102 DirectResult (*read) ( DirectStream *stream,
103 unsigned int length,
104 void *buf,
105 unsigned int *read_out );
106 DirectResult (*seek) ( DirectStream *stream,
107 unsigned int offset );
108 };
109
110
111 static void direct_stream_close( DirectStream *stream );
112
113
114 #if DIRECT_BUILD_NETWORK
115 D_DEBUG_DOMAIN( Direct_Stream, "Direct/Stream", "Stream wrapper" );
116 #endif
117
118 /************************** Begin Network Support ***************************/
119
120 #if DIRECT_BUILD_NETWORK
121
122 #define NET_TIMEOUT 15
123 #define HTTP_PORT 80
124 #define FTP_PORT 21
125 #define RTSP_PORT 554
126 #define HTTP_MAX_REDIRECTS 15
127
128 static DirectResult http_open( DirectStream *stream, const char *filename );
129 static DirectResult ftp_open ( DirectStream *stream, const char *filename );
130 static DirectResult rtsp_open( DirectStream *stream, const char *filename );
131
132
trim(char * s)133 static inline char* trim( char *s )
134 {
135 char *e;
136
137 #define space( c ) ((c) == ' ' || (c) == '\t' || \
138 (c) == '\r' || (c) == '\n' || \
139 (c) == '"' || (c) == '\'')
140
141 for (; space(*s); s++);
142
143 e = s + strlen(s) - 1;
144 for (; e > s && space(*e); *e-- = '\0');
145
146 #undef space
147
148 return s;
149 }
150
151 static void
parse_url(const char * url,char ** ret_host,int * ret_port,char ** ret_user,char ** ret_pass,char ** ret_path)152 parse_url( const char *url, char **ret_host, int *ret_port,
153 char **ret_user, char **ret_pass, char **ret_path )
154 {
155 char *host = NULL;
156 int port = 0;
157 char *user = NULL;
158 char *pass = NULL;
159 char *path;
160 char *tmp;
161
162 tmp = strchr( url, '/' );
163 if (tmp) {
164 host = alloca( tmp - url + 1 );
165 memcpy( host, url, tmp - url );
166 host[tmp-url] = '\0';
167 path = tmp;
168 } else {
169 host = alloca( strlen( url ) + 1 );
170 memcpy( host, url, strlen( url ) + 1 );
171 path = "/";
172 }
173
174 tmp = strrchr( host, '@' );
175 if (tmp) {
176 *tmp = '\0';
177 pass = strchr( host, ':' );
178 if (pass) {
179 *pass = '\0';
180 pass++;
181 }
182 user = host;
183 host = tmp + 1;
184 }
185
186 tmp = strchr( host, ':' );
187 if (tmp) {
188 port = strtol( tmp+1, NULL, 10 );
189 *tmp = '\0';
190 }
191
192 /* IPv6 variant (host within brackets) */
193 if (*host == '[') {
194 host++;
195 tmp = strchr( host, ']' );
196 if (tmp)
197 *tmp = '\0';
198 }
199
200 if (ret_host)
201 *ret_host = D_STRDUP( host );
202
203 if (ret_port && port)
204 *ret_port = port;
205
206 if (ret_user && user)
207 *ret_user = D_STRDUP( user );
208
209 if (ret_pass && pass)
210 *ret_pass = D_STRDUP( pass );
211
212 if (ret_path)
213 *ret_path = D_STRDUP( path );
214 }
215
216 /*****************************************************************************/
217
218 static int
net_response(DirectStream * stream,char * buf,size_t size)219 net_response( DirectStream *stream, char *buf, size_t size )
220 {
221 fd_set set;
222 struct timeval tv;
223 int i;
224
225 FD_ZERO( &set );
226 FD_SET( stream->remote.sd, &set );
227
228 for (i = 0; i < size-1; i++) {
229 tv.tv_sec = NET_TIMEOUT;
230 tv.tv_usec = 0;
231 select( stream->remote.sd+1, &set, NULL, NULL, &tv );
232
233 if (recv( stream->remote.sd, buf+i, 1, 0 ) != 1)
234 break;
235
236 if (buf[i] == '\n') {
237 if (i > 0 && buf[i-1] == '\r')
238 i--;
239 break;
240 }
241 }
242
243 buf[i] = '\0';
244
245 D_DEBUG_AT( Direct_Stream, "got [%s].\n", buf );
246
247 return i;
248 }
249
250 static int
net_command(DirectStream * stream,char * buf,size_t size)251 net_command( DirectStream *stream, char *buf, size_t size )
252 {
253 fd_set s;
254 struct timeval t;
255 int status;
256 int version;
257 char space;
258
259 FD_ZERO( &s );
260 FD_SET( stream->remote.sd, &s );
261
262 t.tv_sec = NET_TIMEOUT;
263 t.tv_usec = 0;
264
265 switch (select( stream->remote.sd+1, NULL, &s, NULL, &t )) {
266 case 0:
267 D_DEBUG_AT( Direct_Stream, "Timeout!\n" );
268 case -1:
269 return -1;
270 }
271
272 send( stream->remote.sd, buf, strlen(buf), 0 );
273 send( stream->remote.sd, "\r\n", 2, 0 );
274
275 D_DEBUG_AT( Direct_Stream, "sent [%s].\n", buf );
276
277 while (net_response( stream, buf, size ) > 0) {
278 status = 0;
279 if (sscanf( buf, "HTTP/1.%d %3d", &version, &status ) == 2 ||
280 sscanf( buf, "RTSP/1.%d %3d", &version, &status ) == 2 ||
281 sscanf( buf, "ICY %3d", &status ) == 1 ||
282 sscanf( buf, "%3d%[ ]", &status, &space ) == 2)
283 return status;
284 }
285
286 return 0;
287 }
288
289 static DirectResult
net_wait(DirectStream * stream,unsigned int length,struct timeval * tv)290 net_wait( DirectStream *stream,
291 unsigned int length,
292 struct timeval *tv )
293 {
294 fd_set s;
295
296 if (stream->cache_size >= length)
297 return DR_OK;
298
299 if (stream->fd == -1)
300 return DR_EOF;
301
302 FD_ZERO( &s );
303 FD_SET( stream->fd, &s );
304
305 switch (select( stream->fd+1, &s, NULL, NULL, tv )) {
306 case 0:
307 if (!tv && !stream->cache_size)
308 return DR_EOF;
309 return DR_TIMEOUT;
310 case -1:
311 return errno2result( errno );
312 }
313
314 return DR_OK;
315 }
316
317 static DirectResult
net_peek(DirectStream * stream,unsigned int length,int offset,void * buf,unsigned int * read_out)318 net_peek( DirectStream *stream,
319 unsigned int length,
320 int offset,
321 void *buf,
322 unsigned int *read_out )
323 {
324 char *tmp;
325 int size;
326
327 if (offset < 0)
328 return DR_UNSUPPORTED;
329
330 tmp = alloca( length + offset );
331
332 size = recv( stream->fd, tmp, length+offset, MSG_PEEK );
333 switch (size) {
334 case 0:
335 return DR_EOF;
336 case -1:
337 if (errno == EAGAIN || errno == EWOULDBLOCK)
338 return DR_BUFFEREMPTY;
339 return errno2result( errno );
340 default:
341 if (size < offset)
342 return DR_BUFFEREMPTY;
343 size -= offset;
344 break;
345 }
346
347 direct_memcpy( buf, tmp+offset, size );
348
349 if (read_out)
350 *read_out = size;
351
352 return DR_OK;
353 }
354
355 static DirectResult
net_read(DirectStream * stream,unsigned int length,void * buf,unsigned int * read_out)356 net_read( DirectStream *stream,
357 unsigned int length,
358 void *buf,
359 unsigned int *read_out )
360 {
361 int size;
362
363 size = recv( stream->fd, buf, length, 0 );
364 switch (size) {
365 case 0:
366 return DR_EOF;
367 case -1:
368 if (errno == EAGAIN || errno == EWOULDBLOCK)
369 return DR_BUFFEREMPTY;
370 return errno2result( errno );
371 }
372
373 stream->offset += size;
374
375 if (read_out)
376 *read_out = size;
377
378 return DR_OK;
379 }
380
381 static DirectResult
net_connect(struct addrinfo * addr,int sock,int proto,int * ret_fd)382 net_connect( struct addrinfo *addr, int sock, int proto, int *ret_fd )
383 {
384 DirectResult ret = DR_OK;
385 int fd = -1;
386 struct addrinfo *tmp;
387
388 D_ASSERT( addr != NULL );
389 D_ASSERT( ret_fd != NULL );
390
391 for (tmp = addr; tmp; tmp = tmp->ai_next) {
392 int err;
393
394 fd = socket( tmp->ai_family, sock, proto );
395 if (fd < 0) {
396 ret = errno2result( errno );
397 D_DEBUG_AT( Direct_Stream,
398 "failed to create socket!\n\t->%s",
399 strerror(errno) );
400 continue;
401 }
402
403 fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK );
404
405 D_DEBUG_AT( Direct_Stream,
406 "connecting to %s...\n", tmp->ai_canonname );
407
408 if (proto == IPPROTO_UDP)
409 err = bind( fd, tmp->ai_addr, tmp->ai_addrlen );
410 else
411 err = connect( fd, tmp->ai_addr, tmp->ai_addrlen );
412
413 if (err == 0 || errno == EINPROGRESS) {
414 struct timeval t = { NET_TIMEOUT, 0 };
415 fd_set s;
416
417 /* Join multicast group? */
418 if (tmp->ai_addr->sa_family == AF_INET) {
419 struct sockaddr_in *saddr = (struct sockaddr_in *) tmp->ai_addr;
420
421 if (IN_MULTICAST( ntohl(saddr->sin_addr.s_addr) )) {
422 struct ip_mreq req;
423
424 D_DEBUG_AT( Direct_Stream,
425 "joining multicast group (%u.%u.%u.%u)...\n",
426 (u8)tmp->ai_addr->sa_data[2], (u8)tmp->ai_addr->sa_data[3],
427 (u8)tmp->ai_addr->sa_data[4], (u8)tmp->ai_addr->sa_data[5] );
428
429 req.imr_multiaddr.s_addr = saddr->sin_addr.s_addr;
430 req.imr_interface.s_addr = 0;
431
432 err = setsockopt( fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req) );
433 if (err < 0) {
434 ret = errno2result( errno );
435 D_PERROR( "Direct/Stream: Could not join multicast group (%u.%u.%u.%u)!\n",
436 (u8)tmp->ai_addr->sa_data[2], (u8)tmp->ai_addr->sa_data[3],
437 (u8)tmp->ai_addr->sa_data[4], (u8)tmp->ai_addr->sa_data[5] );
438 close( fd );
439 continue;
440 }
441
442 setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, saddr, sizeof(*saddr) );
443 }
444 }
445
446 FD_ZERO( &s );
447 FD_SET( fd, &s );
448
449 err = select( fd+1, NULL, &s, NULL, &t );
450 if (err < 1) {
451 D_DEBUG_AT( Direct_Stream, "...connection failed.\n" );
452
453 close( fd );
454 fd = -1;
455
456 if (err == 0) {
457 ret = DR_TIMEOUT;
458 continue;
459 } else {
460 ret = errno2result( errno );
461 break;
462 }
463 }
464
465 D_DEBUG_AT( Direct_Stream, "...connected.\n" );
466
467 ret = DR_OK;
468 break;
469 }
470 }
471
472 *ret_fd = fd;
473
474 return ret;
475 }
476
477 static DirectResult
net_open(DirectStream * stream,const char * filename,int proto)478 net_open( DirectStream *stream, const char *filename, int proto )
479 {
480 DirectResult ret = DR_OK;
481 int sock = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM;
482 struct addrinfo hints;
483 char port[16];
484
485 parse_url( filename,
486 &stream->remote.host,
487 &stream->remote.port,
488 &stream->remote.user,
489 &stream->remote.pass,
490 &stream->remote.path );
491
492 snprintf( port, sizeof(port), "%d", stream->remote.port );
493
494 memset( &hints, 0, sizeof(hints) );
495 hints.ai_flags = AI_CANONNAME;
496 hints.ai_socktype = sock;
497 hints.ai_family = PF_UNSPEC;
498
499 if (getaddrinfo( stream->remote.host, port,
500 &hints, &stream->remote.addr )) {
501 D_ERROR( "Direct/Stream: "
502 "failed to resolve host '%s'!\n", stream->remote.host );
503 return DR_FAILURE;
504 }
505
506 ret = net_connect( stream->remote.addr, sock, proto, &stream->remote.sd );
507 if (ret)
508 return ret;
509
510 stream->fd = stream->remote.sd;
511 stream->length = -1;
512 stream->wait = net_wait;
513 stream->peek = net_peek;
514 stream->read = net_read;
515
516 return ret;
517 }
518
519 /*****************************************************************************/
520
521 static DirectResult
http_seek(DirectStream * stream,unsigned int offset)522 http_seek( DirectStream *stream, unsigned int offset )
523 {
524 DirectResult ret;
525 char buf[1280];
526 int status, len;
527
528 close( stream->remote.sd );
529 stream->remote.sd = -1;
530
531 ret = net_connect( stream->remote.addr,
532 SOCK_STREAM, IPPROTO_TCP, &stream->remote.sd );
533 if (ret)
534 return ret;
535
536 stream->fd = stream->remote.sd;
537
538 len = snprintf( buf, sizeof(buf),
539 "GET %s HTTP/1.0\r\n"
540 "Host: %s:%d\r\n",
541 stream->remote.path,
542 stream->remote.host,
543 stream->remote.port );
544 if (stream->remote.auth) {
545 len += snprintf( buf+len, sizeof(buf)-len,
546 "Authorization: Basic %s\r\n",
547 stream->remote.auth );
548 }
549 snprintf( buf+len, sizeof(buf)-len,
550 "User-Agent: DirectFB/%s\r\n"
551 "Accept: */*\r\n"
552 "Range: bytes=%d-\r\n"
553 "Connection: Close\r\n",
554 DIRECTFB_VERSION, offset );
555
556 status = net_command( stream, buf, sizeof(buf) );
557 switch (status) {
558 case 200 ... 299:
559 stream->offset = offset;
560 break;
561 default:
562 if (status)
563 D_DEBUG_AT( Direct_Stream,
564 "server returned status %d.\n", status );
565 return DR_FAILURE;
566 }
567
568 /* discard remaining headers */
569 while (net_response( stream, buf, sizeof(buf) ) > 0);
570
571 return DR_OK;
572 }
573
574 static DirectResult
http_open(DirectStream * stream,const char * filename)575 http_open( DirectStream *stream, const char *filename )
576 {
577 DirectResult ret;
578 char buf[1280];
579 int status, len;
580
581 stream->remote.port = HTTP_PORT;
582
583 ret = net_open( stream, filename, IPPROTO_TCP );
584 if (ret)
585 return ret;
586
587 if (stream->remote.user) {
588 char *tmp;
589
590 if (stream->remote.pass) {
591 tmp = alloca( strlen( stream->remote.user ) +
592 strlen( stream->remote.pass ) + 2 );
593 len = sprintf( tmp, "%s:%s",
594 stream->remote.user, stream->remote.pass );
595 } else {
596 tmp = alloca( strlen( stream->remote.user ) + 2 );
597 len = sprintf( tmp, "%s:", stream->remote.user );
598 }
599
600 stream->remote.auth = direct_base64_encode( tmp, len );
601 }
602
603 len = snprintf( buf, sizeof(buf),
604 "GET %s HTTP/1.0\r\n"
605 "Host: %s:%d\r\n",
606 stream->remote.path,
607 stream->remote.host,
608 stream->remote.port );
609 if (stream->remote.auth) {
610 len += snprintf( buf+len, sizeof(buf)-len,
611 "Authorization: Basic %s\r\n",
612 stream->remote.auth );
613 }
614 snprintf( buf+len, sizeof(buf)-len,
615 "User-Agent: DirectFB/%s\r\n"
616 "Accept: */*\r\n"
617 "Connection: Close\r\n",
618 DIRECTFB_VERSION );
619
620 status = net_command( stream, buf, sizeof(buf) );
621
622 while (net_response( stream, buf, sizeof(buf) ) > 0) {
623 if (!strncasecmp( buf, "Accept-Ranges:", 14 )) {
624 if (strcmp( trim( buf+14 ), "none" ))
625 stream->seek = http_seek;
626 }
627 else if (!strncasecmp( buf, "Content-Type:", 13 )) {
628 char *mime = trim( buf+13 );
629 char *tmp = strchr( mime, ';' );
630 if (tmp)
631 *tmp = '\0';
632 if (stream->mime)
633 D_FREE( stream->mime );
634 stream->mime = D_STRDUP( mime );
635 }
636 else if (!strncasecmp( buf, "Content-Length:", 15 )) {
637 char *tmp = trim( buf+15 );
638 if (sscanf( tmp, "%d", &stream->length ) < 1)
639 sscanf( tmp, "bytes=%d", &stream->length );
640 }
641 else if (!strncasecmp( buf, "Location:", 9 )) {
642 direct_stream_close( stream );
643 stream->seek = NULL;
644
645 if (++stream->remote.redirects > HTTP_MAX_REDIRECTS) {
646 D_ERROR( "Direct/Stream: "
647 "reached maximum number of redirects (%d).\n",
648 HTTP_MAX_REDIRECTS );
649 return DR_LIMITEXCEEDED;
650 }
651
652 filename = trim( buf+9 );
653 if (!strncmp( filename, "http://", 7 ))
654 return http_open( stream, filename+7 );
655 if (!strncmp( filename, "ftp://", 6 ))
656 return ftp_open( stream, filename+6 );
657 if (!strncmp( filename, "rtsp://", 7 ))
658 return rtsp_open( stream, filename+7 );
659
660 return DR_UNSUPPORTED;
661 }
662 }
663
664 switch (status) {
665 case 200 ... 299:
666 break;
667 default:
668 if (status)
669 D_DEBUG_AT( Direct_Stream,
670 "server returned status %d.\n", status );
671 return (status == 404) ? DR_FILENOTFOUND : DR_FAILURE;
672 }
673
674 return DR_OK;
675 }
676
677 /*****************************************************************************/
678
679 static DirectResult
ftp_open_pasv(DirectStream * stream,char * buf,size_t size)680 ftp_open_pasv( DirectStream *stream, char *buf, size_t size )
681 {
682 DirectResult ret;
683 int i, len;
684
685 snprintf( buf, size, "PASV" );
686 if (net_command( stream, buf, size ) != 227)
687 return DR_FAILURE;
688
689 /* parse IP and port for passive mode */
690 for (i = 4; buf[i]; i++) {
691 unsigned int d[6];
692
693 if (sscanf( &buf[i], "%u,%u,%u,%u,%u,%u",
694 &d[0], &d[1], &d[2], &d[3], &d[4], &d[5] ) == 6)
695 {
696 struct addrinfo hints, *addr;
697
698 /* address */
699 len = snprintf( buf, size,
700 "%u.%u.%u.%u",
701 d[0], d[1], d[2], d[3] );
702 /* port */
703 snprintf( buf+len+1, size-len-1,
704 "%u", ((d[4] & 0xff) << 8) | (d[5] & 0xff) );
705
706 memset( &hints, 0, sizeof(hints) );
707 hints.ai_flags = AI_CANONNAME;
708 hints.ai_socktype = SOCK_STREAM;
709 hints.ai_family = PF_UNSPEC;
710
711 if (getaddrinfo( buf, buf+len+1, &hints, &addr )) {
712 D_DEBUG_AT( Direct_Stream,
713 "failed to resolve host '%s'.\n", buf );
714 return DR_FAILURE;
715 }
716
717 ret = net_connect( addr, SOCK_STREAM, IPPROTO_TCP, &stream->fd );
718
719 freeaddrinfo( addr );
720
721 return ret;
722 }
723 }
724
725 return DR_FAILURE;
726 }
727
728 static DirectResult
ftp_seek(DirectStream * stream,unsigned int offset)729 ftp_seek( DirectStream *stream, unsigned int offset )
730 {
731 DirectResult ret = DR_OK;
732 char buf[512];
733
734 if (stream->fd > 0) {
735 int status;
736
737 close( stream->fd );
738 stream->fd = -1;
739
740 /* ignore response */
741 while (net_response( stream, buf, sizeof(buf) ) > 0) {
742 if (sscanf( buf, "%3d%[ ]", &status, buf ) == 2)
743 break;
744 }
745 }
746
747 ret = ftp_open_pasv( stream, buf, sizeof(buf) );
748 if (ret)
749 return ret;
750
751 snprintf( buf, sizeof(buf), "REST %d", offset );
752 if (net_command( stream, buf, sizeof(buf) ) != 350)
753 goto error;
754
755 snprintf( buf, sizeof(buf), "RETR %s", stream->remote.path );
756 switch (net_command( stream, buf, sizeof(buf) )) {
757 case 150:
758 case 125:
759 break;
760 default:
761 goto error;
762 }
763
764 stream->offset = offset;
765
766 return DR_OK;
767
768 error:
769 close( stream->fd );
770 stream->fd = -1;
771
772 return DR_FAILURE;
773 }
774
775 static DirectResult
ftp_open(DirectStream * stream,const char * filename)776 ftp_open( DirectStream *stream, const char *filename )
777 {
778 DirectResult ret;
779 int status = 0;
780 char buf[512];
781
782 stream->remote.port = FTP_PORT;
783
784 ret = net_open( stream, filename, IPPROTO_TCP );
785 if (ret)
786 return ret;
787
788 while (net_response( stream, buf, sizeof(buf) ) > 0) {
789 if (sscanf( buf, "%3d%[ ]", &status, buf ) == 2)
790 break;
791 }
792 if (status != 220)
793 return DR_FAILURE;
794
795 /* login */
796 snprintf( buf, sizeof(buf), "USER %s", stream->remote.user ? : "anonymous" );
797 switch (net_command( stream, buf, sizeof(buf) )) {
798 case 230:
799 case 331:
800 break;
801 default:
802 return DR_FAILURE;
803 }
804
805 if (stream->remote.pass) {
806 snprintf( buf, sizeof(buf), "PASS %s", stream->remote.pass );
807 if (net_command( stream, buf, sizeof(buf) ) != 230)
808 return DR_FAILURE;
809 }
810
811 /* enter binary mode */
812 snprintf( buf, sizeof(buf), "TYPE I" );
813 if (net_command( stream, buf, sizeof(buf) ) != 200)
814 return DR_FAILURE;
815
816 /* get file size */
817 snprintf( buf, sizeof(buf), "SIZE %s", stream->remote.path );
818 if (net_command( stream, buf, sizeof(buf) ) == 213)
819 stream->length = strtol( buf+4, NULL, 10 );
820
821 /* enter passive mode by default */
822 ret = ftp_open_pasv( stream, buf, sizeof(buf) );
823 if (ret)
824 return ret;
825
826 /* retrieve file */
827 snprintf( buf, sizeof(buf), "RETR %s", stream->remote.path );
828 switch (net_command( stream, buf, sizeof(buf) )) {
829 case 125:
830 case 150:
831 break;
832 default:
833 return DR_FAILURE;
834 }
835
836 stream->seek = ftp_seek;
837
838 return DR_OK;
839 }
840
841 /*****************************************************************************/
842
843 typedef struct {
844 s8 pt; // payload type (-1: dymanic)
845 u8 type; // codec type
846 const char *name;
847 const char *mime;
848 u32 fcc;
849 } RTPPayload;
850
851 typedef struct {
852 char *control;
853
854 int pt;
855 const RTPPayload *payload;
856
857 int dur; // duration
858 int abr; // avg bitrate
859 int mbr; // max bitrate
860 int aps; // avg packet size
861 int mps; // max packet size
862 int str; // start time
863 int prl; // preroll
864
865 char *mime;
866 int mime_size;
867
868 void *data;
869 int data_size;
870 } SDPStreamDesc;
871
872 #define PAYLOAD_VIDEO 1
873 #define PAYLOAD_AUDIO 2
874
875 #define FCC( a, b, c, d ) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
876
877 static const RTPPayload payloads[] = {
878 { 31, PAYLOAD_VIDEO, "H261", "video/h261", FCC('H','2','6','1') },
879 { 34, PAYLOAD_VIDEO, "H263", "video/h263", FCC('H','2','6','3') },
880 //{ -1, PAYLOAD_VIDEO, "H264", "video/h264", FCC('H','2','6','4') },
881 { 32, PAYLOAD_VIDEO, "MPV", "video/mpeg", FCC('M','P','E','G') },
882 { 33, 0, "MP2T", "video/mpegts", 0 },
883 { -1, PAYLOAD_VIDEO, "MP4V-ES", "video/mpeg4", FCC('M','P','4','S') },
884 { 14, PAYLOAD_AUDIO, "MPA", "audio/mpeg", 0x0055 },
885 //{ -1, PAYLOAD_AUDIO, "mpeg4-generic", "audio/aac", 0x00ff },
886 };
887
888 #define NUM_PAYLOADS D_ARRAY_SIZE( payloads )
889
890 static DirectResult
sdp_parse(DirectStream * stream,int length,SDPStreamDesc ** ret_streams,int * ret_num)891 sdp_parse( DirectStream *stream, int length, SDPStreamDesc **ret_streams, int *ret_num )
892 {
893 char *buf, *tmp;
894 SDPStreamDesc *desc = NULL;
895 int num = 0;
896 fd_set set;
897 struct timeval tv;
898 int i;
899
900 buf = D_CALLOC( 1, length+1 );
901 if (!buf)
902 return D_OOM();
903
904 FD_ZERO( &set );
905 FD_SET( stream->remote.sd, &set );
906
907 for (tmp = buf; length;) {
908 int size;
909
910 tv.tv_sec = NET_TIMEOUT;
911 tv.tv_usec = 0;
912 select( stream->remote.sd+1, &set, NULL, NULL, &tv );
913
914 size = recv( stream->remote.sd, tmp, length, MSG_WAITALL );
915 if (size < 1)
916 break;
917 tmp += size;
918 length -= size;
919 }
920
921 for (tmp = buf; tmp && *tmp;) {
922 char *end;
923
924 end = strchr( tmp, '\n' );
925 if (end) {
926 if (end > tmp && *(end-1) == '\r')
927 *(end-1) = '\0';
928 *end = '\0';
929 }
930
931 D_DEBUG_AT( Direct_Stream, "SDP [%s]\n", tmp );
932
933 switch (*tmp) {
934 case 'm':
935 /* media */
936 if (*(tmp+1) == '=') {
937 desc = D_REALLOC( desc, ++num*sizeof(SDPStreamDesc) );
938 memset( &desc[num-1], 0, sizeof(SDPStreamDesc) );
939
940 tmp += 2;
941 if (sscanf( tmp, "audio %d RTP/AVP %d", &i, &desc[num-1].pt ) == 2 ||
942 sscanf( tmp, "video %d RTP/AVP %d", &i, &desc[num-1].pt ) == 2) {
943 for (i = 0; i < NUM_PAYLOADS; i++) {
944 if (desc[num-1].pt == payloads[i].pt) {
945 desc[num-1].payload = &payloads[i];
946 desc[num-1].mime = D_STRDUP( payloads[i].mime );
947 desc[num-1].mime_size = strlen( payloads[i].mime );
948 break;
949 }
950 }
951 }
952 }
953 break;
954 case 'a':
955 /* attribute */
956 if (*(tmp+1) == '=' && num) {
957 tmp += 2;
958 if (!strncmp( tmp, "control:", 8 )) {
959 desc[num-1].control = D_STRDUP( trim( tmp+8 ) );
960 }
961 else if (!strncmp( tmp, "rtpmap:", 7 )) {
962 if (!desc[num-1].payload && desc[num-1].pt) {
963 char *sep;
964
965 tmp = strchr( trim( tmp+7 ), ' ' );
966 if (!tmp) break;
967 sep = strchr( ++tmp, '/' );
968 if (sep) *sep = '\0';
969
970 for (i = 0; i < NUM_PAYLOADS; i++) {
971 if (strcmp( tmp, payloads[i].name ))
972 continue;
973 desc[num-1].payload = &payloads[i];
974 desc[num-1].mime = D_STRDUP( payloads[i].mime );
975 desc[num-1].mime_size = strlen( payloads[i].mime );
976 break;
977 }
978 }
979 }
980 else if (!strncmp( tmp, "length:npt=", 11 )) {
981 double val = atof( tmp+11 );
982 desc[num-1].dur = val * 1000.0;
983 }
984 else if (!strncmp( tmp, "mimetype:string;", 16 )) {
985 if (desc[num-1].mime)
986 D_FREE( desc[num-1].mime );
987 desc[num-1].mime = D_STRDUP( trim( tmp+16 ) );
988 desc[num-1].mime_size = strlen( desc[num-1].mime );
989 }
990 else if (!strncmp( tmp, "AvgBitRate:", 11 )) {
991 sscanf( tmp+11, "integer;%d", &desc[num-1].abr );
992 }
993 else if (!strncmp( tmp, "MaxBitRate:", 11 )) {
994 sscanf( tmp+11, "integer;%d", &desc[num-1].mbr );
995 }
996 else if (!strncmp( tmp, "AvgPacketSize:", 14 )) {
997 sscanf( tmp+14, "integer;%d", &desc[num-1].aps );
998 }
999 else if (!strncmp( tmp, "MaxPacketSize:", 14 )) {
1000 sscanf( tmp+14, "integer;%d", &desc[num-1].mps );
1001 }
1002 else if (!strncmp( tmp, "StartTime:", 10 )) {
1003 sscanf( tmp+10, "integer;%d", &desc[num-1].str );
1004 }
1005 else if (!strncmp( tmp, "Preroll:", 8 )) {
1006 sscanf( tmp+8, "integer;%d", &desc[num-1].prl );
1007 }
1008 else if (!strncmp( tmp, "OpaqueData:buffer;", 18 )) {
1009 desc[num-1].data =
1010 direct_base64_decode( trim( tmp+18 ),
1011 &desc[num-1].data_size );
1012 }
1013 }
1014 break;
1015 default:
1016 break;
1017 }
1018
1019 tmp = end;
1020 if (tmp) tmp++;
1021 }
1022
1023 D_FREE( buf );
1024
1025 *ret_streams = desc;
1026 *ret_num = num;
1027
1028 return desc ? DR_OK : DR_EOF;
1029 }
1030
1031 static void
sdp_free(SDPStreamDesc * streams,int num)1032 sdp_free( SDPStreamDesc *streams, int num )
1033 {
1034 int i;
1035
1036 for (i = 0; i < num; i++) {
1037 if (streams[i].control)
1038 D_FREE( streams[i].control );
1039 if (streams[i].mime)
1040 D_FREE( streams[i].mime );
1041 if (streams[i].data)
1042 D_FREE( streams[i].data );
1043 }
1044 D_FREE( streams );
1045 }
1046
1047 static DirectResult
rmf_write_header(SDPStreamDesc * streams,int n_streams,void ** ret_buf,unsigned int * ret_size)1048 rmf_write_header( SDPStreamDesc *streams, int n_streams, void **ret_buf, unsigned int *ret_size )
1049 {
1050 unsigned char *dst, *tmp;
1051 int abr = 0;
1052 int mbr = 0;
1053 int aps = 0;
1054 int mps = 0;
1055 int str = 0;
1056 int prl = 0;
1057 int dur = 0;
1058 int i, len;
1059
1060 len = 86 + n_streams*46;
1061 for (i = 0; i < n_streams; i++) {
1062 abr += streams[i].abr;
1063 aps += streams[i].aps;
1064 if (mbr < streams[i].mbr)
1065 mbr = streams[i].mbr;
1066 if (mps < streams[i].mps)
1067 mps = streams[i].mps;
1068 if (dur < streams[i].dur)
1069 dur = streams[i].dur;
1070 if (streams[i].mime)
1071 len += streams[i].mime_size;
1072 if (streams[i].data)
1073 len += streams[i].data_size;
1074 else if (streams[i].payload)
1075 len += 74;
1076 }
1077
1078 *ret_buf = dst = D_MALLOC( len );
1079 if (!dst)
1080 return D_OOM();
1081 *ret_size = len;
1082
1083 /* RMF */
1084 dst[0] = '.'; dst[1] = 'R', dst[2] = 'M'; dst[3] = 'F';
1085 dst[4] = dst[5] = dst[6] = 0; dst[7] = 18; // size
1086 dst[8] = dst[9] = 0; // version
1087 dst[10] = dst[11] = dst[12] = dst[13] = 0; // ??
1088 dst[14] = dst[15] = dst[16] = 0; dst[17] = 4+n_streams; // num streams
1089 dst += 18;
1090
1091 /* PROP */
1092 dst[0] = 'P'; dst[1] = 'R'; dst[2] = 'O'; dst[3] = 'P';
1093 dst[4] = dst[5] = dst[6] = 0; dst[7] = 50;
1094 dst[8] = dst[9] = 0;
1095 dst[10] = mbr>>24; dst[11] = mbr>>16; dst[12] = mbr>>8; dst[13] = mbr;
1096 dst[14] = abr>>24; dst[15] = abr>>16; dst[16] = abr>>8; dst[17] = abr;
1097 dst[18] = mps>>24; dst[19] = mps>>16; dst[20] = mps>>8; dst[21] = mps;
1098 dst[22] = aps>>24; dst[23] = aps>>16; dst[24] = aps>>8; dst[25] = aps;
1099 dst[26] = dst[27] = dst[28] = dst[29] = 0; // num packets
1100 dst[30] = dur>>24; dst[31] = dur>>16; dst[32] = dur>>8; dst[33] = dur;
1101 dst[34] = dst[35] = dst[36] = dst[37] = 0; // preroll
1102 dst[38] = dst[39] = dst[40] = dst[41] = 0; // index offset
1103 dst[42] = len>>24; dst[43] = len>>16; dst[44] = len>>8; dst[45] = len;
1104 dst[46] = 0; dst[47] = n_streams; // num streams
1105 dst[48] = 0; dst[49] = 7; // flags
1106 dst += 50;
1107
1108 for (i = 0; i < n_streams; i++) {
1109 len = 46 + streams[i].mime_size;
1110 if (streams[i].data)
1111 len += streams[i].data_size;
1112 else if (streams[i].payload)
1113 len += 74;
1114
1115 abr = streams[i].abr;
1116 mbr = streams[i].mbr;
1117 aps = streams[i].aps;
1118 mps = streams[i].mps;
1119 str = streams[i].str;
1120 prl = streams[i].prl;
1121 dur = streams[i].dur;
1122
1123 /* MDPR */
1124 dst[0] = 'M'; dst[1] = 'D'; dst[2] = 'P'; dst[3] = 'R';
1125 dst[4] = len>>24; dst[5] = len>>16; dst[6] = len>>8; dst[7] = len;
1126 dst[8] = dst[9] = 0;
1127 dst[10] = 0; dst[11] = i;
1128 dst[12] = mbr>>24; dst[13] = mbr>>16; dst[14] = mbr>>8; dst[15] = mbr;
1129 dst[16] = abr>>24; dst[17] = abr>>16; dst[18] = abr>>8; dst[19] = abr;
1130 dst[20] = mps>>24; dst[21] = mps>>16; dst[22] = mps>>8; dst[23] = mps;
1131 dst[24] = aps>>24; dst[25] = aps>>16; dst[26] = aps>>8; dst[27] = aps;
1132 dst[28] = str>>24; dst[29] = str>>16; dst[30] = str>>8; dst[31] = str;
1133 dst[32] = prl>>24; dst[33] = prl>>16; dst[34] = prl>>8; dst[35] = prl;
1134 dst[36] = dur>>24; dst[37] = dur>>16; dst[38] = dur>>8; dst[39] = dur;
1135 dst += 40;
1136
1137 /* description */
1138 *dst++ = 0;
1139 /* mimetype */
1140 *dst++ = streams[i].mime_size;
1141 for (tmp = (unsigned char*)streams[i].mime; tmp && *tmp;)
1142 *dst++ = *tmp++;
1143
1144 /* codec data */
1145 if (streams[i].data) {
1146 len = streams[i].data_size;
1147 dst[0] = len>>24; dst[1] = len>>16; dst[2] = len>>8; dst[3] = len;
1148 direct_memcpy( dst+4, streams[i].data, streams[i].data_size );
1149 dst += len+4;
1150 }
1151 else if (streams[i].payload) {
1152 u32 fcc = streams[i].payload->fcc;
1153
1154 dst[0] = dst[1] = dst[2] = 0; dst[3] = 74;
1155 dst += 4;
1156 memset( dst, 0, 74 );
1157
1158 if (streams[i].payload->type == PAYLOAD_AUDIO) {
1159 dst[0] = '.'; dst[1] = 'r'; dst[2] = 'a'; dst[3] = 0xfd;
1160 dst[4] = 0; dst[5] = 5; // version
1161 dst[66] = fcc; dst[67] = fcc>>8; dst[68] = fcc>>16; dst[69] = fcc>>24;
1162 }
1163 else {
1164 dst[0] = dst[1] = dst[2] = 0; dst[3] = 34;
1165 dst[4] = 'V'; dst[5] = 'I'; dst[6] = 'D'; dst[7] = 'O';
1166 dst[8] = fcc; dst[9] = fcc>>8; dst[10] = fcc>>16; dst[11] = fcc>>24;
1167 dst[30] = 0x10;
1168 }
1169 dst += 74;
1170 }
1171 else {
1172 dst[0] = dst[1] = dst[2] = dst[3] = 0;
1173 dst += 4;
1174 }
1175 }
1176
1177 /* DATA */
1178 dst[0] = 'D'; dst[1] = 'A'; dst[2] = 'T'; dst[3] = 'A';
1179 dst[4] = dst[5] = dst[6] = 0; dst[7] = 18; // size
1180 dst[8] = dst[9] = 0; // version
1181 dst[10] = dst[11] = dst[12] = dst[13] = 0; // num packets
1182 dst[14] = dst[15] = dst[16] = dst[17] = 0; // next data
1183
1184 return DR_OK;
1185 }
1186
1187 static int
rmf_write_pheader(unsigned char * dst,int id,int sz,unsigned int ts)1188 rmf_write_pheader( unsigned char *dst, int id, int sz, unsigned int ts )
1189 {
1190 /* version */
1191 dst[0] = dst[1] = 0;
1192 /* length */
1193 dst[2] = (sz+12)>>8; dst[3] = sz+12;
1194 /* indentifier */
1195 dst[4] = id>>8; dst[5] = id;
1196 /* timestamp */
1197 dst[6] = ts>>24; dst[7] = ts>>16; dst[8] = ts>>8; dst[9] = ts;
1198 /* reserved */
1199 dst[10] = 0;
1200 /* flags */
1201 dst[11] = 0;
1202
1203 return 12;
1204 }
1205
1206 static void
real_calc_challenge2(char response[64],char checksum[32],char * challenge)1207 real_calc_challenge2( char response[64], char checksum[32], char *challenge )
1208 {
1209 const unsigned char xor_table[37] = {
1210 0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
1211 0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
1212 0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
1213 0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
1214 0x10, 0x57, 0x05, 0x18, 0x54
1215 };
1216 char buf[128];
1217 char md5[16];
1218 int len;
1219 int i;
1220
1221 memset( response, 0, 64 );
1222 memset( checksum, 0, 32 );
1223
1224 buf[0] = 0xa1; buf[1] = 0xe9; buf[2] = 0x14; buf[3] = 0x9d;
1225 buf[4] = 0x0e; buf[5] = 0x6b; buf[6] = 0x3b; buf[7] = 0x59;
1226 memset( buf+8, 0, 120 );
1227
1228 len = strlen( challenge );
1229 if (len == 40) {
1230 challenge[32] = '\0';
1231 len = 32;
1232 }
1233 memcpy( buf+8, challenge, MAX(len,56) );
1234
1235 for (i = 0; i < 37; i++)
1236 buf[8+i] ^= xor_table[i];
1237
1238 /* compute response */
1239 direct_md5_sum( md5, buf, 64 );
1240 /* convert to ascii */
1241 for (i = 0; i < 16; i++) {
1242 char a, b;
1243 a = (md5[i] >> 4) & 15;
1244 b = md5[i] & 15;
1245 response[i*2+0] = ((a < 10) ? (a + 48) : (a + 87)) & 255;
1246 response[i*2+1] = ((b < 10) ? (b + 48) : (b + 87)) & 255;
1247 }
1248 /* tail */
1249 len = strlen( response );
1250 direct_snputs( &response[len], "01d0a8e3", 64-len );
1251
1252 /* compute checksum */
1253 for (i = 0; i < len/4; i++)
1254 checksum[i] = response[i*4];
1255 }
1256
1257 static DirectResult
rtsp_session_open(DirectStream * stream)1258 rtsp_session_open( DirectStream *stream )
1259 {
1260 DirectResult ret;
1261 int status;
1262 int cseq = 0;
1263 SDPStreamDesc *streams = NULL;
1264 int n_streams = 0;
1265 char session[32] = {0, };
1266 char challen[64] = {0, };
1267 char buf[1280];
1268 int i, len;
1269
1270 snprintf( buf, sizeof(buf),
1271 "OPTIONS rtsp://%s:%d RTSP/1.0\r\n"
1272 "CSeq: %d\r\n"
1273 "User-Agent: DirectFB/%s\r\n"
1274 "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7\r\n"
1275 "PlayerStarttime: [28/03/2003:22:50:23 00:00]\r\n"
1276 "CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n"
1277 "GUID: 00000000-0000-0000-0000-000000000000\r\n"
1278 "RegionData: 0\r\n",
1279 stream->remote.host,
1280 stream->remote.port,
1281 ++cseq, DIRECTFB_VERSION );
1282
1283 if (net_command( stream, buf, sizeof(buf) ) != 200)
1284 return DR_FAILURE;
1285
1286 while (net_response( stream, buf, sizeof(buf) ) > 0) {
1287 if (!strncmp( buf, "RealChallenge1:", 15 )) {
1288 snprintf( challen, sizeof(challen), "%s", trim( buf+15 ) );
1289 stream->remote.real_rtsp = true;
1290 }
1291 }
1292
1293 len = snprintf( buf, sizeof(buf),
1294 "DESCRIBE rtsp://%s:%d%s RTSP/1.0\r\n"
1295 "CSeq: %d\r\n"
1296 "Accept: application/sdp\r\n"
1297 "Bandwidth: 10485800\r\n",
1298 stream->remote.host,
1299 stream->remote.port,
1300 stream->remote.path,
1301 ++cseq );
1302 if (stream->remote.real_rtsp) {
1303 snprintf( buf+len, sizeof(buf)-len,
1304 "GUID: 00000000-0000-0000-0000-000000000000\r\n"
1305 "RegionData: 0\r\n"
1306 "ClientID: Linux_2.4_6.0.9.1235_play32_RN01_EN_586\r\n"
1307 "SupportsMaximumASMBandwidth: 1\r\n"
1308 "Require: com.real.retain-entity-for-setup\r\n" );
1309 }
1310
1311 status = net_command( stream, buf, sizeof(buf) );
1312 if (status != 200)
1313 return (status == 404) ? DR_FILENOTFOUND : DR_FAILURE;
1314
1315 len = 0;
1316 while (net_response( stream, buf, sizeof(buf) ) > 0) {
1317 if (!strncasecmp( buf, "ETag:", 5 )) {
1318 snprintf( session, sizeof(session), "%s", trim( buf+5 ) );
1319 }
1320 else if (!strncasecmp( buf, "Content-Length:", 15 )) {
1321 char *tmp = trim( buf+15 );
1322 if (sscanf( tmp, "%d", &len ) != 1)
1323 sscanf( tmp, "bytes=%d", &len );
1324 }
1325 }
1326
1327 if (!len) {
1328 D_DEBUG_AT( Direct_Stream, "Couldn't get sdp length!\n" );
1329 return DR_FAILURE;
1330 }
1331
1332 ret = sdp_parse( stream, len, &streams, &n_streams );
1333 if (ret)
1334 return ret;
1335
1336 for (i = 0; i < n_streams; i++) {
1337 /* skip unhandled payload types */
1338 if (!stream->remote.real_rtsp && !streams[i].payload)
1339 continue;
1340
1341 len = snprintf( buf, sizeof(buf),
1342 "SETUP rtsp://%s:%d%s/%s RTSP/1.0\r\n"
1343 "CSeq: %d\r\n",
1344 stream->remote.host,
1345 stream->remote.port,
1346 stream->remote.path,
1347 streams[i].control, ++cseq );
1348 if (*session) {
1349 if (*challen) {
1350 char response[64];
1351 char checksum[32];
1352
1353 real_calc_challenge2( response, checksum, challen );
1354 len += snprintf( buf+len, sizeof(buf)-len,
1355 "RealChallenge2: %s, sd=%s\r\n",
1356 response, checksum );
1357 *challen = '\0';
1358 }
1359 len += snprintf( buf+len, sizeof(buf)-len,
1360 "%s: %s\r\n",
1361 i ? "Session" : "If-Match", session );
1362 }
1363 snprintf( buf+len, sizeof(buf)-len,
1364 "Transport: %s\r\n",
1365 stream->remote.real_rtsp
1366 ? "x-pn-tng/tcp;mode=play,rtp/avp/tcp;unicast;mode=play"
1367 : "RTP/AVP/TCP;unicast" );
1368
1369 if (net_command( stream, buf, sizeof(buf) ) != 200) {
1370 sdp_free( streams, n_streams );
1371 return DR_FAILURE;
1372 }
1373
1374 while (net_response( stream, buf, sizeof(buf) ) > 0) {
1375 if (!strncmp( buf, "Session:", 8 ))
1376 snprintf( session, sizeof(session), "%s", trim( buf+8 ) );
1377 }
1378 }
1379
1380 len = snprintf( buf, sizeof(buf),
1381 "PLAY rtsp://%s:%d%s RTSP/1.0\r\n"
1382 "CSeq: %d\r\n",
1383 stream->remote.host,
1384 stream->remote.port,
1385 stream->remote.path,
1386 ++cseq );
1387 if (*session) {
1388 len += snprintf( buf+len, sizeof(buf)-len,
1389 "Session: %s\r\n", session );
1390 }
1391 snprintf( buf+len, sizeof(buf)-len,
1392 "Range: npt=0-\r\n"
1393 "Connection: Close\r\n" );
1394
1395 if (net_command( stream, buf, sizeof(buf) ) != 200) {
1396 sdp_free( streams, n_streams );
1397 return DR_FAILURE;
1398 }
1399
1400 /* discard remaining headers */
1401 while (net_response( stream, buf, sizeof(buf) ) > 0);
1402
1403 /* revert to blocking mode */
1404 fcntl( stream->fd, F_SETFL,
1405 fcntl( stream->fd, F_GETFL ) & ~O_NONBLOCK );
1406
1407 if (!stream->remote.real_rtsp) {
1408 RTPPayload *p;
1409
1410 stream->remote.data = D_CALLOC( 1, (n_streams+1)*sizeof(RTPPayload) );
1411 if (!stream->remote.data) {
1412 sdp_free( streams, n_streams );
1413 return D_OOM();
1414 }
1415
1416 p = (RTPPayload*) stream->remote.data;
1417 for (i = 0; i < n_streams; i++) {
1418 if (streams[i].payload) {
1419 *p = *(streams[i].payload);
1420 p->pt = streams[i].pt;
1421 p++;
1422 }
1423 }
1424 }
1425
1426 stream->remote.real_pack = true;
1427 if (n_streams == 1 && streams[0].mime) {
1428 if (!strcmp( streams[0].mime, "audio/mpeg" ) ||
1429 !strcmp( streams[0].mime, "audio/aac" ) ||
1430 !strcmp( streams[0].mime, "video/mpeg" ) ||
1431 !strcmp( streams[0].mime, "video/mpegts" ))
1432 {
1433 stream->mime = D_STRDUP( streams[0].mime );
1434 stream->remote.real_pack = false;
1435 }
1436 }
1437 if (stream->remote.real_pack) {
1438 ret = rmf_write_header( streams, n_streams,
1439 &stream->cache, &stream->cache_size );
1440 if (ret) {
1441 sdp_free( streams, n_streams );
1442 return ret;
1443 }
1444 stream->mime = D_STRDUP( "application/vnd.rn-realmedia" );
1445 }
1446
1447 sdp_free( streams, n_streams );
1448
1449 return DR_OK;
1450 }
1451
1452 static DirectResult
rvp_read_packet(DirectStream * stream)1453 rvp_read_packet( DirectStream *stream )
1454 {
1455 unsigned char buf[9];
1456 int size;
1457 int len;
1458 unsigned char id;
1459 unsigned int ts;
1460
1461 while (1) {
1462 do {
1463 size = recv( stream->fd, buf, 1, MSG_WAITALL );
1464 if (size < 1)
1465 return DR_EOF;
1466 } while (buf[0] != '$');
1467
1468 size = recv( stream->fd, buf, 7, MSG_WAITALL );
1469 if (size < 7)
1470 return DR_EOF;
1471
1472 len = (buf[0] << 16) + (buf[1] << 8) + buf[2];
1473 id = buf[3];
1474 if (id != 0x40 && id != 0x42) {
1475 if (buf[5] == 0x06) // EOS
1476 return DR_EOF;
1477 size = recv( stream->fd, buf, 9, MSG_WAITALL );
1478 if (size < 9)
1479 return DR_EOF;
1480 id = buf[5];
1481 len -= 9;
1482 }
1483 id = (id >> 1) & 1;
1484
1485 size = recv( stream->fd, buf, 6, MSG_WAITALL );
1486 if (size < 6)
1487 return DR_EOF;
1488 ts = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
1489
1490 len -= 10;
1491 if (len > 0) {
1492 unsigned char *dst;
1493
1494 size = len + 12;
1495 stream->cache = D_REALLOC( stream->cache, stream->cache_size+size );
1496 if (!stream->cache)
1497 return D_OOM();
1498 dst = stream->cache+stream->cache_size;
1499 stream->cache_size += size;
1500
1501 dst += rmf_write_pheader( dst, id, len, ts );
1502
1503 while (len) {
1504 size = recv( stream->fd, dst, len, MSG_WAITALL );
1505 if (size < 1)
1506 return DR_EOF;
1507 dst += size;
1508 len -= size;
1509 }
1510 break;
1511 }
1512 }
1513
1514 return DR_OK;
1515 }
1516
1517 static DirectResult
rtp_read_packet(DirectStream * stream)1518 rtp_read_packet( DirectStream *stream )
1519 {
1520 RTPPayload *payloads = (RTPPayload*)stream->remote.data;
1521 unsigned char buf[12];
1522 int size;
1523 int len;
1524 unsigned char id;
1525 unsigned short seq;
1526 unsigned int ts;
1527 int skip;
1528
1529 while (1) {
1530 RTPPayload *p;
1531
1532 do {
1533 size = recv( stream->fd, buf, 1, MSG_WAITALL );
1534 if (size < 1)
1535 return DR_EOF;
1536 } while (buf[0] != '$');
1537
1538 size = recv( stream->fd, buf, 3, MSG_WAITALL );
1539 if (size < 3)
1540 return DR_EOF;
1541
1542 id = buf[0];
1543 len = (buf[1] << 8) | buf[2];
1544 if (len < 12)
1545 continue;
1546
1547 size = recv( stream->fd, buf, 12, MSG_WAITALL );
1548 if (size < 12)
1549 return DR_EOF;
1550 len -= 12;
1551
1552 if ((buf[0] & 0xc0) != (2 << 6))
1553 D_DEBUG_AT( Direct_Stream, "Bad RTP version %d!\n", buf[0] );
1554 seq = (buf[2] << 8) | buf[3];
1555 ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
1556
1557 for (p = payloads; p->pt; p++) {
1558 if (p->pt == (buf[1] & 0x7f))
1559 break;
1560 }
1561
1562 switch (p->pt) {
1563 case 0: // Unhandled
1564 skip = len;
1565 break;
1566 case 14: // MPEG Audio
1567 skip = 4;
1568 break;
1569 case 32: // MPEG Video
1570 size = recv( stream->fd, buf, 1, MSG_WAITALL );
1571 if (size < 1)
1572 return DR_EOF;
1573 len--;
1574 skip = 3;
1575 if (buf[0] & (1 << 2))
1576 skip += 4;
1577 break;
1578 case 34: // H263
1579 size = recv( stream->fd, buf, 1, MSG_WAITALL );
1580 if (size < 1)
1581 return DR_EOF;
1582 len--;
1583 skip = 3;
1584 if (buf[0] & (1 << 7))
1585 skip += 4;
1586 if (buf[0] & (1 << 6))
1587 skip += 4;
1588 break;
1589 default:
1590 skip = 0;
1591 break;
1592 }
1593
1594 while (skip) {
1595 size = recv( stream->fd, buf, MIN(skip,12), MSG_WAITALL );
1596 if (size < 1)
1597 return DR_EOF;
1598 len -= size;
1599 skip -= size;
1600 }
1601
1602 if (len > 0) {
1603 unsigned char *dst;
1604
1605 size = len;
1606 if (stream->remote.real_pack) {
1607 size += 12;
1608 if (p->type == PAYLOAD_VIDEO)
1609 size += 7;
1610 }
1611
1612 stream->cache = D_REALLOC( stream->cache, stream->cache_size+size );
1613 if (!stream->cache)
1614 return D_OOM();
1615 dst = stream->cache+stream->cache_size;
1616 stream->cache_size += size;
1617
1618 if (stream->remote.real_pack) {
1619 if (p->type == PAYLOAD_VIDEO) {
1620 dst += rmf_write_pheader( dst, id, len+7, ts );
1621 dst[0] = 0x81;
1622 dst[1] = 0x01;
1623 dst[2] = (len | 0x4000)>>8; dst[3] = len;
1624 dst[4] = (len | 0x4000)>>8; dst[5] = len;
1625 dst[6] = seq;
1626 dst += 7;
1627 } else {
1628 dst += rmf_write_pheader( dst, id, len, ts );
1629 }
1630 }
1631
1632 while (len) {
1633 size = recv( stream->fd, dst, len, MSG_WAITALL );
1634 if (size < 1)
1635 return DR_EOF;
1636 dst += size;
1637 len -= size;
1638 }
1639 break;
1640 }
1641 }
1642
1643 return DR_OK;
1644 }
1645
1646 static DirectResult
rtsp_peek(DirectStream * stream,unsigned int length,int offset,void * buf,unsigned int * read_out)1647 rtsp_peek( DirectStream *stream,
1648 unsigned int length,
1649 int offset,
1650 void *buf,
1651 unsigned int *read_out )
1652 {
1653 DirectResult ret;
1654 unsigned int len;
1655
1656 if (offset < 0)
1657 return DR_UNSUPPORTED;
1658
1659 len = length + offset;
1660 while (len > stream->cache_size) {
1661 ret = (stream->remote.real_rtsp)
1662 ? rvp_read_packet( stream )
1663 : rtp_read_packet( stream );
1664 if (ret) {
1665 if (stream->cache_size < offset)
1666 return ret;
1667 break;
1668 }
1669 }
1670
1671 len = MIN( stream->cache_size-offset, length );
1672 direct_memcpy( buf, stream->cache+offset, len );
1673
1674 if (read_out)
1675 *read_out = len;
1676
1677 return DR_OK;
1678 }
1679
1680 static DirectResult
rtsp_read(DirectStream * stream,unsigned int length,void * buf,unsigned int * read_out)1681 rtsp_read( DirectStream *stream,
1682 unsigned int length,
1683 void *buf,
1684 unsigned int *read_out )
1685 {
1686 DirectResult ret;
1687 unsigned int size = 0;
1688
1689 while (size < length) {
1690 if (stream->cache_size) {
1691 unsigned int len = MIN( stream->cache_size, length-size );
1692
1693 direct_memcpy( buf+size, stream->cache, len );
1694 size += len;
1695 stream->cache_size -= len;
1696
1697 if (stream->cache_size) {
1698 direct_memcpy( stream->cache,
1699 stream->cache+len, stream->cache_size );
1700 } else {
1701 D_FREE( stream->cache );
1702 stream->cache = NULL;
1703 }
1704 }
1705
1706 if (size < length) {
1707 ret = (stream->remote.real_rtsp)
1708 ? rvp_read_packet( stream )
1709 : rtp_read_packet( stream );
1710 if (ret) {
1711 if (!size)
1712 return ret;
1713 break;
1714 }
1715 }
1716 }
1717
1718 stream->offset += size;
1719
1720 if (read_out)
1721 *read_out = size;
1722
1723 return DR_OK;
1724 }
1725
1726 static DirectResult
rtsp_open(DirectStream * stream,const char * filename)1727 rtsp_open( DirectStream *stream, const char *filename )
1728 {
1729 DirectResult ret;
1730
1731 stream->remote.port = RTSP_PORT;
1732
1733 ret = net_open( stream, filename, IPPROTO_TCP );
1734 if (ret)
1735 return ret;
1736
1737 ret = rtsp_session_open( stream );
1738 if (ret) {
1739 close( stream->remote.sd );
1740 return ret;
1741 }
1742
1743 stream->peek = rtsp_peek;
1744 stream->read = rtsp_read;
1745
1746 return DR_OK;
1747 }
1748
1749 #endif /* DIRECT_BUILD_NETWORK */
1750
1751 /************************** End of Network Support ***************************/
1752
1753 static DirectResult
pipe_wait(DirectStream * stream,unsigned int length,struct timeval * tv)1754 pipe_wait( DirectStream *stream,
1755 unsigned int length,
1756 struct timeval *tv )
1757 {
1758 fd_set s;
1759
1760 if (stream->cache_size >= length)
1761 return DR_OK;
1762
1763 FD_ZERO( &s );
1764 FD_SET( stream->fd, &s );
1765
1766 switch (select( stream->fd+1, &s, NULL, NULL, tv )) {
1767 case 0:
1768 if (!tv && !stream->cache_size)
1769 return DR_EOF;
1770 return DR_TIMEOUT;
1771 case -1:
1772 return errno2result( errno );
1773 }
1774
1775 return DR_OK;
1776 }
1777
1778 static DirectResult
pipe_peek(DirectStream * stream,unsigned int length,int offset,void * buf,unsigned int * read_out)1779 pipe_peek( DirectStream *stream,
1780 unsigned int length,
1781 int offset,
1782 void *buf,
1783 unsigned int *read_out )
1784 {
1785 unsigned int size = length;
1786 int len;
1787
1788 if (offset < 0)
1789 return DR_UNSUPPORTED;
1790
1791 len = length + offset;
1792 if (len > stream->cache_size) {
1793 ssize_t s;
1794
1795 stream->cache = D_REALLOC( stream->cache, len );
1796 if (!stream->cache) {
1797 stream->cache_size = 0;
1798 return D_OOM();
1799 }
1800
1801 s = read( stream->fd,
1802 stream->cache + stream->cache_size,
1803 len - stream->cache_size );
1804 if (s < 0) {
1805 if (errno != EAGAIN || stream->cache_size == 0)
1806 return errno2result( errno );
1807 s = 0;
1808 }
1809
1810 stream->cache_size += s;
1811 if (stream->cache_size <= offset)
1812 return DR_BUFFEREMPTY;
1813
1814 size = stream->cache_size - offset;
1815 }
1816
1817 direct_memcpy( buf, stream->cache+offset, size );
1818
1819 if (read_out)
1820 *read_out = size;
1821
1822 return DR_OK;
1823 }
1824
1825 static DirectResult
pipe_read(DirectStream * stream,unsigned int length,void * buf,unsigned int * read_out)1826 pipe_read( DirectStream *stream,
1827 unsigned int length,
1828 void *buf,
1829 unsigned int *read_out )
1830 {
1831 unsigned int size = 0;
1832
1833 if (stream->cache_size) {
1834 size = MIN( stream->cache_size, length );
1835
1836 direct_memcpy( buf, stream->cache, size );
1837
1838 length -= size;
1839 stream->cache_size -= size;
1840
1841 if (stream->cache_size) {
1842 direct_memcpy( stream->cache,
1843 stream->cache+size, stream->cache_size );
1844 } else {
1845 D_FREE( stream->cache );
1846 stream->cache = NULL;
1847 }
1848 }
1849
1850 if (length) {
1851 ssize_t s;
1852
1853 s = read( stream->fd, buf+size, length-size );
1854 switch (s) {
1855 case 0:
1856 if (!size)
1857 return DR_EOF;
1858 break;
1859 case -1:
1860 if (!size) {
1861 return (errno == EAGAIN)
1862 ? DR_BUFFEREMPTY
1863 : errno2result( errno );
1864 }
1865 break;
1866 default:
1867 size += s;
1868 break;
1869 }
1870 }
1871
1872 stream->offset += size;
1873
1874 if (read_out)
1875 *read_out = size;
1876
1877 return DR_OK;
1878 }
1879
1880 /*****************************************************************************/
1881
1882 static DirectResult
file_peek(DirectStream * stream,unsigned int length,int offset,void * buf,unsigned int * read_out)1883 file_peek( DirectStream *stream,
1884 unsigned int length,
1885 int offset,
1886 void *buf,
1887 unsigned int *read_out )
1888 {
1889 DirectResult ret = DR_OK;
1890 ssize_t size;
1891
1892 if (lseek( stream->fd, offset, SEEK_CUR ) < 0)
1893 return DR_FAILURE;
1894
1895 size = read( stream->fd, buf, length );
1896 switch (size) {
1897 case 0:
1898 ret = DR_EOF;
1899 break;
1900 case -1:
1901 ret = (errno == EAGAIN)
1902 ? DR_BUFFEREMPTY
1903 : errno2result( errno );
1904 size = 0;
1905 break;
1906 }
1907
1908 if (lseek( stream->fd, - offset - size, SEEK_CUR ) < 0)
1909 return DR_FAILURE;
1910
1911 if (read_out)
1912 *read_out = size;
1913
1914 return ret;
1915 }
1916
1917 static DirectResult
file_read(DirectStream * stream,unsigned int length,void * buf,unsigned int * read_out)1918 file_read( DirectStream *stream,
1919 unsigned int length,
1920 void *buf,
1921 unsigned int *read_out )
1922 {
1923 ssize_t size;
1924
1925 size = read( stream->fd, buf, length );
1926 switch (size) {
1927 case 0:
1928 return DR_EOF;
1929 case -1:
1930 if (errno == EAGAIN)
1931 return DR_BUFFEREMPTY;
1932 return errno2result( errno );
1933 }
1934
1935 stream->offset += size;
1936
1937 if (read_out)
1938 *read_out = size;
1939
1940 return DR_OK;
1941 }
1942
1943 static DirectResult
file_seek(DirectStream * stream,unsigned int offset)1944 file_seek( DirectStream *stream, unsigned int offset )
1945 {
1946 off_t off;
1947
1948 off = lseek( stream->fd, offset, SEEK_SET );
1949 if (off < 0)
1950 return DR_FAILURE;
1951
1952 stream->offset = off;
1953
1954 return DR_OK;
1955 }
1956
1957 static DirectResult
file_open(DirectStream * stream,const char * filename,int fileno)1958 file_open( DirectStream *stream, const char *filename, int fileno )
1959 {
1960 if (filename)
1961 stream->fd = open( filename, O_RDONLY | O_NONBLOCK );
1962 else
1963 stream->fd = dup( fileno );
1964
1965 if (stream->fd < 0)
1966 return errno2result( errno );
1967
1968 fcntl( stream->fd, F_SETFL,
1969 fcntl( stream->fd, F_GETFL ) | O_NONBLOCK );
1970
1971 if (lseek( stream->fd, 0, SEEK_CUR ) < 0 && errno == ESPIPE) {
1972 stream->length = -1;
1973 stream->wait = pipe_wait;
1974 stream->peek = pipe_peek;
1975 stream->read = pipe_read;
1976 }
1977 else {
1978 struct stat s;
1979
1980 if (fstat( stream->fd, &s ) < 0)
1981 return errno2result( errno );
1982
1983 stream->length = s.st_size;
1984 stream->peek = file_peek;
1985 stream->read = file_read;
1986 stream->seek = file_seek;
1987 }
1988
1989 return DR_OK;
1990 }
1991
1992 /*****************************************************************************/
1993
1994 DirectResult
direct_stream_create(const char * filename,DirectStream ** ret_stream)1995 direct_stream_create( const char *filename,
1996 DirectStream **ret_stream )
1997 {
1998 DirectStream *stream;
1999 DirectResult ret;
2000
2001 D_ASSERT( filename != NULL );
2002 D_ASSERT( ret_stream != NULL );
2003
2004 stream = D_CALLOC( 1, sizeof(DirectStream) );
2005 if (!stream)
2006 return D_OOM();
2007
2008 D_MAGIC_SET( stream, DirectStream );
2009
2010 stream->ref = 1;
2011 stream->fd = -1;
2012
2013 if (!strncmp( filename, "stdin:/", 7 )) {
2014 ret = file_open( stream, NULL, STDIN_FILENO );
2015 }
2016 else if (!strncmp( filename, "file:/", 6 )) {
2017 ret = file_open( stream, filename+6, -1 );
2018 }
2019 else if (!strncmp( filename, "fd:/", 4 )) {
2020 ret = (filename[4] >= '0' && filename[4] <= '9')
2021 ? file_open( stream, NULL, atoi(filename+4) ) : DR_INVARG;
2022 }
2023 #if DIRECT_BUILD_NETWORK
2024 else if (!strncmp( filename, "http://", 7 ) ||
2025 !strncmp( filename, "unsv://", 7 )) {
2026 ret = http_open( stream, filename+7 );
2027 }
2028 else if (!strncmp( filename, "ftp://", 6 )) {
2029 ret = ftp_open( stream, filename+6 );
2030 }
2031 else if (!strncmp( filename, "rtsp://", 7 )) {
2032 ret = rtsp_open( stream, filename+7 );
2033 }
2034 else if (!strncmp( filename, "tcp://", 6 )) {
2035 ret = net_open( stream, filename+6, IPPROTO_TCP );
2036 }
2037 else if (!strncmp( filename, "udp://", 6 )) {
2038 ret = net_open( stream, filename+6, IPPROTO_UDP );
2039 }
2040 #endif
2041 else {
2042 ret = file_open( stream, filename, -1 );
2043 }
2044
2045 if (ret) {
2046 direct_stream_close( stream );
2047 D_FREE( stream );
2048 return ret;
2049 }
2050
2051 *ret_stream = stream;
2052
2053 return DR_OK;
2054 }
2055
2056 DirectStream*
direct_stream_dup(DirectStream * stream)2057 direct_stream_dup( DirectStream *stream )
2058 {
2059 D_ASSERT( stream != NULL );
2060
2061 D_MAGIC_ASSERT( stream, DirectStream );
2062
2063 stream->ref++;
2064
2065 return stream;
2066 }
2067
2068 int
direct_stream_fileno(DirectStream * stream)2069 direct_stream_fileno( DirectStream *stream )
2070 {
2071 D_ASSERT( stream != NULL );
2072
2073 D_MAGIC_ASSERT( stream, DirectStream );
2074
2075 return stream->fd;
2076 }
2077
2078 bool
direct_stream_seekable(DirectStream * stream)2079 direct_stream_seekable( DirectStream *stream )
2080 {
2081 D_ASSERT( stream != NULL );
2082
2083 D_MAGIC_ASSERT( stream, DirectStream );
2084
2085 return stream->seek ? true : false;
2086 }
2087
2088 bool
direct_stream_remote(DirectStream * stream)2089 direct_stream_remote( DirectStream *stream )
2090 {
2091 D_ASSERT( stream != NULL );
2092
2093 D_MAGIC_ASSERT( stream, DirectStream );
2094
2095 #if DIRECT_BUILD_NETWORK
2096 if (stream->remote.host)
2097 return true;
2098 #endif
2099 return false;
2100 }
2101
2102 const char*
direct_stream_mime(DirectStream * stream)2103 direct_stream_mime( DirectStream *stream )
2104 {
2105 D_ASSERT( stream != NULL );
2106
2107 D_MAGIC_ASSERT( stream, DirectStream );
2108
2109 return stream->mime;
2110 }
2111
2112 unsigned int
direct_stream_offset(DirectStream * stream)2113 direct_stream_offset( DirectStream *stream )
2114 {
2115 D_ASSERT( stream != NULL );
2116
2117 D_MAGIC_ASSERT( stream, DirectStream );
2118
2119 return stream->offset;
2120 }
2121
2122 unsigned int
direct_stream_length(DirectStream * stream)2123 direct_stream_length( DirectStream *stream )
2124 {
2125 D_ASSERT( stream != NULL );
2126
2127 D_MAGIC_ASSERT( stream, DirectStream );
2128
2129 return (stream->length >= 0) ? stream->length : stream->offset;
2130 }
2131
2132 DirectResult
direct_stream_wait(DirectStream * stream,unsigned int length,struct timeval * tv)2133 direct_stream_wait( DirectStream *stream,
2134 unsigned int length,
2135 struct timeval *tv )
2136 {
2137 D_ASSERT( stream != NULL );
2138
2139 D_MAGIC_ASSERT( stream, DirectStream );
2140
2141 if (length && stream->wait)
2142 return stream->wait( stream, length, tv );
2143
2144 return DR_OK;
2145 }
2146
2147 DirectResult
direct_stream_peek(DirectStream * stream,unsigned int length,int offset,void * buf,unsigned int * read_out)2148 direct_stream_peek( DirectStream *stream,
2149 unsigned int length,
2150 int offset,
2151 void *buf,
2152 unsigned int *read_out )
2153 {
2154 D_ASSERT( stream != NULL );
2155 D_ASSERT( length != 0 );
2156 D_ASSERT( buf != NULL );
2157
2158 D_MAGIC_ASSERT( stream, DirectStream );
2159
2160 if (stream->length >= 0 && (stream->offset + offset) >= stream->length)
2161 return DR_EOF;
2162
2163 if (stream->peek)
2164 return stream->peek( stream, length, offset, buf, read_out );
2165
2166 return DR_UNSUPPORTED;
2167 }
2168
2169 DirectResult
direct_stream_read(DirectStream * stream,unsigned int length,void * buf,unsigned int * read_out)2170 direct_stream_read( DirectStream *stream,
2171 unsigned int length,
2172 void *buf,
2173 unsigned int *read_out )
2174 {
2175 D_ASSERT( stream != NULL );
2176 D_ASSERT( length != 0 );
2177 D_ASSERT( buf != NULL );
2178
2179 D_MAGIC_ASSERT( stream, DirectStream );
2180
2181 if (stream->length >= 0 && stream->offset >= stream->length)
2182 return DR_EOF;
2183
2184 if (stream->read)
2185 return stream->read( stream, length, buf, read_out );
2186
2187 return DR_UNSUPPORTED;
2188 }
2189
2190 DirectResult
direct_stream_seek(DirectStream * stream,unsigned int offset)2191 direct_stream_seek( DirectStream *stream,
2192 unsigned int offset )
2193 {
2194 D_ASSERT( stream != NULL );
2195
2196 D_MAGIC_ASSERT( stream, DirectStream );
2197
2198 if (stream->offset == offset)
2199 return DR_OK;
2200
2201 if (stream->length >= 0 && offset > stream->length)
2202 offset = stream->length;
2203
2204 if (stream->seek)
2205 return stream->seek( stream, offset );
2206
2207 return DR_UNSUPPORTED;
2208 }
2209
2210 static void
direct_stream_close(DirectStream * stream)2211 direct_stream_close( DirectStream *stream )
2212 {
2213 #if DIRECT_BUILD_NETWORK
2214 if (stream->remote.host) {
2215 D_FREE( stream->remote.host );
2216 stream->remote.host = NULL;
2217 }
2218
2219 if (stream->remote.user) {
2220 D_FREE( stream->remote.user );
2221 stream->remote.user = NULL;
2222 }
2223
2224 if (stream->remote.pass) {
2225 D_FREE( stream->remote.pass );
2226 stream->remote.pass = NULL;
2227 }
2228
2229 if (stream->remote.auth) {
2230 D_FREE( stream->remote.auth );
2231 stream->remote.auth = NULL;
2232 }
2233
2234 if (stream->remote.path) {
2235 D_FREE( stream->remote.path );
2236 stream->remote.path = NULL;
2237 }
2238
2239 if (stream->remote.addr) {
2240 freeaddrinfo( stream->remote.addr );
2241 stream->remote.addr = NULL;
2242 }
2243
2244 if (stream->remote.data) {
2245 D_FREE( stream->remote.data );
2246 stream->remote.data = NULL;
2247 }
2248
2249 if (stream->remote.sd > 0) {
2250 close( stream->remote.sd );
2251 stream->remote.sd = -1;
2252 }
2253 #endif
2254
2255 if (stream->mime) {
2256 D_FREE( stream->mime );
2257 stream->mime = NULL;
2258 }
2259
2260 if (stream->cache) {
2261 D_FREE( stream->cache );
2262 stream->cache = NULL;
2263 stream->cache_size = 0;
2264 }
2265
2266 if (stream->fd >= 0) {
2267 fcntl( stream->fd, F_SETFL,
2268 fcntl( stream->fd, F_GETFL ) & ~O_NONBLOCK );
2269 close( stream->fd );
2270 stream->fd = -1;
2271 }
2272 }
2273
2274 void
direct_stream_destroy(DirectStream * stream)2275 direct_stream_destroy( DirectStream *stream )
2276 {
2277 D_ASSERT( stream != NULL );
2278
2279 D_MAGIC_ASSERT( stream, DirectStream );
2280
2281 if (--stream->ref == 0) {
2282 direct_stream_close( stream );
2283
2284 D_FREE( stream );
2285 }
2286 }
2287