Mercurial > mplayer.hg
comparison libmpdemux/network.c @ 2310:9e059416eea6
libdemuxer...
author | arpi |
---|---|
date | Sat, 20 Oct 2001 18:49:08 +0000 |
parents | network.c@09284c9c2b49 |
children | 0ecc1b4f7cf8 |
comparison
equal
deleted
inserted
replaced
2309:3128b9d8b4ea | 2310:9e059416eea6 |
---|---|
1 /* | |
2 * Network layer for MPlayer | |
3 * by Bertrand BAUDET <bertrand_baudet@yahoo.com> | |
4 * (C) 2001, MPlayer team. | |
5 */ | |
6 | |
7 //#define DUMP2FILE | |
8 | |
9 #include <stdio.h> | |
10 #include <stdlib.h> | |
11 #include <string.h> | |
12 #include <unistd.h> | |
13 | |
14 #include <fcntl.h> | |
15 #include <netdb.h> | |
16 #include <netinet/in.h> | |
17 #include <sys/time.h> | |
18 #include <sys/types.h> | |
19 #include <sys/socket.h> | |
20 #include <arpa/inet.h> | |
21 | |
22 #include <pthread.h> | |
23 | |
24 #include <errno.h> | |
25 #include <ctype.h> | |
26 | |
27 #include "stream.h" | |
28 #include "demuxer.h" | |
29 | |
30 #include "network.h" | |
31 #include "http.h" | |
32 #include "url.h" | |
33 #include "asf.h" | |
34 | |
35 streaming_ctrl_t *streaming_ctrl; | |
36 | |
37 static ASF_StreamType_e streaming_type = ASF_Unknown_e; | |
38 | |
39 Net_Fifo * | |
40 net_fifo_new() { | |
41 Net_Fifo *net_fifo; | |
42 net_fifo = (Net_Fifo*)malloc(sizeof(Net_Fifo)); | |
43 if( net_fifo==NULL ) { | |
44 printf("Memory allocation failed\n"); | |
45 return NULL; | |
46 } | |
47 memset( net_fifo, 0, sizeof(Net_Fifo) ); | |
48 return net_fifo; | |
49 } | |
50 | |
51 void | |
52 net_fifo_free( Net_Fifo *net_fifo ) { | |
53 if( net_fifo->buffer!=NULL ) free( net_fifo->buffer ); | |
54 free( net_fifo ); | |
55 } | |
56 | |
57 int | |
58 net_fifo_push(Net_Fifo *net_fifo, char *buffer, int length ) { | |
59 char *ptr; | |
60 if( net_fifo==NULL || buffer==NULL || length<0 ) return -1; | |
61 | |
62 ptr = (char*)malloc(length+net_fifo->length); | |
63 if( ptr==NULL ) { | |
64 printf("Memory allocation failed\n"); | |
65 return -1; | |
66 } | |
67 if( net_fifo->buffer!=NULL ) { | |
68 memcpy( ptr, net_fifo->buffer, net_fifo->length ); | |
69 free( net_fifo->buffer ); | |
70 } | |
71 memcpy( ptr+net_fifo->length, buffer, length ); | |
72 net_fifo->buffer = ptr; | |
73 net_fifo->length += length; | |
74 return net_fifo->length; | |
75 } | |
76 | |
77 int | |
78 net_fifo_pop(Net_Fifo *net_fifo, char *buffer, int length ) { | |
79 char *ptr; | |
80 int len; | |
81 if( net_fifo==NULL || buffer==NULL || length<0 ) return -1; | |
82 if( net_fifo->buffer==NULL || net_fifo->length==0 ) return -1; | |
83 | |
84 len = MIN(net_fifo->length, length); | |
85 | |
86 ptr = (char*)malloc(net_fifo->length-len); | |
87 if( ptr==NULL ) { | |
88 printf("Memory allocation failed\n"); | |
89 return -1; | |
90 } | |
91 memcpy( buffer, net_fifo->buffer, len ); | |
92 if( net_fifo->length-len!=0 ) { | |
93 memcpy( ptr, net_fifo->buffer+len, net_fifo->length-len ); | |
94 free( net_fifo->buffer ); | |
95 net_fifo->buffer = ptr; | |
96 net_fifo->length -= len; | |
97 } else { | |
98 free( net_fifo->buffer ); | |
99 net_fifo->buffer = NULL; | |
100 net_fifo->length = 0; | |
101 } | |
102 return len; | |
103 } | |
104 | |
105 streaming_ctrl_t * | |
106 streaming_ctrl_new( ) { | |
107 streaming_ctrl_t *streaming_ctrl; | |
108 streaming_ctrl = (streaming_ctrl_t*)malloc(sizeof(streaming_ctrl_t)); | |
109 if( streaming_ctrl==NULL ) { | |
110 printf("Failed to allocate memory\n"); | |
111 return NULL; | |
112 } | |
113 memset( streaming_ctrl, 0, sizeof(streaming_ctrl_t) ); | |
114 streaming_ctrl->buffer = net_fifo_new(); | |
115 return streaming_ctrl; | |
116 } | |
117 | |
118 void | |
119 streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl ) { | |
120 if( streaming_ctrl==NULL ) return; | |
121 if( streaming_ctrl->buffer!=NULL ) net_fifo_free( streaming_ctrl->buffer ); | |
122 free( streaming_ctrl ); | |
123 } | |
124 | |
125 int | |
126 readFromServer(int fd, char *buffer, int length) { | |
127 int ret; | |
128 int done=0; | |
129 fd_set set; | |
130 struct timeval tv; | |
131 if( buffer==NULL || length<0 ) return -1; | |
132 | |
133 | |
134 // fcntl( fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK ); | |
135 return read( fd, buffer, length ); | |
136 | |
137 do { | |
138 tv.tv_sec = 0; | |
139 tv.tv_usec = 10000; // 10 milli-seconds timeout | |
140 FD_ZERO( &set ); | |
141 FD_SET( fd, &set ); | |
142 ret = select( fd+1, &set, NULL, NULL, &tv ); | |
143 if( ret<0 ) { | |
144 perror("select"); | |
145 } else if( ret==0 ) { | |
146 printf("timeout\n"); | |
147 } | |
148 if( FD_ISSET(fd, &set) ) { | |
149 ret = read( fd, buffer, length ); | |
150 if( ret<0 ) { | |
151 if( errno!=EINPROGRESS ) { | |
152 } | |
153 } else { | |
154 done = 1; | |
155 } | |
156 } else { | |
157 return -1; | |
158 } | |
159 } while( !done ); | |
160 | |
161 return ret; | |
162 } | |
163 | |
164 // Connect to a server using a TCP connection | |
165 int | |
166 connect2Server(char *host, int port) { | |
167 int socket_server_fd; | |
168 int err, err_len; | |
169 fd_set set; | |
170 struct timeval tv; | |
171 struct sockaddr_in server_address; | |
172 | |
173 printf("Connecting to server %s:%d ...\n", host, port ); | |
174 | |
175 socket_server_fd = socket(AF_INET, SOCK_STREAM, 0); | |
176 // fcntl( socket_server_fd, F_SETFL, fcntl(socket_server_fd, F_GETFL) | O_NONBLOCK ); | |
177 if( socket_server_fd==-1 ) { | |
178 perror("Failed to create socket"); | |
179 return -1; | |
180 } | |
181 | |
182 if( isalpha(host[0]) ) { | |
183 struct hostent *hp =(struct hostent*)gethostbyname( host ); | |
184 if( hp==NULL ) { | |
185 printf("Counldn't resolve name: %s\n", host); | |
186 return -1; | |
187 } | |
188 memcpy( (void*)&server_address.sin_addr.s_addr, (void*)hp->h_addr, hp->h_length ); | |
189 } else { | |
190 inet_pton(AF_INET, host, &server_address.sin_addr); | |
191 } | |
192 server_address.sin_family=AF_INET; | |
193 server_address.sin_port=htons(port); | |
194 | |
195 if( connect( socket_server_fd, (struct sockaddr*)&server_address, sizeof(server_address) )==-1 ) { | |
196 if( errno!=EINPROGRESS ) { | |
197 perror("Failed to connect to server"); | |
198 close(socket_server_fd); | |
199 return -1; | |
200 } | |
201 } | |
202 | |
203 tv.tv_sec = 0; | |
204 tv.tv_usec = 10000; // 10 milli-seconds timeout | |
205 FD_ZERO( &set ); | |
206 FD_SET( socket_server_fd, &set ); | |
207 if( select(socket_server_fd+1, NULL, &set, NULL, &tv)>0 ) { | |
208 err_len = sizeof( err ); | |
209 getsockopt( socket_server_fd, SOL_SOCKET, SO_ERROR, &err, &err_len ); | |
210 if( err ) { | |
211 printf("Couldn't connect to host %s\n", host ); | |
212 printf("Socket error: %d\n", err ); | |
213 close(socket_server_fd); | |
214 return -1; | |
215 } | |
216 } | |
217 return socket_server_fd; | |
218 } | |
219 | |
220 int | |
221 http_send_request( URL_t *url ) { | |
222 HTTP_header_t *http_hdr; | |
223 int fd; | |
224 http_hdr = http_new_header(); | |
225 http_set_uri( http_hdr, url->file ); | |
226 http_set_field( http_hdr, "User-Agent: MPlayer"); | |
227 http_set_field( http_hdr, "Connection: closed"); | |
228 if( http_build_request( http_hdr )==NULL ) { | |
229 return -1; | |
230 } | |
231 | |
232 fd = connect2Server( url->hostname, url->port ); | |
233 if( fd<0 ) { | |
234 return -1; | |
235 } | |
236 write( fd, http_hdr->buffer, http_hdr->buffer_size ); | |
237 http_free( http_hdr ); | |
238 | |
239 return fd; | |
240 } | |
241 | |
242 HTTP_header_t * | |
243 http_read_response( int fd ) { | |
244 HTTP_header_t *http_hdr; | |
245 char response[BUFFER_SIZE]; | |
246 int i; | |
247 | |
248 http_hdr = http_new_header(); | |
249 if( http_hdr==NULL ) { | |
250 return NULL; | |
251 } | |
252 | |
253 do { | |
254 i = readFromServer( fd, response, BUFFER_SIZE ); | |
255 if( i<0 ) { | |
256 printf("Read failed\n"); | |
257 } | |
258 http_response_append( http_hdr, response, i ); | |
259 } while( !http_is_header_entired( http_hdr ) ); | |
260 http_response_parse( http_hdr ); | |
261 return http_hdr; | |
262 } | |
263 | |
264 // By using the protocol, the extension of the file or the content-type | |
265 // we might be able to guess the streaming type. | |
266 int | |
267 autodetectProtocol(URL_t *url, int *fd_out) { | |
268 HTTP_header_t *http_hdr; | |
269 int fd=-1; | |
270 int i; | |
271 int redirect; | |
272 char *extension; | |
273 char *content_type; | |
274 char *next_url; | |
275 char response[1024]; | |
276 | |
277 do { | |
278 *fd_out=-1; | |
279 next_url = NULL; | |
280 extension = NULL; | |
281 content_type = NULL; | |
282 redirect = 0; | |
283 | |
284 if( url==NULL ) return DEMUXER_TYPE_UNKNOWN; | |
285 | |
286 // Get the extension of the file if present | |
287 if( url->file!=NULL ) { | |
288 for( i=strlen(url->file) ; i>0 ; i-- ) { | |
289 if( url->file[i]=='.' ) { | |
290 extension=(url->file)+i+1; | |
291 break; | |
292 } | |
293 } | |
294 } | |
295 // extension=NULL; | |
296 if( extension!=NULL ) { | |
297 printf("Extension: %s\n", extension ); | |
298 if( !strcasecmp(extension, "asf") || | |
299 !strcasecmp(extension, "wmv") || | |
300 !strcasecmp(extension, "asx") ) { | |
301 if( url->port==0 ) url->port = 80; | |
302 return DEMUXER_TYPE_ASF; | |
303 } | |
304 if( !strcasecmp(extension, "mpg") || | |
305 !strcasecmp(extension, "mpeg") ) { | |
306 if( url->port==0 ) url->port = 80; | |
307 return DEMUXER_TYPE_MPEG_PS; | |
308 } | |
309 if( !strcasecmp(extension, "avi") ) { | |
310 if( url->port==0 ) url->port = 80; | |
311 return DEMUXER_TYPE_AVI; | |
312 } | |
313 } | |
314 | |
315 // Checking for RTSP | |
316 if( !strcasecmp(url->protocol, "rtsp") ) { | |
317 printf("RTSP protocol not yet implemented!\n"); | |
318 return DEMUXER_TYPE_UNKNOWN; | |
319 } | |
320 | |
321 // Checking for ASF | |
322 if( !strcasecmp(url->protocol, "mms") ) { | |
323 if( url->port==0 ) url->port = 80; | |
324 return DEMUXER_TYPE_ASF; | |
325 } | |
326 | |
327 // HTTP based protocol | |
328 if( !strcasecmp(url->protocol, "http") ) { | |
329 if( url->port==0 ) url->port = 80; | |
330 | |
331 fd = http_send_request( url ); | |
332 if( fd<0 ) { | |
333 *fd_out=-1; | |
334 return DEMUXER_TYPE_UNKNOWN; | |
335 } | |
336 | |
337 http_hdr = http_read_response( fd ); | |
338 if( http_hdr==NULL ) { | |
339 close( fd ); | |
340 *fd_out=-1; | |
341 return DEMUXER_TYPE_UNKNOWN; | |
342 } | |
343 | |
344 *fd_out=fd; | |
345 //http_debug_hdr( http_hdr ); | |
346 | |
347 // Check if the response is an ICY status_code reason_phrase | |
348 if( !strcasecmp(http_hdr->protocol, "ICY") ) { | |
349 // Ok, we have detected an mp3 streaming | |
350 return DEMUXER_TYPE_MPEG_PS; | |
351 } | |
352 | |
353 switch( http_hdr->status_code ) { | |
354 case 200: // OK | |
355 // Look if we can use the Content-Type | |
356 content_type = http_get_field( http_hdr, "Content-Type" ); | |
357 if( content_type!=NULL ) { | |
358 printf("Content-Type: [%s]\n", content_type ); | |
359 printf("Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); | |
360 // Check for ASF | |
361 if( asf_http_streaming_type(content_type, NULL)!=ASF_Unknown_e ) { | |
362 return DEMUXER_TYPE_ASF; | |
363 } | |
364 // Check for MP3 streaming | |
365 // Some MP3 streaming server answer with audio/mpeg | |
366 if( !strcasecmp(content_type, "audio/mpeg") ) { | |
367 return DEMUXER_TYPE_MPEG_PS; | |
368 } | |
369 // Check for MPEG streaming | |
370 if( !strcasecmp(content_type, "video/mpeg") ) { | |
371 return DEMUXER_TYPE_MPEG_PS; | |
372 } | |
373 // AVI ??? => video/x-msvideo | |
374 if( !strcasecmp(content_type, "video/x-msvideo") ) { | |
375 return DEMUXER_TYPE_AVI; | |
376 } | |
377 } | |
378 break; | |
379 // Redirect | |
380 case 301: // Permanently | |
381 case 302: // Temporarily | |
382 // TODO: RFC 2616, recommand to detect infinite redirection loops | |
383 next_url = http_get_field( http_hdr, "Location" ); | |
384 if( next_url!=NULL ) { | |
385 close( fd ); | |
386 url_free( url ); | |
387 url = url_new( next_url ); | |
388 redirect = 1; | |
389 } | |
390 break; | |
391 default: | |
392 printf("Server returned %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); | |
393 close( fd ); | |
394 *fd_out=-1; | |
395 return DEMUXER_TYPE_UNKNOWN; | |
396 } | |
397 } | |
398 } while( redirect ); | |
399 | |
400 return DEMUXER_TYPE_UNKNOWN; | |
401 } | |
402 | |
403 int | |
404 nop_streaming_read( streaming_ctrl_t *streaming_ctrl ) { | |
405 char *buffer; | |
406 int len; | |
407 if( streaming_ctrl==NULL ) return -1; | |
408 len = streaming_ctrl->buffer->length; | |
409 if( len==0 ) return 0; | |
410 | |
411 buffer = (char*)malloc( len ); | |
412 if( buffer==NULL ) { | |
413 printf("Memory allocation failed\n"); | |
414 return -1; | |
415 } | |
416 net_fifo_pop( streaming_ctrl->buffer, buffer, len ); | |
417 write( streaming_ctrl->fd_pipe_in, buffer, len ); | |
418 free( buffer ); | |
419 return len; | |
420 } | |
421 | |
422 int | |
423 nop_streaming_start( streaming_ctrl_t *streaming_ctrl ) { | |
424 HTTP_header_t *http_hdr; | |
425 int fd; | |
426 if( streaming_ctrl==NULL ) return -1; | |
427 | |
428 fd = streaming_ctrl->fd_net; | |
429 if( fd<0 ) { | |
430 fd = http_send_request( *(streaming_ctrl->url) ); | |
431 if( fd<0 ) return -1; | |
432 http_hdr = http_read_response( fd ); | |
433 if( http_hdr==NULL ) return -1; | |
434 | |
435 switch( http_hdr->status_code ) { | |
436 case 200: // OK | |
437 printf("Content-Type: [%s]\n", http_get_field(http_hdr, "Content-Type") ); | |
438 printf("Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") ); | |
439 if( http_hdr->body_size>0 ) { | |
440 write( streaming_ctrl->fd_pipe_in, http_hdr->body, http_hdr->body_size ); | |
441 } | |
442 break; | |
443 default: | |
444 printf("Server return %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase ); | |
445 close( fd ); | |
446 fd = -1; | |
447 } | |
448 streaming_ctrl->fd_net = fd; | |
449 } | |
450 | |
451 http_free( http_hdr ); | |
452 | |
453 streaming_ctrl->streaming_read = nop_streaming_read; | |
454 streaming_ctrl->prebuffer_size = 180000; | |
455 // streaming_ctrl->prebuffer_size = 0; | |
456 streaming_ctrl->buffering = 1; | |
457 // streaming_ctrl->buffering = 0; | |
458 streaming_ctrl->status = streaming_playing_e; | |
459 return fd; | |
460 } | |
461 | |
462 void | |
463 network_streaming(void *arg) { | |
464 char buffer[BUFFER_SIZE]; | |
465 fd_set fd_net_in; | |
466 int ret; | |
467 | |
468 arg = arg; | |
469 | |
470 do { | |
471 FD_ZERO( &fd_net_in ); | |
472 FD_SET( streaming_ctrl->fd_net, &fd_net_in ); | |
473 | |
474 ret = select( streaming_ctrl->fd_net+1, &fd_net_in, NULL, NULL, NULL ); | |
475 if( ret<0 ) { | |
476 perror("select"); | |
477 return; //exit(1); // FIXME! | |
478 } | |
479 if( FD_ISSET( streaming_ctrl->fd_net, &fd_net_in ) ) { | |
480 ret = readFromServer( streaming_ctrl->fd_net, buffer, BUFFER_SIZE ); | |
481 if( ret<=0 ) { | |
482 streaming_ctrl->status=streaming_stopped_e; | |
483 } else { | |
484 //printf(" push: 0x%02X\n", *((unsigned int*)buffer) ); | |
485 net_fifo_push( streaming_ctrl->buffer, buffer, ret ); | |
486 if( !streaming_ctrl->buffering ) { | |
487 do { | |
488 ret = streaming_ctrl->streaming_read( streaming_ctrl ); | |
489 if( ret<0 && streaming_ctrl->buffer->length<streaming_ctrl->prebuffer_size ) { | |
490 // Need buffering | |
491 streaming_ctrl->buffering = 1; | |
492 } | |
493 } while( streaming_ctrl->buffer->length>streaming_ctrl->prebuffer_size ); | |
494 } else { | |
495 if( streaming_ctrl->buffer->length>streaming_ctrl->prebuffer_size ) { | |
496 streaming_ctrl->buffering = 0; | |
497 printf("\n"); | |
498 } else { | |
499 printf(" Buffering: %d \%\r", (int)((float)(((float)streaming_ctrl->buffer->length)/((float)streaming_ctrl->prebuffer_size))*100) ); | |
500 fflush(stdout); | |
501 } | |
502 } | |
503 } | |
504 } else { | |
505 printf("Network fd not set\n"); | |
506 } | |
507 } while( streaming_ctrl->status==streaming_playing_e ); | |
508 | |
509 // Flush the buffer | |
510 while( streaming_ctrl->buffer->length>0 ) { | |
511 ret = streaming_ctrl->streaming_read( streaming_ctrl ); | |
512 if( ret<0 ) break; | |
513 } | |
514 | |
515 printf("Network thread done\n"); | |
516 | |
517 // Close to the pipe to stop mplayer. | |
518 close( streaming_ctrl->fd_pipe_in ); | |
519 | |
520 } | |
521 | |
522 int | |
523 streaming_start(URL_t **url, int fd, int streaming_type) { | |
524 int fd_pipe[2]; | |
525 // Open the pipe | |
526 if( pipe(fd_pipe)<0 ) { | |
527 printf("Pipe creation failed\n"); | |
528 return -1; | |
529 } | |
530 | |
531 streaming_ctrl = streaming_ctrl_new( ); | |
532 if( streaming_ctrl==NULL ) { | |
533 return -1; | |
534 } | |
535 streaming_ctrl->url = url; | |
536 streaming_ctrl->fd_pipe_in = fd_pipe[1]; | |
537 streaming_ctrl->fd_net = fd; | |
538 | |
539 #ifdef DUMP2FILE | |
540 { | |
541 int fd_file; | |
542 fd_file = open("dump.stream", O_WRONLY | O_CREAT ); | |
543 if( fd_file<0 ) { | |
544 perror("open"); | |
545 } | |
546 streaming_ctrl->fd_pipe_in = fd_file; | |
547 } | |
548 #endif | |
549 | |
550 switch( streaming_type ) { | |
551 case DEMUXER_TYPE_ASF: | |
552 // Send the appropriate HTTP request | |
553 fd = asf_http_streaming_start( streaming_ctrl ); | |
554 break; | |
555 case DEMUXER_TYPE_AVI: | |
556 case DEMUXER_TYPE_MPEG_ES: | |
557 case DEMUXER_TYPE_MPEG_PS: | |
558 fd = nop_streaming_start( streaming_ctrl ); | |
559 break; | |
560 case DEMUXER_TYPE_UNKNOWN: | |
561 default: | |
562 printf("Unable to detect the streaming type\n"); | |
563 close( fd ); | |
564 free( streaming_ctrl ); | |
565 return -1; | |
566 } | |
567 | |
568 if( fd<0 ) { | |
569 free( streaming_ctrl ); | |
570 return -1; | |
571 } | |
572 | |
573 // Start the network thread | |
574 if( pthread_create( &(streaming_ctrl->thread_id), NULL , (void*)network_streaming, (void*)NULL)<0 ) { | |
575 printf("Unable to start the network thread.\n"); | |
576 close( fd ); | |
577 free( streaming_ctrl ); | |
578 return -1; | |
579 } | |
580 printf("Network thread created with id: %d\n", streaming_ctrl->thread_id ); | |
581 | |
582 // streaming_ctrl->status = streaming_stopped_e; | |
583 | |
584 // return fd; | |
585 return fd_pipe[0]; | |
586 } | |
587 | |
588 int | |
589 streaming_stop( ) { | |
590 streaming_ctrl->status = streaming_stopped_e; | |
591 return 0; | |
592 } |