mirror of
https://github.com/mpv-player/mpv
synced 2025-03-25 04:38:01 +00:00
Remove internal network support
This commit removes the "old" networking code in favor of libavformat's code. The code was still used for mp_http, udp, ftp, cddb. http has been mapped to libavformat's http support since approximately 6 months ago. udp and ftp have support in ffmpeg (though ftp was added only last month). cddb support is removed with this commit - it's probably not important and rarely used if at all, so we don't care about it.
This commit is contained in:
parent
feaa721916
commit
854303ad49
@ -1051,12 +1051,6 @@
|
||||
work (key bindings that normally quit will be shown on OSD only, just
|
||||
like any other binding).
|
||||
|
||||
--ipv4-only-proxy
|
||||
Skip any HTTP proxy for IPv6 addresses. It will still be used for IPv4
|
||||
connections.
|
||||
|
||||
*WARNING*: works with the deprecated ``mp_http://`` protocol only.
|
||||
|
||||
--joystick, --no-joystick
|
||||
Enable/disable joystick support. Enabled by default.
|
||||
|
||||
@ -1416,12 +1410,6 @@
|
||||
allows a zoom factor of up to 4. This feature is experimental. Do not
|
||||
report bugs unless you are using ``--vo=opengl``.
|
||||
|
||||
--passwd=<password>
|
||||
Used with some network protocols. Specify password for HTTP authentication.
|
||||
See also ``--user``.
|
||||
|
||||
*WARNING*: works with the deprecated ``mp_http://`` protocol only.
|
||||
|
||||
--playing-msg=<string>
|
||||
Print out a string after starting playback. The string is expanded for
|
||||
properties, e.g. ``--playing-msg=file: ${filename}`` will print the string
|
||||
@ -1466,16 +1454,6 @@
|
||||
--pphelp
|
||||
See also ``--vf=pp``.
|
||||
|
||||
--prefer-ipv4
|
||||
Use IPv4 on network connections. Falls back on IPv6 automatically.
|
||||
|
||||
*WARNING*: works with the deprecated ``mp_http://`` protocol only.
|
||||
|
||||
--prefer-ipv6
|
||||
Use IPv6 on network connections. Falls back on IPv4 automatically.
|
||||
|
||||
*WARNING*: works with the deprecated ``mp_http://`` protocol only.
|
||||
|
||||
--priority=<prio>
|
||||
(Windows only.)
|
||||
Set process priority for mpv according to the predefined priorities
|
||||
@ -2284,12 +2262,6 @@
|
||||
|
||||
*WARNING*: May be dangerous if playing from untrusted media.
|
||||
|
||||
--user=<username>
|
||||
Used with some network protocols.
|
||||
Specify username for HTTP authentication. See also ``--passwd``.
|
||||
|
||||
*WARNING*: works with the deprecated ``mp_http://`` protocol only.
|
||||
|
||||
--user-agent=<string>
|
||||
Use <string> as user agent for HTTP streaming.
|
||||
|
||||
|
12
Makefile
12
Makefile
@ -28,13 +28,11 @@ SOURCES_AUDIO_INPUT-$(OSS) += stream/ai_oss.c
|
||||
SOURCES-$(AUDIO_INPUT) += $(SOURCES_AUDIO_INPUT-yes)
|
||||
SOURCES-$(CDDA) += stream/stream_cdda.c \
|
||||
stream/cdinfo.c
|
||||
SOURCES-$(CDDB) += stream/stream_cddb.c
|
||||
SOURCES-$(DVBIN) += stream/dvb_tune.c \
|
||||
stream/stream_dvb.c
|
||||
SOURCES-$(DVDREAD) += stream/stream_dvd.c \
|
||||
stream/stream_dvd_common.c
|
||||
|
||||
SOURCES-$(FTP) += stream/stream_ftp.c
|
||||
SOURCES-$(HAVE_SYS_MMAN_H) += audio/filter/af_export.c osdep/mmap_anon.c
|
||||
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
|
||||
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c \
|
||||
@ -56,14 +54,6 @@ SOURCES-$(MPG123) += audio/decode/ad_mpg123.c
|
||||
|
||||
SOURCES-$(NEED_GETTIMEOFDAY) += osdep/gettimeofday.c
|
||||
SOURCES-$(NEED_GLOB) += osdep/glob-win.c
|
||||
SOURCES-$(NETWORKING) += stream/asf_mmst_streaming.c \
|
||||
stream/asf_streaming.c \
|
||||
stream/cookies.c \
|
||||
stream/http.c \
|
||||
stream/network.c \
|
||||
stream/udp.c \
|
||||
stream/tcp.c \
|
||||
stream/stream_udp.c \
|
||||
|
||||
SOURCES-$(PRIORITY) += osdep/priority.c
|
||||
SOURCES-$(PVR) += stream/stream_pvr.c
|
||||
@ -223,6 +213,7 @@ SOURCES = talloc.c \
|
||||
osdep/io.c \
|
||||
osdep/numcores.c \
|
||||
osdep/timer.c \
|
||||
stream/cookies.c \
|
||||
stream/stream.c \
|
||||
stream/stream_avdevice.c \
|
||||
stream/stream_file.c \
|
||||
@ -230,7 +221,6 @@ SOURCES = talloc.c \
|
||||
stream/stream_memory.c \
|
||||
stream/stream_mf.c \
|
||||
stream/stream_null.c \
|
||||
stream/url.c \
|
||||
sub/dec_sub.c \
|
||||
sub/draw_bmp.c \
|
||||
sub/find_subfiles.c \
|
||||
|
190
configure
vendored
190
configure
vendored
@ -307,8 +307,6 @@ Optional features:
|
||||
--disable-tv disable TV interface (TV/DVB grabbers) [enable]
|
||||
--disable-tv-v4l2 disable Video4Linux2 TV interface [autodetect]
|
||||
--disable-pvr disable Video4Linux2 MPEG PVR [autodetect]
|
||||
--disable-networking disable networking [enable]
|
||||
--enable-winsock2_h enable winsock2_h [autodetect]
|
||||
--enable-smb enable Samba (SMB) input [autodetect]
|
||||
--disable-libquvi4 disable libquvi 0.4.x [autodetect]
|
||||
--disable-libquvi9 disable libquvi 0.9.x [autodetect]
|
||||
@ -316,12 +314,10 @@ Optional features:
|
||||
--disable-vcd disable VCD support [autodetect]
|
||||
--disable-bluray disable Blu-ray support [autodetect]
|
||||
--disable-dvdread disable libdvdread [autodetect]
|
||||
--disable-cddb disable cddb [autodetect]
|
||||
--disable-enca disable ENCA charset oracle library [autodetect]
|
||||
--enable-macosx-bundle enable Mac OS X bundle file locations [autodetect]
|
||||
--disable-inet6 disable IPv6 support [autodetect]
|
||||
--disable-gethostbyname2 gethostbyname2 part of the C library [autodetect]
|
||||
--disable-ftp disable FTP support [enabled]
|
||||
--disable-vstream disable TiVo vstream client support [autodetect]
|
||||
--disable-pthreads disable Posix threads support [autodetect]
|
||||
--disable-libass disable subtitle rendering with libass [autodetect]
|
||||
@ -463,8 +459,6 @@ _radio_v4l2=auto
|
||||
_tv=yes
|
||||
_tv_v4l2=auto
|
||||
_pvr=auto
|
||||
networking=yes
|
||||
_winsock2_h=auto
|
||||
_smb=auto
|
||||
_libquvi4=auto
|
||||
_libquvi9=auto
|
||||
@ -477,7 +471,6 @@ _termios=auto
|
||||
_shm=auto
|
||||
_gettext=no
|
||||
_cdda=auto
|
||||
_cddb=auto
|
||||
_coreaudio=auto
|
||||
_corevideo=auto
|
||||
_cocoa=auto
|
||||
@ -485,7 +478,6 @@ _macosx_bundle=auto
|
||||
_enca=auto
|
||||
_inet6=auto
|
||||
_gethostbyname2=auto
|
||||
_ftp=auto
|
||||
_vstream=auto
|
||||
_pthreads=auto
|
||||
_ass=auto
|
||||
@ -662,10 +654,6 @@ for ac_option do
|
||||
--disable-radio-v4l2) _radio_v4l2=no ;;
|
||||
--enable-pvr) _pvr=yes ;;
|
||||
--disable-pvr) _pvr=no ;;
|
||||
--enable-networking) networking=yes ;;
|
||||
--disable-networking) networking=no ;;
|
||||
--enable-winsock2_h) _winsock2_h=yes ;;
|
||||
--disable-winsock2_h) _winsock2_h=no ;;
|
||||
--enable-smb) _smb=yes ;;
|
||||
--disable-smb) _smb=no ;;
|
||||
--enable-libquvi4) _libquvi4=yes ;;
|
||||
@ -692,10 +680,6 @@ for ac_option do
|
||||
--disable-shm) _shm=no ;;
|
||||
--enable-select) _select=yes ;;
|
||||
--disable-select) _select=no ;;
|
||||
--enable-cddb) _cddb=yes ;;
|
||||
--disable-cddb) _cddb=no ;;
|
||||
--enable-ftp) _ftp=yes ;;
|
||||
--disable-ftp) _ftp=no ;;
|
||||
--enable-vstream) _vstream=yes ;;
|
||||
--disable-vstream) _vstream=no ;;
|
||||
--enable-pthreads) _pthreads=yes ;;
|
||||
@ -1247,146 +1231,6 @@ fi
|
||||
echores "$_nanosleep"
|
||||
|
||||
|
||||
echocheck "socklib"
|
||||
# for Solaris (socket stuff is in -lsocket, gethostbyname and friends in -lnsl):
|
||||
cat > $TMPC << EOF
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
int main(void) { gethostbyname(0); socket(AF_INET, SOCK_STREAM, 0); return 0; }
|
||||
EOF
|
||||
_socklib=no
|
||||
for _ld_tmp in "" "-lsocket -lbind" "-lsocket -ldnet" "-lsocket -lnsl" "-lnsl" "-lsocket" ; do
|
||||
cc_check $_ld_tmp && _ld_sock="$_ld_tmp" && _socklib=yes && break
|
||||
done
|
||||
test $_socklib = yes && test $_winsock2_h = auto && _winsock2_h=no
|
||||
if test $_winsock2_h = auto ; then
|
||||
_winsock2_h=no
|
||||
statement_check winsock2.h 'gethostbyname(0)' -lws2_32 && _ld_sock="-lws2_32" && _winsock2_h=yes
|
||||
fi
|
||||
test "$_ld_sock" && res_comment="using $_ld_sock"
|
||||
echores "$_socklib"
|
||||
|
||||
|
||||
if test $_winsock2_h = yes ; then
|
||||
_ld_sock="-lws2_32"
|
||||
def_winsock2_h='#define HAVE_WINSOCK2_H 1'
|
||||
else
|
||||
def_winsock2_h='#define HAVE_WINSOCK2_H 0'
|
||||
fi
|
||||
|
||||
|
||||
echocheck "inet_pton()"
|
||||
def_inet_pton='#define HAVE_INET_PTON 0'
|
||||
inet_pton=no
|
||||
for _ld_tmp in "$_ld_sock" "$_ld_sock -lresolv" ; do
|
||||
statement_check arpa/inet.h 'inet_pton(0, 0, 0)' $_ld_tmp && inet_pton=yes && break
|
||||
done
|
||||
if test $inet_pton = yes ; then
|
||||
test "$_ld_tmp" && res_comment="using $_ld_tmp"
|
||||
def_inet_pton='#define HAVE_INET_PTON 1'
|
||||
fi
|
||||
echores "$inet_pton"
|
||||
|
||||
|
||||
echocheck "inet_aton()"
|
||||
def_inet_aton='#define HAVE_INET_ATON 0'
|
||||
inet_aton=no
|
||||
for _ld_tmp in "$_ld_sock" "$_ld_sock -lresolv" ; do
|
||||
statement_check arpa/inet.h 'inet_aton(0, 0)' $_ld_tmp && inet_aton=yes && break
|
||||
done
|
||||
if test $inet_aton = yes ; then
|
||||
test "$_ld_tmp" && res_comment="using $_ld_tmp"
|
||||
def_inet_aton='#define HAVE_INET_ATON 1'
|
||||
fi
|
||||
echores "$inet_aton"
|
||||
|
||||
|
||||
echocheck "socklen_t"
|
||||
_socklen_t=no
|
||||
for header in "sys/socket.h" "ws2tcpip.h" "sys/types.h" ; do
|
||||
statement_check $header 'socklen_t v = 0' && _socklen_t=yes && break
|
||||
done
|
||||
if test "$_socklen_t" = yes ; then
|
||||
def_socklen_t='#define HAVE_SOCKLEN_T 1'
|
||||
else
|
||||
def_socklen_t='#define HAVE_SOCKLEN_T 0'
|
||||
fi
|
||||
echores "$_socklen_t"
|
||||
|
||||
|
||||
echocheck "closesocket()"
|
||||
_closesocket=no
|
||||
statement_check winsock2.h 'closesocket(~0)' $_ld_sock && _closesocket=yes
|
||||
if test "$_closesocket" = yes ; then
|
||||
def_closesocket='#define HAVE_CLOSESOCKET 1'
|
||||
else
|
||||
def_closesocket='#define HAVE_CLOSESOCKET 0'
|
||||
fi
|
||||
echores "$_closesocket"
|
||||
|
||||
|
||||
echocheck "networking"
|
||||
test $_winsock2_h = no && test $inet_pton = no &&
|
||||
test $inet_aton = no && networking=no
|
||||
if test "$networking" = yes ; then
|
||||
def_network='#define CONFIG_NETWORK 1'
|
||||
def_networking='#define CONFIG_NETWORKING 1'
|
||||
libs_mplayer="$libs_mplayer $_ld_sock"
|
||||
inputmodules="networking $inputmodules"
|
||||
else
|
||||
noinputmodules="networking $noinputmodules"
|
||||
def_network='#define CONFIG_NETWORK 0'
|
||||
def_networking='#undef CONFIG_NETWORKING'
|
||||
fi
|
||||
echores "$networking"
|
||||
|
||||
|
||||
echocheck "inet6"
|
||||
if test "$_inet6" = auto ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <sys/types.h>
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#else
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
int main(void) { struct sockaddr_in6 six; socket(AF_INET6, SOCK_STREAM, AF_INET6); return 0; }
|
||||
EOF
|
||||
_inet6=no
|
||||
if cc_check $_ld_sock ; then
|
||||
_inet6=yes
|
||||
fi
|
||||
fi
|
||||
if test "$_inet6" = yes ; then
|
||||
def_inet6='#define HAVE_AF_INET6 1'
|
||||
else
|
||||
def_inet6='#undef HAVE_AF_INET6'
|
||||
fi
|
||||
echores "$_inet6"
|
||||
|
||||
|
||||
echocheck "gethostbyname2"
|
||||
if test "$_gethostbyname2" = auto ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
int main(void) { gethostbyname2("", AF_INET); return 0; }
|
||||
EOF
|
||||
_gethostbyname2=no
|
||||
if cc_check ; then
|
||||
_gethostbyname2=yes
|
||||
fi
|
||||
fi
|
||||
if test "$_gethostbyname2" = yes ; then
|
||||
def_gethostbyname2='#define HAVE_GETHOSTBYNAME2 1'
|
||||
else
|
||||
def_gethostbyname2='#undef HAVE_GETHOSTBYNAME2'
|
||||
fi
|
||||
echores "$_gethostbyname2"
|
||||
|
||||
|
||||
echocheck "mman.h"
|
||||
_mman=no
|
||||
statement_check sys/mman.h 'mmap(0, 0, 0, 0, 0, 0)' && _mman=yes
|
||||
@ -2585,7 +2429,6 @@ fi
|
||||
if test "$_libcdio" = yes ; then
|
||||
_cdda='yes'
|
||||
def_cdda='#define CONFIG_CDDA 1'
|
||||
test $_cddb = auto && test $networking = yes && _cddb=yes
|
||||
inputmodules="cdda $inputmodules"
|
||||
else
|
||||
_libcdio=no
|
||||
@ -2595,15 +2438,6 @@ else
|
||||
fi
|
||||
echores "$_libcdio"
|
||||
|
||||
if test "$_cddb" = yes ; then
|
||||
def_cddb='#define CONFIG_CDDB 1'
|
||||
inputmodules="cddb $inputmodules"
|
||||
else
|
||||
_cddb=no
|
||||
def_cddb='#undef CONFIG_CDDB'
|
||||
noinputmodules="cddb $noinputmodules"
|
||||
fi
|
||||
|
||||
|
||||
echocheck "SSA/ASS support"
|
||||
if test "$_ass" = auto ; then
|
||||
@ -2999,19 +2833,6 @@ fi
|
||||
echores "$_pvr"
|
||||
|
||||
|
||||
echocheck "ftp"
|
||||
if test "$_ftp" = "auto" ; then
|
||||
test "$networking" = "yes" && _ftp=yes
|
||||
fi
|
||||
if test "$_ftp" = yes ; then
|
||||
def_ftp='#define CONFIG_FTP 1'
|
||||
inputmodules="ftp $inputmodules"
|
||||
else
|
||||
noinputmodules="ftp $noinputmodules"
|
||||
def_ftp='#undef CONFIG_FTP'
|
||||
fi
|
||||
echores "$_ftp"
|
||||
|
||||
echocheck "vstream client"
|
||||
if test "$_vstream" = auto ; then
|
||||
_vstream=no
|
||||
@ -3175,7 +2996,6 @@ ALSA = $_alsa
|
||||
AUDIO_INPUT = $_audio_input
|
||||
CACA = $_caca
|
||||
CDDA = $_cdda
|
||||
CDDB = $_cddb
|
||||
COCOA = $_cocoa
|
||||
COREAUDIO = $_coreaudio
|
||||
COREVIDEO = $_corevideo
|
||||
@ -3187,7 +3007,6 @@ WASAPI0 = $_wasapi0
|
||||
DVBIN = $_dvbin
|
||||
DVDREAD = $_dvdread
|
||||
DXR3 = $_dxr3
|
||||
FTP = $_ftp
|
||||
GL = $_gl
|
||||
GL_COCOA = $_gl_cocoa
|
||||
GL_WIN32 = $_gl_win32
|
||||
@ -3220,7 +3039,6 @@ LIRC = $_lirc
|
||||
MACOSX_BUNDLE = $_macosx_bundle
|
||||
MNG = $_mng
|
||||
MPG123 = $_mpg123
|
||||
NETWORKING = $networking
|
||||
OPENAL = $_openal
|
||||
OSS = $_ossaudio
|
||||
PE_EXECUTABLE = $_pe_executable
|
||||
@ -3344,7 +3162,6 @@ $(ff_config_enable "$subarch_all" "$subarch" "ARCH")
|
||||
$def_bluray
|
||||
$def_bsdi_dvd
|
||||
$def_cdda
|
||||
$def_cddb
|
||||
$def_cdio
|
||||
$def_cdrom
|
||||
$def_dvd
|
||||
@ -3405,17 +3222,10 @@ $def_ass
|
||||
$def_enca
|
||||
|
||||
/* networking */
|
||||
$def_closesocket
|
||||
$def_ftp
|
||||
$def_inet6
|
||||
$def_inet_aton
|
||||
$def_inet_pton
|
||||
$def_networking
|
||||
$def_smb
|
||||
$def_libquvi4
|
||||
$def_libquvi9
|
||||
$def_libguess
|
||||
$def_socklen_t
|
||||
$def_vstream
|
||||
|
||||
$def_lcms2
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
@ -38,7 +39,6 @@
|
||||
#include "core/mp_common.h"
|
||||
#include "core/m_option.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "stream/url.h"
|
||||
|
||||
char *m_option_strerror(int code)
|
||||
{
|
||||
@ -2367,6 +2367,38 @@ const m_option_type_t m_option_type_obj_settings_list = {
|
||||
};
|
||||
|
||||
|
||||
/* Replace escape sequences in an URL (or a part of an URL) */
|
||||
/* works like strcpy(), but without return argument,
|
||||
except that outbuf == inbuf is allowed */
|
||||
static void url_unescape_string(char *outbuf, const char *inbuf)
|
||||
{
|
||||
unsigned char c,c1,c2;
|
||||
int i,len=strlen(inbuf);
|
||||
for (i=0;i<len;i++) {
|
||||
c = inbuf[i];
|
||||
if (c == '%' && i<len-2) { //must have 2 more chars
|
||||
c1 = toupper(inbuf[i+1]); // we need uppercase characters
|
||||
c2 = toupper(inbuf[i+2]);
|
||||
if (((c1>='0' && c1<='9') || (c1>='A' && c1<='F')) &&
|
||||
((c2>='0' && c2<='9') || (c2>='A' && c2<='F')) )
|
||||
{
|
||||
if (c1>='0' && c1<='9')
|
||||
c1-='0';
|
||||
else
|
||||
c1-='A'-10;
|
||||
if (c2>='0' && c2<='9')
|
||||
c2-='0';
|
||||
else
|
||||
c2-='A'-10;
|
||||
c = (c1<<4) + c2;
|
||||
i=i+2; //only skip next 2 chars if valid esc
|
||||
}
|
||||
}
|
||||
*outbuf++ = c;
|
||||
}
|
||||
*outbuf++='\0'; //add nullterm to string
|
||||
}
|
||||
|
||||
static int parse_custom_url(const m_option_t *opt, struct bstr name,
|
||||
struct bstr url, void *dst)
|
||||
{
|
||||
|
@ -40,8 +40,6 @@
|
||||
#include "mp_core.h"
|
||||
#include "osdep/priority.h"
|
||||
|
||||
char *network_username=NULL;
|
||||
char *network_password=NULL;
|
||||
int network_bandwidth=0;
|
||||
int network_cookies_enabled = 0;
|
||||
char *network_useragent="MPlayer 1.1-4.7";
|
||||
@ -351,20 +349,11 @@ const m_option_t mp_opts[] = {
|
||||
{"bluray-angle", &bluray_angle, CONF_TYPE_INT, CONF_RANGE, 0, 999, NULL},
|
||||
#endif /* CONFIG_LIBBLURAY */
|
||||
|
||||
{"user", &network_username, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"passwd", &network_password, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"bandwidth", &network_bandwidth, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL},
|
||||
{"http-header-fields", &network_http_header_fields, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
|
||||
{"user-agent", &network_useragent, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"referrer", &network_referrer, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"cookies", &network_cookies_enabled, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"cookies-file", &cookies_file, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"prefer-ipv4", &network_prefer_ipv4, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"ipv4-only-proxy", &network_ipv4_only_proxy, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"reuse-socket", &reuse_socket, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
#ifdef HAVE_AF_INET6
|
||||
{"prefer-ipv6", &network_prefer_ipv4, CONF_TYPE_FLAG, 0, 1, 0, NULL},
|
||||
#endif /* HAVE_AF_INET6 */
|
||||
|
||||
// ------------------------- demuxer options --------------------
|
||||
|
||||
|
@ -268,9 +268,6 @@ typedef struct MPOpts {
|
||||
|
||||
// Should be moved into MPOpts
|
||||
extern char **network_http_header_fields;
|
||||
extern char *network_username;
|
||||
extern char *network_password;
|
||||
extern int network_bandwidth;
|
||||
extern char *network_useragent;
|
||||
extern char *network_referrer;
|
||||
extern int network_cookies_enabled;
|
||||
|
@ -1,686 +0,0 @@
|
||||
/*
|
||||
* MMST implementation taken from the xine-mms plugin made by
|
||||
* Major MMS (http://geocities.com/majormms/).
|
||||
* Ported to MPlayer by Abhijeet Phatak <abhijeetphatak@yahoo.com>.
|
||||
*
|
||||
* Information about the MMS protocol can be found at http://get.to/sdp
|
||||
*
|
||||
* copyright (C) 2002 Abhijeet Phatak <abhijeetphatak@yahoo.com>
|
||||
* copyright (C) 2002 the xine project
|
||||
* copyright (C) 2000-2001 major mms
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "core/options.h"
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ICONV
|
||||
#include <iconv.h>
|
||||
#endif
|
||||
|
||||
#include "url.h"
|
||||
#include "demux/asf.h"
|
||||
|
||||
#include "stream.h"
|
||||
#include "asf_mmst_streaming.h"
|
||||
#include "network.h"
|
||||
#include "tcp.h"
|
||||
|
||||
extern int audio_id;
|
||||
extern int video_id;
|
||||
|
||||
#define BUF_SIZE 102400
|
||||
#define HDR_BUF_SIZE 8192
|
||||
#define MAX_STREAMS 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t buf[BUF_SIZE];
|
||||
int num_bytes;
|
||||
|
||||
} command_t;
|
||||
|
||||
static int seq_num;
|
||||
static int num_stream_ids;
|
||||
static int stream_ids[MAX_STREAMS];
|
||||
|
||||
static int get_data (int s, char *buf, size_t count);
|
||||
|
||||
static void put_32 (command_t *cmd, uint32_t value)
|
||||
{
|
||||
cmd->buf[cmd->num_bytes ] = value % 256;
|
||||
value = value >> 8;
|
||||
cmd->buf[cmd->num_bytes+1] = value % 256 ;
|
||||
value = value >> 8;
|
||||
cmd->buf[cmd->num_bytes+2] = value % 256 ;
|
||||
value = value >> 8;
|
||||
cmd->buf[cmd->num_bytes+3] = value % 256 ;
|
||||
|
||||
cmd->num_bytes += 4;
|
||||
}
|
||||
|
||||
static uint32_t get_32 (unsigned char *cmd, int offset)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = cmd[offset] ;
|
||||
ret |= cmd[offset+1]<<8 ;
|
||||
ret |= cmd[offset+2]<<16 ;
|
||||
ret |= cmd[offset+3]<<24 ;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void send_command (int s, int command, uint32_t switches,
|
||||
uint32_t extra, int length,
|
||||
char *data)
|
||||
{
|
||||
command_t cmd;
|
||||
int len8;
|
||||
|
||||
len8 = (length + 7) / 8;
|
||||
|
||||
cmd.num_bytes = 0;
|
||||
|
||||
put_32 (&cmd, 0x00000001); /* start sequence */
|
||||
put_32 (&cmd, 0xB00BFACE); /* #-)) */
|
||||
put_32 (&cmd, len8*8 + 32);
|
||||
put_32 (&cmd, 0x20534d4d); /* protocol type "MMS " */
|
||||
put_32 (&cmd, len8 + 4);
|
||||
put_32 (&cmd, seq_num);
|
||||
seq_num++;
|
||||
put_32 (&cmd, 0x0); /* unknown */
|
||||
put_32 (&cmd, 0x0);
|
||||
put_32 (&cmd, len8+2);
|
||||
put_32 (&cmd, 0x00030000 | command); /* dir | command */
|
||||
put_32 (&cmd, switches);
|
||||
put_32 (&cmd, extra);
|
||||
|
||||
memcpy (&cmd.buf[48], data, length);
|
||||
if (length & 7)
|
||||
memset(&cmd.buf[48 + length], 0, 8 - (length & 7));
|
||||
|
||||
if (send (s, cmd.buf, len8*8+48, 0) != (len8*8+48)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"write error\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ICONV
|
||||
static iconv_t url_conv;
|
||||
#endif
|
||||
|
||||
static void string_utf16(char *dest, char *src, int len) {
|
||||
int i;
|
||||
#ifdef CONFIG_ICONV
|
||||
size_t len1, len2;
|
||||
char *ip, *op;
|
||||
|
||||
if (url_conv != (iconv_t)(-1))
|
||||
{
|
||||
memset(dest, 0, 1000);
|
||||
len1 = len; len2 = 1000;
|
||||
ip = src; op = dest;
|
||||
|
||||
iconv(url_conv, &ip, &len1, &op, &len2);
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
if (len > 499) len = 499;
|
||||
for (i=0; i<len; i++) {
|
||||
dest[i*2] = src[i];
|
||||
dest[i*2+1] = 0;
|
||||
}
|
||||
/* trailing zeroes */
|
||||
dest[i*2] = 0;
|
||||
dest[i*2+1] = 0;
|
||||
#ifdef CONFIG_ICONV
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void get_answer (int s)
|
||||
{
|
||||
char data[BUF_SIZE];
|
||||
int command = 0x1b;
|
||||
|
||||
while (command == 0x1b) {
|
||||
int len;
|
||||
|
||||
len = recv (s, data, BUF_SIZE, 0) ;
|
||||
if (!len) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"\nAlert! EOF\n");
|
||||
return;
|
||||
}
|
||||
|
||||
command = get_32 (data, 36) & 0xFFFF;
|
||||
|
||||
if (command == 0x1b)
|
||||
send_command (s, 0x1b, 0, 0, 0, data);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_data (int s, char *buf, size_t count)
|
||||
{
|
||||
ssize_t len;
|
||||
size_t total = 0;
|
||||
|
||||
while (total < count) {
|
||||
|
||||
len = recv (s, &buf[total], count-total, 0);
|
||||
|
||||
if (len<=0) {
|
||||
perror ("read error:");
|
||||
return 0;
|
||||
}
|
||||
|
||||
total += len;
|
||||
|
||||
if (len != 0) {
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"[%d/%d]", total, count);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
static int get_header (int s, uint8_t *header, streaming_ctrl_t *streaming_ctrl)
|
||||
{
|
||||
unsigned char pre_header[8];
|
||||
int header_len;
|
||||
|
||||
header_len = 0;
|
||||
|
||||
while (1) {
|
||||
if (!get_data (s, pre_header, 8)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"pre-header read failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (pre_header[4] == 0x02) {
|
||||
|
||||
int packet_len;
|
||||
|
||||
packet_len = (pre_header[7] << 8 | pre_header[6]) - 8;
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"asf header packet detected, len=%d\n", packet_len);
|
||||
|
||||
if (packet_len < 0 || packet_len > HDR_BUF_SIZE - header_len) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Invalid header size, giving up.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!get_data (s, &header[header_len], packet_len)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Header data read failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
header_len += packet_len;
|
||||
|
||||
if ( (header[header_len-1] == 1) && (header[header_len-2]==1)) {
|
||||
|
||||
|
||||
if( streaming_bufferize( streaming_ctrl, header, header_len )<0 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet finished\n");
|
||||
|
||||
return header_len;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int32_t packet_len;
|
||||
int command;
|
||||
char data[BUF_SIZE];
|
||||
|
||||
if (!get_data (s, (char*)&packet_len, 4)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"packet_len read failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
packet_len = get_32 ((unsigned char*)&packet_len, 0) + 4;
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"command packet detected, len=%d\n", packet_len);
|
||||
|
||||
if (packet_len < 0 || packet_len > BUF_SIZE) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_FATAL,
|
||||
"Invalid RTSP packet size, giving up.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!get_data (s, data, packet_len)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Command data read failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
command = get_32 (data, 24) & 0xFFFF;
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"command: %02x\n", command);
|
||||
|
||||
if (command == 0x1b)
|
||||
send_command (s, 0x1b, 0, 0, 0, data);
|
||||
|
||||
}
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"get header packet succ\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int interp_header (uint8_t *header, int header_len)
|
||||
{
|
||||
int i;
|
||||
int packet_length=-1;
|
||||
|
||||
/*
|
||||
* parse header
|
||||
*/
|
||||
|
||||
i = 30;
|
||||
while (i<header_len) {
|
||||
|
||||
uint64_t guid_1, guid_2, length;
|
||||
|
||||
guid_2 = (uint64_t)header[i] | ((uint64_t)header[i+1]<<8)
|
||||
| ((uint64_t)header[i+2]<<16) | ((uint64_t)header[i+3]<<24)
|
||||
| ((uint64_t)header[i+4]<<32) | ((uint64_t)header[i+5]<<40)
|
||||
| ((uint64_t)header[i+6]<<48) | ((uint64_t)header[i+7]<<56);
|
||||
i += 8;
|
||||
|
||||
guid_1 = (uint64_t)header[i] | ((uint64_t)header[i+1]<<8)
|
||||
| ((uint64_t)header[i+2]<<16) | ((uint64_t)header[i+3]<<24)
|
||||
| ((uint64_t)header[i+4]<<32) | ((uint64_t)header[i+5]<<40)
|
||||
| ((uint64_t)header[i+6]<<48) | ((uint64_t)header[i+7]<<56);
|
||||
i += 8;
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"guid found: %016llx%016llx\n", guid_1, guid_2);
|
||||
|
||||
length = (uint64_t)header[i] | ((uint64_t)header[i+1]<<8)
|
||||
| ((uint64_t)header[i+2]<<16) | ((uint64_t)header[i+3]<<24)
|
||||
| ((uint64_t)header[i+4]<<32) | ((uint64_t)header[i+5]<<40)
|
||||
| ((uint64_t)header[i+6]<<48) | ((uint64_t)header[i+7]<<56);
|
||||
|
||||
i += 8;
|
||||
|
||||
if ( (guid_1 == 0x6cce6200aa00d9a6ULL) && (guid_2 == 0x11cf668e75b22630ULL) ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"header object\n");
|
||||
} else if ((guid_1 == 0x6cce6200aa00d9a6ULL) && (guid_2 == 0x11cf668e75b22636ULL)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"data object\n");
|
||||
} else if ((guid_1 == 0x6553200cc000e48eULL) && (guid_2 == 0x11cfa9478cabdca1ULL)) {
|
||||
|
||||
packet_length = get_32(header, i+92-24);
|
||||
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"file object, packet length = %d (%d)\n",
|
||||
packet_length, get_32(header, i+96-24));
|
||||
|
||||
|
||||
} else if ((guid_1 == 0x6553200cc000e68eULL) && (guid_2 == 0x11cfa9b7b7dc0791ULL)) {
|
||||
|
||||
int stream_id = header[i+48] | header[i+49] << 8;
|
||||
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"stream object, stream ID: %d\n", stream_id);
|
||||
|
||||
if (num_stream_ids < MAX_STREAMS) {
|
||||
stream_ids[num_stream_ids] = stream_id;
|
||||
num_stream_ids++;
|
||||
} else {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Too many IDs, stream skipped.");
|
||||
}
|
||||
|
||||
} else {
|
||||
#if 0
|
||||
int b = i;
|
||||
printf ("unknown object (guid: %016llx, %016llx, len: %lld)\n", guid_1, guid_2, length);
|
||||
for (; b < length; b++)
|
||||
{
|
||||
if (isascii(header[b]) || isalpha(header[b]))
|
||||
printf("%c ", header[b]);
|
||||
else
|
||||
printf("%x ", header[b]);
|
||||
}
|
||||
printf("\n");
|
||||
#else
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_WARN,"unknown object\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"length : %lld\n", length);
|
||||
|
||||
i += length-24;
|
||||
|
||||
}
|
||||
|
||||
return packet_length;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int get_media_packet (int s, int padding, streaming_ctrl_t *stream_ctrl) {
|
||||
unsigned char pre_header[8];
|
||||
char data[BUF_SIZE];
|
||||
|
||||
if (!get_data (s, pre_header, 8)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"pre-header read failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for (i=0; i<8; i++)
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"pre_header[%d] = %02x (%d)\n",
|
||||
// i, pre_header[i], pre_header[i]);
|
||||
|
||||
if (pre_header[4] == 0x04) {
|
||||
|
||||
int packet_len;
|
||||
|
||||
packet_len = (pre_header[7] << 8 | pre_header[6]) - 8;
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"asf media packet detected, len=%d\n", packet_len);
|
||||
|
||||
if (packet_len < 0 || packet_len > BUF_SIZE) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Invalid RTSP packet size, giving up.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!get_data (s, data, packet_len)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Media data read failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
streaming_bufferize(stream_ctrl, data, padding);
|
||||
|
||||
} else {
|
||||
|
||||
int32_t packet_len;
|
||||
int command;
|
||||
|
||||
if (!get_data (s, (char*)&packet_len, 4)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"packet_len read failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
packet_len = get_32 ((unsigned char*)&packet_len, 0) + 4;
|
||||
|
||||
if (packet_len < 0 || packet_len > BUF_SIZE) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Invalid RTSP packet size, giving up.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!get_data (s, data, packet_len)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Command data read failed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( (pre_header[7] != 0xb0) || (pre_header[6] != 0x0b)
|
||||
|| (pre_header[5] != 0xfa) || (pre_header[4] != 0xce) ) {
|
||||
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"missing signature\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
command = get_32 (data, 24) & 0xFFFF;
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"\ncommand packet detected, len=%d cmd=0x%X\n", packet_len, command);
|
||||
|
||||
if (command == 0x1b)
|
||||
send_command (s, 0x1b, 0, 0, 0, data);
|
||||
else if (command == 0x1e) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"Everything done. Thank you for downloading a media file containing proprietary and patented technology.\n");
|
||||
return 0;
|
||||
}
|
||||
else if (command == 0x21 ) {
|
||||
// Looks like it's new in WMS9
|
||||
// Unknown command, but ignoring it seems to work.
|
||||
return 0;
|
||||
}
|
||||
else if (command != 0x05) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"unknown command %02x\n",command);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"get media packet succ\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int packet_length1;
|
||||
|
||||
static int asf_mmst_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl )
|
||||
{
|
||||
int len;
|
||||
|
||||
while( stream_ctrl->buffer_size==0 ) {
|
||||
// buffer is empty - fill it!
|
||||
int ret = get_media_packet( fd, packet_length1, stream_ctrl);
|
||||
if( ret<0 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"get_media_packet error : %s\n",strerror(errno));
|
||||
return -1;
|
||||
} else if (ret==0) //EOF?
|
||||
return ret;
|
||||
}
|
||||
|
||||
len = stream_ctrl->buffer_size-stream_ctrl->buffer_pos;
|
||||
if(len>size) len=size;
|
||||
memcpy( buffer, (stream_ctrl->buffer)+(stream_ctrl->buffer_pos), len );
|
||||
stream_ctrl->buffer_pos += len;
|
||||
if( stream_ctrl->buffer_pos>=stream_ctrl->buffer_size ) {
|
||||
free( stream_ctrl->buffer );
|
||||
stream_ctrl->buffer = NULL;
|
||||
stream_ctrl->buffer_size = 0;
|
||||
stream_ctrl->buffer_pos = 0;
|
||||
}
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
static int asf_mmst_streaming_seek( int fd, int64_t pos, streaming_ctrl_t *streaming_ctrl )
|
||||
{
|
||||
return -1;
|
||||
// Shut up gcc warning
|
||||
fd++;
|
||||
pos++;
|
||||
streaming_ctrl=NULL;
|
||||
}
|
||||
|
||||
int asf_mmst_streaming_start(stream_t *stream)
|
||||
{
|
||||
char str[1024];
|
||||
char data[BUF_SIZE];
|
||||
uint8_t asf_header[HDR_BUF_SIZE];
|
||||
int asf_header_len;
|
||||
int i, packet_length;
|
||||
char *path, *unescpath;
|
||||
URL_t *url1 = stream->streaming_ctrl->url;
|
||||
int s = stream->fd;
|
||||
|
||||
if( s>0 ) {
|
||||
closesocket( stream->fd );
|
||||
stream->fd = -1;
|
||||
}
|
||||
|
||||
/* parse url */
|
||||
path = strchr(url1->file,'/') + 1;
|
||||
|
||||
/* mmst filename are not url_escaped by MS MediaPlayer and are expected as
|
||||
* "plain text" by the server, so need to decode it here
|
||||
*/
|
||||
unescpath=malloc(strlen(path)+1);
|
||||
if (!unescpath) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
url_unescape_string(unescpath,path);
|
||||
path=unescpath;
|
||||
|
||||
|
||||
if( url1->port==0 ) {
|
||||
url1->port=1755;
|
||||
}
|
||||
s = connect2Server( url1->hostname, url1->port, 1);
|
||||
if( s<0 ) {
|
||||
free(path);
|
||||
return s;
|
||||
}
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"Connected\n");
|
||||
|
||||
seq_num=0;
|
||||
|
||||
/*
|
||||
* Send the initial connect info including player version no. Client GUID (random) and the host address being connected to.
|
||||
* This command is sent at the very start of protocol initiation. It sends local information to the serve
|
||||
* cmd 1 0x01
|
||||
* */
|
||||
|
||||
/* prepare for the url encoding conversion */
|
||||
#ifdef CONFIG_ICONV
|
||||
url_conv = iconv_open("UTF-16LE", "UTF-8");
|
||||
#endif
|
||||
|
||||
snprintf (str, 1023, "\034\003NSPlayer/7.0.0.1956; {33715801-BAB3-9D85-24E9-03B90328270A}; Host: %s", url1->hostname);
|
||||
string_utf16 (data, str, strlen(str));
|
||||
// send_command(s, commandno ....)
|
||||
send_command (s, 1, 0, 0x0004000b, strlen(str)*2+2, data);
|
||||
|
||||
recv (s, data, BUF_SIZE, 0) ;
|
||||
|
||||
/*This sends details of the local machine IP address to a Funnel system at the server.
|
||||
* Also, the TCP or UDP transport selection is sent.
|
||||
*
|
||||
* here 192.168.0.1 is local ip address TCP/UDP states the tronsport we r using
|
||||
* and 1037 is the local TCP or UDP socket number
|
||||
* cmd 2 0x02
|
||||
* */
|
||||
|
||||
string_utf16 (&data[8], "\002\000\\\\192.168.0.1\\TCP\\1037", 24);
|
||||
memset (data, 0, 8);
|
||||
send_command (s, 2, 0, 0, 24*2+10, data);
|
||||
|
||||
recv (s, data, BUF_SIZE, 0) ;
|
||||
|
||||
/* This command sends file path (at server) and file name request to the server.
|
||||
* 0x5 */
|
||||
|
||||
string_utf16 (&data[8], path, strlen(path));
|
||||
memset (data, 0, 8);
|
||||
send_command (s, 5, 0, 0, strlen(path)*2+10, data);
|
||||
free(path);
|
||||
|
||||
get_answer (s);
|
||||
|
||||
/* The ASF header chunk request. Includes ?session' variable for pre header value.
|
||||
* After this command is sent,
|
||||
* the server replies with 0x11 command and then the header chunk with header data follows.
|
||||
* 0x15 */
|
||||
|
||||
memset (data, 0, 40);
|
||||
data[32] = 2;
|
||||
|
||||
send_command (s, 0x15, 1, 0, 40, data);
|
||||
|
||||
num_stream_ids = 0;
|
||||
/* get_headers(s, asf_header); */
|
||||
|
||||
asf_header_len = get_header (s, asf_header, stream->streaming_ctrl);
|
||||
// mp_msg(MSGT_NETWORK,MSGL_INFO,"---------------------------------- asf_header %d\n",asf_header);
|
||||
if (asf_header_len==0) { //error reading header
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
packet_length = interp_header (asf_header, asf_header_len);
|
||||
|
||||
|
||||
/*
|
||||
* This command is the media stream MBR selector. Switches are always 6 bytes in length.
|
||||
* After all switch elements, data ends with bytes [00 00] 00 20 ac 40 [02].
|
||||
* Where:
|
||||
* [00 00] shows 0x61 0x00 (on the first 33 sent) or 0xff 0xff for ASF files, and with no ending data for WMV files.
|
||||
* It is not yet understood what all this means.
|
||||
* And the last [02] byte is probably the header ?session' value.
|
||||
*
|
||||
* 0x33 */
|
||||
|
||||
memset (data, 0, 40);
|
||||
|
||||
int audio_id = stream->opts->audio_id;
|
||||
if (audio_id > 0) {
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0xFF;
|
||||
data[4] = audio_id;
|
||||
send_command(s, 0x33, num_stream_ids, 0xFFFF | audio_id << 16, 8, data);
|
||||
} else {
|
||||
for (i=1; i<num_stream_ids; i++) {
|
||||
data [ (i-1) * 6 + 2 ] = 0xFF;
|
||||
data [ (i-1) * 6 + 3 ] = 0xFF;
|
||||
data [ (i-1) * 6 + 4 ] = stream_ids[i];
|
||||
data [ (i-1) * 6 + 5 ] = 0x00;
|
||||
}
|
||||
|
||||
send_command (s, 0x33, num_stream_ids, 0xFFFF | stream_ids[0] << 16, (num_stream_ids-1)*6+2 , data);
|
||||
}
|
||||
|
||||
get_answer (s);
|
||||
|
||||
/* Start sending file from packet xx.
|
||||
* This command is also used for resume downloads or requesting a lost packet.
|
||||
* Also used for seeking by sending a play point value which seeks to the media time point.
|
||||
* Includes ?session' value in pre header and the maximum media stream time.
|
||||
* 0x07 */
|
||||
|
||||
memset (data, 0, 40);
|
||||
|
||||
for (i=8; i<16; i++)
|
||||
data[i] = 0xFF;
|
||||
|
||||
data[20] = 0x04;
|
||||
|
||||
send_command (s, 0x07, 1, 0xFFFF | stream_ids[0] << 16, 24, data);
|
||||
|
||||
stream->fd = s;
|
||||
stream->streaming_ctrl->streaming_read = asf_mmst_streaming_read;
|
||||
stream->streaming_ctrl->streaming_seek = asf_mmst_streaming_seek;
|
||||
stream->streaming_ctrl->status = streaming_playing_e;
|
||||
stream->streaming = true;
|
||||
|
||||
packet_length1 = packet_length;
|
||||
mp_msg(MSGT_NETWORK,MSGL_INFO,"mmst packet_length = %d\n", packet_length);
|
||||
|
||||
#ifdef CONFIG_ICONV
|
||||
if (url_conv != (iconv_t)(-1))
|
||||
iconv_close(url_conv);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_ASF_MMST_STREAMING_H
|
||||
#define MPLAYER_ASF_MMST_STREAMING_H
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
int asf_mmst_streaming_start(stream_t *stream);
|
||||
|
||||
#endif /* MPLAYER_ASF_MMST_STREAMING_H */
|
@ -1,855 +0,0 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <libavutil/intreadwrite.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "core/options.h"
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include "url.h"
|
||||
#include "http.h"
|
||||
#include "demux/asf.h"
|
||||
|
||||
#include "stream.h"
|
||||
#include "demux/demux.h"
|
||||
#include "asf_mmst_streaming.h"
|
||||
#include "network.h"
|
||||
#include "tcp.h"
|
||||
|
||||
#include "demux/asfguid.h"
|
||||
|
||||
static int asf_http_streaming_start(stream_t *stream, int *demuxer_type);
|
||||
|
||||
static int asf_read_wrapper(int fd, void *buffer, int len, streaming_ctrl_t *stream_ctrl) {
|
||||
uint8_t *buf = buffer;
|
||||
while (len > 0) {
|
||||
int got = nop_streaming_read(fd, buf, len, stream_ctrl);
|
||||
if (got <= 0) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_ERR, "Error while reading network stream.\n");
|
||||
return got;
|
||||
}
|
||||
buf += got;
|
||||
len -= got;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We can try several protocol for asf streaming
|
||||
// * First we can try TCP, but if there is a proxy for
|
||||
// internet connection, the TCP connection will not get
|
||||
// through
|
||||
// * Then we can try HTTP.
|
||||
//
|
||||
// Note: Using WMP sequence MMST and then HTTP.
|
||||
|
||||
static int asf_streaming_start( stream_t *stream, int *demuxer_type) {
|
||||
char *proto = stream->streaming_ctrl->url->protocol;
|
||||
int fd = -1;
|
||||
int port = stream->streaming_ctrl->url->port;
|
||||
|
||||
//Is protocol mms or mmst?
|
||||
if (!strcasecmp(proto, "mp_mmst") || !strcasecmp(proto, "mp_mms"))
|
||||
{
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/TCP...\n");
|
||||
fd = asf_mmst_streaming_start( stream );
|
||||
stream->streaming_ctrl->url->port = port;
|
||||
if( fd>-1 ) return fd;
|
||||
mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/TCP failed\n");
|
||||
if( fd==-2 ) return -1;
|
||||
}
|
||||
|
||||
//Is protocol http, http_proxy, or mms?
|
||||
if (!strcasecmp(proto, "mp_http_proxy") || !strcasecmp(proto, "mp_http") ||
|
||||
!strcasecmp(proto, "mp_mms") || !strcasecmp(proto, "mp_mmsh") ||
|
||||
!strcasecmp(proto, "mp_mmshttp"))
|
||||
{
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/HTTP...\n");
|
||||
fd = asf_http_streaming_start( stream, demuxer_type );
|
||||
stream->streaming_ctrl->url->port = port;
|
||||
if( fd>-1 ) return fd;
|
||||
mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/HTTP failed\n");
|
||||
if( fd==-2 ) return -1;
|
||||
}
|
||||
|
||||
//everything failed
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int asf_streaming(ASF_stream_chunck_t *stream_chunck, int *drop_packet ) {
|
||||
/*
|
||||
printf("ASF stream chunck size=%d\n", stream_chunck->size);
|
||||
printf("length: %d\n", length );
|
||||
printf("0x%02X\n", stream_chunck->type );
|
||||
*/
|
||||
if( drop_packet!=NULL ) *drop_packet = 0;
|
||||
|
||||
if( stream_chunck->size<8 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Ahhhh, stream_chunck size is too small: %d\n", stream_chunck->size);
|
||||
return -1;
|
||||
}
|
||||
if( stream_chunck->size!=stream_chunck->size_confirm ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"size_confirm mismatch!: %d %d\n", stream_chunck->size, stream_chunck->size_confirm);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
printf(" type: 0x%02X\n", stream_chunck->type );
|
||||
printf(" size: %d (0x%02X)\n", stream_chunck->size, stream_chunck->size );
|
||||
printf(" sequence_number: 0x%04X\n", stream_chunck->sequence_number );
|
||||
printf(" unknown: 0x%02X\n", stream_chunck->unknown );
|
||||
printf(" size_confirm: 0x%02X\n", stream_chunck->size_confirm );
|
||||
*/
|
||||
switch(stream_chunck->type) {
|
||||
case ASF_STREAMING_CLEAR: // $C Clear ASF configuration
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> Clearing ASF stream configuration!\n");
|
||||
if( drop_packet!=NULL ) *drop_packet = 1;
|
||||
return stream_chunck->size;
|
||||
break;
|
||||
case ASF_STREAMING_DATA: // $D Data follows
|
||||
// printf("=====> Data follows\n");
|
||||
break;
|
||||
case ASF_STREAMING_END_TRANS: // $E Transfer complete
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> Transfer complete\n");
|
||||
if( drop_packet!=NULL ) *drop_packet = 1;
|
||||
return stream_chunck->size;
|
||||
break;
|
||||
case ASF_STREAMING_HEADER: // $H ASF header chunk follows
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF header chunk follows\n");
|
||||
break;
|
||||
default:
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> Unknown stream type 0x%x\n", stream_chunck->type );
|
||||
}
|
||||
return stream_chunck->size+4;
|
||||
}
|
||||
|
||||
static void close_s(stream_t *stream) {
|
||||
closesocket(stream->fd);
|
||||
stream->fd=-1;
|
||||
}
|
||||
|
||||
static int max_idx(int s_count, int *s_rates, int bound) {
|
||||
int i, best = -1, rate = -1;
|
||||
for (i = 0; i < s_count; i++) {
|
||||
if (s_rates[i] > rate && s_rates[i] <= bound) {
|
||||
rate = s_rates[i];
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
static int asf_streaming_parse_header(stream_t *s, int fd) {
|
||||
streaming_ctrl_t* streaming_ctrl = s->streaming_ctrl;
|
||||
ASF_stream_chunck_t chunk;
|
||||
asf_http_streaming_ctrl_t* asf_ctrl = streaming_ctrl->data;
|
||||
char* buffer=NULL, *chunk_buffer=NULL;
|
||||
int i,r,size,pos = 0;
|
||||
int start;
|
||||
int buffer_size = 0;
|
||||
int chunk_size2read = 0;
|
||||
int bw = streaming_ctrl->bandwidth;
|
||||
int *v_rates = NULL, *a_rates = NULL;
|
||||
int v_rate = 0, a_rate = 0, a_idx = -1, v_idx = -1;
|
||||
|
||||
if(asf_ctrl == NULL) return -1;
|
||||
|
||||
// The ASF header can be in several network chunks. For example if the content description
|
||||
// is big, the ASF header will be split in 2 network chunk.
|
||||
// So we need to retrieve all the chunk before starting to parse the header.
|
||||
do {
|
||||
if (asf_read_wrapper(fd, &chunk, sizeof(ASF_stream_chunck_t), streaming_ctrl) <= 0)
|
||||
return -1;
|
||||
// Endian handling of the stream chunk
|
||||
le2me_ASF_stream_chunck_t(&chunk);
|
||||
size = asf_streaming( &chunk, &r) - sizeof(ASF_stream_chunck_t);
|
||||
if(r) mp_tmsg(MSGT_NETWORK,MSGL_WARN,"Warning: drop header ????\n");
|
||||
if(size < 0){
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error while parsing chunk header\n");
|
||||
return -1;
|
||||
}
|
||||
if (chunk.type != ASF_STREAMING_HEADER) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Didn't get a header as first chunk !!!!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// audit: do not overflow buffer_size
|
||||
if (size > SIZE_MAX - buffer_size) return -1;
|
||||
buffer = malloc(size+buffer_size);
|
||||
if(buffer == NULL) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Error: Can't allocate %d bytes buffer.\n",size+buffer_size);
|
||||
return -1;
|
||||
}
|
||||
if( chunk_buffer!=NULL ) {
|
||||
memcpy( buffer, chunk_buffer, buffer_size );
|
||||
free( chunk_buffer );
|
||||
}
|
||||
chunk_buffer = buffer;
|
||||
buffer += buffer_size;
|
||||
buffer_size += size;
|
||||
|
||||
if (asf_read_wrapper(fd, buffer, size, streaming_ctrl) <= 0)
|
||||
return -1;
|
||||
|
||||
if( chunk_size2read==0 ) {
|
||||
ASF_header_t *asfh = (ASF_header_t *)buffer;
|
||||
if(size < (int)sizeof(ASF_header_t)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error: Chunk is too small.\n");
|
||||
return -1;
|
||||
} else mp_msg(MSGT_NETWORK,MSGL_DBG2,"Got chunk\n");
|
||||
chunk_size2read = AV_RL64(&asfh->objh.size);
|
||||
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Size 2 read=%d\n", chunk_size2read);
|
||||
}
|
||||
} while( buffer_size<chunk_size2read);
|
||||
buffer = chunk_buffer;
|
||||
size = buffer_size;
|
||||
|
||||
start = sizeof(ASF_header_t);
|
||||
|
||||
pos = find_asf_guid(buffer, asf_file_header_guid, start, size);
|
||||
if (pos >= 0) {
|
||||
ASF_file_header_t *fileh = (ASF_file_header_t *) &buffer[pos];
|
||||
pos += sizeof(ASF_file_header_t);
|
||||
if (pos > size) goto len_err_out;
|
||||
/*
|
||||
if(fileh.packetsize != fileh.packetsize2) {
|
||||
printf("Error packetsize check don't match\n");
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
asf_ctrl->packet_size = AV_RL32(&fileh->max_packet_size);
|
||||
// before playing.
|
||||
// preroll: time in ms to bufferize before playing
|
||||
unsigned int preroll = (unsigned int)(((double)fileh->preroll/1000.0)*((double)fileh->max_bitrate/8.0));
|
||||
// buffer in KBytes, *5 assuming the prefill is 20% of the buffer.
|
||||
s->cache_size = preroll / 1024 * 5;
|
||||
}
|
||||
|
||||
pos = start;
|
||||
while ((pos = find_asf_guid(buffer, asf_stream_header_guid, pos, size)) >= 0)
|
||||
{
|
||||
ASF_stream_header_t *streamh = (ASF_stream_header_t *)&buffer[pos];
|
||||
pos += sizeof(ASF_stream_header_t);
|
||||
if (pos > size) goto len_err_out;
|
||||
switch(ASF_LOAD_GUID_PREFIX(streamh->type)) {
|
||||
case 0xF8699E40 : // audio stream
|
||||
if(asf_ctrl->audio_streams == NULL){
|
||||
asf_ctrl->audio_streams = malloc(sizeof(int));
|
||||
asf_ctrl->n_audio = 1;
|
||||
} else {
|
||||
asf_ctrl->n_audio++;
|
||||
asf_ctrl->audio_streams = realloc(asf_ctrl->audio_streams,
|
||||
asf_ctrl->n_audio*sizeof(int));
|
||||
}
|
||||
asf_ctrl->audio_streams[asf_ctrl->n_audio-1] = AV_RL16(&streamh->stream_no);
|
||||
break;
|
||||
case 0xBC19EFC0 : // video stream
|
||||
if(asf_ctrl->video_streams == NULL){
|
||||
asf_ctrl->video_streams = malloc(sizeof(int));
|
||||
asf_ctrl->n_video = 1;
|
||||
} else {
|
||||
asf_ctrl->n_video++;
|
||||
asf_ctrl->video_streams = realloc(asf_ctrl->video_streams,
|
||||
asf_ctrl->n_video*sizeof(int));
|
||||
}
|
||||
asf_ctrl->video_streams[asf_ctrl->n_video-1] = AV_RL16(&streamh->stream_no);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// always allocate to avoid lots of ifs later
|
||||
v_rates = calloc(asf_ctrl->n_video, sizeof(int));
|
||||
a_rates = calloc(asf_ctrl->n_audio, sizeof(int));
|
||||
|
||||
pos = find_asf_guid(buffer, asf_stream_group_guid, start, size);
|
||||
if (pos >= 0) {
|
||||
// stream bitrate properties object
|
||||
int stream_count;
|
||||
char *ptr = &buffer[pos];
|
||||
char *end = &buffer[size];
|
||||
|
||||
mp_msg(MSGT_NETWORK, MSGL_V, "Stream bitrate properties object\n");
|
||||
if (ptr + 2 > end) goto len_err_out;
|
||||
stream_count = AV_RL16(ptr);
|
||||
ptr += 2;
|
||||
mp_msg(MSGT_NETWORK, MSGL_V, " stream count=[0x%x][%u]\n",
|
||||
stream_count, stream_count );
|
||||
for( i=0 ; i<stream_count ; i++ ) {
|
||||
uint32_t rate;
|
||||
int id;
|
||||
int j;
|
||||
if (ptr + 6 > end) goto len_err_out;
|
||||
id = AV_RL16(ptr);
|
||||
ptr += 2;
|
||||
rate = AV_RL32(ptr);
|
||||
ptr += 4;
|
||||
mp_msg(MSGT_NETWORK, MSGL_V,
|
||||
" stream id=[0x%x][%u]\n", id, id);
|
||||
mp_msg(MSGT_NETWORK, MSGL_V,
|
||||
" max bitrate=[0x%x][%u]\n", rate, rate);
|
||||
for (j = 0; j < asf_ctrl->n_video; j++) {
|
||||
if (id == asf_ctrl->video_streams[j]) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_V, " is video stream\n");
|
||||
v_rates[j] = rate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < asf_ctrl->n_audio; j++) {
|
||||
if (id == asf_ctrl->audio_streams[j]) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_V, " is audio stream\n");
|
||||
a_rates[j] = rate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
// automatic stream selection based on bandwidth
|
||||
if (bw == 0) bw = INT_MAX;
|
||||
mp_msg(MSGT_NETWORK, MSGL_V, "Max bandwidth set to %d\n", bw);
|
||||
|
||||
if (asf_ctrl->n_audio) {
|
||||
// find lowest-bitrate audio stream
|
||||
a_rate = a_rates[0];
|
||||
a_idx = 0;
|
||||
for (i = 0; i < asf_ctrl->n_audio; i++) {
|
||||
if (a_rates[i] < a_rate) {
|
||||
a_rate = a_rates[i];
|
||||
a_idx = i;
|
||||
}
|
||||
}
|
||||
if (max_idx(asf_ctrl->n_video, v_rates, bw - a_rate) < 0) {
|
||||
// both audio and video are not possible, try video only next
|
||||
a_idx = -1;
|
||||
a_rate = 0;
|
||||
}
|
||||
}
|
||||
// find best video stream
|
||||
v_idx = max_idx(asf_ctrl->n_video, v_rates, bw - a_rate);
|
||||
if (v_idx >= 0)
|
||||
v_rate = v_rates[v_idx];
|
||||
|
||||
// find best audio stream
|
||||
a_idx = max_idx(asf_ctrl->n_audio, a_rates, bw - v_rate);
|
||||
|
||||
free(v_rates);
|
||||
free(a_rates);
|
||||
|
||||
if (a_idx < 0 && v_idx < 0) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Bandwidth too small, file cannot be played!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*streaming_ctrl->audio_id_ptr > 0)
|
||||
// a audio stream was forced
|
||||
asf_ctrl->audio_id = *streaming_ctrl->audio_id_ptr;
|
||||
else if (a_idx >= 0)
|
||||
asf_ctrl->audio_id = asf_ctrl->audio_streams[a_idx];
|
||||
else if (asf_ctrl->n_audio) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_WARN, "Bandwidth too small, deselected audio stream.\n");
|
||||
*streaming_ctrl->audio_id_ptr = -2;
|
||||
}
|
||||
|
||||
if (*streaming_ctrl->video_id_ptr > 0)
|
||||
// a video stream was forced
|
||||
asf_ctrl->video_id = *streaming_ctrl->video_id_ptr;
|
||||
else if (v_idx >= 0)
|
||||
asf_ctrl->video_id = asf_ctrl->video_streams[v_idx];
|
||||
else if (asf_ctrl->n_video) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_WARN, "Bandwidth too small, deselected video stream.\n");
|
||||
*streaming_ctrl->video_id_ptr = -2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
len_err_out:
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Invalid length in ASF header!\n");
|
||||
free(buffer);
|
||||
free(v_rates);
|
||||
free(a_rates);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int asf_http_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *streaming_ctrl ) {
|
||||
static ASF_stream_chunck_t chunk;
|
||||
int read,chunk_size = 0;
|
||||
static int rest = 0, drop_chunk = 0, waiting = 0;
|
||||
asf_http_streaming_ctrl_t *asf_http_ctrl = (asf_http_streaming_ctrl_t*)streaming_ctrl->data;
|
||||
|
||||
while(1) {
|
||||
if (rest == 0 && waiting == 0) {
|
||||
if (asf_read_wrapper(fd, &chunk, sizeof(ASF_stream_chunck_t), streaming_ctrl) <= 0)
|
||||
return -1;
|
||||
|
||||
// Endian handling of the stream chunk
|
||||
le2me_ASF_stream_chunck_t(&chunk);
|
||||
chunk_size = asf_streaming( &chunk, &drop_chunk );
|
||||
if(chunk_size < 0) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error while parsing chunk header\n");
|
||||
return -1;
|
||||
}
|
||||
chunk_size -= sizeof(ASF_stream_chunck_t);
|
||||
|
||||
if(chunk.type != ASF_STREAMING_HEADER && (!drop_chunk)) {
|
||||
if (asf_http_ctrl->packet_size < chunk_size) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error: chunk_size > packet_size\n");
|
||||
return -1;
|
||||
}
|
||||
waiting = asf_http_ctrl->packet_size;
|
||||
} else {
|
||||
waiting = chunk_size;
|
||||
}
|
||||
|
||||
} else if (rest){
|
||||
chunk_size = rest;
|
||||
rest = 0;
|
||||
}
|
||||
|
||||
read = 0;
|
||||
if ( waiting >= chunk_size) {
|
||||
if (chunk_size > size){
|
||||
rest = chunk_size - size;
|
||||
chunk_size = size;
|
||||
}
|
||||
if (asf_read_wrapper(fd, buffer, chunk_size, streaming_ctrl) <= 0)
|
||||
return -1;
|
||||
read = chunk_size;
|
||||
waiting -= read;
|
||||
if (drop_chunk) continue;
|
||||
}
|
||||
if (rest == 0 && waiting > 0 && size-read > 0) {
|
||||
int s = FFMIN(waiting,size-read);
|
||||
memset(buffer+read,0,s);
|
||||
waiting -= s;
|
||||
read += s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
static int asf_http_streaming_seek( int fd, int64_t pos, streaming_ctrl_t *streaming_ctrl ) {
|
||||
return -1;
|
||||
// to shut up gcc warning
|
||||
fd++;
|
||||
pos++;
|
||||
streaming_ctrl=NULL;
|
||||
}
|
||||
|
||||
static int asf_header_check( HTTP_header_t *http_hdr ) {
|
||||
ASF_obj_header_t *objh;
|
||||
if( http_hdr==NULL ) return -1;
|
||||
if( http_hdr->body==NULL || http_hdr->body_size<sizeof(ASF_obj_header_t) ) return -1;
|
||||
|
||||
objh = (ASF_obj_header_t*)http_hdr->body;
|
||||
if( ASF_LOAD_GUID_PREFIX(objh->guid)==0x75B22630 ) return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int asf_http_streaming_type(char *content_type, char *features, HTTP_header_t *http_hdr ) {
|
||||
if( content_type==NULL ) return ASF_Unknown_e;
|
||||
if( !strcasecmp(content_type, "application/octet-stream") ||
|
||||
!strcasecmp(content_type, "application/vnd.ms.wms-hdr.asfv1") || // New in Corona, first request
|
||||
!strcasecmp(content_type, "application/x-mms-framed") || // New in Corana, second request
|
||||
!strcasecmp(content_type, "video/x-ms-wmv") ||
|
||||
!strcasecmp(content_type, "video/x-ms-asf")) {
|
||||
|
||||
if( strstr(features, "broadcast") ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Live stream\n");
|
||||
return ASF_Live_e;
|
||||
} else {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Prerecorded\n");
|
||||
return ASF_Prerecorded_e;
|
||||
}
|
||||
} else {
|
||||
// Ok in a perfect world, web servers should be well configured
|
||||
// so we could used mime type to know the stream type,
|
||||
// but guess what? All of them are not well configured.
|
||||
// So we have to check for an asf header :(, but it works :p
|
||||
if( http_hdr->body_size>sizeof(ASF_obj_header_t) ) {
|
||||
if( asf_header_check( http_hdr )==0 ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Plain text\n");
|
||||
return ASF_PlainText_e;
|
||||
} else if( (!strcasecmp(content_type, "text/html")) ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> HTML, MPlayer is not a browser...yet!\n");
|
||||
return ASF_Unknown_e;
|
||||
} else {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Redirector\n");
|
||||
return ASF_Redirector_e;
|
||||
}
|
||||
} else {
|
||||
if( (!strcasecmp(content_type, "audio/x-ms-wax")) ||
|
||||
(!strcasecmp(content_type, "audio/x-ms-wma")) ||
|
||||
(!strcasecmp(content_type, "video/x-ms-asf")) ||
|
||||
(!strcasecmp(content_type, "video/x-ms-afs")) ||
|
||||
(!strcasecmp(content_type, "video/x-ms-wmv")) ||
|
||||
(!strcasecmp(content_type, "video/x-ms-wma")) ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"=====> ASF Redirector\n");
|
||||
return ASF_Redirector_e;
|
||||
} else if( !strcasecmp(content_type, "text/plain") ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Plain text\n");
|
||||
return ASF_PlainText_e;
|
||||
} else {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF unknown content-type: %s\n", content_type );
|
||||
return ASF_Unknown_e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ASF_Unknown_e;
|
||||
}
|
||||
|
||||
static HTTP_header_t *asf_http_request(streaming_ctrl_t *streaming_ctrl) {
|
||||
HTTP_header_t *http_hdr;
|
||||
URL_t *url = NULL;
|
||||
URL_t *server_url = NULL;
|
||||
asf_http_streaming_ctrl_t *asf_http_ctrl;
|
||||
char str[250];
|
||||
char *ptr;
|
||||
int i, enable;
|
||||
|
||||
int offset_hi=0, offset_lo=0, length=0;
|
||||
int asf_nb_stream=0, stream_id;
|
||||
|
||||
// Sanity check
|
||||
if( streaming_ctrl==NULL ) return NULL;
|
||||
url = streaming_ctrl->url;
|
||||
asf_http_ctrl = (asf_http_streaming_ctrl_t*)streaming_ctrl->data;
|
||||
if( url==NULL || asf_http_ctrl==NULL ) return NULL;
|
||||
|
||||
// Common header for all requests.
|
||||
http_hdr = http_new_header();
|
||||
http_set_field( http_hdr, "Accept: */*" );
|
||||
http_set_field( http_hdr, "User-Agent: NSPlayer/4.1.0.3856" );
|
||||
http_add_basic_authentication( http_hdr, url->username, url->password );
|
||||
|
||||
// Check if we are using a proxy
|
||||
if( !strcasecmp( url->protocol, "mp_http_proxy" ) ) {
|
||||
server_url = url_new( (url->file)+1 );
|
||||
if( server_url==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"invalid proxy URL\n");
|
||||
http_free( http_hdr );
|
||||
return NULL;
|
||||
}
|
||||
http_set_uri( http_hdr, server_url->url );
|
||||
sprintf( str, "Host: %.220s:%d", server_url->hostname, server_url->port );
|
||||
url_free( server_url );
|
||||
} else {
|
||||
http_set_uri( http_hdr, url->file );
|
||||
sprintf( str, "Host: %.220s:%d", url->hostname, url->port );
|
||||
}
|
||||
|
||||
http_set_field( http_hdr, str );
|
||||
http_set_field( http_hdr, "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}" );
|
||||
sprintf(str,
|
||||
"Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=%u",
|
||||
offset_hi, offset_lo, asf_http_ctrl->request, length );
|
||||
http_set_field( http_hdr, str );
|
||||
|
||||
switch( asf_http_ctrl->streaming_type ) {
|
||||
case ASF_Live_e:
|
||||
case ASF_Prerecorded_e:
|
||||
http_set_field( http_hdr, "Pragma: xPlayStrm=1" );
|
||||
ptr = str;
|
||||
ptr += sprintf( ptr, "Pragma: stream-switch-entry=");
|
||||
if(asf_http_ctrl->n_audio > 0) {
|
||||
for( i=0; i<asf_http_ctrl->n_audio ; i++ ) {
|
||||
stream_id = asf_http_ctrl->audio_streams[i];
|
||||
if(stream_id == asf_http_ctrl->audio_id) {
|
||||
enable = 0;
|
||||
} else {
|
||||
enable = 2;
|
||||
continue;
|
||||
}
|
||||
asf_nb_stream++;
|
||||
ptr += sprintf(ptr, "ffff:%x:%d ", stream_id, enable);
|
||||
}
|
||||
}
|
||||
if(asf_http_ctrl->n_video > 0) {
|
||||
for( i=0; i<asf_http_ctrl->n_video ; i++ ) {
|
||||
stream_id = asf_http_ctrl->video_streams[i];
|
||||
if(stream_id == asf_http_ctrl->video_id) {
|
||||
enable = 0;
|
||||
} else {
|
||||
enable = 2;
|
||||
continue;
|
||||
}
|
||||
asf_nb_stream++;
|
||||
ptr += sprintf(ptr, "ffff:%x:%d ", stream_id, enable);
|
||||
}
|
||||
}
|
||||
http_set_field( http_hdr, str );
|
||||
sprintf( str, "Pragma: stream-switch-count=%d", asf_nb_stream );
|
||||
http_set_field( http_hdr, str );
|
||||
break;
|
||||
case ASF_Redirector_e:
|
||||
break;
|
||||
case ASF_Unknown_e:
|
||||
// First request goes here.
|
||||
break;
|
||||
default:
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"unknown ASF stream type\n");
|
||||
}
|
||||
|
||||
http_set_field( http_hdr, "Connection: Close" );
|
||||
http_build_request( http_hdr );
|
||||
|
||||
return http_hdr;
|
||||
}
|
||||
|
||||
static int asf_http_parse_response(asf_http_streaming_ctrl_t *asf_http_ctrl, HTTP_header_t *http_hdr ) {
|
||||
char *content_type, *pragma;
|
||||
char features[64] = "\0";
|
||||
size_t len;
|
||||
if( http_response_parse(http_hdr)<0 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Failed to parse HTTP response.\n");
|
||||
return -1;
|
||||
}
|
||||
switch( http_hdr->status_code ) {
|
||||
case 200:
|
||||
break;
|
||||
case 401: // Authentication required
|
||||
return ASF_Authenticate_e;
|
||||
default:
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Server returned %d:%s\n", http_hdr->status_code, http_hdr->reason_phrase);
|
||||
return -1;
|
||||
}
|
||||
|
||||
content_type = http_get_field( http_hdr, "Content-Type");
|
||||
//printf("Content-Type: [%s]\n", content_type);
|
||||
|
||||
pragma = http_get_field( http_hdr, "Pragma");
|
||||
while( pragma!=NULL ) {
|
||||
char *comma_ptr=NULL;
|
||||
char *end;
|
||||
//printf("Pragma: [%s]\n", pragma );
|
||||
// The pragma line can get severals attributes
|
||||
// separeted with a comma ','.
|
||||
do {
|
||||
if( !strncasecmp( pragma, "features=", 9) ) {
|
||||
pragma += 9;
|
||||
end = strstr( pragma, "," );
|
||||
if( end==NULL ) {
|
||||
len = strlen(pragma);
|
||||
} else {
|
||||
len = (unsigned int)(end-pragma);
|
||||
}
|
||||
if(len > sizeof(features) - 1) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_WARN,"ASF HTTP PARSE WARNING : Pragma %s cut from %zd bytes to %zd\n",pragma,len,sizeof(features) - 1);
|
||||
len = sizeof(features) - 1;
|
||||
}
|
||||
strncpy( features, pragma, len );
|
||||
features[len]='\0';
|
||||
break;
|
||||
}
|
||||
comma_ptr = strstr( pragma, "," );
|
||||
if( comma_ptr!=NULL ) {
|
||||
pragma = comma_ptr+1;
|
||||
if( pragma[0]==' ' ) pragma++;
|
||||
}
|
||||
} while( comma_ptr!=NULL );
|
||||
pragma = http_get_next_field( http_hdr );
|
||||
}
|
||||
asf_http_ctrl->streaming_type = asf_http_streaming_type( content_type, features, http_hdr );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asf_http_streaming_start( stream_t *stream, int *demuxer_type ) {
|
||||
HTTP_header_t *http_hdr=NULL;
|
||||
URL_t *url = stream->streaming_ctrl->url;
|
||||
asf_http_streaming_ctrl_t *asf_http_ctrl;
|
||||
char buffer[BUFFER_SIZE];
|
||||
int i, ret;
|
||||
int fd = stream->fd;
|
||||
int done;
|
||||
int auth_retry = 0;
|
||||
|
||||
asf_http_ctrl = malloc(sizeof(asf_http_streaming_ctrl_t));
|
||||
if( asf_http_ctrl==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
asf_http_ctrl->streaming_type = ASF_Unknown_e;
|
||||
asf_http_ctrl->request = 1;
|
||||
asf_http_ctrl->audio_streams = asf_http_ctrl->video_streams = NULL;
|
||||
asf_http_ctrl->n_audio = asf_http_ctrl->n_video = 0;
|
||||
stream->streaming_ctrl->data = (void*)asf_http_ctrl;
|
||||
|
||||
do {
|
||||
done = 1;
|
||||
if( fd>0 ) closesocket( fd );
|
||||
|
||||
if( !strcasecmp( url->protocol, "mp_http_proxy" ) ) {
|
||||
if( url->port==0 ) url->port = 8080;
|
||||
} else {
|
||||
if( url->port==0 ) url->port = 80;
|
||||
}
|
||||
fd = connect2Server( url->hostname, url->port, 1);
|
||||
if( fd<0 ) return fd;
|
||||
|
||||
http_hdr = asf_http_request( stream->streaming_ctrl );
|
||||
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Request [%s]\n", http_hdr->buffer );
|
||||
for(i=0; i < (int)http_hdr->buffer_size ; ) {
|
||||
int r = send( fd, http_hdr->buffer+i, http_hdr->buffer_size-i, DEFAULT_SEND_FLAGS );
|
||||
if(r <0) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"socket write error: %s\n",strerror(errno));
|
||||
goto err_out;
|
||||
}
|
||||
i += r;
|
||||
}
|
||||
http_free( http_hdr );
|
||||
http_hdr = http_new_header();
|
||||
do {
|
||||
i = recv( fd, buffer, BUFFER_SIZE, 0 );
|
||||
//printf("read: %d\n", i );
|
||||
if( i<=0 ) {
|
||||
perror("read");
|
||||
goto err_out;
|
||||
}
|
||||
http_response_append( http_hdr, buffer, i );
|
||||
} while( !http_is_header_entire( http_hdr ) );
|
||||
if( mp_msg_test(MSGT_NETWORK,MSGL_V) ) {
|
||||
http_hdr->buffer[http_hdr->buffer_size]='\0';
|
||||
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Response [%s]\n", http_hdr->buffer );
|
||||
}
|
||||
ret = asf_http_parse_response(asf_http_ctrl, http_hdr);
|
||||
if( ret<0 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Failed to parse header.\n");
|
||||
goto err_out;
|
||||
}
|
||||
switch( asf_http_ctrl->streaming_type ) {
|
||||
case ASF_Live_e:
|
||||
case ASF_Prerecorded_e:
|
||||
case ASF_PlainText_e:
|
||||
if( http_hdr->body_size>0 ) {
|
||||
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
if( asf_http_ctrl->request==1 ) {
|
||||
if( asf_http_ctrl->streaming_type!=ASF_PlainText_e ) {
|
||||
// First request, we only got the ASF header.
|
||||
ret = asf_streaming_parse_header(stream,fd);
|
||||
if(ret < 0) goto err_out;
|
||||
if(asf_http_ctrl->n_audio == 0 && asf_http_ctrl->n_video == 0) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"No stream found.\n");
|
||||
goto err_out;
|
||||
}
|
||||
asf_http_ctrl->request++;
|
||||
done = 0;
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASF_Redirector_e:
|
||||
if( http_hdr->body_size>0 ) {
|
||||
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
*demuxer_type = DEMUXER_TYPE_PLAYLIST;
|
||||
done = 1;
|
||||
break;
|
||||
case ASF_Authenticate_e:
|
||||
if( http_authenticate( http_hdr, url, &auth_retry)<0 ) return -1;
|
||||
asf_http_ctrl->streaming_type = ASF_Unknown_e;
|
||||
done = 0;
|
||||
break;
|
||||
case ASF_Unknown_e:
|
||||
default:
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"unknown ASF streaming type\n");
|
||||
goto err_out;
|
||||
}
|
||||
// Check if we got a redirect.
|
||||
} while(!done);
|
||||
|
||||
stream->fd = fd;
|
||||
if( asf_http_ctrl->streaming_type==ASF_PlainText_e || asf_http_ctrl->streaming_type==ASF_Redirector_e ) {
|
||||
stream->streaming_ctrl->streaming_read = nop_streaming_read;
|
||||
stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
|
||||
} else {
|
||||
stream->streaming_ctrl->streaming_read = asf_http_streaming_read;
|
||||
stream->streaming_ctrl->streaming_seek = asf_http_streaming_seek;
|
||||
stream->streaming = true;
|
||||
}
|
||||
stream->streaming_ctrl->status = streaming_playing_e;
|
||||
stream->close = close_s;
|
||||
|
||||
http_free( http_hdr );
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
if (fd > 0)
|
||||
closesocket(fd);
|
||||
stream->fd = -1;
|
||||
http_free(http_hdr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
|
||||
stream->streaming_ctrl = streaming_ctrl_new();
|
||||
if( stream->streaming_ctrl==NULL ) {
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
stream->streaming_ctrl->audio_id_ptr = &stream->opts->audio_id;
|
||||
stream->streaming_ctrl->video_id_ptr = &stream->opts->video_id;
|
||||
stream->streaming_ctrl->bandwidth = network_bandwidth;
|
||||
stream->streaming_ctrl->url = url_new_with_proxy(stream->url);
|
||||
|
||||
mp_tmsg(MSGT_OPEN, MSGL_INFO, "STREAM_ASF, URL: %s\n", stream->url);
|
||||
if((!strncmp(stream->url, "mp_http", 4)) && (*file_format!=DEMUXER_TYPE_ASF && *file_format!=DEMUXER_TYPE_UNKNOWN)) {
|
||||
streaming_ctrl_free(stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if(asf_streaming_start(stream, file_format) < 0) {
|
||||
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed, exiting.\n");
|
||||
streaming_ctrl_free(stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (*file_format != DEMUXER_TYPE_PLAYLIST)
|
||||
*file_format = DEMUXER_TYPE_ASF;
|
||||
stream->type = STREAMTYPE_STREAM;
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
const stream_info_t stream_info_asf = {
|
||||
"mms and mms over http streaming",
|
||||
"null",
|
||||
"Bertrand, Reimar Doeffinger, Albeu",
|
||||
"originally based on work by Majormms (is that code still there?)",
|
||||
open_s,
|
||||
{"mp_mms", "mp_mmst", "mp_http", "mp_http_proxy", "mp_mmsh", "mp_mmshttp", NULL},
|
||||
NULL,
|
||||
0 // Urls are an option string
|
||||
};
|
@ -33,8 +33,8 @@
|
||||
|
||||
#include "osdep/io.h"
|
||||
|
||||
#include "core/options.h"
|
||||
#include "cookies.h"
|
||||
#include "http.h"
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
#define MAX_COOKIES 20
|
||||
@ -72,24 +72,6 @@ static char *col_dup(const char *src)
|
||||
return dst;
|
||||
}
|
||||
|
||||
static int right_hand_strcmp(const char *cookie_domain, const char *url_domain)
|
||||
{
|
||||
int c_l;
|
||||
int u_l;
|
||||
|
||||
c_l = strlen(cookie_domain);
|
||||
u_l = strlen(url_domain);
|
||||
|
||||
if (c_l > u_l)
|
||||
return -1;
|
||||
return strcmp(cookie_domain, url_domain + u_l - c_l);
|
||||
}
|
||||
|
||||
static int left_hand_strcmp(const char *cookie_path, const char *url_path)
|
||||
{
|
||||
return strncmp(cookie_path, url_path, strlen(cookie_path));
|
||||
}
|
||||
|
||||
/* Finds the start of all the columns */
|
||||
static int parse_line(char **ptr, char *cols[6])
|
||||
{
|
||||
@ -196,72 +178,6 @@ static struct cookie_list_type *load_cookies(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Take an HTTP_header_t, and insert the correct headers. The cookie files are read if necessary. */
|
||||
void
|
||||
cookies_set(HTTP_header_t * http_hdr, const char *domain, const char *url)
|
||||
{
|
||||
int found_cookies = 0;
|
||||
struct cookie_list_type *cookies[MAX_COOKIES];
|
||||
struct cookie_list_type *list, *start;
|
||||
int i;
|
||||
char *path;
|
||||
char *buf;
|
||||
|
||||
path = strchr(url, '/');
|
||||
if (!path)
|
||||
path = "";
|
||||
|
||||
if (!cookie_list)
|
||||
cookie_list = load_cookies();
|
||||
|
||||
|
||||
list = start = cookie_list;
|
||||
|
||||
/* Find which cookies we want, removing duplicates. Cookies with the longest domain, then longest path take priority */
|
||||
while (list) {
|
||||
/* Check the cookie domain and path. Also, we never send "secure" cookies. These should only be sent over HTTPS. */
|
||||
if ((right_hand_strcmp(list->domain, domain) == 0)
|
||||
&& (left_hand_strcmp(list->path, path) == 0) && !list->secure) {
|
||||
int replacing = 0;
|
||||
for (i = 0; i < found_cookies; i++) {
|
||||
if (strcmp(list->name, cookies[i]->name) == 0) {
|
||||
replacing = 0;
|
||||
if (strlen(list->domain) <= strlen(cookies[i]->domain)) {
|
||||
cookies[i] = list;
|
||||
} else if (strlen(list->path) <= strlen(cookies[i]->path)) {
|
||||
cookies[i] = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found_cookies > MAX_COOKIES) {
|
||||
/* Cookie jar overflow! */
|
||||
break;
|
||||
}
|
||||
if (!replacing)
|
||||
cookies[found_cookies++] = list;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
|
||||
buf = strdup("Cookie:");
|
||||
|
||||
for (i = 0; i < found_cookies; i++) {
|
||||
char *nbuf;
|
||||
|
||||
nbuf = malloc(strlen(buf) + strlen(" ") + strlen(cookies[i]->name) +
|
||||
strlen("=") + strlen(cookies[i]->value) + strlen(";") + 1);
|
||||
sprintf(nbuf, "%s %s=%s;", buf, cookies[i]->name,
|
||||
cookies[i]->value);
|
||||
free(buf);
|
||||
buf = nbuf;
|
||||
}
|
||||
|
||||
if (found_cookies)
|
||||
http_set_field(http_hdr, buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
// Return a cookies string as expected by lavf (libavformat/http.c). The format
|
||||
// is like a Set-Cookie header (http://curl.haxx.se/rfc/cookie_spec.html),
|
||||
// separated by newlines.
|
||||
|
@ -24,10 +24,6 @@
|
||||
#ifndef MPLAYER_COOKIES_H
|
||||
#define MPLAYER_COOKIES_H
|
||||
|
||||
#include "http.h"
|
||||
|
||||
void cookies_set(HTTP_header_t * http_hdr, const char *hostname,
|
||||
const char *url);
|
||||
char *cookies_lavf(void);
|
||||
|
||||
#endif /* MPLAYER_COOKIES_H */
|
||||
|
966
stream/http.c
966
stream/http.c
@ -1,966 +0,0 @@
|
||||
/*
|
||||
* HTTP Helper
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !HAVE_WINSOCK2_H
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "http.h"
|
||||
#include "url.h"
|
||||
#include "core/options.h"
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
#include "stream.h"
|
||||
#include "demux/demux.h"
|
||||
#include "network.h"
|
||||
|
||||
#include "libavutil/base64.h"
|
||||
|
||||
#include <libavutil/avutil.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned metaint;
|
||||
unsigned metapos;
|
||||
int is_ultravox;
|
||||
} scast_data_t;
|
||||
|
||||
/**
|
||||
* \brief first read any data from sc->buffer then from fd
|
||||
* \param fd file descriptor to read data from
|
||||
* \param buffer buffer to read into
|
||||
* \param len how many bytes to read
|
||||
* \param sc streaming control containing buffer to read from first
|
||||
* \return len unless there is a read error or eof
|
||||
*/
|
||||
static unsigned my_read(int fd, char *buffer, int len, streaming_ctrl_t *sc) {
|
||||
unsigned pos = 0;
|
||||
unsigned cp_len = sc->buffer_size - sc->buffer_pos;
|
||||
if (cp_len > len)
|
||||
cp_len = len;
|
||||
memcpy(buffer, &sc->buffer[sc->buffer_pos], cp_len);
|
||||
sc->buffer_pos += cp_len;
|
||||
pos += cp_len;
|
||||
while (pos < len) {
|
||||
int ret = recv(fd, &buffer[pos], len - pos, 0);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
pos += ret;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief read and process (i.e. discard *g*) a block of ultravox metadata
|
||||
* \param fd file descriptor to read from
|
||||
* \param sc streaming_ctrl_t whose buffer is consumed before reading from fd
|
||||
* \return number of real data before next metadata block starts or 0 on error
|
||||
*
|
||||
* You can use unsv://samples.mplayerhq.hu/V-codecs/VP5/vp5_artefacts.nsv to
|
||||
* test.
|
||||
*/
|
||||
static unsigned uvox_meta_read(int fd, streaming_ctrl_t *sc) {
|
||||
unsigned metaint;
|
||||
unsigned char info[6] = {0, 0, 0, 0, 0, 0};
|
||||
int info_read;
|
||||
do {
|
||||
info_read = my_read(fd, info, 1, sc);
|
||||
if (info[0] == 0x00)
|
||||
info_read = my_read(fd, info, 6, sc);
|
||||
else
|
||||
info_read += my_read(fd, &info[1], 5, sc);
|
||||
if (info_read != 6) // read error or eof
|
||||
return 0;
|
||||
// sync byte and reserved flags
|
||||
if (info[0] != 0x5a || (info[1] & 0xfc) != 0x00) {
|
||||
mp_msg(MSGT_DEMUXER, MSGL_ERR, "Invalid or unknown uvox metadata\n");
|
||||
return 0;
|
||||
}
|
||||
if (info[1] & 0x01)
|
||||
mp_msg(MSGT_DEMUXER, MSGL_WARN, "Encrypted ultravox data\n");
|
||||
metaint = info[4] << 8 | info[5];
|
||||
if ((info[3] & 0xf) < 0x07) { // discard any metadata nonsense
|
||||
char *metabuf = malloc(metaint);
|
||||
my_read(fd, metabuf, metaint, sc);
|
||||
free(metabuf);
|
||||
}
|
||||
} while ((info[3] & 0xf) < 0x07);
|
||||
return metaint;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief read one scast meta data entry and print it
|
||||
* \param fd file descriptor to read from
|
||||
* \param sc streaming_ctrl_t whose buffer is consumed before reading from fd
|
||||
*/
|
||||
static void scast_meta_read(int fd, streaming_ctrl_t *sc) {
|
||||
unsigned char tmp = 0;
|
||||
unsigned metalen;
|
||||
my_read(fd, &tmp, 1, sc);
|
||||
metalen = tmp * 16;
|
||||
if (metalen > 0) {
|
||||
int i;
|
||||
uint8_t *info = malloc(metalen + 1);
|
||||
unsigned nlen = my_read(fd, info, metalen, sc);
|
||||
// avoid breaking the user's terminal too much
|
||||
if (nlen > 256) nlen = 256;
|
||||
for (i = 0; i < nlen; i++)
|
||||
if (info[i] && info[i] < 32) info[i] = '?';
|
||||
info[nlen] = 0;
|
||||
mp_msg(MSGT_DEMUXER, MSGL_INFO, "\nICY Info: %s\n", info);
|
||||
free(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief read data from scast/ultravox stream without any metadata
|
||||
* \param fd file descriptor to read from
|
||||
* \param buffer buffer to read data into
|
||||
* \param size number of bytes to read
|
||||
* \param sc streaming_ctrl_t whose buffer is consumed before reading from fd
|
||||
*/
|
||||
static int scast_streaming_read(int fd, char *buffer, int size,
|
||||
streaming_ctrl_t *sc) {
|
||||
scast_data_t *sd = (scast_data_t *)sc->data;
|
||||
unsigned block, ret;
|
||||
unsigned done = 0;
|
||||
|
||||
// first read remaining data up to next metadata
|
||||
block = sd->metaint - sd->metapos;
|
||||
if (block > size)
|
||||
block = size;
|
||||
ret = my_read(fd, buffer, block, sc);
|
||||
sd->metapos += ret;
|
||||
done += ret;
|
||||
if (ret != block) // read problems or eof
|
||||
size = done;
|
||||
|
||||
while (done < size) { // now comes the metadata
|
||||
if (sd->is_ultravox)
|
||||
{
|
||||
sd->metaint = uvox_meta_read(fd, sc);
|
||||
if (!sd->metaint)
|
||||
size = done;
|
||||
}
|
||||
else
|
||||
scast_meta_read(fd, sc); // read and display metadata
|
||||
sd->metapos = 0;
|
||||
block = size - done;
|
||||
if (block > sd->metaint)
|
||||
block = sd->metaint;
|
||||
ret = my_read(fd, &buffer[done], block, sc);
|
||||
sd->metapos += ret;
|
||||
done += ret;
|
||||
if (ret != block) // read problems or eof
|
||||
size = done;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
static int scast_streaming_start(stream_t *stream) {
|
||||
int metaint;
|
||||
scast_data_t *scast_data;
|
||||
HTTP_header_t *http_hdr = stream->streaming_ctrl->data;
|
||||
if (!stream || stream->fd < 0 || !http_hdr)
|
||||
return -1;
|
||||
int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0;
|
||||
if (is_ultravox)
|
||||
metaint = 0;
|
||||
else {
|
||||
metaint = -1;
|
||||
char *h = http_get_field(http_hdr, "Icy-MetaInt");
|
||||
if (h)
|
||||
metaint = atoi(h);
|
||||
if (metaint <= 0)
|
||||
return -1;
|
||||
}
|
||||
stream->streaming_ctrl->buffer = malloc(http_hdr->body_size);
|
||||
stream->streaming_ctrl->buffer_size = http_hdr->body_size;
|
||||
stream->streaming_ctrl->buffer_pos = 0;
|
||||
memcpy(stream->streaming_ctrl->buffer, http_hdr->body, http_hdr->body_size);
|
||||
scast_data = malloc(sizeof(scast_data_t));
|
||||
scast_data->metaint = metaint;
|
||||
scast_data->metapos = 0;
|
||||
scast_data->is_ultravox = is_ultravox;
|
||||
http_free(http_hdr);
|
||||
stream->streaming_ctrl->data = scast_data;
|
||||
stream->streaming_ctrl->streaming_read = scast_streaming_read;
|
||||
stream->streaming_ctrl->streaming_seek = NULL;
|
||||
stream->streaming_ctrl->status = streaming_playing_e;
|
||||
stream->streaming = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nop_streaming_start( stream_t *stream ) {
|
||||
HTTP_header_t *http_hdr = NULL;
|
||||
char *next_url=NULL;
|
||||
int fd,ret;
|
||||
if( stream==NULL ) return -1;
|
||||
|
||||
fd = stream->fd;
|
||||
if( fd<0 ) {
|
||||
fd = http_send_request( stream->streaming_ctrl->url, 0 );
|
||||
if( fd<0 ) return -1;
|
||||
http_hdr = http_read_response( fd );
|
||||
if( http_hdr==NULL ) return -1;
|
||||
|
||||
switch( http_hdr->status_code ) {
|
||||
case 200: // OK
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Content-Type: [%s]\n", http_get_field(http_hdr, "Content-Type") );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") );
|
||||
if( http_hdr->body_size>0 ) {
|
||||
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
|
||||
http_free( http_hdr );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Redirect
|
||||
case 301: // Permanently
|
||||
case 302: // Temporarily
|
||||
case 303: // See Other
|
||||
case 307: // Temporarily (since HTTP/1.1)
|
||||
ret=-1;
|
||||
next_url = http_get_field( http_hdr, "Location" );
|
||||
|
||||
if (next_url != NULL) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_STATUS,"Redirected: Using this url instead %s\n",next_url);
|
||||
stream->streaming_ctrl->url=url_new_with_proxy(next_url);
|
||||
ret=nop_streaming_start(stream); //recursively get streaming started
|
||||
} else {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Redirection failed\n");
|
||||
closesocket( fd );
|
||||
fd = -1;
|
||||
}
|
||||
return ret;
|
||||
break;
|
||||
case 401: //Authorization required
|
||||
case 403: //Forbidden
|
||||
case 404: //Not found
|
||||
case 500: //Server Error
|
||||
default:
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Server returned code %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase );
|
||||
closesocket( fd );
|
||||
fd = -1;
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
stream->fd = fd;
|
||||
} else {
|
||||
http_hdr = (HTTP_header_t*)stream->streaming_ctrl->data;
|
||||
if( http_hdr->body_size>0 ) {
|
||||
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
|
||||
http_free( http_hdr );
|
||||
stream->streaming_ctrl->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( http_hdr ) {
|
||||
http_free( http_hdr );
|
||||
stream->streaming_ctrl->data = NULL;
|
||||
}
|
||||
|
||||
stream->streaming_ctrl->streaming_read = nop_streaming_read;
|
||||
stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
|
||||
stream->streaming_ctrl->status = streaming_playing_e;
|
||||
stream->streaming = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
HTTP_header_t *
|
||||
http_new_header(void) {
|
||||
HTTP_header_t *http_hdr;
|
||||
|
||||
http_hdr = calloc(1, sizeof(*http_hdr));
|
||||
if( http_hdr==NULL ) return NULL;
|
||||
|
||||
return http_hdr;
|
||||
}
|
||||
|
||||
void
|
||||
http_free( HTTP_header_t *http_hdr ) {
|
||||
HTTP_field_t *field, *field2free;
|
||||
if( http_hdr==NULL ) return;
|
||||
free(http_hdr->protocol);
|
||||
free(http_hdr->uri);
|
||||
free(http_hdr->reason_phrase);
|
||||
free(http_hdr->field_search);
|
||||
free(http_hdr->method);
|
||||
free(http_hdr->buffer);
|
||||
field = http_hdr->first_field;
|
||||
while( field!=NULL ) {
|
||||
field2free = field;
|
||||
free(field->field_name);
|
||||
field = field->next;
|
||||
free( field2free );
|
||||
}
|
||||
free( http_hdr );
|
||||
http_hdr = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
http_response_append( HTTP_header_t *http_hdr, char *response, int length ) {
|
||||
if( http_hdr==NULL || response==NULL || length<0 ) return -1;
|
||||
|
||||
if( (unsigned)length > SIZE_MAX - http_hdr->buffer_size - 1) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Bad size in memory (re)allocation\n");
|
||||
return -1;
|
||||
}
|
||||
http_hdr->buffer = realloc( http_hdr->buffer, http_hdr->buffer_size+length+1 );
|
||||
if( http_hdr->buffer==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory (re)allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy( http_hdr->buffer+http_hdr->buffer_size, response, length );
|
||||
http_hdr->buffer_size += length;
|
||||
http_hdr->buffer[http_hdr->buffer_size]=0; // close the string!
|
||||
return http_hdr->buffer_size;
|
||||
}
|
||||
|
||||
int
|
||||
http_is_header_entire( HTTP_header_t *http_hdr ) {
|
||||
if( http_hdr==NULL ) return -1;
|
||||
if( http_hdr->buffer==NULL ) return 0; // empty
|
||||
|
||||
if(http_hdr->buffer_size > 128*1024) return 1;
|
||||
if( strstr(http_hdr->buffer, "\r\n\r\n")==NULL &&
|
||||
strstr(http_hdr->buffer, "\n\n")==NULL ) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
http_response_parse( HTTP_header_t *http_hdr ) {
|
||||
char *hdr_ptr, *ptr;
|
||||
char *field=NULL;
|
||||
int pos_hdr_sep, hdr_sep_len;
|
||||
size_t len;
|
||||
if( http_hdr==NULL ) return -1;
|
||||
if( http_hdr->is_parsed ) return 0;
|
||||
|
||||
// Get the protocol
|
||||
hdr_ptr = strstr( http_hdr->buffer, " " );
|
||||
if( hdr_ptr==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. No space separator found.\n");
|
||||
return -1;
|
||||
}
|
||||
len = hdr_ptr-http_hdr->buffer;
|
||||
http_hdr->protocol = malloc(len+1);
|
||||
if( http_hdr->protocol==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
strncpy( http_hdr->protocol, http_hdr->buffer, len );
|
||||
http_hdr->protocol[len]='\0';
|
||||
if( !strncasecmp( http_hdr->protocol, "HTTP", 4) ) {
|
||||
if( sscanf( http_hdr->protocol+5,"1.%d", &(http_hdr->http_minor_version) )!=1 ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. Unable to get HTTP minor version.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the status code
|
||||
if( sscanf( ++hdr_ptr, "%d", &(http_hdr->status_code) )!=1 ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. Unable to get status code.\n");
|
||||
return -1;
|
||||
}
|
||||
hdr_ptr += 4;
|
||||
|
||||
// Get the reason phrase
|
||||
ptr = strstr( hdr_ptr, "\n" );
|
||||
if( ptr==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Malformed answer. Unable to get the reason phrase.\n");
|
||||
return -1;
|
||||
}
|
||||
len = ptr-hdr_ptr;
|
||||
http_hdr->reason_phrase = malloc(len+1);
|
||||
if( http_hdr->reason_phrase==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
strncpy( http_hdr->reason_phrase, hdr_ptr, len );
|
||||
if( http_hdr->reason_phrase[len-1]=='\r' ) {
|
||||
len--;
|
||||
}
|
||||
http_hdr->reason_phrase[len]='\0';
|
||||
|
||||
// Set the position of the header separator: \r\n\r\n
|
||||
hdr_sep_len = 4;
|
||||
ptr = strstr( http_hdr->buffer, "\r\n\r\n" );
|
||||
if( ptr==NULL ) {
|
||||
hdr_sep_len = 2;
|
||||
ptr = strstr( http_hdr->buffer, "\n\n" );
|
||||
if( ptr==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Header may be incomplete. No CRLF CRLF found.\n");
|
||||
hdr_sep_len = 0;
|
||||
}
|
||||
}
|
||||
pos_hdr_sep = ptr-http_hdr->buffer;
|
||||
|
||||
// Point to the first line after the method line.
|
||||
hdr_ptr = strstr( http_hdr->buffer, "\n" )+1;
|
||||
do {
|
||||
ptr = hdr_ptr;
|
||||
while( *ptr!='\r' && *ptr!='\n' ) ptr++;
|
||||
len = ptr-hdr_ptr;
|
||||
if (len == 0 || !memchr(hdr_ptr, ':', len)) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_ERR, "Broken response header, missing ':'\n");
|
||||
pos_hdr_sep = ptr - http_hdr->buffer;
|
||||
hdr_sep_len = 0;
|
||||
break;
|
||||
}
|
||||
if (len > 16 && !strncasecmp(hdr_ptr + 4, "icy-metaint:", 12))
|
||||
{
|
||||
mp_msg(MSGT_NETWORK, MSGL_WARN, "Server sent a severely broken icy-metaint HTTP header!\n");
|
||||
hdr_ptr += 4;
|
||||
len -= 4;
|
||||
}
|
||||
field = realloc(field, len+1);
|
||||
if( field==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
strncpy( field, hdr_ptr, len );
|
||||
field[len]='\0';
|
||||
http_set_field( http_hdr, field );
|
||||
hdr_ptr = ptr+((*ptr=='\r')?2:1);
|
||||
} while( hdr_ptr<(http_hdr->buffer+pos_hdr_sep) );
|
||||
|
||||
free(field);
|
||||
|
||||
if( pos_hdr_sep+hdr_sep_len<http_hdr->buffer_size ) {
|
||||
// Response has data!
|
||||
http_hdr->body = http_hdr->buffer+pos_hdr_sep+hdr_sep_len;
|
||||
http_hdr->body_size = http_hdr->buffer_size-(pos_hdr_sep+hdr_sep_len);
|
||||
}
|
||||
|
||||
http_hdr->is_parsed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
http_build_request( HTTP_header_t *http_hdr ) {
|
||||
char *ptr;
|
||||
int len;
|
||||
HTTP_field_t *field;
|
||||
if( http_hdr==NULL ) return NULL;
|
||||
if( http_hdr->uri==NULL ) return NULL;
|
||||
|
||||
if( http_hdr->method==NULL ) http_set_method( http_hdr, "GET");
|
||||
if( http_hdr->uri==NULL ) http_set_uri( http_hdr, "/");
|
||||
if( !http_hdr->uri || !http_hdr->method)
|
||||
return NULL;
|
||||
|
||||
//**** Compute the request length
|
||||
// Add the Method line
|
||||
len = strlen(http_hdr->method)+strlen(http_hdr->uri)+12;
|
||||
// Add the fields
|
||||
field = http_hdr->first_field;
|
||||
while( field!=NULL ) {
|
||||
len += strlen(field->field_name)+2;
|
||||
field = field->next;
|
||||
}
|
||||
// Add the CRLF
|
||||
len += 2;
|
||||
// Add the body
|
||||
if( http_hdr->body!=NULL ) {
|
||||
len += http_hdr->body_size;
|
||||
}
|
||||
// Free the buffer if it was previously used
|
||||
if( http_hdr->buffer!=NULL ) {
|
||||
free( http_hdr->buffer );
|
||||
http_hdr->buffer = NULL;
|
||||
}
|
||||
http_hdr->buffer = malloc(len+1);
|
||||
if( http_hdr->buffer==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
http_hdr->buffer_size = len;
|
||||
|
||||
//*** Building the request
|
||||
ptr = http_hdr->buffer;
|
||||
// Add the method line
|
||||
ptr += sprintf( ptr, "%s %s HTTP/1.%d\r\n", http_hdr->method, http_hdr->uri, http_hdr->http_minor_version );
|
||||
field = http_hdr->first_field;
|
||||
// Add the field
|
||||
while( field!=NULL ) {
|
||||
ptr += sprintf( ptr, "%s\r\n", field->field_name );
|
||||
field = field->next;
|
||||
}
|
||||
ptr += sprintf( ptr, "\r\n" );
|
||||
// Add the body
|
||||
if( http_hdr->body!=NULL ) {
|
||||
memcpy( ptr, http_hdr->body, http_hdr->body_size );
|
||||
}
|
||||
|
||||
return http_hdr->buffer;
|
||||
}
|
||||
|
||||
char *
|
||||
http_get_field( HTTP_header_t *http_hdr, const char *field_name ) {
|
||||
if( http_hdr==NULL || field_name==NULL ) return NULL;
|
||||
http_hdr->field_search_pos = http_hdr->first_field;
|
||||
http_hdr->field_search = realloc( http_hdr->field_search, strlen(field_name)+1 );
|
||||
if( http_hdr->field_search==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
strcpy( http_hdr->field_search, field_name );
|
||||
return http_get_next_field( http_hdr );
|
||||
}
|
||||
|
||||
char *
|
||||
http_get_next_field( HTTP_header_t *http_hdr ) {
|
||||
char *ptr;
|
||||
HTTP_field_t *field;
|
||||
if( http_hdr==NULL ) return NULL;
|
||||
|
||||
field = http_hdr->field_search_pos;
|
||||
while( field!=NULL ) {
|
||||
ptr = strstr( field->field_name, ":" );
|
||||
if( ptr==NULL ) return NULL;
|
||||
if( !strncasecmp( field->field_name, http_hdr->field_search, ptr-(field->field_name) ) ) {
|
||||
ptr++; // Skip the column
|
||||
while( ptr[0]==' ' ) ptr++; // Skip the spaces if there is some
|
||||
http_hdr->field_search_pos = field->next;
|
||||
return ptr; // return the value without the field name
|
||||
}
|
||||
field = field->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
http_set_field( HTTP_header_t *http_hdr, const char *field_name ) {
|
||||
HTTP_field_t *new_field;
|
||||
if( http_hdr==NULL || field_name==NULL ) return;
|
||||
|
||||
new_field = malloc(sizeof(HTTP_field_t));
|
||||
if( new_field==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
return;
|
||||
}
|
||||
new_field->next = NULL;
|
||||
new_field->field_name = malloc(strlen(field_name)+1);
|
||||
if( new_field->field_name==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
free(new_field);
|
||||
return;
|
||||
}
|
||||
strcpy( new_field->field_name, field_name );
|
||||
|
||||
if( http_hdr->last_field==NULL ) {
|
||||
http_hdr->first_field = new_field;
|
||||
} else {
|
||||
http_hdr->last_field->next = new_field;
|
||||
}
|
||||
http_hdr->last_field = new_field;
|
||||
http_hdr->field_nb++;
|
||||
}
|
||||
|
||||
void
|
||||
http_set_method( HTTP_header_t *http_hdr, const char *method ) {
|
||||
if( http_hdr==NULL || method==NULL ) return;
|
||||
|
||||
http_hdr->method = malloc(strlen(method)+1);
|
||||
if( http_hdr->method==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
return;
|
||||
}
|
||||
strcpy( http_hdr->method, method );
|
||||
}
|
||||
|
||||
void
|
||||
http_set_uri( HTTP_header_t *http_hdr, const char *uri ) {
|
||||
if( http_hdr==NULL || uri==NULL ) return;
|
||||
|
||||
http_hdr->uri = malloc(strlen(uri)+1);
|
||||
if( http_hdr->uri==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
return;
|
||||
}
|
||||
strcpy( http_hdr->uri, uri );
|
||||
}
|
||||
|
||||
static int
|
||||
http_add_authentication( HTTP_header_t *http_hdr, const char *username, const char *password, const char *auth_str ) {
|
||||
char *auth = NULL, *usr_pass = NULL, *b64_usr_pass = NULL;
|
||||
int encoded_len, pass_len=0;
|
||||
size_t auth_len, usr_pass_len;
|
||||
int res = -1;
|
||||
if( http_hdr==NULL || username==NULL ) return -1;
|
||||
|
||||
if( password!=NULL ) {
|
||||
pass_len = strlen(password);
|
||||
}
|
||||
|
||||
usr_pass_len = strlen(username) + 1 + pass_len;
|
||||
usr_pass = malloc(usr_pass_len + 1);
|
||||
if( usr_pass==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sprintf( usr_pass, "%s:%s", username, (password==NULL)?"":password );
|
||||
|
||||
encoded_len = AV_BASE64_SIZE(usr_pass_len);
|
||||
b64_usr_pass = malloc(encoded_len);
|
||||
if( b64_usr_pass==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
goto out;
|
||||
}
|
||||
av_base64_encode(b64_usr_pass, encoded_len, usr_pass, usr_pass_len);
|
||||
|
||||
auth_len = encoded_len + 100;
|
||||
auth = malloc(auth_len);
|
||||
if( auth==NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
snprintf(auth, auth_len, "%s: Basic %s", auth_str, b64_usr_pass);
|
||||
http_set_field( http_hdr, auth );
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
free( usr_pass );
|
||||
free( b64_usr_pass );
|
||||
free( auth );
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
http_add_basic_authentication( HTTP_header_t *http_hdr, const char *username, const char *password ) {
|
||||
return http_add_authentication(http_hdr, username, password, "Authorization");
|
||||
}
|
||||
|
||||
int
|
||||
http_add_basic_proxy_authentication( HTTP_header_t *http_hdr, const char *username, const char *password ) {
|
||||
return http_add_authentication(http_hdr, username, password, "Proxy-Authorization");
|
||||
}
|
||||
|
||||
void
|
||||
http_debug_hdr( HTTP_header_t *http_hdr ) {
|
||||
HTTP_field_t *field;
|
||||
int i = 0;
|
||||
if( http_hdr==NULL ) return;
|
||||
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"--- HTTP DEBUG HEADER --- START ---\n");
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"protocol: [%s]\n", http_hdr->protocol );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"http minor version: [%d]\n", http_hdr->http_minor_version );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"uri: [%s]\n", http_hdr->uri );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"method: [%s]\n", http_hdr->method );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"status code: [%d]\n", http_hdr->status_code );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"reason phrase: [%s]\n", http_hdr->reason_phrase );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"body size: [%zd]\n", http_hdr->body_size );
|
||||
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Fields:\n");
|
||||
field = http_hdr->first_field;
|
||||
while( field!=NULL ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V," %d - %s\n", i++, field->field_name );
|
||||
field = field->next;
|
||||
}
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"--- HTTP DEBUG HEADER --- END ---\n");
|
||||
}
|
||||
|
||||
static void print_icy_metadata(HTTP_header_t *http_hdr) {
|
||||
const char *field_data;
|
||||
// note: I skip icy-notice1 and 2, as they contain html <BR>
|
||||
// and are IMHO useless info ::atmos
|
||||
if( (field_data = http_get_field(http_hdr, "icy-name")) != NULL )
|
||||
mp_msg(MSGT_NETWORK,MSGL_INFO,"Name : %s\n", field_data);
|
||||
if( (field_data = http_get_field(http_hdr, "icy-genre")) != NULL )
|
||||
mp_msg(MSGT_NETWORK,MSGL_INFO,"Genre : %s\n", field_data);
|
||||
if( (field_data = http_get_field(http_hdr, "icy-url")) != NULL )
|
||||
mp_msg(MSGT_NETWORK,MSGL_INFO,"Website: %s\n", field_data);
|
||||
// XXX: does this really mean public server? ::atmos
|
||||
if( (field_data = http_get_field(http_hdr, "icy-pub")) != NULL )
|
||||
mp_msg(MSGT_NETWORK,MSGL_INFO,"Public : %s\n", atoi(field_data)?"yes":"no");
|
||||
if( (field_data = http_get_field(http_hdr, "icy-br")) != NULL )
|
||||
mp_msg(MSGT_NETWORK,MSGL_INFO,"Bitrate: %skbit/s\n", field_data);
|
||||
}
|
||||
|
||||
//! If this function succeeds you must closesocket stream->fd
|
||||
static int http_streaming_start(stream_t *stream, int* file_format) {
|
||||
HTTP_header_t *http_hdr = NULL;
|
||||
int fd = stream->fd;
|
||||
int res = STREAM_UNSUPPORTED;
|
||||
int redirect = 0;
|
||||
int auth_retry=0;
|
||||
int seekable=0;
|
||||
char *content_type;
|
||||
const char *content_length;
|
||||
char *next_url;
|
||||
URL_t *url = stream->streaming_ctrl->url;
|
||||
|
||||
do
|
||||
{
|
||||
redirect = 0;
|
||||
if (fd >= 0) closesocket(fd);
|
||||
fd = http_send_request( url, 0 );
|
||||
if( fd<0 ) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
http_free(http_hdr);
|
||||
http_hdr = http_read_response( fd );
|
||||
if( http_hdr==NULL ) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if( mp_msg_test(MSGT_NETWORK,MSGL_V) ) {
|
||||
http_debug_hdr( http_hdr );
|
||||
}
|
||||
|
||||
// Check if we can make partial content requests and thus seek in http-streams
|
||||
if( http_hdr!=NULL && http_hdr->status_code==200 ) {
|
||||
const char *accept_ranges = http_get_field(http_hdr,"Accept-Ranges");
|
||||
const char *server = http_get_field(http_hdr, "Server");
|
||||
if (accept_ranges)
|
||||
seekable = strncmp(accept_ranges,"bytes",5)==0;
|
||||
else if (server && (strcmp(server, "gvs 1.0") == 0 ||
|
||||
strncmp(server, "MakeMKV", 7) == 0)) {
|
||||
// HACK for youtube and MakeMKV incorrectly claiming not to support seeking
|
||||
mp_msg(MSGT_NETWORK, MSGL_WARN, "Broken webserver, incorrectly claims to not support Accept-Ranges\n");
|
||||
seekable = 1;
|
||||
}
|
||||
}
|
||||
|
||||
print_icy_metadata(http_hdr);
|
||||
|
||||
// Check if the response is an ICY status_code reason_phrase
|
||||
if( !strcasecmp(http_hdr->protocol, "ICY") ||
|
||||
http_get_field(http_hdr, "Icy-MetaInt") ) {
|
||||
switch( http_hdr->status_code ) {
|
||||
case 200: { // OK
|
||||
char *field_data;
|
||||
// If content-type == video/nsv we most likely have a winamp video stream
|
||||
// otherwise it should be mp3. if there are more types consider adding mime type
|
||||
// handling like later
|
||||
if ( (field_data = http_get_field(http_hdr, "content-type")) != NULL && (!strcmp(field_data, "video/nsv") || !strcmp(field_data, "misc/ultravox")))
|
||||
*file_format = DEMUXER_TYPE_NSV;
|
||||
else if ( (field_data = http_get_field(http_hdr, "content-type")) != NULL && (!strcmp(field_data, "audio/aacp") || !strcmp(field_data, "audio/aac")))
|
||||
*file_format = DEMUXER_TYPE_AAC;
|
||||
else
|
||||
*file_format = DEMUXER_TYPE_LAVF;
|
||||
res = STREAM_ERROR;
|
||||
goto out;
|
||||
}
|
||||
case 400: // Server Full
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server is full, skipping!\n");
|
||||
goto err_out;
|
||||
case 401: // Service Unavailable
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server return service unavailable, skipping!\n");
|
||||
goto err_out;
|
||||
case 403: // Service Forbidden
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server return 'Service Forbidden'\n");
|
||||
goto err_out;
|
||||
case 404: // Resource Not Found
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: ICY-Server couldn't find requested stream, skipping!\n");
|
||||
goto err_out;
|
||||
default:
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Error: unhandled ICY-Errorcode, contact MPlayer developers!\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
// Assume standard http if not ICY
|
||||
switch( http_hdr->status_code ) {
|
||||
case 200: // OK
|
||||
content_length = http_get_field(http_hdr, "Content-Length");
|
||||
if (content_length) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Content-Length: [%s]\n", content_length);
|
||||
stream->end_pos = atoll(content_length);
|
||||
}
|
||||
// Look if we can use the Content-Type
|
||||
content_type = http_get_field( http_hdr, "Content-Type" );
|
||||
if( content_type!=NULL ) {
|
||||
unsigned int i;
|
||||
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Content-Type: [%s]\n", content_type );
|
||||
// Check in the mime type table for a demuxer type
|
||||
for (i = 0; mime_type_table[i].mime_type != NULL; i++) {
|
||||
if( !strcasecmp( content_type, mime_type_table[i].mime_type ) ) {
|
||||
*file_format = mime_type_table[i].demuxer_type;
|
||||
res = seekable;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not found in the mime type table, don't fail,
|
||||
// we should try raw HTTP
|
||||
res = seekable;
|
||||
goto out;
|
||||
// Redirect
|
||||
case 301: // Permanently
|
||||
case 302: // Temporarily
|
||||
case 303: // See Other
|
||||
case 307: // Temporarily (since HTTP/1.1)
|
||||
// TODO: RFC 2616, recommand to detect infinite redirection loops
|
||||
next_url = http_get_field( http_hdr, "Location" );
|
||||
if( next_url!=NULL ) {
|
||||
int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0;
|
||||
stream->streaming_ctrl->url = url_redirect( &url, next_url );
|
||||
if (url_is_protocol(url, "mms")) {
|
||||
res = STREAM_REDIRECTED;
|
||||
goto err_out;
|
||||
}
|
||||
if (!url_is_protocol(url, "http")) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Unsupported http %d redirect to %s protocol\n", http_hdr->status_code, url->protocol);
|
||||
goto err_out;
|
||||
}
|
||||
if (is_ultravox)
|
||||
url_set_protocol(url, "unsv");
|
||||
redirect = 1;
|
||||
}
|
||||
break;
|
||||
case 401: // Authentication required
|
||||
if( http_authenticate(http_hdr, url, &auth_retry)<0 )
|
||||
goto err_out;
|
||||
redirect = 1;
|
||||
break;
|
||||
default:
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"Server returned %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase );
|
||||
goto err_out;
|
||||
}
|
||||
} while( redirect );
|
||||
|
||||
err_out:
|
||||
if (fd >= 0) closesocket( fd );
|
||||
fd = -1;
|
||||
http_free( http_hdr );
|
||||
http_hdr = NULL;
|
||||
out:
|
||||
stream->streaming_ctrl->data = http_hdr;
|
||||
stream->fd = fd;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fixup_open(stream_t *stream,int seekable) {
|
||||
HTTP_header_t *http_hdr = stream->streaming_ctrl->data;
|
||||
int is_icy = http_hdr && http_get_field(http_hdr, "Icy-MetaInt");
|
||||
int is_ultravox = strcasecmp(stream->streaming_ctrl->url->protocol, "unsv") == 0;
|
||||
|
||||
stream->type = STREAMTYPE_STREAM;
|
||||
if(!is_icy && !is_ultravox && seekable)
|
||||
{
|
||||
stream->flags |= MP_STREAM_SEEK;
|
||||
stream->seek = http_seek;
|
||||
}
|
||||
stream->streaming_ctrl->bandwidth = network_bandwidth;
|
||||
if ((!is_icy && !is_ultravox) || scast_streaming_start(stream))
|
||||
if(nop_streaming_start( stream )) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"nop_streaming_start failed\n");
|
||||
if (stream->fd >= 0)
|
||||
closesocket(stream->fd);
|
||||
stream->fd = -1;
|
||||
streaming_ctrl_free(stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
static int open_s1(stream_t *stream,int mode, void* opts, int* file_format) {
|
||||
int seekable=0;
|
||||
|
||||
stream->streaming_ctrl = streaming_ctrl_new();
|
||||
if( stream->streaming_ctrl==NULL ) {
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
stream->streaming_ctrl->bandwidth = network_bandwidth;
|
||||
stream->streaming_ctrl->url = url_new_with_proxy(stream->url);
|
||||
|
||||
mp_msg(MSGT_OPEN, MSGL_V, "STREAM_HTTP(1), URL: %s\n", stream->url);
|
||||
seekable = http_streaming_start(stream, file_format);
|
||||
if((seekable < 0) || (*file_format == DEMUXER_TYPE_ASF)) {
|
||||
if (stream->fd >= 0)
|
||||
closesocket(stream->fd);
|
||||
stream->fd = -1;
|
||||
if (seekable == STREAM_REDIRECTED)
|
||||
return seekable;
|
||||
streaming_ctrl_free(stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return fixup_open(stream, seekable);
|
||||
}
|
||||
|
||||
static int open_s2(stream_t *stream,int mode, void* opts, int* file_format) {
|
||||
int seekable=0;
|
||||
|
||||
stream->streaming_ctrl = streaming_ctrl_new();
|
||||
if( stream->streaming_ctrl==NULL ) {
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
stream->streaming_ctrl->bandwidth = network_bandwidth;
|
||||
stream->streaming_ctrl->url = url_new_with_proxy(stream->url);
|
||||
|
||||
mp_msg(MSGT_OPEN, MSGL_V, "STREAM_HTTP(2), URL: %s\n", stream->url);
|
||||
seekable = http_streaming_start(stream, file_format);
|
||||
if(seekable < 0) {
|
||||
if (stream->fd >= 0)
|
||||
closesocket(stream->fd);
|
||||
stream->fd = -1;
|
||||
streaming_ctrl_free(stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return fixup_open(stream, seekable);
|
||||
}
|
||||
|
||||
|
||||
const stream_info_t stream_info_http1 = {
|
||||
"http streaming",
|
||||
"null",
|
||||
"Bertrand, Albeau, Reimar Doeffinger, Arpi?",
|
||||
"plain http",
|
||||
open_s1,
|
||||
{"mp_http", "mp_http_proxy", "unsv", "icyx", "noicyx", NULL},
|
||||
NULL,
|
||||
0 // Urls are an option string
|
||||
};
|
||||
|
||||
const stream_info_t stream_info_http2 = {
|
||||
"http streaming",
|
||||
"null",
|
||||
"Bertrand, Albeu, Arpi? who?",
|
||||
"plain http, also used as fallback for many other protocols",
|
||||
open_s2,
|
||||
{"mp_http", "mp_http_proxy", "pnm", "mms", "mmsu", "mmst", "rtsp", NULL}, //all the others as fallback
|
||||
NULL,
|
||||
0 // Urls are an option string
|
||||
};
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* HTTP Helper
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_HTTP_H
|
||||
#define MPLAYER_HTTP_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct HTTP_field_type {
|
||||
char *field_name;
|
||||
struct HTTP_field_type *next;
|
||||
} HTTP_field_t;
|
||||
|
||||
typedef struct {
|
||||
char *protocol;
|
||||
char *method;
|
||||
char *uri;
|
||||
unsigned int status_code;
|
||||
char *reason_phrase;
|
||||
unsigned int http_minor_version;
|
||||
// Field variables
|
||||
HTTP_field_t *first_field;
|
||||
HTTP_field_t *last_field;
|
||||
unsigned int field_nb;
|
||||
char *field_search;
|
||||
HTTP_field_t *field_search_pos;
|
||||
// Body variables
|
||||
char *body;
|
||||
size_t body_size;
|
||||
char *buffer;
|
||||
size_t buffer_size;
|
||||
unsigned int is_parsed;
|
||||
} HTTP_header_t;
|
||||
|
||||
HTTP_header_t* http_new_header(void);
|
||||
void http_free( HTTP_header_t *http_hdr );
|
||||
int http_response_append( HTTP_header_t *http_hdr, char *data, int length );
|
||||
int http_response_parse( HTTP_header_t *http_hdr );
|
||||
int http_is_header_entire( HTTP_header_t *http_hdr );
|
||||
char* http_build_request( HTTP_header_t *http_hdr );
|
||||
char* http_get_field( HTTP_header_t *http_hdr, const char *field_name );
|
||||
char* http_get_next_field( HTTP_header_t *http_hdr );
|
||||
void http_set_field( HTTP_header_t *http_hdr, const char *field_name );
|
||||
void http_set_method( HTTP_header_t *http_hdr, const char *method );
|
||||
void http_set_uri( HTTP_header_t *http_hdr, const char *uri );
|
||||
int http_add_basic_authentication( HTTP_header_t *http_hdr, const char *username, const char *password );
|
||||
int http_add_basic_proxy_authentication( HTTP_header_t *http_hdr, const char *username, const char *password );
|
||||
|
||||
void http_debug_hdr( HTTP_header_t *http_hdr );
|
||||
|
||||
#endif /* MPLAYER_HTTP_H */
|
455
stream/network.c
455
stream/network.c
@ -1,455 +0,0 @@
|
||||
/*
|
||||
* Network layer for MPlayer
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "core/options.h"
|
||||
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "stream.h"
|
||||
#include "demux/demux.h"
|
||||
#include "core/mp_common.h"
|
||||
#include "network.h"
|
||||
#include "tcp.h"
|
||||
#include "http.h"
|
||||
#include "cookies.h"
|
||||
#include "url.h"
|
||||
|
||||
/* IPv6 options */
|
||||
int network_ipv4_only_proxy = 0;
|
||||
|
||||
|
||||
const mime_struct_t mime_type_table[] = {
|
||||
// MP3 streaming, some MP3 streaming server answer with audio/mpeg
|
||||
{ "audio/mpeg", DEMUXER_TYPE_LAVF },
|
||||
// ASF
|
||||
{ "audio/x-ms-wax", DEMUXER_TYPE_ASF },
|
||||
{ "audio/x-ms-wma", DEMUXER_TYPE_ASF },
|
||||
{ "video/x-ms-asf", DEMUXER_TYPE_ASF },
|
||||
{ "video/x-ms-afs", DEMUXER_TYPE_ASF },
|
||||
{ "video/x-ms-wmv", DEMUXER_TYPE_ASF },
|
||||
{ "video/x-ms-wma", DEMUXER_TYPE_ASF },
|
||||
{ "application/x-mms-framed", DEMUXER_TYPE_ASF },
|
||||
{ "application/vnd.ms.wms-hdr.asfv1", DEMUXER_TYPE_ASF },
|
||||
// Playlists
|
||||
{ "video/x-ms-wmx", DEMUXER_TYPE_PLAYLIST },
|
||||
{ "video/x-ms-wvx", DEMUXER_TYPE_PLAYLIST },
|
||||
{ "audio/x-scpls", DEMUXER_TYPE_PLAYLIST },
|
||||
{ "audio/x-mpegurl", DEMUXER_TYPE_PLAYLIST },
|
||||
{ "audio/x-pls", DEMUXER_TYPE_PLAYLIST },
|
||||
// Real Media
|
||||
// { "audio/x-pn-realaudio", DEMUXER_TYPE_REAL },
|
||||
{ NULL, DEMUXER_TYPE_UNKNOWN},
|
||||
};
|
||||
|
||||
|
||||
streaming_ctrl_t *
|
||||
streaming_ctrl_new(void) {
|
||||
streaming_ctrl_t *streaming_ctrl = calloc(1, sizeof(*streaming_ctrl));
|
||||
if( streaming_ctrl==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
return streaming_ctrl;
|
||||
}
|
||||
|
||||
void
|
||||
streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl ) {
|
||||
if( streaming_ctrl==NULL ) return;
|
||||
if( streaming_ctrl->url ) url_free( streaming_ctrl->url );
|
||||
free(streaming_ctrl->buffer);
|
||||
free(streaming_ctrl->data);
|
||||
free(streaming_ctrl);
|
||||
}
|
||||
|
||||
URL_t*
|
||||
check4proxies( const URL_t *url ) {
|
||||
URL_t *url_out = NULL;
|
||||
if( url==NULL ) return NULL;
|
||||
url_out = url_new( url->url );
|
||||
if( !strcasecmp(url->protocol, "mp_http_proxy") ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Using HTTP proxy: http://%s:%d\n", url->hostname, url->port );
|
||||
return url_out;
|
||||
}
|
||||
// Check if the http_proxy environment variable is set.
|
||||
if( !strcasecmp(url->protocol, "mp_http") ) {
|
||||
char *proxy;
|
||||
proxy = getenv("http_proxy");
|
||||
if( proxy!=NULL ) {
|
||||
// We got a proxy, build the URL to use it
|
||||
char *new_url;
|
||||
URL_t *tmp_url;
|
||||
URL_t *proxy_url = url_new( proxy );
|
||||
|
||||
if( proxy_url==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_WARN,
|
||||
"Invalid proxy setting... Trying without proxy.\n");
|
||||
return url_out;
|
||||
}
|
||||
|
||||
#ifdef HAVE_AF_INET6
|
||||
if (network_ipv4_only_proxy && (gethostbyname(url->hostname)==NULL)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_WARN,
|
||||
"Could not resolve remote hostname for AF_INET. Trying without proxy.\n");
|
||||
url_free(proxy_url);
|
||||
return url_out;
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Using HTTP proxy: %s\n", proxy_url->url );
|
||||
new_url = get_http_proxy_url(proxy_url, url->url);
|
||||
if( new_url==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
url_free(proxy_url);
|
||||
return url_out;
|
||||
}
|
||||
tmp_url = url_new( new_url );
|
||||
if( tmp_url==NULL ) {
|
||||
free( new_url );
|
||||
url_free( proxy_url );
|
||||
return url_out;
|
||||
}
|
||||
url_free( url_out );
|
||||
url_out = tmp_url;
|
||||
free( new_url );
|
||||
url_free( proxy_url );
|
||||
}
|
||||
}
|
||||
return url_out;
|
||||
}
|
||||
|
||||
URL_t *url_new_with_proxy(const char *urlstr)
|
||||
{
|
||||
URL_t *url = url_new(urlstr);
|
||||
URL_t *url_with_proxy = check4proxies(url);
|
||||
url_free(url);
|
||||
return url_with_proxy;
|
||||
}
|
||||
|
||||
int
|
||||
http_send_request( URL_t *url, int64_t pos ) {
|
||||
HTTP_header_t *http_hdr;
|
||||
URL_t *server_url;
|
||||
char str[256];
|
||||
int fd = -1;
|
||||
int ret;
|
||||
int proxy = 0; // Boolean
|
||||
|
||||
http_hdr = http_new_header();
|
||||
|
||||
if( !strcasecmp(url->protocol, "mp_http_proxy") ) {
|
||||
proxy = 1;
|
||||
if (!strncasecmp(url->file, "/mp_", 3))
|
||||
server_url = url_new( (url->file)+4 );
|
||||
else
|
||||
server_url = url_new( (url->file)+1 );
|
||||
if (!server_url) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_ERR, "Invalid URL '%s' to proxify\n", url->file+1);
|
||||
goto err_out;
|
||||
}
|
||||
http_set_uri( http_hdr, server_url->noauth_url );
|
||||
} else {
|
||||
server_url = url;
|
||||
http_set_uri( http_hdr, server_url->file );
|
||||
}
|
||||
if (server_url->port && server_url->port != 80)
|
||||
snprintf(str, sizeof(str), "Host: %s:%d", server_url->hostname, server_url->port );
|
||||
else
|
||||
snprintf(str, sizeof(str), "Host: %s", server_url->hostname );
|
||||
http_set_field( http_hdr, str);
|
||||
if (network_useragent)
|
||||
snprintf(str, sizeof(str), "User-Agent: %s", network_useragent);
|
||||
else
|
||||
snprintf(str, sizeof(str), "User-Agent: %s", mplayer_version);
|
||||
http_set_field(http_hdr, str);
|
||||
|
||||
if (network_referrer) {
|
||||
char *referrer = NULL;
|
||||
size_t len = strlen(network_referrer) + 10;
|
||||
|
||||
// Check len to ensure we don't do something really bad in case of an overflow
|
||||
if (len > 10)
|
||||
referrer = malloc(len);
|
||||
|
||||
if (referrer == NULL) {
|
||||
mp_tmsg(MSGT_NETWORK, MSGL_FATAL, "Memory allocation failed.\n");
|
||||
} else {
|
||||
snprintf(referrer, len, "Referer: %s", network_referrer);
|
||||
http_set_field(http_hdr, referrer);
|
||||
free(referrer);
|
||||
}
|
||||
}
|
||||
|
||||
if( strcasecmp(url->protocol, "noicyx") )
|
||||
http_set_field(http_hdr, "Icy-MetaData: 1");
|
||||
|
||||
if(pos>0) {
|
||||
// Extend http_send_request with possibility to do partial content retrieval
|
||||
snprintf(str, sizeof(str), "Range: bytes=%"PRId64"-", (int64_t)pos);
|
||||
http_set_field(http_hdr, str);
|
||||
}
|
||||
|
||||
if (network_cookies_enabled) cookies_set( http_hdr, server_url->hostname, server_url->url );
|
||||
|
||||
if (network_http_header_fields) {
|
||||
int i=0;
|
||||
while (network_http_header_fields[i])
|
||||
http_set_field(http_hdr, network_http_header_fields[i++]);
|
||||
}
|
||||
|
||||
http_set_field( http_hdr, "Connection: close");
|
||||
if (proxy)
|
||||
http_add_basic_proxy_authentication(http_hdr, url->username, url->password);
|
||||
http_add_basic_authentication(http_hdr, server_url->username, server_url->password);
|
||||
if( http_build_request( http_hdr )==NULL ) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if( proxy ) {
|
||||
if( url->port==0 ) url->port = 8080; // Default port for the proxy server
|
||||
fd = connect2Server( url->hostname, url->port,1 );
|
||||
url_free( server_url );
|
||||
server_url = NULL;
|
||||
} else {
|
||||
if( server_url->port==0 ) server_url->port = 80; // Default port for the web server
|
||||
fd = connect2Server( server_url->hostname, server_url->port,1 );
|
||||
}
|
||||
if( fd<0 ) {
|
||||
goto err_out;
|
||||
}
|
||||
mp_msg(MSGT_NETWORK,MSGL_DBG2,"Request: [%s]\n", http_hdr->buffer );
|
||||
|
||||
ret = send( fd, http_hdr->buffer, http_hdr->buffer_size, DEFAULT_SEND_FLAGS );
|
||||
if( ret!=(int)http_hdr->buffer_size ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Error while sending HTTP request: Didn't send all the request.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
http_free( http_hdr );
|
||||
|
||||
return fd;
|
||||
err_out:
|
||||
if (fd > 0) closesocket(fd);
|
||||
http_free(http_hdr);
|
||||
if (proxy && server_url)
|
||||
url_free(server_url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
HTTP_header_t *
|
||||
http_read_response( int fd ) {
|
||||
HTTP_header_t *http_hdr;
|
||||
char response[BUFFER_SIZE];
|
||||
int i;
|
||||
|
||||
http_hdr = http_new_header();
|
||||
if( http_hdr==NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
i = recv( fd, response, BUFFER_SIZE, 0 );
|
||||
if( i<0 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Read failed.\n");
|
||||
http_free( http_hdr );
|
||||
return NULL;
|
||||
}
|
||||
if( i==0 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"http_read_response read 0 (i.e. EOF).\n");
|
||||
http_free( http_hdr );
|
||||
return NULL;
|
||||
}
|
||||
http_response_append( http_hdr, response, i );
|
||||
} while( !http_is_header_entire( http_hdr ) );
|
||||
if (http_response_parse( http_hdr ) < 0) {
|
||||
http_free( http_hdr );
|
||||
return NULL;
|
||||
}
|
||||
return http_hdr;
|
||||
}
|
||||
|
||||
int
|
||||
http_authenticate(HTTP_header_t *http_hdr, URL_t *url, int *auth_retry) {
|
||||
char *aut;
|
||||
|
||||
#define MPDEMUX_NW_AuthFailed _(\
|
||||
"Authentication failed. Please use the -user and -passwd options to provide your\n"\
|
||||
"username/password for a list of URLs, or form an URL like:\n"\
|
||||
"http://username:password@hostname/file\n")
|
||||
|
||||
|
||||
if( *auth_retry==1 ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,MPDEMUX_NW_AuthFailed);
|
||||
return -1;
|
||||
}
|
||||
if( *auth_retry>0 ) {
|
||||
free(url->username);
|
||||
url->username = NULL;
|
||||
free(url->password);
|
||||
url->password = NULL;
|
||||
}
|
||||
|
||||
aut = http_get_field(http_hdr, "WWW-Authenticate");
|
||||
if( aut!=NULL ) {
|
||||
char *aut_space;
|
||||
aut_space = strstr(aut, "realm=");
|
||||
if( aut_space!=NULL ) aut_space += 6;
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"Authentication required for %s\n", aut_space);
|
||||
} else {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"Authentication required.\n");
|
||||
}
|
||||
if( network_username ) {
|
||||
url->username = strdup(network_username);
|
||||
if( url->username==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,MPDEMUX_NW_AuthFailed);
|
||||
return -1;
|
||||
}
|
||||
if( network_password ) {
|
||||
url->password = strdup(network_password);
|
||||
if( url->password==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_INFO,"No password provided, trying blank password.\n");
|
||||
}
|
||||
(*auth_retry)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
http_seek( stream_t *stream, int64_t pos ) {
|
||||
HTTP_header_t *http_hdr = NULL;
|
||||
int fd;
|
||||
if( stream==NULL ) return 0;
|
||||
|
||||
if( stream->fd>0 ) closesocket(stream->fd); // need to reconnect to seek in http-stream
|
||||
fd = http_send_request( stream->streaming_ctrl->url, pos );
|
||||
if( fd<0 ) return 0;
|
||||
|
||||
http_hdr = http_read_response( fd );
|
||||
|
||||
if( http_hdr==NULL ) return 0;
|
||||
|
||||
if( mp_msg_test(MSGT_NETWORK,MSGL_V) )
|
||||
http_debug_hdr( http_hdr );
|
||||
|
||||
switch( http_hdr->status_code ) {
|
||||
case 200:
|
||||
case 206: // OK
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Content-Type: [%s]\n", http_get_field(http_hdr, "Content-Type") );
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Content-Length: [%s]\n", http_get_field(http_hdr, "Content-Length") );
|
||||
if( http_hdr->body_size>0 ) {
|
||||
if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
|
||||
http_free( http_hdr );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Server returns %d: %s\n", http_hdr->status_code, http_hdr->reason_phrase );
|
||||
closesocket( fd );
|
||||
fd = -1;
|
||||
}
|
||||
stream->fd = fd;
|
||||
|
||||
if( http_hdr ) {
|
||||
http_free( http_hdr );
|
||||
stream->streaming_ctrl->data = NULL;
|
||||
}
|
||||
|
||||
stream->pos=pos;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
streaming_bufferize( streaming_ctrl_t *streaming_ctrl, char *buffer, int size) {
|
||||
//printf("streaming_bufferize\n");
|
||||
streaming_ctrl->buffer = malloc(size);
|
||||
if( streaming_ctrl->buffer==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy( streaming_ctrl->buffer, buffer, size );
|
||||
streaming_ctrl->buffer_size = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int
|
||||
nop_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl ) {
|
||||
int len=0;
|
||||
//printf("nop_streaming_read\n");
|
||||
if( stream_ctrl->buffer_size!=0 ) {
|
||||
int buffer_len = stream_ctrl->buffer_size-stream_ctrl->buffer_pos;
|
||||
//printf("%d bytes in buffer\n", stream_ctrl->buffer_size);
|
||||
len = (size<buffer_len)?size:buffer_len;
|
||||
memcpy( buffer, (stream_ctrl->buffer)+(stream_ctrl->buffer_pos), len );
|
||||
stream_ctrl->buffer_pos += len;
|
||||
//printf("buffer_pos = %d\n", stream_ctrl->buffer_pos );
|
||||
if( stream_ctrl->buffer_pos>=stream_ctrl->buffer_size ) {
|
||||
free( stream_ctrl->buffer );
|
||||
stream_ctrl->buffer = NULL;
|
||||
stream_ctrl->buffer_size = 0;
|
||||
stream_ctrl->buffer_pos = 0;
|
||||
//printf("buffer cleaned\n");
|
||||
}
|
||||
//printf("read %d bytes from buffer\n", len );
|
||||
}
|
||||
|
||||
if( len<size ) {
|
||||
int ret;
|
||||
ret = recv( fd, buffer+len, size-len, 0 );
|
||||
if( ret<0 ) {
|
||||
mp_msg(MSGT_NETWORK,MSGL_ERR,"nop_streaming_read error : %s\n",strerror(errno));
|
||||
ret = 0;
|
||||
} else if (ret == 0)
|
||||
stream_ctrl->status = streaming_stopped_e;
|
||||
len += ret;
|
||||
//printf("read %d bytes from network\n", len );
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
nop_streaming_seek( int fd, int64_t pos, streaming_ctrl_t *stream_ctrl ) {
|
||||
return -1;
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Network layer for MPlayer
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_NETWORK_H
|
||||
#define MPLAYER_NETWORK_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "config.h"
|
||||
#if !HAVE_WINSOCK2_H
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "stream.h"
|
||||
#include "url.h"
|
||||
#include "http.h"
|
||||
|
||||
#ifdef MSG_NOSIGNAL
|
||||
#define DEFAULT_SEND_FLAGS MSG_NOSIGNAL
|
||||
#else
|
||||
#define DEFAULT_SEND_FLAGS 0
|
||||
#endif
|
||||
|
||||
#if !HAVE_CLOSESOCKET
|
||||
#define closesocket close
|
||||
#endif
|
||||
#if !HAVE_SOCKLEN_T
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE 2048
|
||||
|
||||
typedef struct {
|
||||
const char *mime_type;
|
||||
int demuxer_type;
|
||||
} mime_struct_t;
|
||||
|
||||
extern const mime_struct_t mime_type_table[];
|
||||
|
||||
extern int network_prefer_ipv4;
|
||||
extern int network_ipv4_only_proxy;
|
||||
extern int reuse_socket;
|
||||
|
||||
streaming_ctrl_t *streaming_ctrl_new(void);
|
||||
int streaming_bufferize( streaming_ctrl_t *streaming_ctrl, char *buffer, int size);
|
||||
|
||||
int nop_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl );
|
||||
int nop_streaming_seek( int fd, int64_t pos, streaming_ctrl_t *stream_ctrl );
|
||||
void streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl );
|
||||
|
||||
int http_send_request(URL_t *url, int64_t pos);
|
||||
HTTP_header_t *http_read_response(int fd);
|
||||
|
||||
int http_authenticate(HTTP_header_t *http_hdr, URL_t *url, int *auth_retry);
|
||||
URL_t* check4proxies(const URL_t *url);
|
||||
URL_t *url_new_with_proxy(const char *urlstr);
|
||||
|
||||
int http_seek(stream_t *stream, int64_t pos);
|
||||
|
||||
#endif /* MPLAYER_NETWORK_H */
|
@ -37,14 +37,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include "core/bstr.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "network.h"
|
||||
#include "stream.h"
|
||||
#include "demux/demux.h"
|
||||
|
||||
@ -96,12 +91,6 @@ static const stream_info_t *const auto_open_streams[] = {
|
||||
#endif
|
||||
&stream_info_ffmpeg, // use for rstp:// before http fallback
|
||||
&stream_info_avdevice,
|
||||
#ifdef CONFIG_NETWORKING
|
||||
&stream_info_http1,
|
||||
&stream_info_asf,
|
||||
&stream_info_udp,
|
||||
&stream_info_http2,
|
||||
#endif
|
||||
#ifdef CONFIG_DVBIN
|
||||
&stream_info_dvb,
|
||||
#endif
|
||||
@ -174,16 +163,6 @@ static stream_t *open_stream_plugin(const stream_info_t *sinfo,
|
||||
s->flags |= mode;
|
||||
*ret = sinfo->open(s, mode, arg, file_format);
|
||||
if ((*ret) != STREAM_OK) {
|
||||
#ifdef CONFIG_NETWORKING
|
||||
if (*ret == STREAM_REDIRECTED && redirected_url) {
|
||||
if (s->streaming_ctrl && s->streaming_ctrl->url
|
||||
&& s->streaming_ctrl->url->url)
|
||||
*redirected_url = strdup(s->streaming_ctrl->url->url);
|
||||
else
|
||||
*redirected_url = NULL;
|
||||
}
|
||||
streaming_ctrl_free(s->streaming_ctrl);
|
||||
#endif
|
||||
free(s->url);
|
||||
talloc_free(s);
|
||||
return NULL;
|
||||
@ -363,18 +342,10 @@ static int stream_read_unbuffered(stream_t *s, void *buf, int len)
|
||||
// we will retry even if we already reached EOF previously.
|
||||
switch (s->type) {
|
||||
case STREAMTYPE_STREAM:
|
||||
if (s->streaming_ctrl != NULL && s->streaming_ctrl->streaming_read) {
|
||||
len = s->streaming_ctrl->streaming_read(s->fd, buf, len,
|
||||
s->streaming_ctrl);
|
||||
if (s->streaming_ctrl->status == streaming_stopped_e &&
|
||||
(!s->end_pos || s->pos == s->end_pos))
|
||||
s->eof = 1;
|
||||
} else {
|
||||
if (s->fill_buffer)
|
||||
len = s->fill_buffer(s, buf, len);
|
||||
else
|
||||
len = read(s->fd, buf, len);
|
||||
}
|
||||
if (s->fill_buffer)
|
||||
len = s->fill_buffer(s, buf, len);
|
||||
else
|
||||
len = read(s->fd, buf, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -508,25 +479,6 @@ static int stream_seek_unbuffered(stream_t *s, int64_t newpos)
|
||||
// Some streaming protocol allow to seek backward and forward
|
||||
// A function call that return -1 can tell that the protocol
|
||||
// doesn't support seeking.
|
||||
#ifdef CONFIG_NETWORKING
|
||||
if (s->seek) {
|
||||
if (!s->seek(s, newpos)) {
|
||||
mp_tmsg(MSGT_STREAM, MSGL_ERR, "Seek failed\n");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->streaming_ctrl != NULL &&
|
||||
s->streaming_ctrl->streaming_seek) {
|
||||
if (s->streaming_ctrl->streaming_seek(s->fd, newpos,
|
||||
s->streaming_ctrl) < 0) {
|
||||
mp_tmsg(MSGT_STREAM, MSGL_INFO, "Stream not seekable!\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (newpos < s->pos) {
|
||||
mp_tmsg(MSGT_STREAM, MSGL_INFO,
|
||||
"Cannot seek backward in linear streams!\n");
|
||||
@ -675,14 +627,6 @@ static stream_t *new_stream(size_t min_size)
|
||||
stream_t *s = talloc_size(NULL, sizeof(stream_t) + min_size);
|
||||
memset(s, 0, sizeof(stream_t));
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
{
|
||||
WSADATA wsdata;
|
||||
int temp = WSAStartup(0x0202, &wsdata); // there might be a better place for this (-> later)
|
||||
mp_msg(MSGT_STREAM, MSGL_V, "WINSOCK2 init: %i\n", temp);
|
||||
}
|
||||
#endif
|
||||
|
||||
s->fd = -2;
|
||||
s->type = -2;
|
||||
return s;
|
||||
@ -695,18 +639,8 @@ void free_stream(stream_t *s)
|
||||
if (s->close)
|
||||
s->close(s);
|
||||
if (s->fd > 0) {
|
||||
/* on unix we define closesocket to close
|
||||
on windows however we have to distinguish between
|
||||
network socket and file */
|
||||
if (s->url && strstr(s->url, "://"))
|
||||
closesocket(s->fd);
|
||||
else
|
||||
close(s->fd);
|
||||
close(s->fd);
|
||||
}
|
||||
#if HAVE_WINSOCK2_H
|
||||
mp_msg(MSGT_STREAM, MSGL_V, "WINSOCK2 uninit\n");
|
||||
WSACleanup(); // there might be a better place for this (-> later)
|
||||
#endif
|
||||
free(s->url);
|
||||
if (s->uncached_stream)
|
||||
free_stream(s->uncached_stream);
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "url.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -116,29 +115,6 @@ struct stream_dvd_info_req {
|
||||
int num_subs;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
streaming_stopped_e,
|
||||
streaming_playing_e
|
||||
} streaming_status;
|
||||
|
||||
// All this is for legacy http streams (and other things using tcp/udp)
|
||||
typedef struct streaming_control {
|
||||
URL_t *url;
|
||||
streaming_status status;
|
||||
char *buffer;
|
||||
unsigned int buffer_size;
|
||||
unsigned int buffer_pos;
|
||||
unsigned int bandwidth; // The downstream available
|
||||
int (*streaming_read)(int fd, char *buffer, int buffer_size,
|
||||
struct streaming_control *stream_ctrl);
|
||||
int (*streaming_seek)(int fd, int64_t pos,
|
||||
struct streaming_control *stream_ctrl);
|
||||
void *data;
|
||||
// hacks for asf
|
||||
int *audio_id_ptr;
|
||||
int *video_id_ptr;
|
||||
} streaming_ctrl_t;
|
||||
|
||||
struct stream;
|
||||
typedef struct stream_info_st {
|
||||
const char *info;
|
||||
@ -185,7 +161,6 @@ typedef struct stream {
|
||||
char *mime_type; // when HTTP streaming is used
|
||||
char *lavf_type; // name of expected demuxer type for lavf
|
||||
struct MPOpts *opts;
|
||||
streaming_ctrl_t *streaming_ctrl;
|
||||
|
||||
FILE *capture_file;
|
||||
char *capture_filename;
|
||||
@ -196,10 +171,6 @@ typedef struct stream {
|
||||
unsigned char buffer[];
|
||||
} stream_t;
|
||||
|
||||
#ifdef CONFIG_NETWORKING
|
||||
#include "network.h"
|
||||
#endif
|
||||
|
||||
int stream_fill_buffer(stream_t *s);
|
||||
|
||||
void stream_set_capture_file(stream_t *s, const char *filename);
|
||||
|
@ -340,11 +340,10 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format)
|
||||
int offset = p->toc_offset;
|
||||
cdrom_drive_t *cdd = NULL;
|
||||
cdda_priv *priv;
|
||||
cd_info_t *cd_info, *cddb_info = NULL;
|
||||
cd_info_t *cd_info;
|
||||
unsigned int audiolen = 0;
|
||||
int last_track;
|
||||
int i;
|
||||
char *xmcd_file = NULL;
|
||||
|
||||
if (m != STREAM_READ) {
|
||||
m_struct_free(&stream_opts, opts);
|
||||
@ -358,18 +357,6 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format)
|
||||
p->device = talloc_strdup(NULL, DEFAULT_CDROM_DEVICE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CDDB
|
||||
// cdd_identify returns -1 if it cannot read the TOC,
|
||||
// in which case there is no point in calling cddb_resolve
|
||||
if (cdd_identify(p->device) >= 0 && strncmp(st->url, "cddb", 4) == 0) {
|
||||
i = cddb_resolve(p->device, &xmcd_file);
|
||||
if (i == 0) {
|
||||
cddb_info = cddb_parse_xmcd(xmcd_file);
|
||||
free(xmcd_file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
cdd = cdda_identify_scsi(p->device, p->device, 0, NULL);
|
||||
#else
|
||||
@ -379,7 +366,6 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format)
|
||||
if (!cdd) {
|
||||
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Can't open CDDA device.\n");
|
||||
m_struct_free(&stream_opts, opts);
|
||||
free(cddb_info);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
@ -392,7 +378,6 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format)
|
||||
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Can't open disc.\n");
|
||||
cdda_close(cdd);
|
||||
m_struct_free(&stream_opts, opts);
|
||||
free(cddb_info);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
@ -455,7 +440,6 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format)
|
||||
free(priv);
|
||||
cd_info_free(cd_info);
|
||||
m_struct_free(&stream_opts, opts);
|
||||
free(cddb_info);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
@ -484,14 +468,6 @@ static int open_cdda(stream_t *st, int m, void *opts, int *file_format)
|
||||
paranoia_seek(priv->cdp, priv->start_sector, SEEK_SET);
|
||||
priv->sector = priv->start_sector;
|
||||
|
||||
#ifdef CONFIG_CDDB
|
||||
if (cddb_info) {
|
||||
cd_info_free(cd_info);
|
||||
priv->cd_info = cddb_info;
|
||||
cd_info_debug(cddb_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
st->priv = priv;
|
||||
st->start_pos = priv->start_sector * CDIO_CD_FRAMESIZE_RAW;
|
||||
st->end_pos = (priv->end_sector + 1) * CDIO_CD_FRAMESIZE_RAW;
|
||||
@ -518,11 +494,7 @@ const stream_info_t stream_info_cdda = {
|
||||
"Albeu",
|
||||
"",
|
||||
open_cdda,
|
||||
{"cdda",
|
||||
#ifdef CONFIG_CDDB
|
||||
"cddb",
|
||||
#endif
|
||||
NULL },
|
||||
{"cdda", NULL },
|
||||
&stream_opts,
|
||||
.opts_url = 1,
|
||||
};
|
||||
|
@ -1,902 +0,0 @@
|
||||
/*
|
||||
* CDDB HTTP protocol
|
||||
*
|
||||
* Copyright (C) 2002 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* Implementation follow the freedb.howto1.06.txt specification
|
||||
* from http://freedb.freedb.org
|
||||
*
|
||||
* discid computation by Jeremy D. Zawodny
|
||||
* Copyright (c) 1998-2000 Jeremy D. Zawodny <Jeremy@Zawodny.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include "osdep/io.h"
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#include <windows.h>
|
||||
#if HAVE_WINSOCK2_H
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "core/mp_msg.h"
|
||||
#include "core/path.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/cdrom.h>
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
#include <sys/cdio.h>
|
||||
#elif defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
#include <ntddcdrm.h>
|
||||
#elif defined(__bsdi__)
|
||||
#include <dvd.h>
|
||||
#elif defined(__APPLE__) || defined(__DARWIN__)
|
||||
#include <IOKit/storage/IOCDTypes.h>
|
||||
#include <IOKit/storage/IOCDMediaBSDClient.h>
|
||||
#include "compat/mpbswap.h"
|
||||
#endif
|
||||
|
||||
#include "cdd.h"
|
||||
#include "core/mp_common.h"
|
||||
#include "stream.h"
|
||||
#include "network.h"
|
||||
#include "libavutil/common.h"
|
||||
|
||||
#define DEFAULT_FREEDB_SERVER "freedb.freedb.org"
|
||||
#define DEFAULT_CACHE_DIR "/.cddb/"
|
||||
|
||||
typedef struct {
|
||||
char cddb_hello[1024];
|
||||
unsigned long disc_id;
|
||||
unsigned int tracks;
|
||||
char *cache_dir;
|
||||
char *freedb_server;
|
||||
int freedb_proto_level;
|
||||
int anonymous;
|
||||
char category[100];
|
||||
char *xmcd_file;
|
||||
size_t xmcd_file_size;
|
||||
void *user_data;
|
||||
} cddb_data_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int min, sec, frame;
|
||||
} cd_toc_t;
|
||||
|
||||
static cd_toc_t cdtoc[100];
|
||||
static int cdtoc_last_track;
|
||||
|
||||
static int read_toc(const char *dev)
|
||||
{
|
||||
int first = 0, last = -1;
|
||||
int i;
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
HANDLE drive;
|
||||
DWORD r;
|
||||
CDROM_TOC toc;
|
||||
char device[10];
|
||||
|
||||
snprintf(device, sizeof(device), "\\\\.\\%s", dev);
|
||||
drive = CreateFile(device, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
|
||||
if (!DeviceIoControl(drive, IOCTL_CDROM_READ_TOC, NULL, 0, &toc,
|
||||
sizeof(CDROM_TOC), &r, 0)) {
|
||||
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to read TOC.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
first = toc.FirstTrack - 1; last = toc.LastTrack;
|
||||
for (i = first; i <= last; i++) {
|
||||
cdtoc[i].min = toc.TrackData[i].Address[1];
|
||||
cdtoc[i].sec = toc.TrackData[i].Address[2];
|
||||
cdtoc[i].frame = toc.TrackData[i].Address[3];
|
||||
}
|
||||
CloseHandle(drive);
|
||||
|
||||
#else
|
||||
int drive;
|
||||
drive = open(dev, O_RDONLY | O_NONBLOCK);
|
||||
if (drive < 0) {
|
||||
return drive;
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__bsdi__)
|
||||
{
|
||||
struct cdrom_tochdr tochdr;
|
||||
ioctl(drive, CDROMREADTOCHDR, &tochdr);
|
||||
first = tochdr.cdth_trk0 - 1; last = tochdr.cdth_trk1;
|
||||
}
|
||||
for (i = first; i <= last; i++) {
|
||||
struct cdrom_tocentry tocentry;
|
||||
tocentry.cdte_track = (i == last) ? 0xAA : i + 1;
|
||||
tocentry.cdte_format = CDROM_MSF;
|
||||
ioctl(drive, CDROMREADTOCENTRY, &tocentry);
|
||||
cdtoc[i].min = tocentry.cdte_addr.msf.minute;
|
||||
cdtoc[i].sec = tocentry.cdte_addr.msf.second;
|
||||
cdtoc[i].frame = tocentry.cdte_addr.msf.frame;
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
||||
{
|
||||
struct ioc_toc_header tochdr;
|
||||
ioctl(drive, CDIOREADTOCHEADER, &tochdr);
|
||||
first = tochdr.starting_track - 1; last = tochdr.ending_track;
|
||||
}
|
||||
for (i = first; i <= last; i++) {
|
||||
struct ioc_read_toc_single_entry tocentry;
|
||||
tocentry.track = (i == last) ? 0xAA : i + 1;
|
||||
tocentry.address_format = CD_MSF_FORMAT;
|
||||
ioctl(drive, CDIOREADTOCENTRY, &tocentry);
|
||||
cdtoc[i].min = tocentry.entry.addr.msf.minute;
|
||||
cdtoc[i].sec = tocentry.entry.addr.msf.second;
|
||||
cdtoc[i].frame = tocentry.entry.addr.msf.frame;
|
||||
}
|
||||
#elif defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
{
|
||||
struct ioc_toc_header tochdr;
|
||||
ioctl(drive, CDIOREADTOCHEADER, &tochdr);
|
||||
first = tochdr.starting_track - 1; last = tochdr.ending_track;
|
||||
}
|
||||
for (i = first; i <= last; i++) {
|
||||
struct ioc_read_toc_entry tocentry;
|
||||
struct cd_toc_entry toc_buffer;
|
||||
tocentry.starting_track = (i == last) ? 0xAA : i + 1;
|
||||
tocentry.address_format = CD_MSF_FORMAT;
|
||||
tocentry.data = &toc_buffer;
|
||||
tocentry.data_len = sizeof(toc_buffer);
|
||||
ioctl(drive, CDIOREADTOCENTRYS, &tocentry);
|
||||
cdtoc[i].min = toc_buffer.addr.msf.minute;
|
||||
cdtoc[i].sec = toc_buffer.addr.msf.second;
|
||||
cdtoc[i].frame = toc_buffer.addr.msf.frame;
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__DARWIN__)
|
||||
{
|
||||
dk_cd_read_toc_t tochdr;
|
||||
uint8_t buf[4];
|
||||
uint8_t buf2[100 * sizeof(CDTOCDescriptor) + sizeof(CDTOC)];
|
||||
memset(&tochdr, 0, sizeof(tochdr));
|
||||
tochdr.bufferLength = sizeof(buf);
|
||||
tochdr.buffer = &buf;
|
||||
if (!ioctl(drive, DKIOCCDREADTOC, &tochdr)
|
||||
&& tochdr.bufferLength == sizeof(buf)) {
|
||||
first = buf[2] - 1;
|
||||
last = buf[3];
|
||||
}
|
||||
if (last >= 0) {
|
||||
memset(&tochdr, 0, sizeof(tochdr));
|
||||
tochdr.bufferLength = sizeof(buf2);
|
||||
tochdr.buffer = &buf2;
|
||||
tochdr.format = kCDTOCFormatTOC;
|
||||
if (ioctl(drive, DKIOCCDREADTOC, &tochdr)
|
||||
|| tochdr.bufferLength < sizeof(CDTOC))
|
||||
last = -1;
|
||||
}
|
||||
if (last >= 0) {
|
||||
CDTOC *cdToc = (CDTOC *)buf2;
|
||||
CDTrackInfo lastTrack;
|
||||
dk_cd_read_track_info_t trackInfoParams;
|
||||
for (i = first; i < last; ++i) {
|
||||
CDMSF msf = CDConvertTrackNumberToMSF(i + 1, cdToc);
|
||||
cdtoc[i].min = msf.minute;
|
||||
cdtoc[i].sec = msf.second;
|
||||
cdtoc[i].frame = msf.frame;
|
||||
}
|
||||
memset(&trackInfoParams, 0, sizeof(trackInfoParams));
|
||||
trackInfoParams.addressType = kCDTrackInfoAddressTypeTrackNumber;
|
||||
trackInfoParams.bufferLength = sizeof(lastTrack);
|
||||
trackInfoParams.address = last;
|
||||
trackInfoParams.buffer = &lastTrack;
|
||||
if (!ioctl(drive, DKIOCCDREADTRACKINFO, &trackInfoParams)) {
|
||||
CDMSF msf = CDConvertLBAToMSF(be2me_32(lastTrack.trackStartAddress)
|
||||
+ be2me_32(lastTrack.trackSize));
|
||||
cdtoc[last].min = msf.minute;
|
||||
cdtoc[last].sec = msf.second;
|
||||
cdtoc[last].frame = msf.frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
close(drive);
|
||||
#endif
|
||||
for (i = first; i <= last; i++)
|
||||
cdtoc[i].frame += (cdtoc[i].min * 60 + cdtoc[i].sec) * 75;
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Reads TOC from CD in the given device and prints the number of tracks
|
||||
and the length of each track in minute:second:frame format.
|
||||
\param *dev the device to analyse
|
||||
\return if the command line -identify is given, returns the last track of
|
||||
the TOC or -1 if the TOC can't be read,
|
||||
otherwise just returns 0 and let cddb_resolve the TOC
|
||||
*/
|
||||
int cdd_identify(const char *dev)
|
||||
{
|
||||
cdtoc_last_track = 0;
|
||||
if (mp_msg_test(MSGT_IDENTIFY, MSGL_INFO)) {
|
||||
int i, min, sec, frame;
|
||||
cdtoc_last_track = read_toc(dev);
|
||||
if (cdtoc_last_track < 0) {
|
||||
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s device.\n", dev);
|
||||
return -1;
|
||||
}
|
||||
mp_msg(MSGT_GLOBAL, MSGL_INFO, "ID_CDDA_TRACKS=%d\n", cdtoc_last_track);
|
||||
for (i = 1; i <= cdtoc_last_track; i++) {
|
||||
frame = cdtoc[i].frame - cdtoc[i-1].frame;
|
||||
sec = frame / 75;
|
||||
frame -= sec * 75;
|
||||
min = sec / 60;
|
||||
sec -= min * 60;
|
||||
mp_msg(MSGT_IDENTIFY, MSGL_INFO,
|
||||
"ID_CDDA_TRACK_%d_MSF=%02d:%02d:%02d\n", i, min, sec, frame);
|
||||
}
|
||||
}
|
||||
return cdtoc_last_track;
|
||||
}
|
||||
|
||||
static unsigned int cddb_sum(int n)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
ret = 0;
|
||||
while (n > 0) {
|
||||
ret += (n % 10);
|
||||
n /= 10;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long cddb_discid(int tot_trks)
|
||||
{
|
||||
unsigned int i, t = 0, n = 0;
|
||||
|
||||
i = 0;
|
||||
while (i < (unsigned int)tot_trks) {
|
||||
n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
|
||||
i++;
|
||||
}
|
||||
t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) -
|
||||
((cdtoc[0].min * 60) + cdtoc[0].sec);
|
||||
return (n % 0xff) << 24 | t << 8 | tot_trks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cddb_http_request(char *command,
|
||||
int (*reply_parser)(HTTP_header_t*, cddb_data_t*),
|
||||
cddb_data_t *cddb_data)
|
||||
{
|
||||
char request[4096];
|
||||
int fd, ret = 0;
|
||||
URL_t *url;
|
||||
HTTP_header_t *http_hdr;
|
||||
|
||||
if (reply_parser == NULL || command == NULL || cddb_data == NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(request, sizeof(request), "http://%s/~cddb/cddb.cgi?cmd=%s%s&proto=%d",
|
||||
cddb_data->freedb_server, command, cddb_data->cddb_hello,
|
||||
cddb_data->freedb_proto_level);
|
||||
mp_msg(MSGT_OPEN, MSGL_INFO,"Request[%s]\n", request);
|
||||
|
||||
url = url_new(request);
|
||||
if (url == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "not a valid URL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = http_send_request(url,0);
|
||||
if (fd < 0) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to send the HTTP request.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
http_hdr = http_read_response(fd);
|
||||
if (http_hdr == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to read the HTTP response.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
http_debug_hdr(http_hdr);
|
||||
mp_msg(MSGT_OPEN, MSGL_INFO,"body=[%s]\n", http_hdr->body);
|
||||
|
||||
switch (http_hdr->status_code) {
|
||||
case 200:
|
||||
ret = reply_parser(http_hdr, cddb_data);
|
||||
break;
|
||||
case 400:
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Not Found.\n");
|
||||
break;
|
||||
default:
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "unknown error code\n");
|
||||
}
|
||||
|
||||
http_free(http_hdr);
|
||||
url_free(url);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cddb_read_cache(cddb_data_t *cddb_data)
|
||||
{
|
||||
char file_name[100];
|
||||
struct stat stats;
|
||||
int file_fd, ret;
|
||||
size_t file_size;
|
||||
|
||||
if (cddb_data == NULL || cddb_data->cache_dir == NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id);
|
||||
|
||||
file_fd = open(file_name, O_RDONLY | O_BINARY);
|
||||
if (file_fd < 0) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "No cache found.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fstat(file_fd, &stats);
|
||||
if (ret < 0) {
|
||||
perror("fstat");
|
||||
file_size = 4096;
|
||||
} else {
|
||||
file_size = stats.st_size < UINT_MAX ? stats.st_size : UINT_MAX - 1;
|
||||
}
|
||||
|
||||
cddb_data->xmcd_file = malloc(file_size + 1);
|
||||
if (cddb_data->xmcd_file == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Memory allocation failed.\n");
|
||||
close(file_fd);
|
||||
return -1;
|
||||
}
|
||||
cddb_data->xmcd_file_size = read(file_fd, cddb_data->xmcd_file, file_size);
|
||||
if (cddb_data->xmcd_file_size != file_size) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Not all the xmcd file has been read.\n");
|
||||
close(file_fd);
|
||||
return -1;
|
||||
}
|
||||
cddb_data->xmcd_file[cddb_data->xmcd_file_size] = 0;
|
||||
|
||||
close(file_fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cddb_write_cache(cddb_data_t *cddb_data)
|
||||
{
|
||||
// We have the file, save it for cache.
|
||||
char file_name[100];
|
||||
int file_fd, ret;
|
||||
int wrote = 0;
|
||||
|
||||
if (cddb_data == NULL || cddb_data->cache_dir == NULL)
|
||||
return -1;
|
||||
|
||||
// Check if the CDDB cache dir exist
|
||||
if (!mp_path_exists(cddb_data->cache_dir)) {
|
||||
// Directory not present, create it.
|
||||
ret = mkdir(cddb_data->cache_dir, 0755);
|
||||
#ifdef __MINGW32__
|
||||
if (ret < 0 && errno != EEXIST) {
|
||||
#else
|
||||
if (ret < 0) {
|
||||
#endif
|
||||
perror("mkdir");
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to create directory %s.\n",
|
||||
cddb_data->cache_dir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "%s%08lx", cddb_data->cache_dir, cddb_data->disc_id);
|
||||
|
||||
file_fd = creat(file_name, S_IRUSR | S_IWUSR);
|
||||
if (file_fd < 0) {
|
||||
perror("create");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wrote = write(file_fd, cddb_data->xmcd_file, cddb_data->xmcd_file_size);
|
||||
if (wrote < 0) {
|
||||
perror("write");
|
||||
close(file_fd);
|
||||
return -1;
|
||||
}
|
||||
if ((unsigned int) wrote != cddb_data->xmcd_file_size) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Not all of the xmcd file has been written.\n");
|
||||
close(file_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(file_fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cddb_read_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
|
||||
{
|
||||
unsigned long disc_id;
|
||||
char category[100];
|
||||
char *ptr = NULL, *ptr2 = NULL;
|
||||
int ret, status;
|
||||
|
||||
if (http_hdr == NULL || cddb_data == NULL)
|
||||
return -1;
|
||||
|
||||
ret = sscanf(http_hdr->body, "%d ", &status);
|
||||
if (ret != 1) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 210:
|
||||
ret = sscanf(http_hdr->body, "%d %99s %08lx", &status,
|
||||
category, &disc_id);
|
||||
if (ret != 3) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
// Check if it's a xmcd database file
|
||||
ptr = strstr(http_hdr->body, "# xmcd");
|
||||
if (ptr == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR,
|
||||
"Invalid xmcd database file returned.\n");
|
||||
return -1;
|
||||
}
|
||||
ptr = strdup(ptr);
|
||||
// Ok found the beginning of the file
|
||||
// look for the end
|
||||
ptr2 = strstr(ptr, "\n.\r\n");
|
||||
if (!ptr2)
|
||||
ptr2 = strstr(ptr, "\n.\n");
|
||||
if (ptr2) {
|
||||
ptr2++;
|
||||
} else {
|
||||
mp_msg(MSGT_DEMUX, MSGL_FIXME, "Unable to find '.'\n");
|
||||
ptr2 = ptr + strlen(ptr); //return -1;
|
||||
}
|
||||
// Ok found the end
|
||||
// do a sanity check
|
||||
if (http_hdr->body_size < (unsigned int)(ptr2 - ptr)) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "unexpected FIXME\n");
|
||||
return -1;
|
||||
}
|
||||
cddb_data->xmcd_file = ptr;
|
||||
cddb_data->xmcd_file_size = ptr2 - ptr;
|
||||
cddb_data->xmcd_file[cddb_data->xmcd_file_size] = '\0';
|
||||
return cddb_write_cache(cddb_data);
|
||||
default:
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cddb_request_titles(cddb_data_t *cddb_data)
|
||||
{
|
||||
char command[1024];
|
||||
snprintf(command, sizeof(command), "cddb+read+%s+%08lx",
|
||||
cddb_data->category, cddb_data->disc_id);
|
||||
return cddb_http_request(command, cddb_read_parse, cddb_data);
|
||||
}
|
||||
|
||||
static int cddb_parse_matches_list(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
|
||||
{
|
||||
char album_title[100];
|
||||
char *ptr = NULL;
|
||||
int ret;
|
||||
|
||||
ptr = strstr(http_hdr->body, "\n");
|
||||
if (ptr == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Unable to find end of line.\n");
|
||||
return -1;
|
||||
}
|
||||
ptr++;
|
||||
// We have a list of exact/inexact matches, so which one do we use?
|
||||
// So let's take the first one.
|
||||
ret = sscanf(ptr, "%99s %08lx %99s", cddb_data->category,
|
||||
&(cddb_data->disc_id), album_title);
|
||||
if (ret != 3) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
ptr = strstr(http_hdr->body, album_title);
|
||||
if (ptr != NULL) {
|
||||
char *ptr2;
|
||||
int len;
|
||||
ptr2 = strstr(ptr, "\n");
|
||||
if (ptr2 == NULL) {
|
||||
len = (http_hdr->body_size)-(ptr-(http_hdr->body));
|
||||
} else {
|
||||
len = ptr2-ptr+1;
|
||||
}
|
||||
len = FFMIN(sizeof(album_title) - 1, len);
|
||||
strncpy(album_title, ptr, len);
|
||||
album_title[len]='\0';
|
||||
}
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_STATUS, "Parse OK, found: %s\n", album_title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cddb_query_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
|
||||
{
|
||||
char album_title[100];
|
||||
char *ptr = NULL;
|
||||
int ret, status;
|
||||
|
||||
ret = sscanf(http_hdr->body, "%d ", &status);
|
||||
if (ret != 1) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 200:
|
||||
// Found exact match
|
||||
ret = sscanf(http_hdr->body, "%d %99s %08lx %99s", &status,
|
||||
cddb_data->category, &(cddb_data->disc_id), album_title);
|
||||
if (ret != 4) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
ptr = strstr(http_hdr->body, album_title);
|
||||
if (ptr != NULL) {
|
||||
char *ptr2;
|
||||
int len;
|
||||
ptr2 = strstr(ptr, "\n");
|
||||
if (ptr2 == NULL) {
|
||||
len = (http_hdr->body_size)-(ptr-(http_hdr->body));
|
||||
} else {
|
||||
len = ptr2-ptr+1;
|
||||
}
|
||||
len = FFMIN(sizeof(album_title) - 1, len);
|
||||
strncpy(album_title, ptr, len);
|
||||
album_title[len]='\0';
|
||||
}
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_STATUS, "Parse OK, found: %s\n", album_title);
|
||||
return cddb_request_titles(cddb_data);
|
||||
case 202:
|
||||
// No match found
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_WARN, "Album not found.\n");
|
||||
break;
|
||||
case 210:
|
||||
// Found exact matches, list follows
|
||||
cddb_parse_matches_list(http_hdr, cddb_data);
|
||||
return cddb_request_titles(cddb_data);
|
||||
/*
|
||||
body=[210 Found exact matches, list follows (until terminating `.')
|
||||
misc c711930d Santana / Supernatural
|
||||
rock c711930d Santana / Supernatural
|
||||
blues c711930d Santana / Supernatural
|
||||
.]
|
||||
*/
|
||||
case 211:
|
||||
// Found inexact matches, list follows
|
||||
cddb_parse_matches_list(http_hdr, cddb_data);
|
||||
return cddb_request_titles(cddb_data);
|
||||
case 500:
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_FIXME,
|
||||
"Server returns: Command syntax error\n");
|
||||
break;
|
||||
default:
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cddb_proto_level_parse(HTTP_header_t *http_hdr, cddb_data_t *cddb_data)
|
||||
{
|
||||
int max;
|
||||
int ret, status;
|
||||
char *ptr;
|
||||
|
||||
ret = sscanf(http_hdr->body, "%d ", &status);
|
||||
if (ret != 1) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 210:
|
||||
ptr = strstr(http_hdr->body, "max proto:");
|
||||
if (ptr == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
ret = sscanf(ptr, "max proto: %d", &max);
|
||||
if (ret != 1) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "parse error");
|
||||
return -1;
|
||||
}
|
||||
cddb_data->freedb_proto_level = max;
|
||||
return 0;
|
||||
default:
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_FIXME, "unhandled code\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cddb_get_proto_level(cddb_data_t *cddb_data)
|
||||
{
|
||||
return cddb_http_request("stat", cddb_proto_level_parse, cddb_data);
|
||||
}
|
||||
|
||||
static void cddb_create_hello(cddb_data_t *cddb_data)
|
||||
{
|
||||
char host_name[51];
|
||||
char *user_name;
|
||||
|
||||
if (cddb_data->anonymous) { // Default is anonymous
|
||||
/* Note from Eduardo Pérez Ureta <eperez@it.uc3m.es> :
|
||||
* We don't send current user/host name in hello to prevent spam.
|
||||
* Software that sends this is considered spyware
|
||||
* that most people don't like.
|
||||
*/
|
||||
user_name = "anonymous";
|
||||
strcpy(host_name, "localhost");
|
||||
} else {
|
||||
if (gethostname(host_name, 50) < 0) {
|
||||
strcpy(host_name, "localhost");
|
||||
}
|
||||
user_name = getenv("LOGNAME");
|
||||
}
|
||||
snprintf(cddb_data->cddb_hello, sizeof(cddb_data->cddb_hello),
|
||||
"&hello=%s+%s+%s",
|
||||
user_name, host_name, mplayer_version);
|
||||
}
|
||||
|
||||
static int cddb_retrieve(cddb_data_t *cddb_data)
|
||||
{
|
||||
char offsets[1024], command[1024];
|
||||
char *ptr;
|
||||
unsigned int i, time_len;
|
||||
int ret;
|
||||
|
||||
ptr = offsets;
|
||||
for (i = 0; i < cddb_data->tracks ; i++) {
|
||||
unsigned space = sizeof(offsets) - (ptr - offsets);
|
||||
if (space < 40) break;
|
||||
ptr += snprintf(ptr, space, "%d+", cdtoc[i].frame);
|
||||
}
|
||||
ptr[0] = 0;
|
||||
time_len = (cdtoc[cddb_data->tracks].frame)/75;
|
||||
|
||||
cddb_data->freedb_server = DEFAULT_FREEDB_SERVER;
|
||||
cddb_data->freedb_proto_level = 1;
|
||||
cddb_data->xmcd_file = NULL;
|
||||
|
||||
cddb_create_hello(cddb_data);
|
||||
if (cddb_get_proto_level(cddb_data) < 0) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Failed to get the protocol level.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(command, sizeof(command), "cddb+query+%08lx+%d+%s%d", cddb_data->disc_id,
|
||||
cddb_data->tracks, offsets, time_len);
|
||||
ret = cddb_http_request(command, cddb_query_parse, cddb_data);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
free(cddb_data->cache_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cddb_resolve(const char *dev, char **xmcd_file)
|
||||
{
|
||||
char cddb_cache_dir[] = DEFAULT_CACHE_DIR;
|
||||
char *home_dir = NULL;
|
||||
cddb_data_t cddb_data;
|
||||
void *talloc_ctx = talloc_new(NULL);
|
||||
|
||||
if (cdtoc_last_track <= 0) {
|
||||
cdtoc_last_track = read_toc(dev);
|
||||
if (cdtoc_last_track < 0) {
|
||||
mp_tmsg(MSGT_OPEN, MSGL_ERR, "Failed to open %s device.\n", dev);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
cddb_data.tracks = cdtoc_last_track;
|
||||
cddb_data.disc_id = cddb_discid(cddb_data.tracks);
|
||||
cddb_data.anonymous = 1; // Don't send user info by default
|
||||
|
||||
mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_CDDB_DISCID=%08lx\n",
|
||||
cddb_data.disc_id);
|
||||
|
||||
// Check if there is a CD in the drive
|
||||
// FIXME: That's not really a good way to check
|
||||
if (cddb_data.disc_id == 0) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "No CD in the drive.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
home_dir = getenv("HOME");
|
||||
#ifdef __MINGW32__
|
||||
if (home_dir == NULL)
|
||||
home_dir = getenv("USERPROFILE");
|
||||
if (home_dir == NULL)
|
||||
home_dir = getenv("HOMEPATH");
|
||||
// Last resort, store the cddb cache in the mplayer directory
|
||||
if (home_dir == NULL)
|
||||
home_dir = (char *)talloc_steal(talloc_ctx,
|
||||
mp_find_user_config_file(""));
|
||||
#endif
|
||||
if (home_dir == NULL) {
|
||||
cddb_data.cache_dir = NULL;
|
||||
} else {
|
||||
unsigned len = strlen(home_dir) + strlen(cddb_cache_dir) + 1;
|
||||
cddb_data.cache_dir = malloc(len);
|
||||
if (cddb_data.cache_dir == NULL) {
|
||||
mp_tmsg(MSGT_DEMUX, MSGL_ERR, "Memory allocation failed.\n");
|
||||
talloc_free(talloc_ctx);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cddb_data.cache_dir, len, "%s%s", home_dir, cddb_cache_dir);
|
||||
}
|
||||
talloc_free(talloc_ctx);
|
||||
|
||||
// Check for a cached file
|
||||
if (cddb_read_cache(&cddb_data) < 0) {
|
||||
// No Cache found
|
||||
if (cddb_retrieve(&cddb_data) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cddb_data.xmcd_file != NULL) {
|
||||
// printf("%s\n", cddb_data.xmcd_file);
|
||||
*xmcd_file = cddb_data.xmcd_file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/***************
|
||||
* xmcd parser *
|
||||
***************/
|
||||
static char *xmcd_parse_dtitle(cd_info_t *cd_info, char *line)
|
||||
{
|
||||
char *ptr, *album;
|
||||
ptr = strstr(line, "DTITLE=");
|
||||
if (ptr != NULL) {
|
||||
ptr += 7;
|
||||
album = strstr(ptr, "/");
|
||||
if (album == NULL)
|
||||
return NULL;
|
||||
cd_info->album = malloc(strlen(album + 2) + 1);
|
||||
if (cd_info->album == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(cd_info->album, album + 2);
|
||||
album--;
|
||||
album[0] = '\0';
|
||||
cd_info->artist = malloc(strlen(ptr) + 1);
|
||||
if (cd_info->artist == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(cd_info->artist, ptr);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static char *xmcd_parse_dgenre(cd_info_t *cd_info, char *line)
|
||||
{
|
||||
char *ptr;
|
||||
ptr = strstr(line, "DGENRE=");
|
||||
if (ptr != NULL) {
|
||||
ptr += 7;
|
||||
cd_info->genre = malloc(strlen(ptr)+1);
|
||||
if (cd_info->genre == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(cd_info->genre, ptr);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static char *xmcd_parse_ttitle(cd_info_t *cd_info, char *line)
|
||||
{
|
||||
unsigned int track_nb;
|
||||
unsigned long sec, off;
|
||||
char *ptr;
|
||||
ptr = strstr(line, "TTITLE");
|
||||
if (ptr != NULL) {
|
||||
ptr += 6;
|
||||
// Here we point to the track number
|
||||
track_nb = atoi(ptr);
|
||||
ptr = strstr(ptr, "=");
|
||||
if (ptr == NULL)
|
||||
return NULL;
|
||||
ptr++;
|
||||
|
||||
sec = cdtoc[track_nb].frame;
|
||||
off = cdtoc[track_nb + 1].frame - sec + 1;
|
||||
|
||||
cd_info_add_track(cd_info, ptr, track_nb + 1,
|
||||
(unsigned int) (off / (60 * 75)),
|
||||
(unsigned int) ((off / 75) % 60),
|
||||
(unsigned int) (off % 75),
|
||||
sec, off);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
cd_info_t *cddb_parse_xmcd(char *xmcd_file)
|
||||
{
|
||||
cd_info_t *cd_info = NULL;
|
||||
int length, pos = 0;
|
||||
char *ptr, *ptr2;
|
||||
if (xmcd_file == NULL)
|
||||
return NULL;
|
||||
|
||||
cd_info = cd_info_new();
|
||||
if (cd_info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
length = strlen(xmcd_file);
|
||||
ptr = xmcd_file;
|
||||
while (ptr != NULL && pos < length) {
|
||||
// Read a line
|
||||
ptr2 = ptr;
|
||||
while(ptr2[0] != '\0' && ptr2[0] != '\r' && ptr2[0] != '\n')
|
||||
ptr2++;
|
||||
if (ptr2[0] == '\0') {
|
||||
break;
|
||||
}
|
||||
ptr2[0] = '\0';
|
||||
// Ignore comments
|
||||
if (ptr[0] != '#') {
|
||||
// Search for the album title
|
||||
if (!xmcd_parse_dtitle(cd_info, ptr)) {
|
||||
// Search for the genre
|
||||
if (!xmcd_parse_dgenre(cd_info, ptr)) {
|
||||
// Search for a track title
|
||||
xmcd_parse_ttitle(cd_info, ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ptr2[1] == '\n')
|
||||
ptr2++;
|
||||
pos = (ptr2 + 1) - ptr;
|
||||
ptr = ptr2 + 1;
|
||||
}
|
||||
|
||||
unsigned int audiolen = cdtoc[cd_info->nb_tracks].frame-cdtoc[0].frame;
|
||||
cd_info->min = (unsigned int) (audiolen / (60 * 75));
|
||||
cd_info->sec = (unsigned int) ((audiolen / 75) % 60);
|
||||
cd_info->msec = (unsigned int) (audiolen % 75);
|
||||
|
||||
return cd_info;
|
||||
}
|
@ -1,520 +0,0 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#if !HAVE_WINSOCK2_H
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "core/mp_msg.h"
|
||||
#include "network.h"
|
||||
#include "stream.h"
|
||||
#include "core/m_option.h"
|
||||
#include "core/m_struct.h"
|
||||
#include "tcp.h"
|
||||
|
||||
static struct stream_priv_s {
|
||||
char* user;
|
||||
char* pass;
|
||||
char* host;
|
||||
int port;
|
||||
char* filename;
|
||||
|
||||
char *cput,*cget;
|
||||
int handle;
|
||||
int cavail,cleft;
|
||||
char *buf;
|
||||
char *cmd_buf;
|
||||
} stream_priv_dflts = {
|
||||
.user = "anonymous",
|
||||
.pass = "no@spam",
|
||||
.port = 21,
|
||||
.handle = -1,
|
||||
};
|
||||
|
||||
#define CMD_BUFSIZE 8192
|
||||
|
||||
#define BUFSIZE 2048
|
||||
|
||||
#define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
|
||||
/// URL definition
|
||||
static const m_option_t stream_opts_fields[] = {
|
||||
{"username", ST_OFF(user), CONF_TYPE_STRING, 0, 0 ,0, NULL},
|
||||
{"password", ST_OFF(pass), CONF_TYPE_STRING, 0, 0 ,0, NULL},
|
||||
{"hostname", ST_OFF(host), CONF_TYPE_STRING, 0, 0 ,0, NULL},
|
||||
{"port", ST_OFF(port), CONF_TYPE_INT, 0, 0 ,65635, NULL},
|
||||
{"filename", ST_OFF(filename), CONF_TYPE_STRING, 0, 0 ,0, NULL},
|
||||
{ NULL, NULL, 0, 0, 0, 0, NULL }
|
||||
};
|
||||
static const struct m_struct_st stream_opts = {
|
||||
"ftp",
|
||||
sizeof(struct stream_priv_s),
|
||||
&stream_priv_dflts,
|
||||
stream_opts_fields
|
||||
};
|
||||
|
||||
#define TELNET_IAC 255 /* interpret as command: */
|
||||
#define TELNET_IP 244 /* interrupt process--permanently */
|
||||
#define TELNET_SYNCH 242 /* for telfunc calls */
|
||||
|
||||
// Check if there is something to read on a fd. This avoid hanging
|
||||
// forever if the network stop responding.
|
||||
static int fd_can_read(int fd,int timeout) {
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd,&fds);
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
return select(fd+1, &fds, NULL, NULL, &tv) > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* read a line of text
|
||||
*
|
||||
* If the line is too long to fit in the buffer, provided via parameters
|
||||
* buf and max, the remaining characters are skipped. So the next call to
|
||||
* this function is synchronized to the start of the following response
|
||||
* line.
|
||||
*
|
||||
* The parameter buf will always be initialized as long as max is bigger
|
||||
* then 1. If nothing is read it will contain an empty string.
|
||||
*
|
||||
* return -1 on error or bytecount
|
||||
*/
|
||||
static int readline(char *buf,int max,struct stream_priv_s *ctl)
|
||||
{
|
||||
int x,retval = 0;
|
||||
char *end,*bp=buf;
|
||||
int eof = 0;
|
||||
|
||||
if (max <= 0) {
|
||||
return -1;
|
||||
}
|
||||
*bp = '\0';
|
||||
|
||||
do {
|
||||
if (ctl->cavail > 0) {
|
||||
x = FFMIN(ctl->cavail, max-1);
|
||||
end = memccpy(bp,ctl->cget,'\n',x);
|
||||
if (end != NULL)
|
||||
x = end - bp;
|
||||
retval += x;
|
||||
bp += x;
|
||||
*bp = '\0';
|
||||
max -= x;
|
||||
ctl->cget += x;
|
||||
ctl->cavail -= x;
|
||||
if (end != NULL) {
|
||||
bp -= 2;
|
||||
if (strcmp(bp,"\r\n") == 0) {
|
||||
*bp++ = '\n';
|
||||
*bp++ = '\0';
|
||||
--retval;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (max == 1) {
|
||||
char *q = memchr(ctl->cget, '\n', ctl->cavail);
|
||||
|
||||
if (q) { // found EOL: update state and return
|
||||
++q;
|
||||
ctl->cavail -= q - ctl->cget;
|
||||
ctl->cget = q;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// receive more data to find end of current line
|
||||
ctl->cget = ctl->cput;
|
||||
}
|
||||
if (ctl->cput == ctl->cget) {
|
||||
ctl->cput = ctl->cget = ctl->buf;
|
||||
ctl->cavail = 0;
|
||||
ctl->cleft = BUFSIZE;
|
||||
}
|
||||
if(eof) {
|
||||
if (retval == 0)
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!fd_can_read(ctl->handle, 15)) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n");
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((x = recv(ctl->handle,ctl->cput,ctl->cleft,0)) == -1) {
|
||||
mp_msg(MSGT_STREAM,MSGL_ERR, "[ftp] read error: %s\n",strerror(errno));
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
if (x == 0)
|
||||
eof = 1;
|
||||
ctl->cleft -= x;
|
||||
ctl->cavail += x;
|
||||
ctl->cput += x;
|
||||
} while (1);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* read a response from the server
|
||||
*
|
||||
* return 0 if first char doesn't match
|
||||
* return 1 if first char matches
|
||||
*/
|
||||
static int readresp(struct stream_priv_s* ctl,char* rsp)
|
||||
{
|
||||
static char response[256];
|
||||
char match[5];
|
||||
int r, len;
|
||||
|
||||
len = readline(response,256,ctl);
|
||||
if (rsp) strcpy(rsp,response);
|
||||
if (len == -1)
|
||||
return 0;
|
||||
|
||||
r = atoi(response)/100;
|
||||
|
||||
mp_msg(MSGT_STREAM,MSGL_V, "[ftp] < %s",response);
|
||||
|
||||
if (response[3] == '-') {
|
||||
strncpy(match,response,3);
|
||||
match[3] = ' ';
|
||||
match[4] = '\0';
|
||||
do {
|
||||
if (readline(response,256,ctl) == -1) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Control socket read failed\n");
|
||||
return 0;
|
||||
}
|
||||
mp_msg(MSGT_OPEN,MSGL_V, "[ftp] < %s",response);
|
||||
} while (strncmp(response,match,4));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp)
|
||||
{
|
||||
int l = strlen(cmd);
|
||||
int hascrlf = cmd[l - 2] == '\r' && cmd[l - 1] == '\n';
|
||||
|
||||
if(hascrlf && l == 2) mp_msg(MSGT_STREAM,MSGL_V, "\n");
|
||||
else mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd);
|
||||
while(l > 0) {
|
||||
int s = send(nControl->handle,cmd,l,DEFAULT_SEND_FLAGS);
|
||||
|
||||
if(s <= 0) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] write error: %s\n",strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd += s;
|
||||
l -= s;
|
||||
}
|
||||
|
||||
if (hascrlf)
|
||||
return readresp(nControl,rsp);
|
||||
else
|
||||
return FtpSendCmd("\r\n", nControl, rsp);
|
||||
}
|
||||
|
||||
static int FtpOpenPort(struct stream_priv_s* p) {
|
||||
int resp,fd;
|
||||
char rsp_txt[256];
|
||||
char* par,str[128];
|
||||
int num[6];
|
||||
|
||||
resp = FtpSendCmd("PASV",p,rsp_txt);
|
||||
if(resp != 2) {
|
||||
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'PASV' failed: %s\n",rsp_txt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
par = strchr(rsp_txt,'(');
|
||||
|
||||
if(!par || !par[0] || !par[1]) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] invalid server response: %s ??\n",rsp_txt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sscanf(par+1,"%u,%u,%u,%u,%u,%u",&num[0],&num[1],&num[2],
|
||||
&num[3],&num[4],&num[5]);
|
||||
snprintf(str,sizeof(str),"%d.%d.%d.%d",num[0],num[1],num[2],num[3]);
|
||||
fd = connect2Server(str,(num[4]<<8)+num[5],0);
|
||||
|
||||
if(fd < 0)
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] failed to create data connection\n");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int FtpOpenData(stream_t* s,int64_t newpos) {
|
||||
struct stream_priv_s* p = s->priv;
|
||||
int resp;
|
||||
char rsp_txt[256];
|
||||
|
||||
// Open a new connection
|
||||
s->fd = FtpOpenPort(p);
|
||||
|
||||
if(s->fd < 0) return 0;
|
||||
|
||||
if(newpos > 0) {
|
||||
snprintf(p->cmd_buf,CMD_BUFSIZE,"REST %"PRId64, (int64_t)newpos);
|
||||
|
||||
resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
|
||||
if(resp != 3) {
|
||||
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
|
||||
newpos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the file
|
||||
snprintf(p->cmd_buf,CMD_BUFSIZE,"RETR %s",p->filename);
|
||||
resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
|
||||
|
||||
if(resp != 1) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s->pos = newpos;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fill_buffer(stream_t *s, char* buffer, int max_len){
|
||||
int r;
|
||||
|
||||
if(s->fd < 0 && !FtpOpenData(s,s->pos))
|
||||
return -1;
|
||||
|
||||
if(!fd_can_read(s->fd, 15)) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = recv(s->fd,buffer,max_len,0);
|
||||
return (r <= 0) ? -1 : r;
|
||||
}
|
||||
|
||||
static int seek(stream_t *s,int64_t newpos) {
|
||||
struct stream_priv_s* p = s->priv;
|
||||
int resp;
|
||||
char rsp_txt[256];
|
||||
|
||||
if(s->pos > s->end_pos) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check to see if the server did not already terminate the transfer
|
||||
if(fd_can_read(p->handle, 0)) {
|
||||
if(readresp(p,rsp_txt) != 2)
|
||||
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] Warning the server didn't finished the transfer correctly: %s\n",rsp_txt);
|
||||
closesocket(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
// Close current download
|
||||
if(s->fd >= 0) {
|
||||
static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_SYNCH};
|
||||
//int fl;
|
||||
|
||||
// First close the fd
|
||||
closesocket(s->fd);
|
||||
s->fd = -1;
|
||||
|
||||
// Send send the telnet sequence needed to make the server react
|
||||
|
||||
// Dunno if this is really needed, lftp have it. I let
|
||||
// it here in case it turn out to be needed on some other OS
|
||||
//fl=fcntl(p->handle,F_GETFL);
|
||||
//fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
|
||||
|
||||
// send only first byte as OOB due to OOB braindamage in many unices
|
||||
send(p->handle,pre_cmd,1,MSG_OOB|DEFAULT_SEND_FLAGS);
|
||||
send(p->handle,pre_cmd+1,sizeof(pre_cmd)-1,DEFAULT_SEND_FLAGS);
|
||||
|
||||
//fcntl(p->handle,F_SETFL,fl);
|
||||
|
||||
// Get the 426 Transfer aborted
|
||||
// Or the 226 Transfer complete
|
||||
resp = readresp(p,rsp_txt);
|
||||
if(resp != 4 && resp != 2) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Server didn't abort correctly: %s\n",rsp_txt);
|
||||
return 0;
|
||||
}
|
||||
// Send the ABOR command
|
||||
// Ignore the return code as sometimes it fail with "nothing to abort"
|
||||
FtpSendCmd("ABOR",p,rsp_txt);
|
||||
}
|
||||
return FtpOpenData(s,newpos);
|
||||
}
|
||||
|
||||
|
||||
static void close_f(stream_t *s) {
|
||||
struct stream_priv_s* p = s->priv;
|
||||
|
||||
if(!p) return;
|
||||
|
||||
if(s->fd >= 0) {
|
||||
closesocket(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
if (p->handle >= 0) {
|
||||
FtpSendCmd("QUIT", p, NULL);
|
||||
closesocket(p->handle);
|
||||
}
|
||||
|
||||
free(p->buf);
|
||||
free(p->cmd_buf);
|
||||
|
||||
m_struct_free(&stream_opts,p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int open_f(stream_t *stream,int mode, void* opts, av_unused int* file_format) {
|
||||
int resp;
|
||||
int64_t len = 0;
|
||||
struct stream_priv_s* p = opts;
|
||||
char rsp_txt[256];
|
||||
|
||||
if(mode != STREAM_READ) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Unknown open mode %d\n",mode);
|
||||
m_struct_free(&stream_opts,opts);
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if(!p->filename || !p->host) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Bad url\n");
|
||||
m_struct_free(&stream_opts,opts);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
// Allocate buffers
|
||||
p->buf = malloc(BUFSIZE);
|
||||
p->cmd_buf = malloc(CMD_BUFSIZE);
|
||||
|
||||
if (!p->buf || !p->cmd_buf) {
|
||||
close_f(stream);
|
||||
m_struct_free(&stream_opts,opts);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
// Open the control connection
|
||||
p->handle = connect2Server(p->host,p->port,1);
|
||||
|
||||
if(p->handle < 0) {
|
||||
m_struct_free(&stream_opts,opts);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
// We got a connection, let's start serious things
|
||||
stream->fd = -1;
|
||||
stream->priv = p;
|
||||
|
||||
if (readresp(p, NULL) == 0) {
|
||||
close_f(stream);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
// Login
|
||||
snprintf(p->cmd_buf,CMD_BUFSIZE,"USER %s",p->user);
|
||||
resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
|
||||
|
||||
// password needed
|
||||
if(resp == 3) {
|
||||
snprintf(p->cmd_buf,CMD_BUFSIZE,"PASS %s",p->pass);
|
||||
resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
|
||||
if(resp != 2) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
|
||||
close_f(stream);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
} else if(resp != 2) {
|
||||
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
|
||||
close_f(stream);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
// Set the transfer type
|
||||
resp = FtpSendCmd("TYPE I",p,rsp_txt);
|
||||
if(resp != 2) {
|
||||
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt);
|
||||
close_f(stream);
|
||||
return STREAM_ERROR;
|
||||
}
|
||||
|
||||
// Get the filesize
|
||||
snprintf(p->cmd_buf,CMD_BUFSIZE,"SIZE %s",p->filename);
|
||||
resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
|
||||
if(resp != 2) {
|
||||
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
|
||||
} else {
|
||||
int dummy;
|
||||
sscanf(rsp_txt,"%d %"SCNd64,&dummy,&len);
|
||||
}
|
||||
|
||||
if(len > 0) {
|
||||
stream->seek = seek;
|
||||
stream->end_pos = len;
|
||||
}
|
||||
|
||||
// The data connection is really opened only at the first
|
||||
// read/seek. This must be done when the cache is used
|
||||
// because the connection would stay open in the main process,
|
||||
// preventing correct abort with many servers.
|
||||
stream->fd = -1;
|
||||
stream->priv = p;
|
||||
stream->fill_buffer = fill_buffer;
|
||||
stream->close = close_f;
|
||||
stream->type = STREAMTYPE_STREAM;
|
||||
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
const stream_info_t stream_info_ftp = {
|
||||
"File Transfer Protocol",
|
||||
"ftp",
|
||||
"Albeu",
|
||||
"reuse a bit of code from ftplib written by Thomas Pfau",
|
||||
open_f,
|
||||
{ "ftp", NULL },
|
||||
&stream_opts,
|
||||
1 // Urls are an option string
|
||||
};
|
@ -28,7 +28,6 @@
|
||||
#include "core/m_struct.h"
|
||||
#include "demux/demux.h"
|
||||
|
||||
#include "network.h"
|
||||
#include "cookies.h"
|
||||
|
||||
#include "core/bstr.h"
|
||||
@ -317,7 +316,7 @@ const stream_info_t stream_info_ffmpeg = {
|
||||
"",
|
||||
open_f,
|
||||
{ "lavf", "ffmpeg", "rtmp", "rtsp", "http", "https", "mms", "mmst", "mmsh",
|
||||
"mmshttp", NULL },
|
||||
"mmshttp", "udp", "ftp", NULL },
|
||||
NULL,
|
||||
1 // Urls are an option string
|
||||
};
|
||||
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* stream layer for MPEG over UDP, based on previous work from Dave Chapman
|
||||
*
|
||||
* Copyright (C) 2006 Benjamin Zores
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core/options.h"
|
||||
#include "stream.h"
|
||||
#include "url.h"
|
||||
#include "udp.h"
|
||||
|
||||
static int
|
||||
udp_streaming_start (stream_t *stream)
|
||||
{
|
||||
streaming_ctrl_t *streaming_ctrl;
|
||||
int fd;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
streaming_ctrl = stream->streaming_ctrl;
|
||||
fd = stream->fd;
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
fd = udp_open_socket (streaming_ctrl->url);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
stream->fd = fd;
|
||||
}
|
||||
|
||||
streaming_ctrl->streaming_read = nop_streaming_read;
|
||||
streaming_ctrl->streaming_seek = nop_streaming_seek;
|
||||
streaming_ctrl->status = streaming_playing_e;
|
||||
stream->streaming = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
udp_stream_open (stream_t *stream, int mode, void *opts, int *file_format)
|
||||
{
|
||||
mp_msg (MSGT_OPEN, MSGL_INFO, "STREAM_UDP, URL: %s\n", stream->url);
|
||||
stream->streaming_ctrl = streaming_ctrl_new ();
|
||||
if (!stream->streaming_ctrl)
|
||||
return STREAM_ERROR;
|
||||
|
||||
stream->streaming_ctrl->bandwidth = network_bandwidth;
|
||||
stream->streaming_ctrl->url = url_new(stream->url);
|
||||
|
||||
if (stream->streaming_ctrl->url->port == 0)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR,
|
||||
"You must enter a port number for UDP streams!\n");
|
||||
streaming_ctrl_free (stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (udp_streaming_start (stream) < 0)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR, "udp_streaming_start failed\n");
|
||||
streaming_ctrl_free (stream->streaming_ctrl);
|
||||
stream->streaming_ctrl = NULL;
|
||||
|
||||
return STREAM_UNSUPPORTED;
|
||||
}
|
||||
|
||||
stream->type = STREAMTYPE_STREAM;
|
||||
|
||||
return STREAM_OK;
|
||||
}
|
||||
|
||||
const stream_info_t stream_info_udp = {
|
||||
"MPEG over UDP streaming",
|
||||
"udp",
|
||||
"Dave Chapman, Benjamin Zores",
|
||||
"native udp support",
|
||||
udp_stream_open,
|
||||
{ "udp", NULL},
|
||||
NULL,
|
||||
0 // Urls are an option string
|
||||
};
|
279
stream/tcp.c
279
stream/tcp.c
@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Network layer for MPlayer
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand BAUDET <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
#if !HAVE_WINSOCK2_H
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "network.h"
|
||||
#include "stream.h"
|
||||
#include "tcp.h"
|
||||
#include "libavutil/avstring.h"
|
||||
|
||||
/* IPv6 options */
|
||||
int network_prefer_ipv4 = 0;
|
||||
|
||||
// Converts an address family constant to a string
|
||||
|
||||
static const char *af2String(int af) {
|
||||
switch (af) {
|
||||
case AF_INET: return "AF_INET";
|
||||
|
||||
#ifdef HAVE_AF_INET6
|
||||
case AF_INET6: return "AF_INET6";
|
||||
#endif
|
||||
default: return "Unknown address family!";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Connect to a server using a TCP connection, with specified address family
|
||||
// return -2 for fatal error, like unable to resolve name, connection timeout...
|
||||
// return -1 is unable to connect to a particular port
|
||||
|
||||
static int
|
||||
connect2Server_with_af(char *host, int port, int af,int verb) {
|
||||
int socket_server_fd;
|
||||
int err;
|
||||
socklen_t err_len;
|
||||
int ret,count = 0;
|
||||
fd_set set;
|
||||
struct timeval tv;
|
||||
union {
|
||||
struct sockaddr_in four;
|
||||
#ifdef HAVE_AF_INET6
|
||||
struct sockaddr_in6 six;
|
||||
#endif
|
||||
} server_address;
|
||||
size_t server_address_size;
|
||||
void *our_s_addr; // Pointer to sin_addr or sin6_addr
|
||||
struct hostent *hp=NULL;
|
||||
char buf[255];
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
unsigned long val;
|
||||
int to;
|
||||
#else
|
||||
struct timeval to;
|
||||
#endif
|
||||
|
||||
#if HAVE_WINSOCK2_H && defined(HAVE_AF_INET6)
|
||||
// our winsock name resolution code can not handle IPv6
|
||||
if (af == AF_INET6) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_WARN, "IPv6 not supported for winsock2\n");
|
||||
return TCP_ERROR_FATAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
socket_server_fd = socket(af, SOCK_STREAM, 0);
|
||||
|
||||
|
||||
if( socket_server_fd==-1 ) {
|
||||
// mp_msg(MSGT_NETWORK,MSGL_ERR,"Failed to create %s socket:\n", af2String(af));
|
||||
return TCP_ERROR_FATAL;
|
||||
}
|
||||
|
||||
#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
|
||||
#if HAVE_WINSOCK2_H
|
||||
/* timeout in milliseconds */
|
||||
to = 10 * 1000;
|
||||
#else
|
||||
to.tv_sec = 10;
|
||||
to.tv_usec = 0;
|
||||
#endif
|
||||
setsockopt(socket_server_fd, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to));
|
||||
setsockopt(socket_server_fd, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof(to));
|
||||
#endif
|
||||
|
||||
switch (af) {
|
||||
case AF_INET: our_s_addr = &server_address.four.sin_addr; break;
|
||||
#ifdef HAVE_AF_INET6
|
||||
case AF_INET6: our_s_addr = &server_address.six.sin6_addr; break;
|
||||
#endif
|
||||
default:
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR, "Unknown address family %d\n", af);
|
||||
return TCP_ERROR_FATAL;
|
||||
}
|
||||
|
||||
|
||||
memset(&server_address, 0, sizeof(server_address));
|
||||
|
||||
#if HAVE_INET_PTON
|
||||
if (inet_pton(af, host, our_s_addr)!=1)
|
||||
#elif HAVE_INET_ATON
|
||||
if (inet_aton(host, our_s_addr)!=1)
|
||||
#elif HAVE_WINSOCK2_H
|
||||
if ( inet_addr(host)==INADDR_NONE )
|
||||
#endif
|
||||
{
|
||||
if(verb) mp_tmsg(MSGT_NETWORK,MSGL_STATUS,"Resolving %s for %s...\n", host, af2String(af));
|
||||
|
||||
#ifdef HAVE_GETHOSTBYNAME2
|
||||
hp=gethostbyname2( host, af );
|
||||
#else
|
||||
hp=gethostbyname( host );
|
||||
#endif
|
||||
if( hp==NULL ) {
|
||||
if(verb) mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Couldn't resolve name for %s: %s\n", af2String(af), host);
|
||||
return TCP_ERROR_FATAL;
|
||||
}
|
||||
|
||||
memcpy( our_s_addr, hp->h_addr_list[0], hp->h_length );
|
||||
}
|
||||
#if HAVE_WINSOCK2_H
|
||||
else {
|
||||
unsigned long addr = inet_addr(host);
|
||||
memcpy( our_s_addr, &addr, sizeof(addr) );
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
server_address.four.sin_family=af;
|
||||
server_address.four.sin_port=htons(port);
|
||||
server_address_size = sizeof(server_address.four);
|
||||
break;
|
||||
#ifdef HAVE_AF_INET6
|
||||
case AF_INET6:
|
||||
server_address.six.sin6_family=af;
|
||||
server_address.six.sin6_port=htons(port);
|
||||
server_address_size = sizeof(server_address.six);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR, "Unknown address family %d\n", af);
|
||||
return TCP_ERROR_FATAL;
|
||||
}
|
||||
|
||||
#if HAVE_INET_PTON
|
||||
inet_ntop(af, our_s_addr, buf, 255);
|
||||
#elif HAVE_INET_ATON || defined(HAVE_WINSOCK2_H)
|
||||
av_strlcpy( buf, inet_ntoa( *((struct in_addr*)our_s_addr) ), 255);
|
||||
#endif
|
||||
if(verb) mp_tmsg(MSGT_NETWORK,MSGL_STATUS,"Connecting to server %s[%s]: %d...\n", host, buf , port );
|
||||
|
||||
// Turn the socket as non blocking so we can timeout on the connection
|
||||
#if !HAVE_WINSOCK2_H
|
||||
fcntl( socket_server_fd, F_SETFL, fcntl(socket_server_fd, F_GETFL) | O_NONBLOCK );
|
||||
#else
|
||||
val = 1;
|
||||
ioctlsocket( socket_server_fd, FIONBIO, &val );
|
||||
#endif
|
||||
if( connect( socket_server_fd, (struct sockaddr*)&server_address, server_address_size )==-1 ) {
|
||||
#if !HAVE_WINSOCK2_H
|
||||
if( errno!=EINPROGRESS ) {
|
||||
#else
|
||||
if( (WSAGetLastError() != WSAEINPROGRESS) && (WSAGetLastError() != WSAEWOULDBLOCK) ) {
|
||||
#endif
|
||||
if(verb) mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Failed to connect to server with %s\n", af2String(af));
|
||||
closesocket(socket_server_fd);
|
||||
return TCP_ERROR_PORT;
|
||||
}
|
||||
}
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 500000;
|
||||
FD_ZERO( &set );
|
||||
FD_SET( socket_server_fd, &set );
|
||||
// When the connection will be made, we will have a writeable fd
|
||||
while((ret = select(socket_server_fd+1, NULL, &set, NULL, &tv)) == 0) {
|
||||
if(count > 30 || stream_check_interrupt(500)) {
|
||||
if(count > 30)
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"connection timeout\n");
|
||||
else
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Connection interrupted by user\n");
|
||||
return TCP_ERROR_TIMEOUT;
|
||||
}
|
||||
count++;
|
||||
FD_ZERO( &set );
|
||||
FD_SET( socket_server_fd, &set );
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 500000;
|
||||
}
|
||||
if (ret < 0) mp_tmsg(MSGT_NETWORK,MSGL_ERR,"Select failed.\n");
|
||||
|
||||
// Turn back the socket as blocking
|
||||
#if !HAVE_WINSOCK2_H
|
||||
fcntl( socket_server_fd, F_SETFL, fcntl(socket_server_fd, F_GETFL) & ~O_NONBLOCK );
|
||||
#else
|
||||
val = 0;
|
||||
ioctlsocket( socket_server_fd, FIONBIO, &val );
|
||||
#endif
|
||||
// Check if there were any errors
|
||||
err_len = sizeof(int);
|
||||
ret = getsockopt(socket_server_fd,SOL_SOCKET,SO_ERROR,&err,&err_len);
|
||||
if(ret < 0) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"getsockopt failed: %s\n",strerror(errno));
|
||||
return TCP_ERROR_FATAL;
|
||||
}
|
||||
if(err > 0) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"connect error: %s\n",strerror(err));
|
||||
return TCP_ERROR_PORT;
|
||||
}
|
||||
|
||||
return socket_server_fd;
|
||||
}
|
||||
|
||||
// Connect to a server using a TCP connection
|
||||
// return -2 for fatal error, like unable to resolve name, connection timeout...
|
||||
// return -1 is unable to connect to a particular port
|
||||
|
||||
|
||||
int
|
||||
connect2Server(char *host, int port, int verb) {
|
||||
#ifdef HAVE_AF_INET6
|
||||
int r;
|
||||
int s = TCP_ERROR_FATAL;
|
||||
|
||||
r = connect2Server_with_af(host, port, network_prefer_ipv4 ? AF_INET:AF_INET6,verb);
|
||||
if (r >= 0) return r;
|
||||
|
||||
s = connect2Server_with_af(host, port, network_prefer_ipv4 ? AF_INET6:AF_INET,verb);
|
||||
if (s == TCP_ERROR_FATAL) return r;
|
||||
return s;
|
||||
#else
|
||||
return connect2Server_with_af(host, port, AF_INET,verb);
|
||||
#endif
|
||||
|
||||
|
||||
}
|
35
stream/tcp.h
35
stream/tcp.h
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* network helpers for TCP connections
|
||||
* (originally borrowed from network.c,
|
||||
* by Bertrand BAUDET <bertrand_baudet@yahoo.com>)
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand BAUDET, 2006 Benjamin Zores
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_TCP_H
|
||||
#define MPLAYER_TCP_H
|
||||
|
||||
/* Connect to a server using a TCP connection */
|
||||
int connect2Server (char *host, int port, int verb);
|
||||
|
||||
#define TCP_ERROR_TIMEOUT -3 /* connection timeout */
|
||||
#define TCP_ERROR_FATAL -2 /* unable to resolve name */
|
||||
#define TCP_ERROR_PORT -1 /* unable to connect to a particular port */
|
||||
|
||||
#endif /* MPLAYER_TCP_H */
|
201
stream/udp.c
201
stream/udp.c
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* network helpers for UDP connections (originally borrowed from rtp.c)
|
||||
*
|
||||
* Copyright (C) 2006 Benjamin Zores
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if !HAVE_WINSOCK2_H
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "core/mp_msg.h"
|
||||
#include "network.h"
|
||||
#include "url.h"
|
||||
#include "udp.h"
|
||||
|
||||
int reuse_socket=0;
|
||||
|
||||
/* Start listening on a UDP port. If multicast, join the group. */
|
||||
int
|
||||
udp_open_socket (URL_t *url)
|
||||
{
|
||||
int socket_server_fd, rxsockbufsz;
|
||||
int err;
|
||||
socklen_t err_len;
|
||||
fd_set set;
|
||||
struct sockaddr_in server_address;
|
||||
struct ip_mreq mcast;
|
||||
struct timeval tv;
|
||||
struct hostent *hp;
|
||||
int reuse=reuse_socket;
|
||||
|
||||
mp_msg (MSGT_NETWORK, MSGL_V,
|
||||
"Listening for traffic on %s:%d ...\n", url->hostname, url->port);
|
||||
|
||||
socket_server_fd = socket (AF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_server_fd == -1)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR, "Failed to create socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&server_address, 0, sizeof(server_address));
|
||||
if (isalpha (url->hostname[0]))
|
||||
{
|
||||
#if !HAVE_WINSOCK2_H
|
||||
hp = gethostbyname (url->hostname);
|
||||
if (!hp)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR,
|
||||
"Counldn't resolve name: %s\n", url->hostname);
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
memcpy (&server_address.sin_addr.s_addr,
|
||||
hp->h_addr_list[0], hp->h_length);
|
||||
#else
|
||||
server_address.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
#endif /* HAVE_WINSOCK2_H */
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_INET_PTON
|
||||
inet_pton (AF_INET, url->hostname, &server_address.sin_addr);
|
||||
#elif HAVE_INET_ATON
|
||||
inet_aton (url->hostname, &server_address.sin_addr);
|
||||
#elif !HAVE_WINSOCK2_H
|
||||
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
#endif
|
||||
}
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_port = htons (url->port);
|
||||
|
||||
if(reuse_socket && setsockopt(socket_server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
|
||||
mp_msg(MSGT_NETWORK, MSGL_ERR, "SO_REUSEADDR failed! ignore.\n");
|
||||
|
||||
if (bind (socket_server_fd, (struct sockaddr *) &server_address,
|
||||
sizeof (server_address)) == -1)
|
||||
{
|
||||
#if !HAVE_WINSOCK2_H
|
||||
if (errno != EINPROGRESS)
|
||||
#else
|
||||
if (WSAGetLastError () != WSAEINPROGRESS)
|
||||
#endif /* HAVE_WINSOCK2_H */
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR, "Failed to connect to server\n");
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_WINSOCK2_H
|
||||
if (isalpha (url->hostname[0]))
|
||||
{
|
||||
hp = gethostbyname (url->hostname);
|
||||
if (!hp)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR,
|
||||
"Could not resolve name: %s\n", url->hostname);
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
memcpy (&server_address.sin_addr.s_addr,
|
||||
hp->h_addr, hp->h_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int addr = inet_addr (url->hostname);
|
||||
memcpy (&server_address.sin_addr, &addr, sizeof (addr));
|
||||
}
|
||||
#endif /* HAVE_WINSOCK2_H */
|
||||
|
||||
/* Increase the socket rx buffer size to maximum -- this is UDP */
|
||||
rxsockbufsz = 240 * 1024;
|
||||
if (setsockopt (socket_server_fd, SOL_SOCKET, SO_RCVBUF,
|
||||
&rxsockbufsz, sizeof (rxsockbufsz)))
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR,
|
||||
"Couldn't set receive socket buffer size\n");
|
||||
}
|
||||
|
||||
if ((ntohl (server_address.sin_addr.s_addr) >> 28) == 0xe)
|
||||
{
|
||||
mcast.imr_multiaddr.s_addr = server_address.sin_addr.s_addr;
|
||||
mcast.imr_interface.s_addr = 0;
|
||||
|
||||
if (setsockopt (socket_server_fd, IPPROTO_IP,
|
||||
IP_ADD_MEMBERSHIP, &mcast, sizeof (mcast)))
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR, "IP_ADD_MEMBERSHIP failed (do you have multicasting enabled in your kernel?)\n");
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
tv.tv_sec = 1; /* 1 second timeout */
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO (&set);
|
||||
FD_SET (socket_server_fd, &set);
|
||||
|
||||
err = select (socket_server_fd + 1, &set, NULL, NULL, &tv);
|
||||
if (err < 0)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_FATAL,
|
||||
"Select failed: %s\n", strerror (errno));
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_ERR,
|
||||
"Timeout! No data from host %s\n", url->hostname);
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err_len = sizeof (err);
|
||||
getsockopt (socket_server_fd, SOL_SOCKET, SO_ERROR, &err, &err_len);
|
||||
if (err)
|
||||
{
|
||||
mp_msg (MSGT_NETWORK, MSGL_DBG2, "Socket error: %d\n", err);
|
||||
closesocket (socket_server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return socket_server_fd;
|
||||
}
|
30
stream/udp.h
30
stream/udp.h
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* network helpers for UDP connections (originally borrowed from rtp.c)
|
||||
*
|
||||
* Copyright (C) 2006 Benjamin Zores
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_UDP_H
|
||||
#define MPLAYER_UDP_H
|
||||
|
||||
#include "url.h"
|
||||
|
||||
int udp_open_socket (URL_t *url);
|
||||
|
||||
#endif /* MPLAYER_UDP_H */
|
506
stream/url.c
506
stream/url.c
@ -1,506 +0,0 @@
|
||||
/*
|
||||
* URL Helper
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "url.h"
|
||||
#include "core/mp_msg.h"
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t)-1)
|
||||
#endif
|
||||
|
||||
static char *mp_asprintf(const char *fmt, ...)
|
||||
{
|
||||
char *p = NULL;
|
||||
va_list va, va_bak;
|
||||
int len;
|
||||
|
||||
va_start(va, fmt);
|
||||
va_copy(va_bak, va);
|
||||
|
||||
len = vsnprintf(NULL, 0, fmt, va);
|
||||
if (len < 0)
|
||||
goto end;
|
||||
|
||||
p = malloc(len + 1);
|
||||
if (!p)
|
||||
goto end;
|
||||
|
||||
len = vsnprintf(p, len + 1, fmt, va_bak);
|
||||
if (len < 0)
|
||||
free(p), p = NULL;
|
||||
|
||||
end:
|
||||
va_end(va);
|
||||
return p;
|
||||
}
|
||||
|
||||
static int is_proxy(const URL_t *url) {
|
||||
return !strcasecmp(url->protocol, "mp_http_proxy") && url->file && strstr(url->file, "://");
|
||||
}
|
||||
|
||||
int url_is_protocol(const URL_t *url, const char *proto) {
|
||||
int proxy = is_proxy(url);
|
||||
if (proxy) {
|
||||
URL_t *tmp = url_new(url->file + 1);
|
||||
int res = !strcasecmp(tmp->protocol, proto);
|
||||
url_free(tmp);
|
||||
return res;
|
||||
}
|
||||
return !strcasecmp(url->protocol, proto);
|
||||
}
|
||||
|
||||
void url_set_protocol(URL_t *url, const char *proto) {
|
||||
int proxy = is_proxy(url);
|
||||
if (proxy) {
|
||||
char *dst = url->file + 1;
|
||||
int oldlen = strstr(dst, "://") - dst;
|
||||
int newlen = strlen(proto);
|
||||
if (newlen != oldlen) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_ERR, "Setting protocol not implemented!\n");
|
||||
return;
|
||||
}
|
||||
memcpy(dst, proto, newlen);
|
||||
return;
|
||||
}
|
||||
free(url->protocol);
|
||||
url->protocol = strdup(proto);
|
||||
}
|
||||
|
||||
URL_t *url_redirect(URL_t **url, const char *redir) {
|
||||
URL_t *u = *url;
|
||||
int proxy = is_proxy(u);
|
||||
const char *oldurl = proxy ? u->file + 1 : u->url;
|
||||
const char *newurl = redir;
|
||||
char *buffer = NULL;
|
||||
URL_t *res;
|
||||
if (!strchr(redir, '/') || *redir == '/') {
|
||||
char *tmp;
|
||||
newurl = buffer = malloc(strlen(oldurl) + strlen(redir) + 1);
|
||||
strcpy(buffer, oldurl);
|
||||
if (*redir == '/') {
|
||||
redir++;
|
||||
tmp = strstr(buffer, "://");
|
||||
if (tmp) tmp = strchr(tmp + 3, '/');
|
||||
} else
|
||||
tmp = strrchr(buffer, '/');
|
||||
if (tmp) tmp[1] = 0;
|
||||
strcat(buffer, redir);
|
||||
}
|
||||
if (proxy) {
|
||||
char *tmp = get_http_proxy_url(u, newurl);
|
||||
free(buffer);
|
||||
newurl = buffer = tmp;
|
||||
}
|
||||
res = url_new(newurl);
|
||||
free(buffer);
|
||||
url_free(u);
|
||||
*url = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *get_noauth_url(const URL_t *url)
|
||||
{
|
||||
if (url->port)
|
||||
return mp_asprintf("%s://%s:%d%s",
|
||||
url->protocol, url->hostname, url->port, url->file);
|
||||
else
|
||||
return mp_asprintf("%s://%s%s",
|
||||
url->protocol, url->hostname, url->file);
|
||||
}
|
||||
|
||||
char *get_http_proxy_url(const URL_t *proxy, const char *host_url)
|
||||
{
|
||||
if (proxy->username)
|
||||
return mp_asprintf("mp_http_proxy://%s:%s@%s:%d/%s",
|
||||
proxy->username,
|
||||
proxy->password ? proxy->password : "",
|
||||
proxy->hostname, proxy->port, host_url);
|
||||
else
|
||||
return mp_asprintf("mp_http_proxy://%s:%d/%s",
|
||||
proxy->hostname, proxy->port, host_url);
|
||||
}
|
||||
|
||||
URL_t*
|
||||
url_new(const char* url) {
|
||||
int pos1, pos2,v6addr = 0;
|
||||
URL_t* Curl = NULL;
|
||||
char *escfilename=NULL;
|
||||
char *ptr1=NULL, *ptr2=NULL, *ptr3=NULL, *ptr4=NULL;
|
||||
int jumpSize = 3;
|
||||
|
||||
if( url==NULL ) return NULL;
|
||||
|
||||
if (strlen(url) > (SIZE_MAX / 3 - 1)) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
escfilename=malloc(strlen(url)*3+1);
|
||||
if (!escfilename ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
// Create the URL container
|
||||
Curl = calloc(1, sizeof(*Curl));
|
||||
if( Curl==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
url_escape_string(escfilename,url);
|
||||
|
||||
// Copy the url in the URL container
|
||||
Curl->url = strdup(escfilename);
|
||||
if( Curl->url==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
mp_msg(MSGT_OPEN,MSGL_V,"Filename for url is now %s\n",escfilename);
|
||||
|
||||
// extract the protocol
|
||||
ptr1 = strstr(escfilename, "://");
|
||||
if( ptr1==NULL ) {
|
||||
// Check for a special case: "sip:" (without "//"):
|
||||
if (strstr(escfilename, "sip:") == escfilename) {
|
||||
ptr1 = (char *)&url[3]; // points to ':'
|
||||
jumpSize = 1;
|
||||
} else {
|
||||
mp_msg(MSGT_NETWORK,MSGL_V,"Not an URL!\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
pos1 = ptr1-escfilename;
|
||||
Curl->protocol = malloc(pos1+1);
|
||||
if( Curl->protocol==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
strncpy(Curl->protocol, escfilename, pos1);
|
||||
Curl->protocol[pos1] = '\0';
|
||||
|
||||
// jump the "://"
|
||||
ptr1 += jumpSize;
|
||||
pos1 += jumpSize;
|
||||
|
||||
// check if a username:password is given
|
||||
ptr2 = strstr(ptr1, "@");
|
||||
ptr3 = strstr(ptr1, "/");
|
||||
if( ptr3!=NULL && ptr3<ptr2 ) {
|
||||
// it isn't really a username but rather a part of the path
|
||||
ptr2 = NULL;
|
||||
}
|
||||
if( ptr2!=NULL ) {
|
||||
// We got something, at least a username...
|
||||
int len = ptr2-ptr1;
|
||||
Curl->username = malloc(len+1);
|
||||
if( Curl->username==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
strncpy(Curl->username, ptr1, len);
|
||||
Curl->username[len] = '\0';
|
||||
|
||||
ptr3 = strstr(ptr1, ":");
|
||||
if( ptr3!=NULL && ptr3<ptr2 ) {
|
||||
// We also have a password
|
||||
int len2 = ptr2-ptr3-1;
|
||||
Curl->username[ptr3-ptr1]='\0';
|
||||
Curl->password = malloc(len2+1);
|
||||
if( Curl->password==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
strncpy( Curl->password, ptr3+1, len2);
|
||||
Curl->password[len2]='\0';
|
||||
url_unescape_string(Curl->password, Curl->password);
|
||||
}
|
||||
url_unescape_string(Curl->username, Curl->username);
|
||||
ptr1 = ptr2+1;
|
||||
pos1 = ptr1-escfilename;
|
||||
}
|
||||
|
||||
// before looking for a port number check if we have an IPv6 type numeric address
|
||||
// in IPv6 URL the numeric address should be inside square braces.
|
||||
ptr2 = strstr(ptr1, "[");
|
||||
ptr3 = strstr(ptr1, "]");
|
||||
ptr4 = strstr(ptr1, "/");
|
||||
if( ptr2!=NULL && ptr3!=NULL && ptr2 < ptr3 && (!ptr4 || ptr4 > ptr3)) {
|
||||
// we have an IPv6 numeric address
|
||||
ptr1++;
|
||||
pos1++;
|
||||
ptr2 = ptr3;
|
||||
v6addr = 1;
|
||||
} else {
|
||||
ptr2 = ptr1;
|
||||
|
||||
}
|
||||
|
||||
// look if the port is given
|
||||
ptr2 = strstr(ptr2, ":");
|
||||
// If the : is after the first / it isn't the port
|
||||
ptr3 = strstr(ptr1, "/");
|
||||
if(ptr3 && ptr3 - ptr2 < 0) ptr2 = NULL;
|
||||
if( ptr2==NULL ) {
|
||||
// No port is given
|
||||
// Look if a path is given
|
||||
if( ptr3==NULL ) {
|
||||
// No path/filename
|
||||
// So we have an URL like http://www.hostname.com
|
||||
pos2 = strlen(escfilename);
|
||||
} else {
|
||||
// We have an URL like http://www.hostname.com/file.txt
|
||||
pos2 = ptr3-escfilename;
|
||||
}
|
||||
} else {
|
||||
// We have an URL beginning like http://www.hostname.com:1212
|
||||
// Get the port number
|
||||
Curl->port = atoi(ptr2+1);
|
||||
pos2 = ptr2-escfilename;
|
||||
}
|
||||
if( v6addr ) pos2--;
|
||||
// copy the hostname in the URL container
|
||||
Curl->hostname = malloc(pos2-pos1+1);
|
||||
if( Curl->hostname==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
strncpy(Curl->hostname, ptr1, pos2-pos1);
|
||||
Curl->hostname[pos2-pos1] = '\0';
|
||||
|
||||
// Look if a path is given
|
||||
ptr2 = strstr(ptr1, "/");
|
||||
if( ptr2!=NULL ) {
|
||||
// A path/filename is given
|
||||
// check if it's not a trailing '/'
|
||||
if( strlen(ptr2)>1 ) {
|
||||
// copy the path/filename in the URL container
|
||||
Curl->file = strdup(ptr2);
|
||||
if( Curl->file==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if a filename was given or set, else set it with '/'
|
||||
if( Curl->file==NULL ) {
|
||||
Curl->file = malloc(2);
|
||||
if( Curl->file==NULL ) {
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_FATAL,"Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
strcpy(Curl->file, "/");
|
||||
}
|
||||
|
||||
Curl->noauth_url = get_noauth_url(Curl);
|
||||
if (!Curl->noauth_url) {
|
||||
mp_msg(MSGT_NETWORK, MSGL_FATAL, "Memory allocation failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
free(escfilename);
|
||||
return Curl;
|
||||
err_out:
|
||||
free(escfilename);
|
||||
if (Curl) url_free(Curl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
url_free(URL_t* url) {
|
||||
if(!url) return;
|
||||
free(url->url);
|
||||
free(url->protocol);
|
||||
free(url->hostname);
|
||||
free(url->file);
|
||||
free(url->username);
|
||||
free(url->password);
|
||||
free(url);
|
||||
}
|
||||
|
||||
|
||||
/* Replace escape sequences in an URL (or a part of an URL) */
|
||||
/* works like strcpy(), but without return argument,
|
||||
except that outbuf == inbuf is allowed */
|
||||
void
|
||||
url_unescape_string(char *outbuf, const char *inbuf)
|
||||
{
|
||||
unsigned char c,c1,c2;
|
||||
int i,len=strlen(inbuf);
|
||||
for (i=0;i<len;i++){
|
||||
c = inbuf[i];
|
||||
if (c == '%' && i<len-2) { //must have 2 more chars
|
||||
c1 = toupper(inbuf[i+1]); // we need uppercase characters
|
||||
c2 = toupper(inbuf[i+2]);
|
||||
if ( ((c1>='0' && c1<='9') || (c1>='A' && c1<='F')) &&
|
||||
((c2>='0' && c2<='9') || (c2>='A' && c2<='F')) ) {
|
||||
if (c1>='0' && c1<='9') c1-='0';
|
||||
else c1-='A'-10;
|
||||
if (c2>='0' && c2<='9') c2-='0';
|
||||
else c2-='A'-10;
|
||||
c = (c1<<4) + c2;
|
||||
i=i+2; //only skip next 2 chars if valid esc
|
||||
}
|
||||
}
|
||||
*outbuf++ = c;
|
||||
}
|
||||
*outbuf++='\0'; //add nullterm to string
|
||||
}
|
||||
|
||||
static void
|
||||
url_escape_string_part(char *outbuf, const char *inbuf) {
|
||||
unsigned char c,c1,c2;
|
||||
int i,len=strlen(inbuf);
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
c = inbuf[i];
|
||||
if ((c=='%') && i<len-2 ) { //need 2 more characters
|
||||
c1=toupper(inbuf[i+1]); c2=toupper(inbuf[i+2]); // need uppercase chars
|
||||
} else {
|
||||
c1=129; c2=129; //not escape chars
|
||||
}
|
||||
|
||||
if( (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9')) {
|
||||
*outbuf++ = c;
|
||||
} else if ( c=='%' && ((c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'F')) &&
|
||||
((c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'F'))) {
|
||||
// check if part of an escape sequence
|
||||
*outbuf++=c; // already
|
||||
|
||||
// dont escape again
|
||||
mp_tmsg(MSGT_NETWORK,MSGL_ERR,"String appears to be already escaped in url_escape %c%c1%c2\n",c,c1,c2);
|
||||
// error as this should not happen against RFC 2396
|
||||
// to escape a string twice
|
||||
} else {
|
||||
/* all others will be escaped */
|
||||
c1 = ((c & 0xf0) >> 4);
|
||||
c2 = (c & 0x0f);
|
||||
if (c1 < 10) c1+='0';
|
||||
else c1+='A'-10;
|
||||
if (c2 < 10) c2+='0';
|
||||
else c2+='A'-10;
|
||||
*outbuf++ = '%';
|
||||
*outbuf++ = c1;
|
||||
*outbuf++ = c2;
|
||||
}
|
||||
}
|
||||
*outbuf++='\0';
|
||||
}
|
||||
|
||||
/* Replace specific characters in the URL string by an escape sequence */
|
||||
/* works like strcpy(), but without return argument */
|
||||
void
|
||||
url_escape_string(char *outbuf, const char *inbuf) {
|
||||
unsigned char c;
|
||||
int i = 0,j,len = strlen(inbuf);
|
||||
char* tmp,*unesc = NULL, *in;
|
||||
|
||||
// Look if we have an ip6 address, if so skip it there is
|
||||
// no need to escape anything in there.
|
||||
tmp = strstr(inbuf,"://[");
|
||||
if(tmp) {
|
||||
tmp = strchr(tmp+4,']');
|
||||
if(tmp && (tmp[1] == '/' || tmp[1] == ':' ||
|
||||
tmp[1] == '\0')) {
|
||||
i = tmp+1-inbuf;
|
||||
strncpy(outbuf,inbuf,i);
|
||||
outbuf += i;
|
||||
tmp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = NULL;
|
||||
while(i < len) {
|
||||
// look for the next char that must be kept
|
||||
for (j=i;j<len;j++) {
|
||||
c = inbuf[j];
|
||||
if(c=='-' || c=='_' || c=='.' || c=='!' || c=='~' || /* mark characters */
|
||||
c=='*' || c=='\'' || c=='(' || c==')' || /* do not touch escape character */
|
||||
c==';' || c=='/' || c=='?' || c==':' || c=='@' || /* reserved characters */
|
||||
c=='&' || c=='=' || c=='+' || c=='$' || c==',') /* see RFC 2396 */
|
||||
break;
|
||||
}
|
||||
// we are on a reserved char, write it out
|
||||
if(j == i) {
|
||||
*outbuf++ = c;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// we found one, take that part of the string
|
||||
if(j < len) {
|
||||
if(!tmp) tmp = malloc(len+1);
|
||||
strncpy(tmp,inbuf+i,j-i);
|
||||
tmp[j-i] = '\0';
|
||||
in = tmp;
|
||||
} else // take the rest of the string
|
||||
in = (char*)inbuf+i;
|
||||
|
||||
if(!unesc) unesc = malloc(len+1);
|
||||
// unescape first to avoid escaping escape
|
||||
url_unescape_string(unesc,in);
|
||||
// then escape, including mark and other reserved chars
|
||||
// that can come from escape sequences
|
||||
url_escape_string_part(outbuf,unesc);
|
||||
outbuf += strlen(outbuf);
|
||||
i += strlen(in);
|
||||
}
|
||||
*outbuf = '\0';
|
||||
free(tmp);
|
||||
free(unesc);
|
||||
}
|
||||
|
||||
#ifdef URL_DEBUG
|
||||
void
|
||||
url_debug(const URL_t *url) {
|
||||
if( url==NULL ) {
|
||||
printf("URL pointer NULL\n");
|
||||
return;
|
||||
}
|
||||
if( url->url!=NULL ) {
|
||||
printf("url=%s\n", url->url );
|
||||
}
|
||||
if( url->protocol!=NULL ) {
|
||||
printf("protocol=%s\n", url->protocol );
|
||||
}
|
||||
if( url->hostname!=NULL ) {
|
||||
printf("hostname=%s\n", url->hostname );
|
||||
}
|
||||
printf("port=%d\n", url->port );
|
||||
if( url->file!=NULL ) {
|
||||
printf("file=%s\n", url->file );
|
||||
}
|
||||
if( url->username!=NULL ) {
|
||||
printf("username=%s\n", url->username );
|
||||
}
|
||||
if( url->password!=NULL ) {
|
||||
printf("password=%s\n", url->password );
|
||||
}
|
||||
}
|
||||
#endif /* URL_DEBUG */
|
55
stream/url.h
55
stream/url.h
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* URL Helper
|
||||
*
|
||||
* Copyright (C) 2001 Bertrand Baudet <bertrand_baudet@yahoo.com>
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_URL_H
|
||||
#define MPLAYER_URL_H
|
||||
|
||||
//#define URL_DEBUG
|
||||
|
||||
typedef struct {
|
||||
char *url;
|
||||
char *noauth_url;
|
||||
char *protocol;
|
||||
char *hostname;
|
||||
char *file;
|
||||
unsigned int port;
|
||||
char *username;
|
||||
char *password;
|
||||
} URL_t;
|
||||
|
||||
int url_is_protocol(const URL_t *url, const char *proto);
|
||||
void url_set_protocol(URL_t *url, const char *proto);
|
||||
URL_t *url_redirect(URL_t **url, const char *redir);
|
||||
|
||||
char *get_http_proxy_url(const URL_t *proxy, const char *host_url);
|
||||
|
||||
URL_t* url_new(const char* url);
|
||||
void url_free(URL_t* url);
|
||||
|
||||
void url_unescape_string(char *outbuf, const char *inbuf);
|
||||
void url_escape_string(char *outbuf, const char *inbuf);
|
||||
|
||||
#ifdef URL_DEBUG
|
||||
void url_debug(const URL_t* url);
|
||||
#endif /* URL_DEBUG */
|
||||
|
||||
#endif /* MPLAYER_URL_H */
|
Loading…
Reference in New Issue
Block a user