2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <sys/types.h>
26 #include <sys/ioctl.h>
40 #include "osdep/shmem.h"
43 #include "libmpdemux/demuxer.h"
44 #include "libavutil/intreadwrite.h"
51 static int (*stream_check_interrupt_cb
)(int time
) = NULL
;
53 extern const stream_info_t stream_info_vcd
;
54 extern const stream_info_t stream_info_cdda
;
55 extern const stream_info_t stream_info_netstream
;
56 extern const stream_info_t stream_info_pnm
;
57 extern const stream_info_t stream_info_asf
;
58 extern const stream_info_t stream_info_rtsp
;
59 extern const stream_info_t stream_info_rtp
;
60 extern const stream_info_t stream_info_udp
;
61 extern const stream_info_t stream_info_http1
;
62 extern const stream_info_t stream_info_http2
;
63 extern const stream_info_t stream_info_dvb
;
64 extern const stream_info_t stream_info_tv
;
65 extern const stream_info_t stream_info_radio
;
66 extern const stream_info_t stream_info_pvr
;
67 extern const stream_info_t stream_info_ftp
;
68 extern const stream_info_t stream_info_vstream
;
69 extern const stream_info_t stream_info_dvdnav
;
70 extern const stream_info_t stream_info_smb
;
71 extern const stream_info_t stream_info_sdp
;
72 extern const stream_info_t stream_info_rtsp_sip
;
74 extern const stream_info_t stream_info_cue
;
75 extern const stream_info_t stream_info_null
;
76 extern const stream_info_t stream_info_mf
;
77 extern const stream_info_t stream_info_ffmpeg
;
78 extern const stream_info_t stream_info_file
;
79 extern const stream_info_t stream_info_ifo
;
80 extern const stream_info_t stream_info_dvd
;
82 static const stream_info_t
* const auto_open_streams
[] = {
90 &stream_info_netstream
,
97 &stream_info_rtsp_sip
,
118 #ifdef CONFIG_VSTREAM
119 &stream_info_vstream
,
121 #ifdef CONFIG_LIBSMBCLIENT
125 #ifdef CONFIG_DVDREAD
132 #ifdef CONFIG_LIBAVFORMAT
142 static stream_t
* open_stream_plugin(const stream_info_t
* sinfo
, const char* filename
,
143 int mode
, char** options
, int* file_format
,
144 int* ret
, char** redirected_url
)
148 m_struct_t
* desc
= (m_struct_t
*)sinfo
->opts
;
152 arg
= m_struct_alloc(desc
);
153 if(sinfo
->opts_url
) {
155 { "stream url", arg
, CONF_TYPE_CUSTOM_URL
, 0, 0 ,0, sinfo
->opts
};
156 if(m_option_parse(&url_opt
,"stream url",filename
,arg
,M_CONFIG_FILE
) < 0) {
157 mp_msg(MSGT_OPEN
,MSGL_ERR
, "URL parsing failed on url %s\n",filename
);
158 m_struct_free(desc
,arg
);
164 for(i
= 0 ; options
[i
] != NULL
; i
+= 2) {
165 mp_msg(MSGT_OPEN
,MSGL_DBG2
, "Set stream arg %s=%s\n",
166 options
[i
],options
[i
+1]);
167 if(!m_struct_set(desc
,arg
,options
[i
],options
[i
+1]))
168 mp_msg(MSGT_OPEN
,MSGL_WARN
, "Failed to set stream option %s=%s\n",
169 options
[i
],options
[i
+1]);
173 s
= new_stream(-2,-2);
174 s
->url
=strdup(filename
);
176 *ret
= sinfo
->open(s
,mode
,arg
,file_format
);
177 if((*ret
) != STREAM_OK
) {
178 #ifdef CONFIG_NETWORK
179 if (*ret
== STREAM_REDIRECTED
&& redirected_url
) {
180 if (s
->streaming_ctrl
&& s
->streaming_ctrl
->url
181 && s
->streaming_ctrl
->url
->url
)
182 *redirected_url
= strdup(s
->streaming_ctrl
->url
->url
);
184 *redirected_url
= NULL
;
186 streaming_ctrl_free(s
->streaming_ctrl
);
193 mp_msg(MSGT_OPEN
,MSGL_WARN
, "Warning streams need a type !!!!\n");
194 if(s
->flags
& MP_STREAM_SEEK
&& !s
->seek
)
195 s
->flags
&= ~MP_STREAM_SEEK
;
196 if(s
->seek
&& !(s
->flags
& MP_STREAM_SEEK
))
197 s
->flags
|= MP_STREAM_SEEK
;
201 mp_msg(MSGT_OPEN
,MSGL_V
, "STREAM: [%s] %s\n",sinfo
->name
,filename
);
202 mp_msg(MSGT_OPEN
,MSGL_V
, "STREAM: Description: %s\n",sinfo
->info
);
203 mp_msg(MSGT_OPEN
,MSGL_V
, "STREAM: Author: %s\n", sinfo
->author
);
204 mp_msg(MSGT_OPEN
,MSGL_V
, "STREAM: Comment: %s\n", sinfo
->comment
);
210 stream_t
* open_stream_full(const char* filename
,int mode
, char** options
, int* file_format
) {
212 const stream_info_t
* sinfo
;
214 char *redirected_url
= NULL
;
216 for(i
= 0 ; auto_open_streams
[i
] ; i
++) {
217 sinfo
= auto_open_streams
[i
];
218 if(!sinfo
->protocols
) {
219 mp_msg(MSGT_OPEN
,MSGL_WARN
, "Stream type %s has protocols == NULL, it's a bug\n", sinfo
->name
);
222 for(j
= 0 ; sinfo
->protocols
[j
] ; j
++) {
223 l
= strlen(sinfo
->protocols
[j
]);
224 // l == 0 => Don't do protocol matching (ie network and filenames)
225 if((l
== 0 && !strstr(filename
, "://")) ||
226 ((strncasecmp(sinfo
->protocols
[j
],filename
,l
) == 0) &&
227 (strncmp("://",filename
+l
,3) == 0))) {
228 *file_format
= DEMUXER_TYPE_UNKNOWN
;
229 s
= open_stream_plugin(sinfo
,filename
,mode
,options
,file_format
,&r
,
232 if(r
== STREAM_REDIRECTED
&& redirected_url
) {
233 mp_msg(MSGT_OPEN
,MSGL_V
, "[%s] open %s redirected to %s\n",
234 sinfo
->info
, filename
, redirected_url
);
235 s
= open_stream_full(redirected_url
, mode
, options
, file_format
);
236 free(redirected_url
);
239 else if(r
!= STREAM_UNSUPPORTED
) {
240 mp_msg(MSGT_OPEN
,MSGL_ERR
, MSGTR_FailedToOpen
,filename
);
248 mp_msg(MSGT_OPEN
,MSGL_ERR
, "No stream found to handle url %s\n",filename
);
252 stream_t
* open_output_stream(const char* filename
, char** options
) {
253 int file_format
; //unused
255 mp_msg(MSGT_OPEN
,MSGL_ERR
,"open_output_stream(), NULL filename, report this bug\n");
259 return open_stream_full(filename
,STREAM_WRITE
,options
,&file_format
);
262 //=================== STREAMER =========================
264 int stream_fill_buffer(stream_t
*s
){
266 // we will retry even if we already reached EOF previously.
268 case STREAMTYPE_STREAM
:
269 #ifdef CONFIG_NETWORK
270 if( s
->streaming_ctrl
!=NULL
&& s
->streaming_ctrl
->streaming_read
) {
271 len
=s
->streaming_ctrl
->streaming_read(s
->fd
,s
->buffer
,STREAM_BUFFER_SIZE
, s
->streaming_ctrl
);
275 len
= s
->fill_buffer(s
, s
->buffer
, STREAM_BUFFER_SIZE
);
277 len
=read(s
->fd
,s
->buffer
,STREAM_BUFFER_SIZE
);
280 len
= demux_read_data((demux_stream_t
*)s
->priv
,s
->buffer
,STREAM_BUFFER_SIZE
);
285 len
= s
->fill_buffer
? s
->fill_buffer(s
,s
->buffer
,STREAM_BUFFER_SIZE
) : 0;
287 if(len
<=0){ s
->eof
=1; return 0; }
288 // When reading succeeded we are obviously not at eof.
289 // This e.g. avoids issues with eof getting stuck when lavf seeks in MPEG-TS
294 // printf("[%d]",len);fflush(stdout);
298 int stream_write_buffer(stream_t
*s
, unsigned char *buf
, int len
) {
302 rd
= s
->write_buffer(s
, buf
, len
);
309 int stream_seek_long(stream_t
*s
,off_t pos
){
312 // if( mp_msg_test(MSGT_STREAM,MSGL_DBG3) ) printf("seek_long to 0x%X\n",(unsigned int)pos);
314 s
->buf_pos
=s
->buf_len
=0;
316 if(s
->mode
== STREAM_WRITE
) {
317 if(!s
->seek
|| !s
->seek(s
,pos
))
323 newpos
= (pos
/s
->sector_size
)*s
->sector_size
;
325 newpos
= pos
&(~((off_t
)STREAM_BUFFER_SIZE
-1));
327 if( mp_msg_test(MSGT_STREAM
,MSGL_DBG3
) ){
328 mp_msg(MSGT_STREAM
,MSGL_DBG3
, "s->pos=%"PRIX64
" newpos=%"PRIX64
" new_bufpos=%"PRIX64
" buflen=%X \n",
329 (int64_t)s
->pos
,(int64_t)newpos
,(int64_t)pos
,s
->buf_len
);
333 if(newpos
==0 || newpos
!=s
->pos
){
335 case STREAMTYPE_STREAM
:
336 //s->pos=newpos; // real seek
337 // Some streaming protocol allow to seek backward and forward
338 // A function call that return -1 can tell that the protocol
339 // doesn't support seeking.
340 #ifdef CONFIG_NETWORK
341 if(s
->seek
) { // new stream seek is much cleaner than streaming_ctrl one
342 if(!s
->seek(s
,newpos
)) {
343 mp_msg(MSGT_STREAM
,MSGL_ERR
, "Seek failed\n");
349 if( s
->streaming_ctrl
!=NULL
&& s
->streaming_ctrl
->streaming_seek
) {
350 if( s
->streaming_ctrl
->streaming_seek( s
->fd
, pos
, s
->streaming_ctrl
)<0 ) {
351 mp_msg(MSGT_STREAM
,MSGL_INFO
,"Stream not seekable!\n");
358 mp_msg(MSGT_STREAM
,MSGL_INFO
,"Cannot seek backward in linear streams!\n");
361 while(s
->pos
<newpos
){
362 if(stream_fill_buffer(s
)<=0) break; // EOF
366 // This should at the beginning as soon as all streams are converted
370 if(!s
->seek(s
,newpos
)) {
371 mp_msg(MSGT_STREAM
,MSGL_ERR
, "Seek failed\n");
375 // putchar('.');fflush(stdout);
377 // putchar('%');fflush(stdout);
380 while(stream_fill_buffer(s
) > 0 && pos
>= 0) {
382 s
->buf_pos
=pos
; // byte position in sector
388 // if(pos==s->buf_len) printf("XXX Seek to last byte of file -> EOF\n");
390 mp_msg(MSGT_STREAM
,MSGL_V
,"stream_seek: WARNING! Can't seek to 0x%"PRIX64
" !\n",(int64_t)(pos
+newpos
));
395 void stream_reset(stream_t
*s
){
398 s
->buf_pos
=s
->buf_len
=0;
401 if(s
->control
) s
->control(s
,STREAM_CTRL_RESET
,NULL
);
405 int stream_control(stream_t
*s
, int cmd
, void *arg
){
406 if(!s
->control
) return STREAM_UNSUPPORTED
;
407 #ifdef CONFIG_STREAM_CACHE
409 return cache_do_control(s
, cmd
, arg
);
411 return s
->control(s
, cmd
, arg
);
414 stream_t
* new_memory_stream(unsigned char* data
,int len
){
419 s
=calloc(1, sizeof(stream_t
)+len
);
421 s
->type
=STREAMTYPE_MEMORY
;
422 s
->buf_pos
=0; s
->buf_len
=len
;
423 s
->start_pos
=0; s
->end_pos
=len
;
426 memcpy(s
->buffer
,data
,len
);
430 stream_t
* new_stream(int fd
,int type
){
431 stream_t
*s
=calloc(1, sizeof(stream_t
));
432 if(s
==NULL
) return NULL
;
437 int temp
= WSAStartup(0x0202, &wsdata
); // there might be a better place for this (-> later)
438 mp_msg(MSGT_STREAM
,MSGL_V
,"WINSOCK2 init: %i\n", temp
);
444 s
->buf_pos
=s
->buf_len
=0;
445 s
->start_pos
=s
->end_pos
=0;
453 void free_stream(stream_t
*s
){
454 // printf("\n*** free_stream() called ***\n");
455 #ifdef CONFIG_STREAM_CACHE
458 if(s
->close
) s
->close(s
);
460 /* on unix we define closesocket to close
461 on windows however we have to distinguish between
462 network socket and file */
463 if(s
->url
&& strstr(s
->url
,"://"))
468 mp_msg(MSGT_STREAM
,MSGL_V
,"WINSOCK2 uninit\n");
469 WSACleanup(); // there might be a better place for this (-> later)
471 // Disabled atm, i don't like that. s->priv can be anything after all
472 // streams should destroy their priv on close
473 //if(s->priv) free(s->priv);
474 if(s
->url
) free(s
->url
);
478 stream_t
* new_ds_stream(demux_stream_t
*ds
) {
479 stream_t
* s
= new_stream(-1,STREAMTYPE_DS
);
484 void stream_set_interrupt_callback(int (*cb
)(int)) {
485 stream_check_interrupt_cb
= cb
;
488 int stream_check_interrupt(int time
) {
489 if(!stream_check_interrupt_cb
) {
490 usec_sleep(time
* 1000);
493 return stream_check_interrupt_cb(time
);
497 * Helper function to read 16 bits little-endian and advance pointer
499 static uint16_t get_le16_inc(const uint8_t **buf
)
501 uint16_t v
= AV_RL16(*buf
);
507 * Helper function to read 16 bits big-endian and advance pointer
509 static uint16_t get_be16_inc(const uint8_t **buf
)
511 uint16_t v
= AV_RB16(*buf
);
517 * Find a newline character in buffer
518 * \param buf buffer to search
519 * \param len amount of bytes to search in buffer, may not overread
520 * \param utf16 chose between UTF-8/ASCII/other and LE and BE UTF-16
521 * 0 = UTF-8/ASCII/other, 1 = UTF-16-LE, 2 = UTF-16-BE
523 static const uint8_t *find_newline(const uint8_t *buf
, int len
, int utf16
)
526 const uint8_t *end
= buf
+ len
;
529 return (uint8_t *)memchr(buf
, '\n', len
);
531 while (buf
< end
- 1) {
532 GET_UTF16(c
, buf
< end
- 1 ? get_le16_inc(&buf
) : 0, return NULL
;)
533 if (buf
<= end
&& c
== '\n')
538 while (buf
< end
- 1) {
539 GET_UTF16(c
, buf
< end
- 1 ? get_be16_inc(&buf
) : 0, return NULL
;)
540 if (buf
<= end
&& c
== '\n')
549 * Copy a number of bytes, converting to UTF-8 if input is UTF-16
550 * \param dst buffer to copy to
551 * \param dstsize size of dst buffer
552 * \param src buffer to copy from
553 * \param len amount of bytes to copy from src
554 * \param utf16 chose between UTF-8/ASCII/other and LE and BE UTF-16
555 * 0 = UTF-8/ASCII/other, 1 = UTF-16-LE, 2 = UTF-16-BE
557 static int copy_characters(uint8_t *dst
, int dstsize
,
558 const uint8_t *src
, int *len
, int utf16
)
561 uint8_t *dst_end
= dst
+ dstsize
;
562 const uint8_t *end
= src
+ *len
;
567 memcpy(dst
, src
, *len
);
570 while (src
< end
- 1 && dst_end
- dst
> 8) {
572 GET_UTF16(c
, src
< end
- 1 ? get_le16_inc(&src
) : 0, ;)
573 PUT_UTF8(c
, tmp
, *dst
++ = tmp
;)
576 return dstsize
- (dst_end
- dst
);
578 while (src
< end
- 1 && dst_end
- dst
> 8) {
580 GET_UTF16(c
, src
< end
- 1 ? get_be16_inc(&src
) : 0, ;)
581 PUT_UTF8(c
, tmp
, *dst
++ = tmp
;)
584 return dstsize
- (dst_end
- dst
);
589 unsigned char* stream_read_line(stream_t
*s
,unsigned char* mem
, int max
, int utf16
) {
591 const unsigned char *end
;
592 unsigned char *ptr
= mem
;
593 if (max
< 1) return NULL
;
594 max
--; // reserve one for 0-termination
596 len
= s
->buf_len
-s
->buf_pos
;
597 // try to fill the buffer
599 (!cache_stream_fill_buffer(s
) ||
600 (len
= s
->buf_len
-s
->buf_pos
) <= 0)) break;
601 end
= find_newline(s
->buffer
+s
->buf_pos
, len
, utf16
);
602 if(end
) len
= end
- (s
->buffer
+s
->buf_pos
) + 1;
603 if(len
> 0 && max
> 0) {
604 int l
= copy_characters(ptr
, max
, s
->buffer
+s
->buf_pos
, &len
, utf16
);
612 if(s
->eof
&& ptr
== mem
) return NULL
;