diff --git a/.gitignore b/.gitignore index 9b4f7f0373cd9..c29bcdf3c9361 100644 --- a/.gitignore +++ b/.gitignore @@ -591,6 +591,8 @@ lib/cmyth/Makefile /xbmc/cores/amlplayer/Makefile /xbmc/cores/omxplayer/Makefile +/xbmc/utils/Makefile + # /lib/ffmpeg/ /lib/ffmpeg/config.h /lib/ffmpeg/config.err @@ -1099,6 +1101,7 @@ lib/cmyth/Makefile # /xbmc/network/ /xbmc/network/Makefile +/xbmc/network/linux/Makefile /xbmc/network/upnp/Makefile # /lib/libRTV/ diff --git a/Makefile.in b/Makefile.in index 39dc6f9000ff0..4d438d93b3cb4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -77,6 +77,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \ xbmc/pvr/timers/pvrtimers.a \ xbmc/pvr/windows/pvrwindows.a \ xbmc/rendering/rendering.a \ + xbmc/security/security.a \ xbmc/settings/settings.a \ xbmc/storage/storage.a \ xbmc/utils/utils.a \ diff --git a/addons/skin.confluence/720p/DialogAccessPoints.xml b/addons/skin.confluence/720p/DialogAccessPoints.xml new file mode 100644 index 0000000000000..8da2799a7f910 --- /dev/null +++ b/addons/skin.confluence/720p/DialogAccessPoints.xml @@ -0,0 +1,179 @@ + + 3 + dialogeffect + + + 340 + 110 + 600 + 500 + + background image + 0 + 0 + 600 + 500 + DialogBack.png + ![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)] + + + background image + 0 + 0 + 600 + 500 + DialogBack2.png + Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation) + + + Dialog Header image + 40 + 16 + 520 + 40 + dialogheader.png + + + heading label + 40 + 20 + 520 + 30 + font13_title + + center + center + selected + black + + + Close Window button + 510 + 15 + 64 + 32 + + - + PreviousMenu + DialogCloseButton-focus.png + DialogCloseButton.png + 3 + 3 + 3 + 3 + system.getbool(input.enablemouse) + + + 20 + 70 + 530 + 400 + 3 + 3 + 61 + 61 + 61 + 200 + + + 0 + 0 + 530 + 40 + MenuItemNF.png + + + 475 + 10 + 20 + 20 + !IsEmpty(ListItem.Property(encryption)) + ap-lock.png + + + 440 + 5 + 30 + 30 + ap-signal$INFO[ListItem.Property(signal)].png + + + 0 + 0 + 40 + 40 + $INFO[ListItem.Property(state)].png + + + 40 + 0 + 490 + 40 + font13 + grey2 + left + center + + + + + + 0 + 0 + 530 + 40 + MenuItemFO.png + + + 475 + 10 + 20 + 20 + !IsEmpty(ListItem.Property(encryption)) + ap-lock.png + + + 440 + 5 + 30 + 30 + ap-signal$INFO[ListItem.Property(signal)].png + + + 0 + 0 + 40 + 40 + $INFO[ListItem.Property(state)].png + + + 40 + 0 + 490 + 40 + font13 + selected + left + center + + + + + + 555 + 70 + 25 + 400 + ScrollBarV.png + ScrollBarV_bar.png + ScrollBarV_bar_focus.png + ScrollBarNib.png + ScrollBarNib.png + 3 + 3 + false + vertical + + + + diff --git a/addons/skin.confluence/media/Makefile.in b/addons/skin.confluence/media/Makefile.in index 3cf7d1bcd30c5..bfb18c54537fd 100644 --- a/addons/skin.confluence/media/Makefile.in +++ b/addons/skin.confluence/media/Makefile.in @@ -10,7 +10,7 @@ all: $(TARGET) .PHONY: force $(TARGET): $(XBMCTEX) $(IMAGES) -ifeq (@USE_ANDROID@,1) +ifeq (@ARCH@,arm) $(XBMCTEX) -use_none -input . -output $(TARGET) else $(XBMCTEX) -input . -output $(TARGET) diff --git a/addons/skin.confluence/media/ap-lock.png b/addons/skin.confluence/media/ap-lock.png new file mode 100644 index 0000000000000..2ebc4f6f9663e Binary files /dev/null and b/addons/skin.confluence/media/ap-lock.png differ diff --git a/addons/skin.confluence/media/ap-signal1.png b/addons/skin.confluence/media/ap-signal1.png new file mode 100644 index 0000000000000..f53d5cfa4aeb5 Binary files /dev/null and b/addons/skin.confluence/media/ap-signal1.png differ diff --git a/addons/skin.confluence/media/ap-signal2.png b/addons/skin.confluence/media/ap-signal2.png new file mode 100644 index 0000000000000..d760d5b333d2f Binary files /dev/null and b/addons/skin.confluence/media/ap-signal2.png differ diff --git a/addons/skin.confluence/media/ap-signal3.png b/addons/skin.confluence/media/ap-signal3.png new file mode 100644 index 0000000000000..39fb84e29307a Binary files /dev/null and b/addons/skin.confluence/media/ap-signal3.png differ diff --git a/addons/skin.confluence/media/ap-signal4.png b/addons/skin.confluence/media/ap-signal4.png new file mode 100644 index 0000000000000..d4ec0969c560a Binary files /dev/null and b/addons/skin.confluence/media/ap-signal4.png differ diff --git a/addons/skin.confluence/media/ap-signal5.png b/addons/skin.confluence/media/ap-signal5.png new file mode 100644 index 0000000000000..5b833bd44b327 Binary files /dev/null and b/addons/skin.confluence/media/ap-signal5.png differ diff --git a/addons/skin.confluence/media/connected.png b/addons/skin.confluence/media/connected.png new file mode 100644 index 0000000000000..d98f5e0e943f7 Binary files /dev/null and b/addons/skin.confluence/media/connected.png differ diff --git a/addons/skin.confluence/media/connecting.png b/addons/skin.confluence/media/connecting.png new file mode 100644 index 0000000000000..4e985f18a536f Binary files /dev/null and b/addons/skin.confluence/media/connecting.png differ diff --git a/addons/skin.confluence/media/disconnected.png b/addons/skin.confluence/media/disconnected.png new file mode 100644 index 0000000000000..437debc07b1d7 Binary files /dev/null and b/addons/skin.confluence/media/disconnected.png differ diff --git a/configure.in b/configure.in index 476931559a212..f037c2f496370 100644 --- a/configure.in +++ b/configure.in @@ -30,6 +30,21 @@ AC_DEFUN([XB_ADD_PLAYER], esac ]) +# check for enabling additional codecs +AC_DEFUN([XB_ADD_CODEC], +[ + AC_MSG_CHECKING([for $2]) + case $add_codecs in + *$2*) + AC_SUBST([USE_$1], 1) + AC_DEFINE([HAS_$1], 1, [using $2]) + AC_MSG_RESULT([enabling $2]) + ;; + *) + AC_MSG_RESULT([$2 is not enabled]) + esac +]) + # check for library basenames AC_DEFUN([XB_FIND_SONAME], [ @@ -507,6 +522,12 @@ AC_ARG_ENABLE([gtest], [configure_gtest=$enableval], [configure_gtest=no]) +AC_ARG_ENABLE([codec], + [AS_HELP_STRING([--enable-codec], + [enable additional codecs from a list of comma separated names, (default is none, choices are amcodec)])], + [add_codecs=$enableval], + [add_codecs=no]) + ### External libraries options AC_ARG_ENABLE([external-libraries], [AS_HELP_STRING([--enable-external-libraries], @@ -622,10 +643,12 @@ case $host in AC_SUBST(ARCH_DEFINES, "-DTARGET_POSIX -DTARGET_LINUX -D_LINUX -D_POWERPC64") ;; arm*-*-linux-gnu*) - use_texturepacker=no + use_texturepacker_native=yes ARCH="arm" use_arch="arm" AC_SUBST(ARCH_DEFINES, "-DTARGET_POSIX -DTARGET_LINUX -D_LINUX") + CFLAGS="$CFLAGS -Wno-psabi -Wa,-mno-warn-deprecated" + CXXFLAGS="$CXXFLAGS -Wno-psabi -Wa,-mno-warn-deprecated" ;; arm*-*linux-android*) target_platform=target_android @@ -1830,6 +1853,14 @@ case $add_players in ;; esac +# additional internal codecs +case $add_codecs in + *amcodec*) + AC_CHECK_HEADER([amlplayer/codec_error.h],, AC_MSG_ERROR($missing_headers)) + XB_ADD_CODEC([LIBAMCODEC], [amcodec]) + ;; +esac + # platform specific bin utilities if test "$host_vendor" != "apple" ; then AC_CHECK_PROG(HAVE_GAWK,gawk,"yes","no",) @@ -2283,11 +2314,17 @@ else fi if test "x$add_players" != "xno"; then - final_message="$final_message\n additional players:\tYes" + final_message="$final_message\n additional players:\tYes, $add_players" else final_message="$final_message\n additional players:\tNo" fi +if test "x$add_codecs" != "xno"; then + final_message="$final_message\n additional codecs:\tYes, $add_codecs" +else + final_message="$final_message\n additional codecs:\tNo" +fi + ### External libraries messages if test "$use_external_ffmpeg" = "yes"; then @@ -2355,6 +2392,7 @@ OUTPUT_FILES="Makefile \ xbmc/guilib/Makefile \ xbmc/interfaces/Makefile \ xbmc/network/Makefile \ + xbmc/network/linux/Makefile \ xbmc/network/upnp/Makefile \ lib/libRTV/Makefile \ lib/libexif/Makefile \ @@ -2387,7 +2425,8 @@ OUTPUT_FILES="Makefile \ xbmc/peripherals/devices/Makefile \ xbmc/android/activity/Makefile \ xbmc/android/loader/Makefile \ - xbmc/main/Makefile" + xbmc/main/Makefile \ + xbmc/utils/Makefile" if test "$use_skin_touched" = "yes"; then OUTPUT_FILES="$OUTPUT_FILES addons/skin.touched/media/Makefile" diff --git a/language/English/strings.po b/language/English/strings.po index dff2978eed0c7..6be1244943b1b 100644 --- a/language/English/strings.po +++ b/language/English/strings.po @@ -2555,7 +2555,9 @@ msgctxt "#713" msgid "HTTP proxy" msgstr "" -#empty string with id 714 +msgctxt "#714" +msgid "- Network Connection" +msgstr "" msgctxt "#715" msgid "- Assignment" @@ -11015,8 +11017,20 @@ msgctxt "#33200" msgid "Detected New Connection" msgstr "" -#empty strings from id 33201 to 33999 +msgctxt "#33201" +msgid "Available connections" +msgstr "" + +msgctxt "#33202" +msgid "Disconnected" +msgstr "" + +msgctxt "#33203" +msgid "Connecting" +msgstr "" + #translators: no need to add these to your language files +#empty strings from id 33204 to 33999 msgctxt "#34000" msgid "Lame" diff --git a/lib/ffmpeg/libavformat/rtpdec_h264.c b/lib/ffmpeg/libavformat/rtpdec_h264.c index 730ed14807828..d8c87f4d6ef9c 100644 --- a/lib/ffmpeg/libavformat/rtpdec_h264.c +++ b/lib/ffmpeg/libavformat/rtpdec_h264.c @@ -113,7 +113,7 @@ static int sdp_parse_fmtp_config_h264(AVStream * stream, h264_data->level_idc = level_idc; } } else if (!strcmp(attr, "sprop-parameter-sets")) { - uint8_t start_sequence[]= { 0, 0, 1 }; + uint8_t start_sequence[]= { 0, 0, 0, 1 }; codec->extradata_size= 0; codec->extradata= NULL; @@ -176,7 +176,7 @@ static int h264_handle_packet(AVFormatContext *ctx, uint8_t nal = buf[0]; uint8_t type = (nal & 0x1f); int result= 0; - uint8_t start_sequence[]= {0, 0, 1}; + uint8_t start_sequence[]= {0, 0, 0, 1}; #ifdef DEBUG assert(data); diff --git a/lib/ffmpeg/libavformat/rtsp.c b/lib/ffmpeg/libavformat/rtsp.c index 2fccea1bea37f..9714695eb02f9 100644 --- a/lib/ffmpeg/libavformat/rtsp.c +++ b/lib/ffmpeg/libavformat/rtsp.c @@ -403,7 +403,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, break; case 'a': if (av_strstart(p, "control:", &p)) { - if (s->nb_streams == 0) { + if (rt->nb_rtsp_streams == 0) { if (!strncmp(p, "rtsp://", 7)) av_strlcpy(rt->control_uri, p, sizeof(rt->control_uri)); diff --git a/lib/ffmpeg/libavformat/rtsp.h b/lib/ffmpeg/libavformat/rtsp.h index f67ceb89cad03..fa607bbc46ea2 100644 --- a/lib/ffmpeg/libavformat/rtsp.h +++ b/lib/ffmpeg/libavformat/rtsp.h @@ -35,8 +35,8 @@ * Network layer over which RTP/etc packet data will be transported. */ enum RTSPLowerTransport { - RTSP_LOWER_TRANSPORT_UDP = 0, /**< UDP/unicast */ - RTSP_LOWER_TRANSPORT_TCP = 1, /**< TCP; interleaved in RTSP */ + RTSP_LOWER_TRANSPORT_TCP = 0, /**< TCP; interleaved in RTSP */ + RTSP_LOWER_TRANSPORT_UDP = 1, /**< UDP/unicast */ RTSP_LOWER_TRANSPORT_UDP_MULTICAST = 2, /**< UDP/multicast */ RTSP_LOWER_TRANSPORT_NB, RTSP_LOWER_TRANSPORT_HTTP = 8, /**< HTTP tunneled - not a proper diff --git a/lib/ffmpeg/patches/0036-rtp-use-four-byte-startcodes.patch b/lib/ffmpeg/patches/0036-rtp-use-four-byte-startcodes.patch new file mode 100644 index 0000000000000..48a1cdd32eaed --- /dev/null +++ b/lib/ffmpeg/patches/0036-rtp-use-four-byte-startcodes.patch @@ -0,0 +1,23 @@ +diff --git a/lib/ffmpeg/libavformat/rtpdec_h264.c b/lib/ffmpeg/libavformat/rtpdec_h264.c +index 730ed14..d8c87f4 100644 +--- a/lib/ffmpeg/libavformat/rtpdec_h264.c ++++ b/lib/ffmpeg/libavformat/rtpdec_h264.c +@@ -113,7 +113,7 @@ static int sdp_parse_fmtp_config_h264(AVStream * stream, + h264_data->level_idc = level_idc; + } + } else if (!strcmp(attr, "sprop-parameter-sets")) { +- uint8_t start_sequence[]= { 0, 0, 1 }; ++ uint8_t start_sequence[]= { 0, 0, 0, 1 }; + codec->extradata_size= 0; + codec->extradata= NULL; + +@@ -176,7 +176,7 @@ static int h264_handle_packet(AVFormatContext *ctx, + uint8_t nal = buf[0]; + uint8_t type = (nal & 0x1f); + int result= 0; +- uint8_t start_sequence[]= {0, 0, 1}; ++ uint8_t start_sequence[]= {0, 0, 0, 1}; + + #ifdef DEBUG + assert(data); + diff --git a/lib/ffmpeg/patches/0037-rtp-try-tcp-first.patch b/lib/ffmpeg/patches/0037-rtp-try-tcp-first.patch new file mode 100644 index 0000000000000..9405404fa19f8 --- /dev/null +++ b/lib/ffmpeg/patches/0037-rtp-try-tcp-first.patch @@ -0,0 +1,16 @@ +diff --git a/lib/ffmpeg/libavformat/rtsp.h b/lib/ffmpeg/libavformat/rtsp.h +index f67ceb8..fa607bb 100644 +--- a/lib/ffmpeg/libavformat/rtsp.h ++++ b/lib/ffmpeg/libavformat/rtsp.h +@@ -35,8 +35,8 @@ + * Network layer over which RTP/etc packet data will be transported. + */ + enum RTSPLowerTransport { +- RTSP_LOWER_TRANSPORT_UDP = 0, /**< UDP/unicast */ +- RTSP_LOWER_TRANSPORT_TCP = 1, /**< TCP; interleaved in RTSP */ ++ RTSP_LOWER_TRANSPORT_TCP = 0, /**< TCP; interleaved in RTSP */ ++ RTSP_LOWER_TRANSPORT_UDP = 1, /**< UDP/unicast */ + RTSP_LOWER_TRANSPORT_UDP_MULTICAST = 2, /**< UDP/multicast */ + RTSP_LOWER_TRANSPORT_NB, + RTSP_LOWER_TRANSPORT_HTTP = 8, /**< HTTP tunneled - not a proper + diff --git a/lib/ffmpeg/patches/0038-rtp-fix-nb_rtsp_streams-bug-on-send-SETUP-cmd.patch b/lib/ffmpeg/patches/0038-rtp-fix-nb_rtsp_streams-bug-on-send-SETUP-cmd.patch new file mode 100644 index 0000000000000..b8b3cd5593aad --- /dev/null +++ b/lib/ffmpeg/patches/0038-rtp-fix-nb_rtsp_streams-bug-on-send-SETUP-cmd.patch @@ -0,0 +1,14 @@ +diff --git a/lib/ffmpeg/libavformat/rtsp.c b/lib/ffmpeg/libavformat/rtsp.c +index 2fccea1..9714695 100644 +--- a/lib/ffmpeg/libavformat/rtsp.c ++++ b/lib/ffmpeg/libavformat/rtsp.c +@@ -403,7 +403,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, + break; + case 'a': + if (av_strstart(p, "control:", &p)) { +- if (s->nb_streams == 0) { ++ if (rt->nb_rtsp_streams == 0) { + if (!strncmp(p, "rtsp://", 7)) + av_strlcpy(rt->control_uri, p, + sizeof(rt->control_uri)); + diff --git a/lib/libdvd/libdvdnav/Makefile b/lib/libdvd/libdvdnav/Makefile index 85245b7a4d764..8b743463d7397 100644 --- a/lib/libdvd/libdvdnav/Makefile +++ b/lib/libdvd/libdvdnav/Makefile @@ -21,7 +21,7 @@ CFLAGS += -DHAVE_CONFIG_H -DHAVE_DLFCN_H L=libdvdnav MINI_L=libdvdnavmini -CFLAGS += $(DVDREAD_CFLAGS) +#CFLAGS += $(DVDREAD_CFLAGS) LIB = $(L).a SHLIB = $(L).so diff --git a/lib/libdvd/libdvdread/src/dvd_reader.c b/lib/libdvd/libdvdread/src/dvd_reader.c index 8849e848e3725..5ea16c4f73ddc 100644 --- a/lib/libdvd/libdvdread/src/dvd_reader.c +++ b/lib/libdvd/libdvdread/src/dvd_reader.c @@ -248,6 +248,7 @@ static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css ) dvdinput_close(dev); return NULL; } + memset( dvd, 0, sizeof( dvd_reader_t ) ); dvd->isImageFile = 1; dvd->dev = dev; dvd->path_root = NULL; @@ -495,6 +496,8 @@ dvd_reader_t *DVDOpen( const char *ppath ) fclose( mntfile ); } #elif defined(__linux__) + /* amlogic/linux hack, tries to use /root as a device, wtf? */ + if (strcmp("/root", path_copy )) { mntfile = fopen( _PATH_MOUNTED, "r" ); if( mntfile ) { struct mntent *me; @@ -513,6 +516,7 @@ dvd_reader_t *DVDOpen( const char *ppath ) } fclose( mntfile ); } + } #elif defined(_MSC_VER) auth_drive = DVDOpenImageFile( path, have_css ); #endif diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml index 76027b6315ed1..9992693a0fdfb 100644 --- a/system/playercorefactory.xml +++ b/system/playercorefactory.xml @@ -11,7 +11,7 @@ - + @@ -25,6 +25,7 @@ + @@ -35,5 +36,9 @@ + + + + diff --git a/system/shaders/guishader_frag_multi_blendcolor.glsl b/system/shaders/guishader_frag_multi_blendcolor.glsl index b311350ff96fa..58c3e55801291 100644 --- a/system/shaders/guishader_frag_multi_blendcolor.glsl +++ b/system/shaders/guishader_frag_multi_blendcolor.glsl @@ -24,10 +24,10 @@ uniform sampler2D m_samp0; uniform sampler2D m_samp1; varying vec4 m_cord0; varying vec4 m_cord1; -varying lowp vec4 m_colour; +uniform lowp vec4 m_unicol; // SM_MULTI shader void main () { - gl_FragColor.rgba = (texture2D(m_samp0, m_cord0.xy) * texture2D(m_samp1, m_cord1.xy)).rgba * m_colour; + gl_FragColor.rgba = m_unicol * texture2D(m_samp0, m_cord0.xy) * texture2D(m_samp1, m_cord1.xy); } diff --git a/system/shaders/guishader_frag_texture.glsl b/system/shaders/guishader_frag_texture.glsl index 16bb84e442d0c..fbce8a3166296 100644 --- a/system/shaders/guishader_frag_texture.glsl +++ b/system/shaders/guishader_frag_texture.glsl @@ -21,11 +21,11 @@ precision mediump float; uniform sampler2D m_samp0; +uniform lowp vec4 m_unicol; varying vec4 m_cord0; -varying lowp vec4 m_colour; // SM_TEXTURE shader void main () { - gl_FragColor.rgba = vec4(texture2D(m_samp0, m_cord0.xy).rgba * m_colour); + gl_FragColor.rgba = vec4(texture2D(m_samp0, m_cord0.xy).rgba * m_unicol); } diff --git a/tools/amlogic/depends/xbmc/Makefile b/tools/amlogic/depends/xbmc/Makefile new file mode 100644 index 0000000000000..94185efe100a8 --- /dev/null +++ b/tools/amlogic/depends/xbmc/Makefile @@ -0,0 +1,3 @@ +export SOURCE=$(CURDIR)/../../../.. +all: + $(MAKE) -I $(BUILDROOT) -f xbmc.mk diff --git a/tools/amlogic/depends/xbmc/buildroot-sdk.mk b/tools/amlogic/depends/xbmc/buildroot-sdk.mk new file mode 100644 index 0000000000000..1e3509116c594 --- /dev/null +++ b/tools/amlogic/depends/xbmc/buildroot-sdk.mk @@ -0,0 +1,54 @@ +qstrip=$(strip $(subst ",,$(1)))#")) +-include $(BUILDROOT)/.config +-include $(BUILDROOT)/toolchain/gcc/Makefile.in +BASE_DIR=$(BUILDROOT)/output +TOOLCHAIN_EXTERNAL_DIR=$(BASE_DIR)/external-toolchain +ARCH=$(call qstrip,$(BR2_ARCH)) +HOST_DIR=$(call qstrip,$(BR2_HOST_DIR)) +HOST=$(call qstrip,$(BR2_TOOLCHAIN_EXTERNAL_PREFIX)) +PKG_CONFIG_HOST_BINARY=$(HOST_DIR)/usr/bin/pkg-config + +ifndef HOSTAR +HOSTAR:=ar +endif +ifndef HOSTAS +HOSTAS:=as +endif +ifndef HOSTCC +HOSTCC:=gcc +HOSTCC:=$(shell which $(HOSTCC) || type -p $(HOSTCC) || echo gcc) +HOSTCC_NOCCACHE:=$(HOSTCC) +endif +ifndef HOSTCXX +HOSTCXX:=g++ +HOSTCXX:=$(shell which $(HOSTCXX) || type -p $(HOSTCXX) || echo g++) +HOSTCXX_NOCCACHE:=$(HOSTCXX) +endif +ifndef HOSTFC +HOSTFC:=gfortran +endif +ifndef HOSTCPP +HOSTCPP:=cpp +endif +ifndef HOSTLD +HOSTLD:=ld +endif +ifndef HOSTLN +HOSTLN:=ln +endif +ifndef HOSTNM +HOSTNM:=nm +endif +HOSTAR:=$(shell which $(HOSTAR) || type -p $(HOSTAR) || echo ar) +HOSTAS:=$(shell which $(HOSTAS) || type -p $(HOSTAS) || echo as) +HOSTFC:=$(shell which $(HOSTLD) || type -p $(HOSTLD) || echo || which g77 || type -p g77 || echo gfortran) +HOSTCPP:=$(shell which $(HOSTCPP) || type -p $(HOSTCPP) || echo cpp) +HOSTLD:=$(shell which $(HOSTLD) || type -p $(HOSTLD) || echo ld) +HOSTLN:=$(shell which $(HOSTLN) || type -p $(HOSTLN) || echo ln) +HOSTNM:=$(shell which $(HOSTNM) || type -p $(HOSTNM) || echo nm) + +all: +-include $(BUILDROOT)/package/Makefile.in +-include $(BUILDROOT)/package/Makefile.autotools.in +-include $(BUILDROOT)/package/Makefile.cmake.in +-include $(BUILDROOT)/package/Makefile.package.in diff --git a/tools/amlogic/depends/xbmc/xbmc.mk b/tools/amlogic/depends/xbmc/xbmc.mk new file mode 100644 index 0000000000000..da2ae74b1bc6e --- /dev/null +++ b/tools/amlogic/depends/xbmc/xbmc.mk @@ -0,0 +1,21 @@ +all: $(SOURCE)/xbmc.bin + +include buildroot-sdk.mk +-include $(BUILDROOT)/package/python/python.mk +-include $(BUILDROOT)/package/thirdparty/xbmc/xbmc.mk +$(SOURCE)/xbmc.bin: $(SOURCE)/Makefile + cd $(SOURCE); $(MAKE) + +$(SOURCE)/Makefile: $(SOURCE)/configure + cd $(SOURCE); $(TARGET_CONFIGURE_OPTS) $(TARGET_CONFIGURE_ARGS) $(XBMC_CONF_ENV) \ + ./configure --target=$(GNU_TARGET_NAME) --host=$(GNU_TARGET_NAME) --build=$(GNU_HOST_NAME) \ + --prefix=/usr --exec-prefix=/usr --sysconfdir=/etc $(XBMC_CONF_OPT) --disable-external-ffmpeg + +$(SOURCE)/configure: $(BUILDROOT)/package/thirdparty/xbmc/xbmc.mk $(GCC) + cd $(SOURCE); ./bootstrap + +$(GCC): + $(error gcc does not exist. Have you built Buildroot?) + +$(BUILDROOT)/package/thirdparty/xbmc/xbmc.mk: + $(error BUILDROOT must be defined in buildroot-sdk.mk) diff --git a/tools/android/depends/libamplayer/libamplayer/include/codec.h b/tools/android/depends/libamplayer/libamplayer/include/codec.h new file mode 100644 index 0000000000000..187d4d5c82531 --- /dev/null +++ b/tools/android/depends/libamplayer/libamplayer/include/codec.h @@ -0,0 +1,93 @@ +/** +* @file codec.h +* @brief Function prototypes of codec lib +* @author Zhang Chen +* @version 1.0.0 +* @date 2011-02-24 +*/ +/* Copyright (C) 2007-2011, Amlogic Inc. +* All right reserved +* +*/ +#ifndef CODEC_CTRL_H_ +#define CODEC_CTRL_H_ + +#include +#include + + +int codec_init(codec_para_t *); +int codec_close(codec_para_t *); +void codec_audio_basic_init(void); +void codec_close_audio(codec_para_t *); +void codec_resume_audio(codec_para_t *, unsigned int); +int codec_reset(codec_para_t *); +int codec_init_sub(codec_para_t *); +int codec_open_sub_read(void); +int codec_close_sub(codec_para_t *); +int codec_close_sub_fd(CODEC_HANDLE); +int codec_reset_subtile(codec_para_t *pcodec); +int codec_poll_sub(codec_para_t *); +int codec_poll_sub_fd(CODEC_HANDLE, int); +int codec_get_sub_size(codec_para_t *); +int codec_get_sub_size_fd(CODEC_HANDLE); +int codec_read_sub_data(codec_para_t *, char *, unsigned int); +int codec_read_sub_data_fd(CODEC_HANDLE, char *, unsigned int); +int codec_write_sub_data(codec_para_t *, char *, unsigned int); +int codec_init_cntl(codec_para_t *); +int codec_close_cntl(codec_para_t *); +int codec_poll_cntl(codec_para_t *); +int codec_get_cntl_state(codec_para_t *); +int codec_set_cntl_mode(codec_para_t *, unsigned int); +int codec_set_cntl_avthresh(codec_para_t *, unsigned int); +int codec_set_cntl_syncthresh(codec_para_t *pcodec, unsigned int syncthresh); +int codec_reset_audio(codec_para_t *pcodec); +int codec_set_audio_pid(codec_para_t *pcodec); +int codec_set_sub_id(codec_para_t *pcodec); +int codec_set_sub_type(codec_para_t *pcodec); +int codec_audio_reinit(codec_para_t *pcodec); +int codec_set_dec_reset(codec_para_t *pcodec); + +int codec_write(codec_para_t *pcodec, void *buffer, int len); +int codec_checkin_pts(codec_para_t *pcodec, unsigned long pts); +int codec_get_vbuf_state(codec_para_t *, struct buf_status *); +int codec_get_abuf_state(codec_para_t *, struct buf_status *); +int codec_get_vdec_state(codec_para_t *, struct vdec_status *); +int codec_get_adec_state(codec_para_t *, struct adec_status *); + +int codec_pause(codec_para_t *); +int codec_resume(codec_para_t *); +int codec_audio_search(codec_para_t *p); +int codec_set_mute(codec_para_t *p, int mute); +int codec_get_volume_range(codec_para_t *, int *min, int *max); +int codec_set_volume(codec_para_t *, float val); +int codec_get_volume(codec_para_t *, float *val); +int codec_set_lrvolume(codec_para_t *, float lvol,float rvol); +int codec_get_lrvolume(codec_para_t *, float *lvol,float* rvol); +int codec_get_mutesta(codec_para_t *); +int codec_set_volume_balance(codec_para_t *, int); /*left£¨0-100)right*/ +int codec_swap_left_right(codec_para_t *); +int codec_left_mono(codec_para_t *p); +int codec_right_mono(codec_para_t *p); +int codec_stereo(codec_para_t *p); +int codec_get_soundtrack(codec_para_t *p,int* strack); +int codec_audio_automute(void *priv, int auto_mute); +int codec_audio_spectrum_switch(codec_para_t *p, int isStart, int interval); +int codec_audio_isready(codec_para_t *p); +int codec_audio_get_nb_frames(codec_para_t *p); +int codec_audio_set_audioinfo(codec_para_t *p); + +int codec_get_apts(codec_para_t *pcodec); +int codec_get_vpts(codec_para_t *pcodec); +int codec_get_pcrscr(codec_para_t *pcodec); +int codec_set_pcrscr(codec_para_t *pcodec, int val); +int codec_set_syncenable(codec_para_t *pcodec, int enable); +int codec_set_sync_audio_discont(codec_para_t *pcodec, int discontinue); +int codec_get_sync_audio_discont(codec_para_t *pcodec); +int codec_set_sync_video_discont(codec_para_t *pcodec, int discontinue); +int codec_get_sync_video_discont(codec_para_t *pcodec); + +int codec_get_sub_num(codec_para_t *pcodec); +int codec_get_sub_info(codec_para_t *pcodec, subtitle_info_t *sub_info); + +#endif diff --git a/tools/android/depends/libamplayer/libamplayer/include/codec_error.h b/tools/android/depends/libamplayer/libamplayer/include/codec_error.h new file mode 100644 index 0000000000000..0d6df8fdf4ff4 --- /dev/null +++ b/tools/android/depends/libamplayer/libamplayer/include/codec_error.h @@ -0,0 +1,37 @@ +/** +* @file codec_error.h +* @brief Codec error type definitions +* @author Zhang Chen +* @version 1.0.0 +* @date 2011-02-24 +*/ +/* Copyright (C) 2007-2011, Amlogic Inc. +* All right reserved +* +*/ + +#ifndef CODEC_ERROR_H_ +#define CODEC_ERROR_H_ + +#define C_PAE (0x01000000) + +#define CODEC_ERROR_NONE ( 0) +#define CODEC_ERROR_INVAL (C_PAE | 1) +#define CODEC_ERROR_NOMEM (C_PAE | 2) +#define CODEC_ERROR_BUSY (C_PAE | 3) +#define CODEC_ERROR_IO (C_PAE | 4) +#define CODEC_ERROR_PARAMETER (C_PAE | 5) +#define CODEC_ERROR_AUDIO_TYPE_UNKNOW (C_PAE | 6) +#define CODEC_ERROR_VIDEO_TYPE_UNKNOW (C_PAE | 7) +#define CODEC_ERROR_STREAM_TYPE_UNKNOW (C_PAE | 8) +#define CODEC_ERROR_VDEC_TYPE_UNKNOW (C_PAE | 9) + +#define CODEC_ERROR_INIT_FAILED (C_PAE | 10) +#define CODEC_ERROR_SET_BUFSIZE_FAILED (C_PAE | 11) +#define CODEC_OPEN_HANDLE_FAILED (C_PAE | 12) + + + + +#endif + diff --git a/tools/android/depends/libamplayer/libamplayer/include/codec_msg.h b/tools/android/depends/libamplayer/libamplayer/include/codec_msg.h new file mode 100644 index 0000000000000..f898af3756fa6 --- /dev/null +++ b/tools/android/depends/libamplayer/libamplayer/include/codec_msg.h @@ -0,0 +1,19 @@ +/** +* @file codec_msg.h +* @brief Function prototype of codec error +* @author Zhang Chen +* @version 1.0.0 +* @date 2011-02-24 +*/ +/* Copyright (C) 2007-2011, Amlogic Inc. +* All right reserved +* +*/ +#ifndef CODEC_MSG_H +#define CODEC_MSG_H + +const char * codec_error_msg(int error); +int system_error_to_codec_error(int error); +void print_error_msg(int error, int syserr, char *func, int line); + +#endif diff --git a/tools/android/depends/libamplayer/libamplayer/include/codec_type.h b/tools/android/depends/libamplayer/libamplayer/include/codec_type.h new file mode 100644 index 0000000000000..5d61ca8722312 --- /dev/null +++ b/tools/android/depends/libamplayer/libamplayer/include/codec_type.h @@ -0,0 +1,108 @@ +/** +* @file codec_type.h +* @brief Definitions of codec type and structures +* @author Zhang Chen +* @version 1.0.0 +* @date 2011-02-24 +*/ +/* Copyright (C) 2007-2011, Amlogic Inc. +* All right reserved +* +*/ +#ifndef CODEC_TYPE_H_ +#define CODEC_TYPE_H_ + +#include "amports/amstream.h" +#include "amports/vformat.h" +#include "amports/aformat.h" + +typedef int CODEC_HANDLE; + +typedef enum { + STREAM_TYPE_UNKNOW, + STREAM_TYPE_ES_VIDEO, + STREAM_TYPE_ES_AUDIO, + STREAM_TYPE_ES_SUB, + STREAM_TYPE_PS, + STREAM_TYPE_TS, + STREAM_TYPE_RM, +} stream_type_t; + +typedef struct { + unsigned int format; ///< video format, such as H264, MPEG2... + unsigned int width; ///< video source width + unsigned int height; ///< video source height + unsigned int rate; ///< video source frame duration + unsigned int extra; ///< extra data information of video stream + unsigned int status; ///< status of video stream + unsigned int ratio; ///< aspect ratio of video source + void * param; ///< other parameters for video decoder + unsigned long long ratio64; ///< aspect ratio of video source +} dec_sysinfo_t; + +typedef struct { + int valid; ///< audio extradata valid(1) or invalid(0), set by dsp + int sample_rate; ///< audio stream sample rate + int channels; ///< audio stream channels + int bitrate; ///< audio stream bit rate + int codec_id; ///< codec format id + int block_align; ///< audio block align from ffmpeg + int extradata_size; ///< extra data size + char extradata[AUDIO_EXTRA_DATA_SIZE]; ///< extra data information for decoder +} audio_info_t; + +typedef struct { + CODEC_HANDLE handle; ///< codec device handler + CODEC_HANDLE cntl_handle; ///< video control device handler + CODEC_HANDLE sub_handle; ///< subtile device handler + stream_type_t stream_type; ///< stream type(es, ps, rm, ts) + unsigned int has_video:1; ///< stream has video(1) or not(0) + unsigned int has_audio:1; ///< stream has audio(1) or not(0) + unsigned int has_sub:1; ///< stream has subtitle(1) or not(0) + unsigned int noblock:1; ///< codec device is NONBLOCK(1) or not(0) + int video_type; ///< stream video type(H264, VC1...) + int audio_type; ///< stream audio type(PCM, WMA...) + int sub_type; ///< stream subtitle type(TXT, SSA...) + int video_pid; ///< stream video pid + int audio_pid; ///< stream audio pid + int sub_pid; ///< stream subtitle pid + int audio_channels; ///< stream audio channel number + int audio_samplerate; ///< steram audio sample rate + int vbuf_size; ///< video buffer size of codec device + int abuf_size; ///< audio buffer size of codec device + dec_sysinfo_t am_sysinfo; ///< system information for video + audio_info_t audio_info; ///< audio information pass to audiodsp + int packet_size; ///< data size per packet + int avsync_threshold; /// + void * adec_priv; /// +} codec_para_t; + +typedef struct +{ + signed char id; + unsigned char width; + unsigned char height; + unsigned char type; +} subtitle_info_t; +#define MAX_SUB_NUM (32) + +#define IS_VALID_PID(t) (t>=0 && t<=0x1fff) +#define IS_VALID_STREAM(t) (t>0 && t<=0x1fff) +#define IS_VALID_ATYPE(t) (t>=0 && t=0 && t #define MSG_SIZE 64 +#if defined(HAS_AMLPLAYER_CHAPTERS) +#define MAX_CHAPTERS 64 +#endif +#if defined(HAS_AMLPLAYER_VIDEO_STREAMS10) +#define MAX_VIDEO_STREAMS 10 +#else #define MAX_VIDEO_STREAMS 8 +#endif #define MAX_AUDIO_STREAMS 8 #define MAX_SUB_INTERNAL 8 #define MAX_SUB_EXTERNAL 24 @@ -64,8 +71,6 @@ typedef enum PLAYER_DIVX_AUTHORERR = 0x40001, PLAYER_DIVX_RENTAL_EXPIRED = 0x40002, PLAYER_DIVX_RENTAL_VIEW = 0x40003, - - }player_status; @@ -107,6 +112,7 @@ typedef struct typedef struct { + int index; int id; int channel; int sample_rate; @@ -114,10 +120,14 @@ typedef struct aformat_t aformat; int duration; audio_tag_info *audio_tag; +#if defined(HAS_AMLPLAYER_AUDIO_LANG) + char audio_language[4]; +#endif }maudio_info_t; typedef struct { + int index; char id; char internal_external; //0:internal_sub 1:external_sub unsigned short width; @@ -147,14 +157,33 @@ typedef struct int cur_sub_index; int seekable; int drm_check; +#if defined(HAS_AMLPLAYER_VIDEO_STREAMS10) + int t1; + int t2; +#endif +#if defined(HAS_AMLPLAYER_CHAPTERS) + int has_chapter; + int total_chapter_num; +#endif }mstream_info_t; +#if defined(HAS_AMLPLAYER_CHAPTERS) +typedef struct +{ + char *name; + int64_t seekto_ms; +} mchapter_info_t; +#endif + typedef struct { mstream_info_t stream_info; mvideo_info_t *video_info[MAX_VIDEO_STREAMS]; maudio_info_t *audio_info[MAX_AUDIO_STREAMS]; msub_info_t *sub_info[MAX_SUB_STREAMS]; +#if defined(HAS_AMLPLAYER_CHAPTERS) + mchapter_info_t *chapter_info[MAX_CHAPTERS]; +#endif }media_info_t; typedef struct player_info @@ -213,8 +242,23 @@ typedef enum PLAYER_EVENTS_ERROR, ////dev/null || git fetch origin "+refs/heads/*:refs/remotes/origin/*" + git rev-list -1 $(VERSION) >/dev/null + rm -rf $(GIT_DIR)/current; mkdir -p $(GIT_DIR)/current + touch $@ + +$(PLATFORM)/bootstrap: $(GIT_DIR)/current/$(VERSION) + rm -rf $(PLATFORM); mkdir -p $(PLATFORM) + git checkout $(VERSION) -- . + +$(PLATFORM): $(PLATFORM)/bootstrap + cd $(PLATFORM); patch -p1 < ../librtmp-60-second-fix.patch cd $(PLATFORM); patch -p0 < ../prefix.patch $(LIBDYLIB): $(PLATFORM) - $(MAKE) -C $(PLATFORM)/librtmp CROSS_COMPILE=$(CROSSTOOLS) PREFIX=$(PREFIX) + $(MAKE) -C $(PLATFORM)/librtmp CROSS_COMPILE=$(CROSSTOOLS) PREFIX=$(PREFIX) XCFLAGS="$(CFLAGS)" XLDFLAGS="$(LDFLAGS) -lm" .installed-$(PLATFORM): $(LIBDYLIB) $(MAKE) -C $(PLATFORM)/librtmp install PREFIX=$(PREFIX) diff --git a/tools/android/depends/librtmp/librtmp-60-second-fix.patch b/tools/android/depends/librtmp/librtmp-60-second-fix.patch new file mode 100644 index 0000000000000..2914fc1b8eb20 --- /dev/null +++ b/tools/android/depends/librtmp/librtmp-60-second-fix.patch @@ -0,0 +1,1913 @@ +diff --git a/librtmp/amf.c b/librtmp/amf.c +index ce84f81..a25bc04 100644 +--- a/librtmp/amf.c ++++ b/librtmp/amf.c +@@ -610,6 +610,9 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize, + return -1; + } + ++ if (*pBuffer == AMF_NULL) ++ bDecodeName = 0; ++ + if (bDecodeName && nSize < 4) + { /* at least name (length + at least 1 byte) and 1 byte of data */ + RTMP_Log(RTMP_LOGDEBUG, +@@ -801,8 +804,8 @@ AMFProp_Dump(AMFObjectProperty *prop) + } + else + { +- name.av_val = "no-name."; +- name.av_len = sizeof("no-name.") - 1; ++ name.av_val = "no-name"; ++ name.av_len = sizeof("no-name") - 1; + } + if (name.av_len > 18) + name.av_len = 18; +diff --git a/librtmp/dh.h b/librtmp/dh.h +index 9959532..e29587b 100644 +--- a/librtmp/dh.h ++++ b/librtmp/dh.h +@@ -61,7 +61,7 @@ static int MDH_generate_key(MDH *dh) + MP_set(&dh->ctx.P, dh->p); + MP_set(&dh->ctx.G, dh->g); + dh->ctx.len = 128; +- dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs); ++ dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs); + MP_new(dh->pub_key); + MP_new(dh->priv_key); + MP_set(dh->pub_key, &dh->ctx.GX); +diff --git a/librtmp/handshake.h b/librtmp/handshake.h +index 0438486..102ba82 100644 +--- a/librtmp/handshake.h ++++ b/librtmp/handshake.h +@@ -965,8 +965,18 @@ HandShake(RTMP * r, int FP9HandShake) + __FUNCTION__); + RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE); + #endif +- if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE)) +- return FALSE; ++ if (r->Link.CombineConnectPacket) ++ { ++ char *HandshakeResponse = malloc(RTMP_SIG_SIZE); ++ memcpy(HandshakeResponse, (char *) reply, RTMP_SIG_SIZE); ++ r->Link.HandshakeResponse.av_val = HandshakeResponse; ++ r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE; ++ } ++ else ++ { ++ if (!WriteN(r, (char *) reply, RTMP_SIG_SIZE)) ++ return FALSE; ++ } + + /* 2nd part of handshake */ + if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) +diff --git a/librtmp/hashswf.c b/librtmp/hashswf.c +index 9f4e2c0..eeed34c 100644 +--- a/librtmp/hashswf.c ++++ b/librtmp/hashswf.c +@@ -70,7 +70,7 @@ extern TLS_CTX RTMP_TLS_ctx; + + #endif /* CRYPTO */ + +-#define AGENT "Mozilla/5.0" ++#define AGENT "Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20100101 Firefox/8.0" + + HTTPResult + HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb) +@@ -528,7 +528,7 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, + + if (strncmp(buf, "url: ", 5)) + continue; +- if (strncmp(buf + 5, url, hlen)) ++ if (strncmp(buf + 5, url, strlen(buf + 5) - 1)) + continue; + r1 = strrchr(buf, '/'); + i = strlen(r1); +diff --git a/librtmp/log.c b/librtmp/log.c +index 0012985..856e3e4 100644 +--- a/librtmp/log.c ++++ b/librtmp/log.c +@@ -52,8 +52,8 @@ static void rtmp_log_default(int level, const char *format, va_list vl) + vsnprintf(str, MAX_PRINT_LEN-1, format, vl); + + /* Filter out 'no-name' */ +- if ( RTMP_debuglevel + #include + #include ++#include + + #include "rtmp_sys.h" + #include "log.h" +@@ -45,6 +46,7 @@ TLS_CTX RTMP_TLS_ctx; + + #define RTMP_SIG_SIZE 1536 + #define RTMP_LARGE_HEADER_SIZE 12 ++#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf)) + + static const int packetSize[] = { 12, 8, 4, 1 }; + +@@ -97,6 +99,9 @@ static int SendFCSubscribe(RTMP *r, AVal *subscribepath); + static int SendPlay(RTMP *r); + static int SendBytesReceived(RTMP *r); + static int SendUsherToken(RTMP *r, AVal *usherToken); ++static int SendInvoke(RTMP *r, AVal *Command, int queue); ++static int SendGetStreamLength(RTMP *r); ++static int strsplit(char *src, int srclen, char delim, char ***params); + + #if 0 /* unused */ + static int SendBGHasStream(RTMP *r, double dId, AVal *playpath); +@@ -259,6 +264,8 @@ RTMP_Init(RTMP *r) + r->m_fVideoCodecs = 252.0; + r->Link.timeout = 30; + r->Link.swfAge = 30; ++ r->Link.CombineConnectPacket = TRUE; ++ r->Link.ConnectPacket = FALSE; + } + + void +@@ -337,6 +344,7 @@ RTMP_SetupStream(RTMP *r, + AVal *flashVer, + AVal *subscribepath, + AVal *usherToken, ++ AVal *WeebToken, + int dStart, + int dStop, int bLiveStream, long int timeout) + { +@@ -359,6 +367,8 @@ RTMP_SetupStream(RTMP *r, + RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val); + if (usherToken && usherToken->av_val) + RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val); ++ if (WeebToken && WeebToken->av_val) ++ RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", WeebToken->av_val); + if (flashVer && flashVer->av_val) + RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val); + if (dStart > 0) +@@ -426,6 +436,8 @@ RTMP_SetupStream(RTMP *r, + r->Link.subscribepath = *subscribepath; + if (usherToken && usherToken->av_len) + r->Link.usherToken = *usherToken; ++ if (WeebToken && WeebToken->av_len) ++ r->Link.WeebToken = *WeebToken; + r->Link.seekTime = dStart; + r->Link.stopTime = dStop; + if (bLiveStream) +@@ -483,14 +495,22 @@ static struct urlopt { + "Stream is live, no seeking possible" }, + { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0, + "Stream to subscribe to" }, +- { AVC("jtv"), OFF(Link.usherToken), OPT_STR, 0, +- "Justin.tv authentication token" }, +- { AVC("token"), OFF(Link.token), OPT_STR, 0, ++ { AVC("jtv"), OFF(Link.usherToken), OPT_STR, 0, ++ "Justin.tv authentication token"}, ++ { AVC("weeb"), OFF(Link.WeebToken), OPT_STR, 0, ++ "Weeb.tv authentication token"}, ++ { AVC("token"), OFF(Link.token), OPT_STR, 0, + "Key for SecureToken response" }, + { AVC("swfVfy"), OFF(Link.lFlags), OPT_BOOL, RTMP_LF_SWFV, + "Perform SWF Verification" }, + { AVC("swfAge"), OFF(Link.swfAge), OPT_INT, 0, + "Number of days to use cached SWF hash" }, ++#ifdef CRYPTO ++ { AVC("swfsize"), OFF(Link.swfSize), OPT_INT, 0, ++ "Size of the decompressed SWF file"}, ++ { AVC("swfhash"), OFF(Link.swfHash), OPT_STR, 0, ++ "SHA256 hash of the decompressed SWF file"}, ++#endif + { AVC("start"), OFF(Link.seekTime), OPT_INT, 0, + "Stream start position in milliseconds" }, + { AVC("stop"), OFF(Link.stopTime), OPT_INT, 0, +@@ -751,9 +771,16 @@ int RTMP_SetupURL(RTMP *r, char *url) + } + + #ifdef CRYPTO +- if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len) +- RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, +- (unsigned char *)r->Link.SWFHash, r->Link.swfAge); ++ RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %d %d %s\n", r->Link.swfSize, r->Link.swfHash.av_len, r->Link.swfHash.av_val); ++ if (r->Link.swfSize && r->Link.swfHash.av_len) ++ { ++ int i, j = 0; ++ for (i = 0; i < r->Link.swfHash.av_len; i += 2) ++ r->Link.SWFHash[j++] = (HEX2BIN(r->Link.swfHash.av_val[i]) << 4) | HEX2BIN(r->Link.swfHash.av_val[i + 1]); ++ r->Link.SWFSize = (uint32_t) r->Link.swfSize; ++ } ++ else if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len) ++ RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, (unsigned char *) r->Link.SWFHash, r->Link.swfAge); + #endif + + if (r->Link.port == 0) +@@ -854,6 +881,8 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service) + } + + setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)); ++ if (r->Link.protocol & RTMP_FEATURE_HTTP) ++ setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on)); + + return TRUE; + } +@@ -1308,8 +1337,24 @@ ReadN(RTMP *r, char *buffer, int n) + return 0; + } + } +- if (r->m_resplen && !r->m_sb.sb_size) +- RTMPSockBuf_Fill(&r->m_sb); ++ ++ // Try to fill the whole buffer. previous buffer needs to be consumed ++ // completely before receiving new data. ++ if (r->m_resplen && (r->m_sb.sb_size <= 0)) ++ { ++ do ++ { ++ nBytes = RTMPSockBuf_Fill(&r->m_sb); ++ if (nBytes == -1) ++ { ++ if (!r->m_sb.sb_timedout) ++ RTMP_Close(r); ++ return 0; ++ } ++ } ++ while (r->m_resplen && (r->m_sb.sb_size < r->m_resplen) && (nBytes > 0)); ++ } ++ + avail = r->m_sb.sb_size; + if (avail > r->m_resplen) + avail = r->m_resplen; +@@ -1336,10 +1381,9 @@ ReadN(RTMP *r, char *buffer, int n) + r->m_sb.sb_size -= nRead; + nBytes = nRead; + r->m_nBytesIn += nRead; +- if (r->m_bSendCounter +- && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10)) +- if (!SendBytesReceived(r)) +- return FALSE; ++ if (r->m_bSendCounter && r->m_nBytesIn > (r->m_nBytesInSent + r->m_nClientBW / 10)) ++ if (!SendBytesReceived(r)) ++ return FALSE; + } + /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */ + #ifdef _DEBUG +@@ -1390,6 +1434,16 @@ WriteN(RTMP *r, const char *buffer, int n) + } + #endif + ++ if (r->Link.ConnectPacket) ++ { ++ char *ConnectPacket = malloc(r->Link.HandshakeResponse.av_len + n); ++ memcpy(ConnectPacket, r->Link.HandshakeResponse.av_val, r->Link.HandshakeResponse.av_len); ++ memcpy(ConnectPacket + r->Link.HandshakeResponse.av_len, ptr, n); ++ ptr = ConnectPacket; ++ n += r->Link.HandshakeResponse.av_len; ++ r->Link.ConnectPacket = FALSE; ++ } ++ + while (n > 0) + { + int nBytes; +@@ -1455,6 +1509,9 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp) + char pbuf[4096], *pend = pbuf + sizeof(pbuf); + char *enc; + ++ if (r->Link.CombineConnectPacket) ++ r->Link.ConnectPacket = TRUE; ++ + if (cp) + return RTMP_SendPacket(r, cp, TRUE); + +@@ -1667,7 +1724,7 @@ SendUsherToken(RTMP *r, AVal *usherToken) + packet.m_hasAbsTimestamp = 0; + packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; + +- RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val); ++ RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %.*s", usherToken->av_len, usherToken->av_val); + enc = packet.m_body; + enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken); + enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); +@@ -2096,10 +2153,8 @@ SendPlay(RTMP *r) + enc = AMF_EncodeNumber(enc, pend, -1000.0); + else + { +- if (r->Link.seekTime > 0.0) +- enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */ +- else +- enc = AMF_EncodeNumber(enc, pend, 0.0); /*-2000.0);*/ /* recorded as default, -2000.0 is not reliable since that freezes the player if the stream is not found */ ++ if (r->Link.seekTime > 0.0 || r->Link.stopTime) ++ enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */ + } + if (!enc) + return FALSE; +@@ -2215,7 +2270,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime) + int nSize; + char *buf; + +- RTMP_Log(RTMP_LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType); ++ RTMP_Log(RTMP_LOGDEBUG, "sending ctrl, type: 0x%04x", (unsigned short)nType); + + packet.m_nChannel = 0x02; /* control channel (ping) */ + packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; +@@ -2247,8 +2302,8 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime) + } + else if (nType == 0x1A) + { +- *buf = nObject & 0xff; +- } ++ *buf = nObject & 0xff; ++ } + else + { + if (nSize > 2) +@@ -2305,6 +2360,7 @@ AV_clear(RTMP_METHOD *vals, int num) + free(vals); + } + ++SAVC(onBWCheck); + SAVC(onBWDone); + SAVC(onFCSubscribe); + SAVC(onFCUnsubscribe); +@@ -2314,24 +2370,26 @@ SAVC(_error); + SAVC(close); + SAVC(code); + SAVC(level); ++SAVC(description); + SAVC(onStatus); + SAVC(playlist_ready); + static const AVal av_NetStream_Failed = AVC("NetStream.Failed"); + static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed"); +-static const AVal av_NetStream_Play_StreamNotFound = +-AVC("NetStream.Play.StreamNotFound"); +-static const AVal av_NetConnection_Connect_InvalidApp = +-AVC("NetConnection.Connect.InvalidApp"); ++static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound"); ++static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp"); + static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start"); + static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete"); + static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop"); + static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify"); + static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify"); +-static const AVal av_NetStream_Play_PublishNotify = +-AVC("NetStream.Play.PublishNotify"); +-static const AVal av_NetStream_Play_UnpublishNotify = +-AVC("NetStream.Play.UnpublishNotify"); ++static const AVal av_NetStream_Play_PublishNotify = AVC("NetStream.Play.PublishNotify"); ++static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify"); + static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start"); ++static const AVal av_NetConnection_confStream = AVC("NetConnection.confStream"); ++static const AVal av_verifyClient = AVC("verifyClient"); ++static const AVal av_sendStatus = AVC("sendStatus"); ++static const AVal av_getStreamLength = AVC("getStreamLength"); ++static const AVal av_ReceiveCheckPublicStatus = AVC("ReceiveCheckPublicStatus"); + + /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */ + static int +@@ -2341,6 +2399,11 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + AVal method; + double txn; + int ret = 0, nRes; ++ char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc, **params = NULL; ++ char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : ""; ++ char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : ""; ++ int param_count; ++ AVal av_Command, av_Response; + if (body[0] != 0x02) /* make sure it is a string method name we start with */ + { + RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", +@@ -2402,23 +2465,137 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + RTMP_SendServerBW(r); + RTMP_SendCtrl(r, 3, 0, 300); + } +- RTMP_SendCreateStream(r); +- +- if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) +- { +- /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */ +- if (r->Link.usherToken.av_len) +- SendUsherToken(r, &r->Link.usherToken); +- /* Send the FCSubscribe if live stream or if subscribepath is set */ +- if (r->Link.subscribepath.av_len) +- SendFCSubscribe(r, &r->Link.subscribepath); +- else if (r->Link.lFlags & RTMP_LF_LIVE) +- SendFCSubscribe(r, &r->Link.playpath); +- } +- } ++ if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to")) ++ { ++ static char auth[] = {'h', 0xC2, 0xA7, '4', 'j', 'h', 'H', '4', '3', 'd'}; ++ AVal av_auth; ++ SAVC(requestAccess); ++ av_auth.av_val = auth; ++ av_auth.av_len = sizeof (auth); ++ ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_requestAccess); ++ enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeString(enc, pend, &av_auth); ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ SendInvoke(r, &av_Command, FALSE); ++ ++ SAVC(getConnectionCount); ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_getConnectionCount); ++ enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); ++ *enc++ = AMF_NULL; ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ SendInvoke(r, &av_Command, FALSE); ++ ++ SendGetStreamLength(r); ++ } ++ else if (strstr(host, "jampo.com.ua") || strstr(pageUrl, "jampo.com.ua")) ++ { ++ SendGetStreamLength(r); ++ } ++ else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc") ++ || strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in")) ++ { ++ SAVC(r); ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_r); ++ enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); ++ *enc++ = AMF_NULL; ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ SendInvoke(r, &av_Command, FALSE); ++ ++ SendGetStreamLength(r); ++ } ++ else if (strstr(host, "chaturbate.com") || strstr(pageUrl, "chaturbate.com")) ++ { ++ AVal av_ModelName; ++ SAVC(CheckPublicStatus); ++ ++ if (strlen(pageUrl) > 7) ++ { ++ strsplit(pageUrl + 7, FALSE, '/', ¶ms); ++ av_ModelName.av_val = params[1]; ++ av_ModelName.av_len = strlen(params[1]); ++ ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus); ++ enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeString(enc, pend, &av_ModelName); ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ ++ SendInvoke(r, &av_Command, FALSE); ++ } ++ else ++ { ++ RTMP_Log(RTMP_LOGERROR, "you must specify the pageUrl"); ++ RTMP_Close(r); ++ } ++ } ++ /* Weeb.tv specific authentication */ ++ else if (r->Link.WeebToken.av_len) ++ { ++ AVal av_Token, av_Username, av_Password; ++ SAVC(determineAccess); ++ ++ param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', ¶ms); ++ if (param_count >= 1) ++ { ++ av_Token.av_val = params[0]; ++ av_Token.av_len = strlen(params[0]); ++ } ++ if (param_count >= 2) ++ { ++ av_Username.av_val = params[1]; ++ av_Username.av_len = strlen(params[1]); ++ } ++ if (param_count >= 3) ++ { ++ av_Password.av_val = params[2]; ++ av_Password.av_len = strlen(params[2]); ++ } ++ ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_determineAccess); ++ enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeString(enc, pend, &av_Token); ++ enc = AMF_EncodeString(enc, pend, &av_Username); ++ enc = AMF_EncodeString(enc, pend, &av_Password); ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ ++ RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val); ++ SendInvoke(r, &av_Command, FALSE); ++ } ++ else ++ RTMP_SendCreateStream(r); ++ } ++ else if (AVMATCH(&methodInvoked, &av_getStreamLength)) ++ { ++ RTMP_SendCreateStream(r); ++ } + else if (AVMATCH(&methodInvoked, &av_createStream)) +- { +- r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); ++ { ++ r->m_stream_id = (int) AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); ++ ++ if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) ++ { ++ /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */ ++ if (r->Link.usherToken.av_len) ++ SendUsherToken(r, &r->Link.usherToken); ++ /* Send the FCSubscribe if live stream or if subscribepath is set */ ++ if (r->Link.subscribepath.av_len) ++ SendFCSubscribe(r, &r->Link.subscribepath); ++ else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len)) ++ SendFCSubscribe(r, &r->Link.playpath); ++ } + + if (r->Link.protocol & RTMP_FEATURE_WRITE) + { +@@ -2441,7 +2618,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + } + else if (AVMATCH(&method, &av_onBWDone)) + { +- if (!r->m_nBWCheckCounter) ++ if (!r->m_nBWCheckCounter) + SendCheckBW(r); + } + else if (AVMATCH(&method, &av_onFCSubscribe)) +@@ -2457,7 +2634,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + { + SendPong(r, txn); + } +- else if (AVMATCH(&method, &av__onbwcheck)) ++ else if (AVMATCH(&method, &av__onbwcheck) || AVMATCH(&method, &av_onBWCheck)) + { + SendCheckBWResult(r, txn); + } +@@ -2473,20 +2650,63 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + } + else if (AVMATCH(&method, &av__error)) + { +- RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); ++ double code = 0; ++ unsigned int parsedPort; ++ AMFObject obj2; ++ AMFObjectProperty p; ++ AVal redirect; ++ SAVC(ex); ++ SAVC(redirect); ++ ++ AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); ++ if (RTMP_FindFirstMatchingProperty(&obj2, &av_ex, &p)) ++ { ++ AMFProp_GetObject(&p, &obj2); ++ if (RTMP_FindFirstMatchingProperty(&obj2, &av_code, &p)) ++ code = AMFProp_GetNumber(&p); ++ if (code == 302 && RTMP_FindFirstMatchingProperty(&obj2, &av_redirect, &p)) ++ { ++ AMFProp_GetString(&p, &redirect); ++ r->Link.redirected = TRUE; ++ ++ char *url = malloc(redirect.av_len + sizeof ("/playpath")); ++ strncpy(url, redirect.av_val, redirect.av_len); ++ url[redirect.av_len] = '\0'; ++ r->Link.tcUrl.av_val = url; ++ r->Link.tcUrl.av_len = redirect.av_len; ++ strcat(url, "/playpath"); ++ RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, &parsedPort, &r->Link.playpath0, &r->Link.app); ++ r->Link.port = parsedPort; ++ } ++ } ++ if (r->Link.redirected) ++ RTMP_Log(RTMP_LOGINFO, "rtmp server sent redirect"); ++ else ++ RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); + } + else if (AVMATCH(&method, &av_close)) + { +- RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); +- RTMP_Close(r); ++ if (r->Link.redirected) ++ { ++ RTMP_Log(RTMP_LOGINFO, "trying to connect with redirected url"); ++ RTMP_Close(r); ++ r->Link.redirected = FALSE; ++ RTMP_Connect(r, NULL); ++ } ++ else ++ { ++ RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); ++ RTMP_Close(r); ++ } + } + else if (AVMATCH(&method, &av_onStatus)) + { + AMFObject obj2; +- AVal code, level; ++ AVal code, level, description; + AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); + AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code); + AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level); ++ AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description); + + RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); + if (AVMATCH(&code, &av_NetStream_Failed) +@@ -2550,6 +2770,45 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + r->m_pausing = 3; + } + } ++ ++ else if (AVMATCH(&code, &av_NetConnection_confStream)) ++ { ++#ifdef CRYPTO ++ static const char hexdig[] = "0123456789abcdef"; ++ SAVC(cf_stream); ++ int i; ++ char hash_hex[33] = {0}; ++ unsigned char hash[16]; ++ AVal auth; ++ param_count = strsplit(description.av_val, description.av_len, ':', ¶ms); ++ if (param_count >= 3) ++ { ++ char *buf = malloc(strlen(params[0]) + r->Link.playpath.av_len + 1); ++ strcpy(buf, params[0]); ++ strncat(buf, r->Link.playpath.av_val, r->Link.playpath.av_len); ++ md5_hash((unsigned char *) buf, strlen(buf), hash); ++ for (i = 0; i < 16; i++) ++ { ++ hash_hex[i * 2] = hexdig[0x0f & (hash[i] >> 4)]; ++ hash_hex[i * 2 + 1] = hexdig[0x0f & (hash[i])]; ++ } ++ auth.av_val = &hash_hex[atoi(params[1]) - 1]; ++ auth.av_len = atoi(params[2]); ++ RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %.*s", auth.av_len, auth.av_val); ++ ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_cf_stream); ++ enc = AMF_EncodeNumber(enc, pend, txn); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeString(enc, pend, &auth); ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ ++ SendInvoke(r, &av_Command, FALSE); ++ free(buf); ++ } ++#endif ++ } + } + else if (AVMATCH(&method, &av_playlist_ready)) + { +@@ -2563,6 +2822,74 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) + } + } + } ++ else if (AVMATCH(&method, &av_verifyClient)) ++ { ++ double VerificationNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); ++ RTMP_Log(RTMP_LOGDEBUG, "VerificationNumber: %.2f", VerificationNumber); ++ ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av__result); ++ enc = AMF_EncodeNumber(enc, pend, txn); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeNumber(enc, pend, exp(atan(sqrt(VerificationNumber))) + 1); ++ av_Response.av_val = pbuf; ++ av_Response.av_len = enc - pbuf; ++ ++ AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE); ++ AMF_Dump(&obj); ++ SendInvoke(r, &av_Response, FALSE); ++ } ++ else if (AVMATCH(&method, &av_sendStatus)) ++ { ++ if (r->Link.WeebToken.av_len) ++ { ++ AVal av_Authorized = AVC("User.hasAccess"); ++ AVal av_TransferLimit = AVC("User.noPremium.limited"); ++ AVal av_UserLimit = AVC("User.noPremium.tooManyUsers"); ++ AVal av_TimeLeft = AVC("timeLeft"); ++ AVal av_Status, av_ReconnectionTime; ++ ++ AMFObject Status; ++ AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &Status); ++ AMFProp_GetString(AMF_GetProp(&Status, &av_code, -1), &av_Status); ++ RTMP_Log(RTMP_LOGINFO, "%.*s", av_Status.av_len, av_Status.av_val); ++ if (AVMATCH(&av_Status, &av_Authorized)) ++ { ++ RTMP_Log(RTMP_LOGINFO, "Weeb.tv authentication successful"); ++ RTMP_SendCreateStream(r); ++ } ++ else if (AVMATCH(&av_Status, &av_UserLimit)) ++ { ++ RTMP_Log(RTMP_LOGINFO, "No free slots available"); ++ RTMP_Close(r); ++ } ++ else if (AVMATCH(&av_Status, &av_TransferLimit)) ++ { ++ AMFProp_GetString(AMF_GetProp(&Status, &av_TimeLeft, -1), &av_ReconnectionTime); ++ RTMP_Log(RTMP_LOGINFO, "Viewing limit exceeded. try again in %.*s minutes.", av_ReconnectionTime.av_len, av_ReconnectionTime.av_val); ++ RTMP_Close(r); ++ } ++ } ++ } ++ else if (AVMATCH(&method, &av_ReceiveCheckPublicStatus)) ++ { ++ AVal Status; ++ AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &Status); ++ strsplit(Status.av_val, Status.av_len, ',', ¶ms); ++ if (strcmp(params[0], "0") == 0) ++ { ++ RTMP_Log(RTMP_LOGINFO, "Model status is %s", params[1]); ++ RTMP_Close(r); ++ } ++ else ++ { ++ AVal Playpath; ++ Playpath.av_val = params[1]; ++ Playpath.av_len = strlen(params[1]); ++ RTMP_ParsePlaypath(&Playpath, &r->Link.playpath); ++ RTMP_SendCreateStream(r); ++ } ++ } + else + { + +@@ -2748,7 +3075,7 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet) + unsigned int tmp; + if (packet->m_body && packet->m_nBodySize >= 2) + nType = AMF_DecodeInt16(packet->m_body); +- RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType, ++ RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType, + packet->m_nBodySize); + /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ + +@@ -2856,15 +3183,15 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet) + RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__); + if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01) + { +- RTMP_Log(RTMP_LOGERROR, +- "%s: SWFVerification Type %d request not supported! Patches welcome...", +- __FUNCTION__, packet->m_body[2]); ++ RTMP_Log(RTMP_LOGERROR, ++ "%s: SWFVerification Type %d request not supported, attempting to use SWFVerification Type 1! Patches welcome...", ++ __FUNCTION__, packet->m_body[2]); + } + #ifdef CRYPTO + /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ + + /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */ +- else if (r->Link.SWFSize) ++ if (r->Link.SWFSize) + { + RTMP_SendCtrl(r, 0x1B, 0, 0); + } +@@ -3142,8 +3469,18 @@ HandShake(RTMP *r, int FP9HandShake) + serversig[4], serversig[5], serversig[6], serversig[7]); + + /* 2nd part of handshake */ +- if (!WriteN(r, serversig, RTMP_SIG_SIZE)) +- return FALSE; ++ if (r->Link.CombineConnectPacket) ++ { ++ char *HandshakeResponse = malloc(RTMP_SIG_SIZE); ++ memcpy(HandshakeResponse, (char *) serversig, RTMP_SIG_SIZE); ++ r->Link.HandshakeResponse.av_val = HandshakeResponse; ++ r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE; ++ } ++ else ++ { ++ if (!WriteN(r, (char *) serversig, RTMP_SIG_SIZE)) ++ return FALSE; ++ } + + if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) + return FALSE; +@@ -3709,12 +4046,11 @@ HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len) + char hbuf[512]; + int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n" + "Host: %.*s:%d\r\n" +- "Accept: */*\r\n" +- "User-Agent: Shockwave Flash\n" +- "Connection: Keep-Alive\n" ++ "User-Agent: Shockwave Flash\r\n" ++ "Connection: Keep-Alive\r\n" + "Cache-Control: no-cache\r\n" +- "Content-type: application/x-fcs\r\n" +- "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd], ++ "Content-Type: application/x-fcs\r\n" ++ "Content-Length: %d\r\n\r\n", RTMPT_cmds[cmd], + r->m_clientID.av_val ? r->m_clientID.av_val : "", + r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val, + r->Link.port, len); +@@ -3749,6 +4085,14 @@ HTTP_read(RTMP *r, int fill) + if (!ptr) + return -1; + ptr += 4; ++ int resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start); ++ if (hlen < 4096) ++ while (resplen < hlen) ++ { ++ if (RTMPSockBuf_Fill(&r->m_sb) == -1) ++ return -1; ++ resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start); ++ } + r->m_sb.sb_size -= ptr - r->m_sb.sb_start; + r->m_sb.sb_start = ptr; + r->m_unackd--; +@@ -4301,13 +4645,21 @@ fail: + r->m_read.status = nRead; + goto fail; + } +- /* buffer overflow, fix buffer and give up */ +- if (r->m_read.buf < mybuf || r->m_read.buf > end) { +- mybuf = realloc(mybuf, cnt + nRead); +- memcpy(mybuf+cnt, r->m_read.buf, nRead); +- r->m_read.buf = mybuf+cnt+nRead; +- break; +- } ++ /* buffer overflow, fix buffer and give up */ ++ if (r->m_read.buf < mybuf || r->m_read.buf > end) ++ { ++ if (!cnt) ++ { ++ mybuf = realloc(mybuf, sizeof (flvHeader) + cnt + nRead); ++ memcpy(mybuf, flvHeader, sizeof (flvHeader)); ++ cnt += sizeof (flvHeader); ++ } ++ else ++ mybuf = realloc(mybuf, cnt + nRead); ++ memcpy(mybuf + cnt, r->m_read.buf, nRead); ++ r->m_read.buf = mybuf + cnt + nRead; ++ break; ++ } + cnt += nRead; + r->m_read.buf += nRead; + r->m_read.buflen -= nRead; +@@ -4458,3 +4810,90 @@ RTMP_Write(RTMP *r, const char *buf, int size) + } + return size+s2; + } ++ ++static int ++SendInvoke(RTMP *r, AVal *Command, int queue) ++{ ++ RTMPPacket packet; ++ char pbuf[512], *enc; ++ ++ packet.m_nChannel = 0x03; /* control channel (invoke) */ ++ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; ++ packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; ++ packet.m_nTimeStamp = 0; ++ packet.m_nInfoField2 = 0; ++ packet.m_hasAbsTimestamp = 0; ++ packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; ++ ++ enc = packet.m_body; ++ if (Command->av_len) ++ { ++ memcpy(enc, Command->av_val, Command->av_len); ++ enc += Command->av_len; ++ } ++ else ++ return FALSE; ++ packet.m_nBodySize = enc - packet.m_body; ++ ++ return RTMP_SendPacket(r, &packet, queue); ++} ++ ++static int ++strsplit(char *src, int srclen, char delim, char ***params) ++{ ++ char *sptr, *srcbeg, *srcend, *dstr; ++ int count = 1, i = 0, len = 0; ++ ++ if (src == NULL) ++ return 0; ++ if (!srclen) ++ srclen = strlen(src); ++ srcbeg = src; ++ srcend = srcbeg + srclen; ++ sptr = srcbeg; ++ ++ /* count the delimiters */ ++ while (sptr < srcend) ++ { ++ if (*sptr++ == delim) ++ count++; ++ } ++ sptr = srcbeg; ++ *params = calloc(count, sizeof (size_t)); ++ char **param = *params; ++ ++ for (i = 0; i < (count - 1); i++) ++ { ++ dstr = strchr(sptr, delim); ++ len = dstr - sptr; ++ param[i] = calloc(len + 1, sizeof (char)); ++ strncpy(param[i], sptr, len); ++ sptr += len + 1; ++ } ++ ++ /* copy the last string */ ++ if (sptr <= srcend) ++ { ++ len = srclen - (sptr - srcbeg); ++ param[i] = calloc(len + 1, sizeof (char)); ++ strncpy(param[i], sptr, len); ++ } ++ return count; ++} ++ ++static int ++SendGetStreamLength(RTMP *r) ++{ ++ char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc; ++ AVal av_Command; ++ ++ enc = pbuf; ++ enc = AMF_EncodeString(enc, pend, &av_getStreamLength); ++ enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeString(enc, pend, &r->Link.playpath); ++ av_Command.av_val = pbuf; ++ av_Command.av_len = enc - pbuf; ++ ++ return SendInvoke(r, &av_Command, TRUE); ++} +diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h +index 6b2ae5b..411b488 100644 +--- a/librtmp/rtmp.h ++++ b/librtmp/rtmp.h +@@ -150,12 +150,14 @@ extern "C" + AVal playpath; /* passed in explicitly */ + AVal tcUrl; + AVal swfUrl; ++ AVal swfHash; + AVal pageUrl; + AVal app; + AVal auth; + AVal flashVer; + AVal subscribepath; + AVal usherToken; ++ AVal WeebToken; + AVal token; + AMFObject extras; + int edepth; +@@ -172,9 +174,15 @@ extern "C" + int lFlags; + + int swfAge; ++ int swfSize; + + int protocol; ++ int ConnectPacket; ++ int CombineConnectPacket; ++ int redirected; + int timeout; /* connection timeout in seconds */ ++ AVal Extras; ++ AVal HandshakeResponse; + + unsigned short socksport; + unsigned short port; +@@ -299,6 +307,7 @@ extern "C" + AVal *flashVer, + AVal *subscribepath, + AVal *usherToken, ++ AVal *WeebToken, + int dStart, + int dStop, int bLiveStream, long int timeout); + +diff --git a/librtmp/rtmp_sys.h b/librtmp/rtmp_sys.h +index c3fd4a6..1bfb562 100644 +--- a/librtmp/rtmp_sys.h ++++ b/librtmp/rtmp_sys.h +@@ -64,6 +64,7 @@ + #include + #include + #include ++#include + typedef struct tls_ctx { + havege_state hs; + ssl_session ssn; +@@ -71,7 +72,7 @@ typedef struct tls_ctx { + #define TLS_CTX tls_ctx * + #define TLS_client(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\ + ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\ +- ssl_set_rng(s, havege_rand, &ctx->hs);\ ++ ssl_set_rng(s, havege_random, &ctx->hs);\ + ssl_set_ciphersuites(s, ssl_default_ciphersuites);\ + ssl_set_session(s, 1, 600, &ctx->ssn) + #define TLS_setfd(s,fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd) +@@ -80,6 +81,7 @@ typedef struct tls_ctx { + #define TLS_write(s,b,l) ssl_write(s,(unsigned char *)b,l) + #define TLS_shutdown(s) ssl_close_notify(s) + #define TLS_close(s) ssl_free(s); free(s) ++#define md5_hash(i, ilen, o) md5(i, ilen, o) + + #elif defined(USE_GNUTLS) + #include +@@ -95,6 +97,8 @@ typedef struct tls_ctx { + #define TLS_write(s,b,l) gnutls_record_send(s,b,l) + #define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR) + #define TLS_close(s) gnutls_deinit(s) ++#define md5_hash(i, ilen, o) gnutls_digest_algorithm_t algorithm = GNUTLS_DIG_MD5;\ ++ gnutls_hash_fast(algorithm, i, ilen, o); + + #else /* USE_OPENSSL */ + #define TLS_CTX SSL_CTX * +@@ -105,6 +109,7 @@ typedef struct tls_ctx { + #define TLS_write(s,b,l) SSL_write(s,b,l) + #define TLS_shutdown(s) SSL_shutdown(s) + #define TLS_close(s) SSL_free(s) ++#define md5_hash(i, ilen, o) MD5(i, ilen, o) + + #endif + #endif +diff --git a/rtmpdump.c b/rtmpdump.c +index e52f7d4..7bb0890 100644 +--- a/rtmpdump.c ++++ b/rtmpdump.c +@@ -701,6 +701,8 @@ void usage(char *prog) + RTMP_LogPrintf + ("--jtv|-j JSON Authentication token for Justin.tv legacy servers\n"); + RTMP_LogPrintf ++ ("--weeb|-J string Authentication token for weeb.tv servers\n"); ++ RTMP_LogPrintf + ("--hashes|-# Display progress with hashes, not with the byte counter\n"); + RTMP_LogPrintf + ("--buffer|-b Buffer time in milliseconds (default: %u)\n", +@@ -747,7 +749,8 @@ main(int argc, char **argv) + AVal hostname = { 0, 0 }; + AVal playpath = { 0, 0 }; + AVal subscribepath = { 0, 0 }; +- AVal usherToken = { 0, 0 }; //Justin.tv auth token ++ AVal usherToken = { 0, 0 }; // Justin.tv auth token ++ AVal WeebToken = { 0, 0 }; // Weeb.tv auth token + int port = -1; + int protocol = RTMP_PROTOCOL_UNDEFINED; + int retries = 0; +@@ -852,12 +855,13 @@ main(int argc, char **argv) + {"quiet", 0, NULL, 'q'}, + {"verbose", 0, NULL, 'V'}, + {"jtv", 1, NULL, 'j'}, ++ {"weeb", 1, NULL, 'J'}, + {0, 0, 0, 0} + }; + + while ((opt = + getopt_long(argc, argv, +- "hVveqzRr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:", ++ "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:J:", + longopts, NULL)) != -1) + { + switch (opt) +@@ -1070,6 +1074,9 @@ main(int argc, char **argv) + case 'j': + STR2AVAL(usherToken, optarg); + break; ++ case 'J': ++ STR2AVAL(WeebToken, optarg); ++ break; + default: + RTMP_LogPrintf("unknown option: %c\n", opt); + usage(argv[0]); +@@ -1161,14 +1168,14 @@ main(int argc, char **argv) + + if (tcUrl.av_len == 0) + { +- tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + +- hostname.av_len + app.av_len + sizeof("://:65535/"); ++ tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + ++ hostname.av_len + app.av_len + sizeof ("://:65535/"); + tcUrl.av_val = (char *) malloc(tcUrl.av_len); +- if (!tcUrl.av_val) +- return RD_FAILED; ++ if (!tcUrl.av_val) ++ return RD_FAILED; + tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s", +- RTMPProtocolStringsLower[protocol], hostname.av_len, +- hostname.av_val, port, app.av_len, app.av_val); ++ RTMPProtocolStringsLower[protocol], hostname.av_len, ++ hostname.av_val, port, app.av_len, app.av_val); + } + + int first = 1; +@@ -1187,7 +1194,7 @@ main(int argc, char **argv) + + RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, + &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, +- &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); ++ &flashVer, &subscribepath, &usherToken, &WeebToken, dSeek, dStopOffset, bLiveStream, timeout); + + /* Try to keep the stream moving if it pauses on us */ + if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP)) +diff --git a/rtmpgw.c b/rtmpgw.c +index 0cf56bb..cd4396d 100644 +--- a/rtmpgw.c ++++ b/rtmpgw.c +@@ -95,7 +95,8 @@ typedef struct + AVal flashVer; + AVal token; + AVal subscribepath; +- AVal usherToken; //Justin.tv auth token ++ AVal usherToken; // Justin.tv auth token ++ AVal WeebToken; // Weeb.tv auth token + AVal sockshost; + AMFObject extras; + int edepth; +@@ -553,7 +554,7 @@ void processTCPrequest(STREAMING_SERVER * server, // server socket and state (ou + RTMP_Init(&rtmp); + RTMP_SetBufferMS(&rtmp, req.bufferTime); + RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost, +- &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, dSeek, req.dStopOffset, ++ &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, &req.WeebToken, dSeek, req.dStopOffset, + req.bLiveStream, req.timeout); + /* backward compatibility, we always sent this as true before */ + if (req.auth.av_len) +@@ -957,6 +958,9 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req) + case 'j': + STR2AVAL(req->usherToken, arg); + break; ++ case 'J': ++ STR2AVAL(req->WeebToken, arg); ++ break; + default: + RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg); + return FALSE; +@@ -1028,6 +1032,7 @@ main(int argc, char **argv) + {"quiet", 0, NULL, 'q'}, + {"verbose", 0, NULL, 'V'}, + {"jtv", 1, NULL, 'j'}, ++ {"weeb", 1, NULL, 'J'}, + {0, 0, 0, 0} + }; + +@@ -1040,7 +1045,7 @@ main(int argc, char **argv) + + while ((opt = + getopt_long(argc, argv, +- "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:", longopts, ++ "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:J:", longopts, + NULL)) != -1) + { + switch (opt) +@@ -1103,6 +1108,8 @@ main(int argc, char **argv) + RTMP_LogPrintf + ("--jtv|-j JSON Authentication token for Justin.tv legacy servers\n"); + RTMP_LogPrintf ++ ("--weeb|-J string Authentication token for weeb.tv servers\n"); ++ RTMP_LogPrintf + ("--buffer|-b Buffer time in milliseconds (default: %u)\n\n", + defaultRTMPRequest.bufferTime); + +diff --git a/rtmpsrv.c b/rtmpsrv.c +index 9aa62f3..9ec8f23 100644 +--- a/rtmpsrv.c ++++ b/rtmpsrv.c +@@ -96,9 +96,20 @@ STREAMING_SERVER *rtmpServer = 0; // server structure pointer + STREAMING_SERVER *startStreaming(const char *address, int port); + void stopStreaming(STREAMING_SERVER * server); + void AVreplace(AVal *src, const AVal *orig, const AVal *repl); ++char *strreplace(char *srcstr, int srclen, char *orig, char *repl); ++int file_exists(const char *fname); ++int SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit); ++AVal AVcopy(AVal src); ++AVal StripParams(AVal *src); + + static const AVal av_dquote = AVC("\""); + static const AVal av_escdquote = AVC("\\\""); ++#ifdef WIN32 ++static const AVal av_caret = AVC("^"); ++static const AVal av_esccaret = AVC("^^"); ++static const AVal av_pipe = AVC("|"); ++static const AVal av_escpipe = AVC("^|"); ++#endif + + typedef struct + { +@@ -167,6 +178,10 @@ SAVC(level); + SAVC(code); + SAVC(description); + SAVC(secureToken); ++SAVC(_checkbw); ++SAVC(_onbwdone); ++SAVC(checkBandwidth); ++SAVC(onBWDone); + + static int + SendConnectResult(RTMP *r, double txn) +@@ -190,7 +205,7 @@ SendConnectResult(RTMP *r, double txn) + enc = AMF_EncodeNumber(enc, pend, txn); + *enc++ = AMF_OBJECT; + +- STR2AVAL(av, "FMS/3,5,1,525"); ++ STR2AVAL(av, "FMS/3,5,7,7009"); + enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av); + enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0); + enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0); +@@ -212,7 +227,7 @@ SendConnectResult(RTMP *r, double txn) + enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av); + #endif + STR2AVAL(p.p_name, "version"); +- STR2AVAL(p.p_vu.p_aval, "3,5,1,525"); ++ STR2AVAL(p.p_vu.p_aval, "3,5,7,7009"); + p.p_type = AMF_STRING; + obj.o_num = 1; + obj.o_props = &p; +@@ -268,7 +283,7 @@ static int + SendPlayStart(RTMP *r) + { + RTMPPacket packet; +- char pbuf[512], *pend = pbuf+sizeof(pbuf); ++ char pbuf[1024], *pend = pbuf + sizeof (pbuf); + + packet.m_nChannel = 0x03; // control channel (invoke) + packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */ +@@ -300,7 +315,7 @@ static int + SendPlayStop(RTMP *r) + { + RTMPPacket packet; +- char pbuf[512], *pend = pbuf+sizeof(pbuf); ++ char pbuf[1024], *pend = pbuf + sizeof (pbuf); + + packet.m_nChannel = 0x03; // control channel (invoke) + packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */ +@@ -328,6 +343,49 @@ SendPlayStop(RTMP *r) + return RTMP_SendPacket(r, &packet, FALSE); + } + ++int ++SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit) ++{ ++ RTMPPacket packet; ++ char pbuf[256], *pend = pbuf + sizeof (pbuf); ++ char *enc; ++ ++ packet.m_nChannel = 0x03; /* control channel (invoke) */ ++ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; ++ packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; ++ packet.m_nTimeStamp = 0; ++ packet.m_nInfoField2 = 0; ++ packet.m_hasAbsTimestamp = 0; ++ packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; ++ ++ enc = packet.m_body; ++ if (oldMethodType) ++ { ++ enc = AMF_EncodeString(enc, pend, &av__onbwdone); ++ enc = AMF_EncodeNumber(enc, pend, 0); ++ *enc++ = AMF_NULL; ++ enc = AMF_EncodeNumber(enc, pend, 10240); ++ enc = AMF_EncodeNumber(enc, pend, 10240); ++ } ++ else ++ { ++ enc = AMF_EncodeString(enc, pend, &av_onBWDone); ++ enc = AMF_EncodeNumber(enc, pend, 0); ++ *enc++ = AMF_NULL; ++ if (!onBWDoneInit) ++ { ++ enc = AMF_EncodeNumber(enc, pend, 10240); ++ enc = AMF_EncodeNumber(enc, pend, 10240); ++ enc = AMF_EncodeNumber(enc, pend, 0); ++ enc = AMF_EncodeNumber(enc, pend, 0); ++ } ++ } ++ ++ packet.m_nBodySize = enc - packet.m_body; ++ ++ return RTMP_SendPacket(r, &packet, FALSE); ++} ++ + static void + spawn_dumper(int argc, AVal *av, char *cmd) + { +@@ -568,6 +626,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + server->arglen += countAMF(&r->Link.extras, &server->argc); + } + SendConnectResult(r, txn); ++ SendCheckBWResponse(r, FALSE, TRUE); + } + else if (AVMATCH(&method, &av_createStream)) + { +@@ -582,10 +641,22 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + AVal usherToken; + AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken); + AVreplace(&usherToken, &av_dquote, &av_escdquote); ++#ifdef WIN32 ++ AVreplace(&usherToken, &av_caret, &av_esccaret); ++ AVreplace(&usherToken, &av_pipe, &av_escpipe); ++#endif + server->arglen += 6 + usherToken.av_len; + server->argc += 2; + r->Link.usherToken = usherToken; + } ++ else if (AVMATCH(&method, &av__checkbw)) ++ { ++ SendCheckBWResponse(r, TRUE, FALSE); ++ } ++ else if (AVMATCH(&method, &av_checkBandwidth)) ++ { ++ SendCheckBWResponse(r, FALSE, FALSE); ++ } + else if (AVMATCH(&method, &av_play)) + { + char *file, *p, *q, *cmd, *ptr; +@@ -599,6 +670,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + if (obj.o_num > 5) + r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5)); + */ ++ double StartFlag = 0; ++ AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4); ++ if (!(Start->p_type == AMF_INVALID)) ++ StartFlag = AMFProp_GetNumber(Start); ++ r->Link.app = AVcopy(r->Link.app); ++ if (StartFlag == -1000 || strstr(r->Link.app.av_val, "live")) ++ { ++ StartFlag = -1000; ++ server->arglen += 7; ++ server->argc += 1; ++ } + if (r->Link.tcUrl.av_len) + { + len = server->arglen + r->Link.playpath.av_len + 4 + +@@ -616,6 +698,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + argv[argc].av_val = ptr + 1; + argv[argc++].av_len = 2; + argv[argc].av_val = ptr + 5; ++ r->Link.tcUrl = StripParams(&r->Link.tcUrl); + ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val); + argv[argc++].av_len = r->Link.tcUrl.av_len; + +@@ -640,6 +723,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + argv[argc].av_val = ptr + 1; + argv[argc++].av_len = 2; + argv[argc].av_val = ptr + 5; ++ r->Link.swfUrl = StripParams(&r->Link.swfUrl); + ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val); + argv[argc++].av_len = r->Link.swfUrl.av_len; + } +@@ -662,10 +746,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + r->Link.usherToken.av_val = NULL; + r->Link.usherToken.av_len = 0; + } +- if (r->Link.extras.o_num) { +- ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc); +- AMF_Reset(&r->Link.extras); +- } ++ if (StartFlag == -1000) ++ { ++ argv[argc].av_val = ptr + 1; ++ argv[argc++].av_len = 6; ++ ptr += sprintf(ptr, " --live"); ++ } ++ if (r->Link.extras.o_num) ++ { ++ ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc); ++ AMF_Reset(&r->Link.extras); ++ } + argv[argc].av_val = ptr + 1; + argv[argc++].av_len = 2; + argv[argc].av_val = ptr + 5; +@@ -673,7 +764,13 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + r->Link.playpath.av_len, r->Link.playpath.av_val); + argv[argc++].av_len = r->Link.playpath.av_len; + +- av = r->Link.playpath; ++ if (r->Link.playpath.av_len) ++ av = r->Link.playpath; ++ else ++ { ++ av.av_val = "file"; ++ av.av_len = 4; ++ } + /* strip trailing URL parameters */ + q = memchr(av.av_val, '?', av.av_len); + if (q) +@@ -725,7 +822,30 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + argv[argc++].av_len = 2; + argv[argc].av_val = file; + argv[argc].av_len = av.av_len; +- ptr += sprintf(ptr, " -o %s", file); ++#ifdef VLC ++ char *vlc; ++ int didAlloc = FALSE; ++ ++ if (getenv("VLC")) ++ vlc = getenv("VLC"); ++ else if (getenv("ProgramFiles")) ++ { ++ vlc = malloc(512 * sizeof (char)); ++ didAlloc = TRUE; ++ char *ProgramFiles = getenv("ProgramFiles"); ++ sprintf(vlc, "%s%s", ProgramFiles, " (x86)\\VideoLAN\\VLC\\vlc.exe"); ++ if (!file_exists(vlc)) ++ sprintf(vlc, "%s%s", ProgramFiles, "\\VideoLAN\\VLC\\vlc.exe"); ++ } ++ else ++ vlc = "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe"; ++ ++ ptr += sprintf(ptr, " | %s -", vlc); ++ if (didAlloc) ++ free(vlc); ++#else ++ ptr += sprintf(ptr, " -o %s", file); ++#endif + now = RTMP_GetTime(); + if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename)) + { +@@ -739,7 +859,23 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int + server->filetime = now; + free(server->filename.av_val); + server->filename = argv[argc++]; +- spawn_dumper(argc, argv, cmd); ++#ifdef VLC ++ FILE *vlc_cmdfile = fopen("VLC.bat", "w"); ++ char *vlc_batchcmd = strreplace(cmd, 0, "%", "%%"); ++ fprintf(vlc_cmdfile, "%s\n", vlc_batchcmd); ++ fclose(vlc_cmdfile); ++ free(vlc_batchcmd); ++ spawn_dumper(argc, argv, "VLC.bat"); ++#else ++ spawn_dumper(argc, argv, cmd); ++#endif ++ ++#ifdef WIN32 ++ // Dump command to batch file ++ FILE *cmdfile = fopen("Command.bat", "a"); ++ fprintf(cmdfile, "%s\n", cmd); ++ fclose(cmdfile); ++#endif + } + + free(cmd); +@@ -1178,3 +1314,115 @@ AVreplace(AVal *src, const AVal *orig, const AVal *repl) + src->av_val = dest; + src->av_len = dptr - dest; + } ++ ++char * ++strreplace(char *srcstr, int srclen, char *orig, char *repl) ++{ ++ char *ptr = NULL, *sptr = srcstr; ++ int origlen = strlen(orig); ++ int repllen = strlen(repl); ++ if (!srclen) ++ srclen = strlen(srcstr); ++ char *srcend = srcstr + srclen; ++ int dstbuffer = srclen / origlen * repllen; ++ if (dstbuffer < srclen) ++ dstbuffer = srclen; ++ char *dststr = calloc(dstbuffer + 1, sizeof (char)); ++ char *dptr = dststr; ++ ++ if ((ptr = strstr(srcstr, orig))) ++ { ++ while (ptr < srcend && (ptr = strstr(sptr, orig))) ++ { ++ int len = ptr - sptr; ++ memcpy(dptr, sptr, len); ++ sptr += len + origlen; ++ dptr += len; ++ memcpy(dptr, repl, repllen); ++ dptr += repllen; ++ } ++ memcpy(dptr, sptr, srcend - sptr); ++ return dststr; ++ } ++ ++ memcpy(dststr, srcstr, srclen); ++ return dststr; ++} ++ ++AVal ++StripParams(AVal *src) ++{ ++ AVal str; ++ if (src->av_val) ++ { ++ str.av_val = calloc(src->av_len + 1, sizeof (char)); ++ strncpy(str.av_val, src->av_val, src->av_len); ++ str.av_len = src->av_len; ++ char *start = str.av_val; ++ char *end = start + str.av_len; ++ char *ptr = start; ++ ++ while (ptr < end) ++ { ++ if (*ptr == '?') ++ { ++ str.av_len = ptr - start; ++ break; ++ } ++ ptr++; ++ } ++ memset(start + str.av_len, 0, 1); ++ ++ char *dynamic = strstr(start, "[[DYNAMIC]]"); ++ if (dynamic) ++ { ++ dynamic -= 1; ++ memset(dynamic, 0, 1); ++ str.av_len = dynamic - start; ++ end = start + str.av_len; ++ } ++ ++ char *import = strstr(start, "[[IMPORT]]"); ++ if (import) ++ { ++ str.av_val = import + 11; ++ strcpy(start, "http://"); ++ str.av_val = strcat(start, str.av_val); ++ str.av_len = strlen(str.av_val); ++ } ++ return str; ++ } ++ str = *src; ++ return str; ++} ++ ++int ++file_exists(const char *fname) ++{ ++ FILE *file; ++ if ((file = fopen(fname, "r"))) ++ { ++ fclose(file); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++AVal ++AVcopy(AVal src) ++{ ++ AVal dst; ++ if (src.av_len) ++ { ++ dst.av_val = malloc(src.av_len + 1); ++ memcpy(dst.av_val, src.av_val, src.av_len); ++ dst.av_val[src.av_len] = '\0'; ++ dst.av_len = src.av_len; ++ } ++ else ++ { ++ dst.av_val = NULL; ++ dst.av_len = 0; ++ } ++ return dst; ++} +diff --git a/rtmpsuck.c b/rtmpsuck.c +index e886179..e80c686 100644 +--- a/rtmpsuck.c ++++ b/rtmpsuck.c +@@ -143,15 +143,18 @@ SAVC(onStatus); + SAVC(close); + static const AVal av_NetStream_Failed = AVC("NetStream.Failed"); + static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed"); +-static const AVal av_NetStream_Play_StreamNotFound = +-AVC("NetStream.Play.StreamNotFound"); +-static const AVal av_NetConnection_Connect_InvalidApp = +-AVC("NetConnection.Connect.InvalidApp"); ++static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound"); ++static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp"); + static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start"); + static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete"); + static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop"); ++static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken"); + + static const char *cst[] = { "client", "server" }; ++char *dumpAMF(AMFObject *obj, char *ptr); ++char *strreplace(char *srcstr, int srclen, char *orig, char *repl); ++AVal AVcopy(AVal src); ++AVal StripParams(AVal *src); + + // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' + int +@@ -198,26 +201,28 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + if (cobj.o_props[i].p_type == AMF_STRING) + { + pval = cobj.o_props[i].p_vu.p_aval; +- RTMP_LogPrintf("%.*s: %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val); ++ RTMP_LogPrintf("%10.*s : %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val); + } + if (AVMATCH(&pname, &av_app)) + { +- server->rc.Link.app = pval; ++ server->rc.Link.app = AVcopy(pval); + pval.av_val = NULL; + } + else if (AVMATCH(&pname, &av_flashVer)) + { +- server->rc.Link.flashVer = pval; ++ server->rc.Link.flashVer = AVcopy(pval); + pval.av_val = NULL; + } + else if (AVMATCH(&pname, &av_swfUrl)) + { + #ifdef CRYPTO + if (pval.av_val) +- RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize, +- (unsigned char *)server->rc.Link.SWFHash, 30); ++ { ++ AVal swfUrl = StripParams(&pval); ++ RTMP_HashSWF(swfUrl.av_val, &server->rc.Link.SWFSize, (unsigned char *) server->rc.Link.SWFHash, 30); ++ } + #endif +- server->rc.Link.swfUrl = pval; ++ server->rc.Link.swfUrl = AVcopy(pval); + pval.av_val = NULL; + } + else if (AVMATCH(&pname, &av_tcUrl)) +@@ -225,7 +230,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + char *r1 = NULL, *r2; + int len; + +- server->rc.Link.tcUrl = pval; ++ server->rc.Link.tcUrl = AVcopy(pval); + if ((pval.av_val[0] | 0x40) == 'r' && + (pval.av_val[1] | 0x40) == 't' && + (pval.av_val[2] | 0x40) == 'm' && +@@ -267,7 +272,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + } + else if (AVMATCH(&pname, &av_pageUrl)) + { +- server->rc.Link.pageUrl = pval; ++ server->rc.Link.pageUrl = AVcopy(pval); + pval.av_val = NULL; + } + else if (AVMATCH(&pname, &av_audioCodecs)) +@@ -287,14 +292,21 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + if (pval.av_val) + free(pval.av_val); + } ++ + if (obj.o_num > 3) + { +- if (AMFProp_GetBoolean(&obj.o_props[3])) +- server->rc.Link.lFlags |= RTMP_LF_AUTH; +- if (obj.o_num > 4) +- { +- AMFProp_GetString(&obj.o_props[4], &server->rc.Link.auth); +- } ++ int i = obj.o_num - 3; ++ server->rc.Link.extras.o_num = i; ++ server->rc.Link.extras.o_props = malloc(i * sizeof (AMFObjectProperty)); ++ memcpy(server->rc.Link.extras.o_props, obj.o_props + 3, i * sizeof (AMFObjectProperty)); ++ obj.o_num = 3; ++ } ++ ++ if (server->rc.Link.extras.o_num) ++ { ++ server->rc.Link.Extras.av_val = calloc(1024, sizeof (char)); ++ dumpAMF(&server->rc.Link.extras, server->rc.Link.Extras.av_val); ++ server->rc.Link.Extras.av_len = strlen(server->rc.Link.Extras.av_val); + } + + if (!RTMP_Connect(&server->rc, pack)) +@@ -303,6 +315,16 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + return 1; + } + server->rc.m_bSendCounter = FALSE; ++ ++ if (server->rc.Link.extras.o_props) ++ { ++ AMF_Reset(&server->rc.Link.extras); ++ } ++ } ++ else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken)) ++ { ++ AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &server->rc.Link.usherToken); ++ RTMP_LogPrintf("%10s : %.*s\n", "usherToken", server->rc.Link.usherToken.av_len, server->rc.Link.usherToken.av_val); + } + else if (AVMATCH(&method, &av_play)) + { +@@ -323,6 +345,14 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + if (!av.av_val) + goto out; + ++ double StartFlag = 0; ++ AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4); ++ if (!(Start->p_type == AMF_INVALID)) ++ StartFlag = AMFProp_GetNumber(Start); ++ if (StartFlag == -1000 || strstr(server->rc.Link.app.av_val, "live")) ++ StartFlag = -1000; ++ RTMP_LogPrintf("%10s : %s\n", "live", (StartFlag == -1000) ? "yes" : "no"); ++ + /* check for duplicates */ + for (fl = server->f_head; fl; fl=fl->f_next) + { +@@ -372,9 +402,51 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b + for (p=file; *p; p++) + if (*p == ':') + *p = '_'; +- RTMP_LogPrintf("Playpath: %.*s\nSaving as: %s\n", +- server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val, +- file); ++ RTMP_LogPrintf("%10s : %.*s\n%10s : %s\n", "Playpath", server->rc.Link.playpath.av_len, ++ server->rc.Link.playpath.av_val, "Saving as", file); ++ ++#ifdef WIN32 ++ // Dump command to batch file ++ char *cmd = NULL, *ptr = NULL; ++ AVal swfUrl, tcUrl; ++ ++ cmd = calloc(2048, sizeof (char)); ++ ptr = cmd; ++ tcUrl = StripParams(&server->rc.Link.tcUrl); ++ swfUrl = StripParams(&server->rc.Link.swfUrl); ++ ptr += sprintf(ptr, "rtmpdump -r \"%.*s\" -a \"%.*s\" -f \"%.*s\" -W \"%.*s\" -p \"%.*s\"", ++ tcUrl.av_len, tcUrl.av_val, ++ server->rc.Link.app.av_len, server->rc.Link.app.av_val, ++ server->rc.Link.flashVer.av_len, server->rc.Link.flashVer.av_val, ++ swfUrl.av_len, swfUrl.av_val, ++ server->rc.Link.pageUrl.av_len, server->rc.Link.pageUrl.av_val); ++ ++ if (server->rc.Link.usherToken.av_val) ++ { ++ char *usherToken = strreplace(server->rc.Link.usherToken.av_val, server->rc.Link.usherToken.av_len, "\"", "\\\""); ++ usherToken = strreplace(usherToken, 0, "^", "^^"); ++ usherToken = strreplace(usherToken, 0, "|", "^|"); ++ ptr += sprintf(ptr, " --jtv \"%s\"", usherToken); ++ free(usherToken); ++ } ++ ++ if (server->rc.Link.Extras.av_len) ++ { ++ ptr += sprintf(ptr, "%.*s", server->rc.Link.Extras.av_len, server->rc.Link.Extras.av_val); ++ } ++ ++ if (StartFlag == -1000) ++ ptr += sprintf(ptr, "%s", " --live"); ++ ptr += sprintf(ptr, " -y \"%.*s\"", server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val); ++ ptr += sprintf(ptr, " -o \"%s.flv\"\n", file); ++ ++ FILE *cmdfile = fopen("Command.bat", "a"); ++ fprintf(cmdfile, "%s", cmd); ++ fclose(cmdfile); ++ ++ free(cmd); ++#endif ++ + out = fopen(file, "wb"); + free(file); + if (!out) +@@ -1196,3 +1268,146 @@ main(int argc, char **argv) + #endif + return nStatus; + } ++ ++char * ++dumpAMF(AMFObject *obj, char *ptr) ++{ ++ int i; ++ const char opt[] = "NBSO Z"; ++ ++ for (i = 0; i < obj->o_num; i++) ++ { ++ AMFObjectProperty *p = &obj->o_props[i]; ++ if (p->p_type > 5) ++ continue; ++ ptr += sprintf(ptr, " -C "); ++ if (p->p_name.av_val) ++ *ptr++ = 'N'; ++ *ptr++ = opt[p->p_type]; ++ *ptr++ = ':'; ++ if (p->p_name.av_val) ++ ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val); ++ switch (p->p_type) ++ { ++ case AMF_BOOLEAN: ++ *ptr++ = p->p_vu.p_number != 0 ? '1' : '0'; ++ break; ++ case AMF_STRING: ++ memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len); ++ ptr += p->p_vu.p_aval.av_len; ++ break; ++ case AMF_NUMBER: ++ ptr += sprintf(ptr, "%f", p->p_vu.p_number); ++ break; ++ case AMF_OBJECT: ++ *ptr++ = '1'; ++ ptr = dumpAMF(&p->p_vu.p_object, ptr); ++ ptr += sprintf(ptr, " -C O:0"); ++ break; ++ case AMF_NULL: ++ default: ++ break; ++ } ++ } ++ return ptr; ++} ++ ++char * ++strreplace(char *srcstr, int srclen, char *orig, char *repl) ++{ ++ char *ptr = NULL, *sptr = srcstr; ++ int origlen = strlen(orig); ++ int repllen = strlen(repl); ++ if (!srclen) ++ srclen = strlen(srcstr); ++ char *srcend = srcstr + srclen; ++ int dstbuffer = srclen / origlen * repllen; ++ if (dstbuffer < srclen) ++ dstbuffer = srclen; ++ char *dststr = calloc(dstbuffer + 1, sizeof (char)); ++ char *dptr = dststr; ++ ++ if ((ptr = strstr(srcstr, orig))) ++ { ++ while (ptr < srcend && (ptr = strstr(sptr, orig))) ++ { ++ int len = ptr - sptr; ++ memcpy(dptr, sptr, len); ++ sptr += len + origlen; ++ dptr += len; ++ memcpy(dptr, repl, repllen); ++ dptr += repllen; ++ } ++ memcpy(dptr, sptr, srcend - sptr); ++ return dststr; ++ } ++ ++ memcpy(dststr, srcstr, srclen); ++ return dststr; ++} ++ ++AVal ++StripParams(AVal *src) ++{ ++ AVal str; ++ if (src->av_val) ++ { ++ str.av_val = calloc(src->av_len + 1, sizeof (char)); ++ strncpy(str.av_val, src->av_val, src->av_len); ++ str.av_len = src->av_len; ++ char *start = str.av_val; ++ char *end = start + str.av_len; ++ char *ptr = start; ++ ++ while (ptr < end) ++ { ++ if (*ptr == '?') ++ { ++ str.av_len = ptr - start; ++ break; ++ } ++ ptr++; ++ } ++ memset(start + str.av_len, 0, 1); ++ ++ char *dynamic = strstr(start, "[[DYNAMIC]]"); ++ if (dynamic) ++ { ++ dynamic -= 1; ++ memset(dynamic, 0, 1); ++ str.av_len = dynamic - start; ++ end = start + str.av_len; ++ } ++ ++ char *import = strstr(start, "[[IMPORT]]"); ++ if (import) ++ { ++ str.av_val = import + 11; ++ strcpy(start, "http://"); ++ str.av_val = strcat(start, str.av_val); ++ str.av_len = strlen(str.av_val); ++ } ++ return str; ++ } ++ str = *src; ++ return str; ++} ++ ++AVal ++AVcopy(AVal src) ++{ ++ AVal dst; ++ if (src.av_len) ++ { ++ dst.av_val = malloc(src.av_len + 1); ++ memcpy(dst.av_val, src.av_val, src.av_len); ++ dst.av_val[src.av_len] = '\0'; ++ dst.av_len = src.av_len; ++ } ++ else ++ { ++ dst.av_val = NULL; ++ dst.av_len = 0; ++ } ++ return dst; ++} diff --git a/tools/android/depends/libssh/android.patch b/tools/android/depends/libssh/android.patch index c76e648f7f31c..b2fc58d11b080 100644 --- a/tools/android/depends/libssh/android.patch +++ b/tools/android/depends/libssh/android.patch @@ -1,49 +1,61 @@ ---- src/connect.c 2011-05-31 10:29:52.000000000 -0400 -+++ src/connect.c 2012-07-25 23:15:22.281514489 -0400 +--- src/connect.c 2011-05-31 10:29:52.000000000 -0400 ++++ src/connect.c 2012-07-25 23:15:22.281514489 -0400 @@ -159,7 +159,7 @@ int timeout_ms; ssh_pollfd_t fds; int rc = 0; - unsigned int len = sizeof(rc); + socklen_t len = sizeof(rc); - + enter_function(); ---- src/misc.c 2011-05-31 10:29:52.000000000 -0400 -+++ src/misc.c 2012-07-25 23:24:46.611514133 -0400 -@@ -207,6 +207,9 @@ - #endif /* NSS_BUFLEN_PASSWD */ +--- src/misc.c 2012-10-08 23:50:23.567426941 -0400 ++++ src/misc.c 2012-10-09 00:19:57.217425822 -0400 +@@ -208,17 +208,12 @@ char *ssh_get_user_home_dir(void) { -+#if defined(__ANDROID__) -+ return NULL; -+#else char *szPath = NULL; - struct passwd pwd; - struct passwd *pwdbuf; -@@ -221,6 +224,7 @@ - szPath = strdup(pwd.pw_dir); +- struct passwd pwd; +- struct passwd *pwdbuf; +- char buf[NSS_BUFLEN_PASSWD]; +- int rc; +- +- rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); +- if (rc != 0) { ++ struct passwd *pwd = NULL; ++ pwd = getpwuid(getuid()); ++ if ( pwd == NULL) + return NULL; +- } + +- szPath = strdup(pwd.pw_dir); ++ szPath = strdup(pwd->pw_dir); return szPath; -+#endif } - - /* we have read access on file */ -@@ -233,6 +237,9 @@ +@@ -233,20 +228,16 @@ } char *ssh_get_local_username(ssh_session session) { -+#if defined(__ANDROID__) -+ return NULL; -+#else - struct passwd pwd; - struct passwd *pwdbuf; - char buf[NSS_BUFLEN_PASSWD]; -@@ -254,6 +261,7 @@ +- struct passwd pwd; +- struct passwd *pwdbuf; +- char buf[NSS_BUFLEN_PASSWD]; +- char *name; +- int rc; +- +- rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); +- if (rc != 0) { ++ char *name = NULL; ++ struct passwd *pwd = NULL; ++ pwd = getpwuid(getuid()); ++ if ( pwd == NULL) { + ssh_set_error(session, SSH_FATAL, + "Couldn't retrieve information for current user!"); + return NULL; } - return name; -+#endif - } +- name = strdup(pwd.pw_name); ++ name = strdup(pwd->pw_name); - int ssh_is_ipaddr_v4(const char *str) { + if (name == NULL) { + ssh_set_error_oom(session); diff --git a/tools/android/depends/xbmc/Makefile b/tools/android/depends/xbmc/Makefile index a5c5c3207b36f..986b19855143b 100644 --- a/tools/android/depends/xbmc/Makefile +++ b/tools/android/depends/xbmc/Makefile @@ -20,7 +20,8 @@ CONFIGURE=cp -f $(CONFIG_SUB) $(CONFIG_GUESS) build-aux/ ;\ --enable-neon --enable-gles --enable-debug \ --disable-sdl --disable-x11 --disable-xrandr \ --disable-optical-drive --disable-joystick \ - --enable-shared-lib --disable-alsa + --enable-shared-lib --disable-alsa \ + --enable-codec=amcodec --enable-player=amlplayer all: $(SOURCE)/libxbmc.so diff --git a/tools/android/packaging/Makefile b/tools/android/packaging/Makefile index 124be50dc2021..72d01244fc42c 100644 --- a/tools/android/packaging/Makefile +++ b/tools/android/packaging/Makefile @@ -5,7 +5,7 @@ OBJS = libcurl.so \ libafpclient.so \ libshairport.so libplist.so \ libxbogg.so libxbvorbis.so libxbvorbisfile.so libxbFLAC.so libxbmpeg2.so \ - libxbmpeg2convert.so libnfs.so + libxbmpeg2convert.so libnfs.so libass.so PLATFORM_OBJS = EXCLUDED_ADDONS = screensaver.rsxs.euphoria visualization.dxspectrum visualization.milkdrop visualization.projectm @@ -65,6 +65,7 @@ extras: libs cd xbmc/assets/python2.6/lib/python2.6/; rm -rf test config lib-dynload mkdir -p tmp/res; $(AAPT) c -S xbmc/res -C tmp/res; cp -r -n xbmc/res tmp/ || true $(AAPT) p -f -I $(SDKROOT)/platforms/$(SDK_PLATFORM)/android.jar -S tmp/res/ -M xbmc/AndroidManifest.xml -F images/xbmcapp-debug-skeleton.apk -J xbmc/src + test -d config && cp -f config/* xbmc/assets/system @rm -rf tmp/ libs: $(PREFIX)/lib/xbmc/libxbmc.so diff --git a/tools/android/packaging/config/advancedsettings.xml b/tools/android/packaging/config/advancedsettings.xml new file mode 100644 index 0000000000000..f9270929277ce --- /dev/null +++ b/tools/android/packaging/config/advancedsettings.xml @@ -0,0 +1,25 @@ + + + lookandfeel.soundskin + videoplayer.rendermethod + videoplayer.adjustrefreshrate + videoplayer.pauseafterrefreshchange + videoplayer.usedisplayasclock + videoplayer.synctype + audiocds + karaoke + videoscreen.screenmode + videoscreen.screen + videoscreen.vsync + videoscreen.testpattern + audiooutput.mode + audiooutput.channellayout + audiooutput.audiodevice + audiooutput.customdevice + audiooutput.passthroughdevice + audiooutput.custompassthrough + input.remoteaskeyboard + audiooutput.guisoundmode + + + diff --git a/tools/android/packaging/config/guisettings.xml b/tools/android/packaging/config/guisettings.xml new file mode 100644 index 0000000000000..1b71e6020c349 --- /dev/null +++ b/tools/android/packaging/config/guisettings.xml @@ -0,0 +1,20 @@ + + + false + + + 0 + false + false + false + false> + false + 2 + + + 2 + + + true + + diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index c6f0a14361e12..6367e1b77523f 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "threads/SystemClock.h" #include "system.h" #include "Application.h" @@ -249,9 +248,7 @@ #include "pictures/GUIDialogPictureInfo.h" #include "addons/GUIDialogAddonSettings.h" #include "addons/GUIDialogAddonInfo.h" -#ifdef HAS_LINUX_NETWORK #include "network/GUIDialogAccessPoints.h" -#endif /* PVR related include Files */ #include "pvr/PVRManager.h" @@ -335,6 +332,10 @@ #include "input/SDLJoystick.h" #endif +#if defined(TARGET_ANDROID) +#include "android/activity/XBMCApp.h" +#endif + using namespace std; using namespace ADDON; using namespace XFILE; @@ -386,7 +387,6 @@ CApplication::CApplication(void) , m_musicInfoScanner(new CMusicInfoScanner) , m_seekHandler(new CSeekHandler) { - m_network = NULL; TiXmlBase::SetCondenseWhiteSpace(false); m_iPlaySpeed = 1; m_bInhibitIdleShutdown = false; @@ -567,14 +567,6 @@ void CApplication::Preflight() bool CApplication::Create() { -#if defined(HAS_LINUX_NETWORK) - m_network = new CNetworkLinux(); -#elif defined(HAS_WIN32_NETWORK) - m_network = new CNetworkWin32(); -#else - m_network = new CNetwork(); -#endif - Preflight(); g_settings.Initialize(); //Initialize default AdvancedSettings @@ -647,7 +639,7 @@ bool CApplication::Create() CStdString executable = CUtil::ResolveExecutablePath(); CLog::Log(LOGNOTICE, "The executable running is: %s", executable.c_str()); - CLog::Log(LOGNOTICE, "Local hostname: %s", m_network->GetHostName().c_str()); + CLog::Log(LOGNOTICE, "Local hostname: %s", m_network.GetDefaultConnectionName().c_str()); CLog::Log(LOGNOTICE, "Log File is located: %sxbmc.log", g_settings.m_logFolder.c_str()); CLog::Log(LOGNOTICE, "-----------------------------------------------------------------------"); @@ -744,6 +736,8 @@ bool CApplication::Create() CAEFactory::SetMute (g_settings.m_bMute); CAEFactory::SetSoundMode(g_guiSettings.GetInt("audiooutput.guisoundmode")); + m_network.Initialize(); + // initialize the addon database (must be before the addon manager is init'd) CDatabaseManager::Get().Initialize(true); @@ -754,6 +748,9 @@ bool CApplication::Create() CLog::Log(LOGFATAL, "CApplication::Create: Unable to start CAddonMgr"); return false; } +#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) + g_RemoteControl.Initialize(); +#endif g_peripherals.Initialise(); @@ -763,9 +760,6 @@ bool CApplication::Create() g_Mouse.SetEnabled(g_guiSettings.GetBool("input.enablemouse")); g_Keyboard.Initialize(); -#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) - g_RemoteControl.Initialize(); -#endif #if defined(TARGET_DARWIN_OSX) // Configure and possible manually start the helper. @@ -1280,10 +1274,8 @@ bool CApplication::Initialize() g_windowManager.Add(new CGUIDialogBusy); g_windowManager.Add(new CGUIDialogPictureInfo); g_windowManager.Add(new CGUIDialogAddonInfo); - g_windowManager.Add(new CGUIDialogAddonSettings); -#ifdef HAS_LINUX_NETWORK - g_windowManager.Add(new CGUIDialogAccessPoints); -#endif + g_windowManager.Add(new CGUIDialogAddonSettings); // window id = 140 + g_windowManager.Add(new CGUIDialogAccessPoints); // window id = 141 g_windowManager.Add(new CGUIDialogLockSettings); @@ -1411,6 +1403,7 @@ bool CApplication::Initialize() // reset our screensaver (starts timers etc.) ResetScreenSaver(); + m_keyringManager.Initialize(); #ifdef HAS_SDL_JOYSTICK g_Joystick.SetEnabled(g_guiSettings.GetBool("input.enablejoystick") && @@ -1517,7 +1510,7 @@ bool CApplication::StartServer(enum ESERVERS eServer, bool bStart, bool bWait/* bool CApplication::StartWebServer() { #ifdef HAS_WEB_SERVER - if (g_guiSettings.GetBool("services.webserver") && m_network->IsAvailable()) + if (g_guiSettings.GetBool("services.webserver") && m_network.IsAvailable()) { int webPort = atoi(g_guiSettings.GetString("services.webserverport")); CLog::Log(LOGNOTICE, "Webserver: Starting..."); @@ -1532,7 +1525,7 @@ bool CApplication::StartWebServer() bool started = false; if (m_WebServer.Start(webPort, g_guiSettings.GetString("services.webserverusername"), g_guiSettings.GetString("services.webserverpassword"))) { - std::map txt; + std::vector > txt; started = true; // publish web frontend and API services #ifdef HAS_WEB_INTERFACE @@ -1573,7 +1566,7 @@ bool CApplication::StartAirplayServer() { bool ret = false; #ifdef HAS_AIRPLAY - if (g_guiSettings.GetBool("services.airplay") && m_network->IsAvailable()) + if (g_guiSettings.GetBool("services.airplay") && m_network.IsAvailable()) { int listenPort = g_advancedSettings.m_airPlayPort; CStdString password = g_guiSettings.GetString("services.airplaypassword"); @@ -1582,19 +1575,16 @@ bool CApplication::StartAirplayServer() if (CAirPlayServer::StartServer(listenPort, true)) { CAirPlayServer::SetCredentials(usePassword, password); - std::map txt; - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface) - { - txt["deviceid"] = iface->GetMacAddress(); - } - else - { - txt["deviceid"] = "FF:FF:FF:FF:FF:F2"; - } - txt["features"] = "0x77"; - txt["model"] = "AppleTV2,1"; - txt["srcvers"] = AIRPLAY_SERVER_VERSION_STR; + std::vector > txt; + + std::string mac_address(m_network.GetDefaultConnectionMacAddress()); + if (mac_address.empty()) + mac_address = "FF:FF:FF:FF:FF:F2"; + txt.push_back(std::make_pair("deviceid", mac_address)); + txt.push_back(std::make_pair("features", "0x77")); + txt.push_back(std::make_pair("model", "Xbmc,1")); + txt.push_back(std::make_pair("srcvers", AIRPLAY_SERVER_VERSION_STR)); + CZeroconf::GetInstance()->PublishService("servers.airplay", "_airplay._tcp", g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME), listenPort, txt); ret = true; } @@ -1603,7 +1593,7 @@ bool CApplication::StartAirplayServer() #endif { #ifdef HAS_AIRTUNES - if (g_guiSettings.GetBool("services.airplay") && m_network->IsAvailable()) + if (g_guiSettings.GetBool("services.airplay") && m_network.IsAvailable()) { int listenPort = g_advancedSettings.m_airTunesPort; CStdString password = g_guiSettings.GetString("services.airplaypassword"); @@ -1638,7 +1628,7 @@ bool CApplication::StartJSONRPCServer() { if (CTCPServer::StartServer(g_advancedSettings.m_jsonTcpPort, g_guiSettings.GetBool("services.esallinterfaces"))) { - std::map txt; + std::vector > txt; CZeroconf::GetInstance()->PublishService("servers.jsonrpc-tpc", "_xbmc-jsonrpc._tcp", g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME), g_advancedSettings.m_jsonTcpPort, txt); return true; } @@ -1866,8 +1856,6 @@ void CApplication::StartServices() void CApplication::StopServices() { - m_network->NetworkMessage(CNetwork::SERVICES_DOWN, 0); - #if !defined(_WIN32) && defined(HAS_DVD_DRIVE) CLog::Log(LOGNOTICE, "stop dvd detect media"); m_DetectDVDType.StopThread(); @@ -2280,7 +2268,7 @@ void CApplication::Render() bool extPlayerActive = m_eCurrentPlayer == EPC_EXTPLAYER && IsPlaying() && !m_AppFocused; m_bPresentFrame = false; - if (!extPlayerActive && g_graphicsContext.IsFullScreenVideo() && !IsPaused()) + if (!extPlayerActive && g_graphicsContext.IsFullScreenVideo() && !IsPaused() && g_renderManager.RendererHandlesPresent()) { CSingleLock lock(m_frameMutex); @@ -2291,6 +2279,12 @@ void CApplication::Render() decrement = m_bPresentFrame; hasRendered = true; } + else if(!extPlayerActive && g_graphicsContext.IsFullScreenVideo() && !g_renderManager.RendererHandlesPresent()) + { + //Whether we're paused or not, if the renderer isn't in charge of presenting and we're fullscreen, we limit + singleFrameTime = 30; + limitFrames = true; + } else { // engage the frame limiter as needed @@ -2859,15 +2853,19 @@ bool CApplication::OnAction(const CAction &action) if (g_settings.m_bMute) UnMute(); float volume = g_settings.m_fVolumeLevel; +// Android has steps based on the max available volume level +#if defined(TARGET_ANDROID) + float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / CXBMCApp::GetMaxSystemVolume(); +#else float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / VOLUME_CONTROL_STEPS; + if (action.GetRepeat()) step *= action.GetRepeat() * 50; // 50 fps - +#endif if (action.GetID() == ACTION_VOLUME_UP) volume += (float)fabs(action.GetAmount()) * action.GetAmount() * step; else volume -= (float)fabs(action.GetAmount()) * action.GetAmount() * step; - SetVolume(volume, false); } // show visual feedback of volume change... @@ -3484,9 +3482,6 @@ bool CApplication::Cleanup() while(1); // execution ends #endif - delete m_network; - m_network = NULL; - return true; } catch (...) @@ -3622,6 +3617,8 @@ void CApplication::Stop(int exitCode) } #endif + m_network.StopServices(); + g_Windowing.DestroyRenderSystem(); g_Windowing.DestroyWindow(); g_Windowing.DestroyWindowSystem(); @@ -5113,6 +5110,7 @@ void CApplication::Process() void CApplication::ProcessSlow() { g_powerManager.ProcessEvents(); + m_network.PumpNetworkEvents(); #if defined(TARGET_DARWIN_OSX) // There is an issue on OS X that several system services ask the cursor to become visible @@ -5886,9 +5884,14 @@ void CApplication::SetRenderGUI(bool renderGUI) m_renderGUI = renderGUI; } -CNetwork& CApplication::getNetwork() +CNetworkManager& CApplication::getNetworkManager() { - return *m_network; + return m_network; +} + +CKeyringManager& CApplication::getKeyringManager() +{ + return m_keyringManager; } #ifdef HAS_PERFORMANCE_SAMPLE CPerformanceStats &CApplication::GetPerformanceStats() @@ -5896,4 +5899,3 @@ CPerformanceStats &CApplication::GetPerformanceStats() return m_perfStats; } #endif - diff --git a/xbmc/Application.h b/xbmc/Application.h index 69609fae744d1..3fbb499e54128 100644 --- a/xbmc/Application.h +++ b/xbmc/Application.h @@ -53,6 +53,8 @@ namespace MEDIA_DETECT #ifdef _WIN32 #include "win32/WIN32Util.h" #endif +#include "network/NetworkManager.h" +#include "security/KeyringManager.h" #include "utils/Stopwatch.h" #include "utils/CharsetConverter.h" #ifdef HAS_PERFORMANCE_SAMPLE @@ -265,7 +267,10 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs static bool OnEvent(XBMC_Event& newEvent); - CNetwork& getNetwork(); + CNetworkManager& getNetworkManager(); + + CKeyringManager& getKeyringManager(); + #ifdef HAS_PERFORMANCE_SAMPLE CPerformanceStats &GetPerformanceStats(); #endif @@ -460,7 +465,8 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs CSeekHandler *m_seekHandler; CInertialScrollingHandler *m_pInertialScrollingHandler; - CNetwork *m_network; + CNetworkManager m_network; + CKeyringManager m_keyringManager; #ifdef HAS_PERFORMANCE_SAMPLE CPerformanceStats m_perfStats; #endif diff --git a/xbmc/ApplicationMessenger.cpp b/xbmc/ApplicationMessenger.cpp index 59c2ee23e33e5..724073436dd83 100644 --- a/xbmc/ApplicationMessenger.cpp +++ b/xbmc/ApplicationMessenger.cpp @@ -29,7 +29,6 @@ #endif #include "pictures/GUIWindowSlideShow.h" #include "interfaces/Builtins.h" -#include "network/Network.h" #include "utils/log.h" #include "utils/URIUtils.h" #include "utils/Variant.h" @@ -646,7 +645,7 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg) case TMSG_NETWORKMESSAGE: { - g_application.getNetwork().NetworkMessage((CNetwork::EMESSAGE)pMsg->dwParam1, (int)pMsg->dwParam2); + g_application.getNetworkManager().NetworkMessage((CNetworkManager::EMESSAGE)pMsg->dwParam1, (int)pMsg->dwParam2); } break; diff --git a/xbmc/DynamicDll.h b/xbmc/DynamicDll.h index 8a4972783b9be..739250f867f20 100644 --- a/xbmc/DynamicDll.h +++ b/xbmc/DynamicDll.h @@ -1,5 +1,4 @@ #pragma once - /* * Copyright (C) 2005-2012 Team XBMC * http://www.xbmc.org @@ -356,12 +355,10 @@ public: \ #define BEGIN_METHOD_RESOLVE() \ protected: \ virtual bool ResolveExports() \ - { \ - return ( + { #define END_METHOD_RESOLVE() \ - 1 \ - ); \ + return true; \ } /////////////////////////////////////////////////////////// @@ -374,10 +371,33 @@ public: \ // or DEFINE_METHOD_LINKAGE // #define RESOLVE_METHOD(method) \ - m_dll->ResolveExport( #method , & m_##method##_ptr ) && + if (!m_dll->ResolveExport( #method , & m_##method##_ptr )) \ + return false; #define RESOLVE_METHOD_FP(method) \ - m_dll->ResolveExport( #method , & method##_ptr ) && + if (!m_dll->ResolveExport( #method , & method##_ptr )) \ + return false; + + +/////////////////////////////////////////////////////////// +// +// RESOLVE_METHOD_OPTIONAL +// +// Resolves a method from a dll. does not abort if the +// method is missing +// +// method: Name of the method defined with DEFINE_METHOD +// or DEFINE_METHOD_LINKAGE +// + +#define RESOLVE_METHOD_OPTIONAL(method) \ + m_dll->ResolveExport( #method , & m_##method##_ptr ); + +#define RESOLVE_METHOD_OPTIONAL_FP(method) \ + method##_ptr = NULL; \ + m_dll->ResolveExport( #method , & method##_ptr ); + + /////////////////////////////////////////////////////////// // @@ -390,10 +410,12 @@ public: \ // or DEFINE_METHOD_LINKAGE // #define RESOLVE_METHOD_RENAME(dllmethod, method) \ - m_dll->ResolveExport( #dllmethod , & m_##method##_ptr ) && + if (!m_dll->ResolveExport( #dllmethod , & m_##method##_ptr )) \ + return false; #define RESOLVE_METHOD_RENAME_FP(dllmethod, method) \ - m_dll->ResolveExport( #dllmethod , & method##_ptr ) && + if (!m_dll->ResolveExport( #dllmethod , & method##_ptr )) \ + return false; //////////////////////////////////////////////////////////////////// diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index a4098e00f3d2b..e50194174f3a9 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "system.h" #include "GUIInfoManager.h" #include "windows/GUIMediaWindow.h" @@ -1513,7 +1512,6 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa break; case SYSTEM_VIDEO_ENCODER_INFO: - case NETWORK_MAC_ADDRESS: case SYSTEM_KERNEL_VERSION: case SYSTEM_CPUFREQUENCY: case SYSTEM_INTERNET_STATE: @@ -1712,7 +1710,7 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa { CStdString friendlyName = g_guiSettings.GetString("services.devicename"); if (friendlyName.Equals("XBMC")) - strLabel.Format("%s (%s)", friendlyName.c_str(), g_application.getNetwork().GetHostName().c_str()); + strLabel.Format("%s (%s)", friendlyName.c_str(), g_application.getNetworkManager().GetDefaultConnectionName().c_str()); else strLabel = friendlyName; } @@ -1759,39 +1757,20 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa break; #endif case NETWORK_IP_ADDRESS: - { - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface) - return iface->GetCurrentIPAddress(); - } + return g_application.getNetworkManager().GetDefaultConnectionAddress(); break; case NETWORK_SUBNET_MASK: - { - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface) - return iface->GetCurrentNetmask(); - } + return g_application.getNetworkManager().GetDefaultConnectionNetmask(); + break; + case NETWORK_MAC_ADDRESS: + return g_application.getNetworkManager().GetDefaultConnectionMacAddress(); break; case NETWORK_GATEWAY_ADDRESS: - { - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface) - return iface->GetCurrentDefaultGateway(); - } + return g_application.getNetworkManager().GetDefaultConnectionGateway(); break; case NETWORK_DNS1_ADDRESS: - { - vector nss = g_application.getNetwork().GetNameServers(); - if (nss.size() >= 1) - return nss[0]; - } - break; case NETWORK_DNS2_ADDRESS: - { - vector nss = g_application.getNetwork().GetNameServers(); - if (nss.size() >= 2) - return nss[1]; - } + return g_application.getNetworkManager().GetDefaultConnectionNameServer(); break; case NETWORK_DHCP_ADDRESS: { @@ -1803,8 +1782,7 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa { CStdString linkStatus = g_localizeStrings.Get(151); linkStatus += " "; - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface && iface->IsConnected()) + if (g_application.getNetworkManager().IsConnected()) linkStatus += g_localizeStrings.Get(15207); else linkStatus += g_localizeStrings.Get(15208); diff --git a/xbmc/LangInfo.cpp b/xbmc/LangInfo.cpp index 5071fe90d43bf..9d273e6247ed4 100644 --- a/xbmc/LangInfo.cpp +++ b/xbmc/LangInfo.cpp @@ -156,7 +156,7 @@ void CLangInfo::CRegion::SetGlobalLocale() // decimal separator is changed depending of the current language // (ie. "," in French or Dutch instead of "."). This breaks atof() and // others similar functions. -#if defined(__FreeBSD__) || defined(TARGET_DARWIN_OSX) +#if defined(__FreeBSD__) || defined(TARGET_DARWIN_OSX) || defined(TARGET_LINUX) // on FreeBSD and darwin libstdc++ is compiled with "generic" locale support if (setlocale(LC_COLLATE, strLocale.c_str()) == NULL || setlocale(LC_CTYPE, strLocale.c_str()) == NULL) diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp index 241bc9a62e740..4385d0cf8f87c 100644 --- a/xbmc/Util.cpp +++ b/xbmc/Util.cpp @@ -17,7 +17,6 @@ * . * */ -#include "network/Network.h" #include "threads/SystemClock.h" #include "system.h" #if defined(TARGET_DARWIN) @@ -1899,7 +1898,7 @@ void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vectorclazz; + jclass cActivity = env->GetObjectClass(oActivity); + + // Get Audio manager + // (AudioManager)getSystemService(Context.AUDIO_SERVICE) + jmethodID mgetSystemService = env->GetMethodID(cActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;"); + jstring sAudioService = env->NewStringUTF("audio"); + jobject oAudioManager = env->CallObjectMethod(oActivity, mgetSystemService, sAudioService); + env->DeleteLocalRef(sAudioService); + env->DeleteLocalRef(cActivity); + + // Get max volume + // int max_volume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + jclass cAudioManager = env->GetObjectClass(oAudioManager); + jmethodID mgetStreamMaxVolume = env->GetMethodID(cAudioManager, "getStreamMaxVolume", "(I)I"); + jfieldID fstreamMusic = env->GetStaticFieldID(cAudioManager, "STREAM_MUSIC", "I"); + jint stream_music = env->GetStaticIntField(cAudioManager, fstreamMusic); + int maxVolume = (int)env->CallObjectMethod(oAudioManager, mgetStreamMaxVolume, stream_music); // AudioManager.STREAM_MUSIC + + env->DeleteLocalRef(oAudioManager); + env->DeleteLocalRef(cAudioManager); + + return maxVolume; +} + +void CXBMCApp::SetSystemVolume(JNIEnv *env, float percent) +{ + CLog::Log(LOGDEBUG, "CXBMCApp::SetSystemVolume: %f", percent); + + jobject oActivity = m_activity->clazz; + jclass cActivity = env->GetObjectClass(oActivity); + + // Get Audio manager + // (AudioManager)getSystemService(Context.AUDIO_SERVICE) + jmethodID mgetSystemService = env->GetMethodID(cActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;"); + jstring sAudioService = env->NewStringUTF("audio"); + jobject oAudioManager = env->CallObjectMethod(oActivity, mgetSystemService, sAudioService); + jclass cAudioManager = env->GetObjectClass(oAudioManager); + env->DeleteLocalRef(sAudioService); + env->DeleteLocalRef(cActivity); + + // Set volume + // mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, max_volume, 0); + jfieldID fstreamMusic = env->GetStaticFieldID(cAudioManager, "STREAM_MUSIC", "I"); + jint stream_music = env->GetStaticIntField(cAudioManager, fstreamMusic); + jmethodID msetStreamVolume = env->GetMethodID(cAudioManager, "setStreamVolume", "(III)V"); + env->CallObjectMethod(oAudioManager, msetStreamVolume, stream_music, int(GetMaxSystemVolume(env)*percent), 0); + env->DeleteLocalRef(oAudioManager); + env->DeleteLocalRef(cAudioManager); +} + diff --git a/xbmc/android/activity/XBMCApp.h b/xbmc/android/activity/XBMCApp.h index 4bf2d2e6a5231..c620966db3cb7 100644 --- a/xbmc/android/activity/XBMCApp.h +++ b/xbmc/android/activity/XBMCApp.h @@ -87,6 +87,7 @@ class CXBMCApp : public IActivityHandler static bool ListApplications(std::vector *applications); static bool GetIconSize(const std::string &packageName, int *width, int *height); static bool GetIcon(const std::string &packageName, void* buffer, unsigned int bufSize); + /*! * \brief If external storage is available, it returns the path for the external storage (for the specified type) * \param path will contain the path of the external storage (for the specified type) @@ -95,6 +96,7 @@ class CXBMCApp : public IActivityHandler */ static bool GetExternalStorage(std::string &path, const std::string &type = ""); static bool GetStorageUsage(const std::string &path, std::string &usage); + static int GetMaxSystemVolume(); static int GetDPI(); protected: @@ -106,6 +108,9 @@ class CXBMCApp : public IActivityHandler static int AttachCurrentThread(JNIEnv** p_env, void* thr_args = NULL); static int DetachCurrentThread(); + static int GetMaxSystemVolume(JNIEnv *env); + static void SetSystemVolume(JNIEnv *env, float percent); + private: static bool HasLaunchIntent(const std::string &package); bool getWakeLock(JNIEnv *env); @@ -116,7 +121,6 @@ class CXBMCApp : public IActivityHandler static ANativeActivity *m_activity; jobject m_wakeLock; - typedef enum { // XBMC_Initialize hasn't been executed yet Uninitialized, diff --git a/xbmc/cores/AudioEngine/AEAudioFormat.h b/xbmc/cores/AudioEngine/AEAudioFormat.h index 11d2d29dc5f2e..5a9053f9fe7a4 100644 --- a/xbmc/cores/AudioEngine/AEAudioFormat.h +++ b/xbmc/cores/AudioEngine/AEAudioFormat.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/AEFactory.cpp b/xbmc/cores/AudioEngine/AEFactory.cpp index 86392c1fff01c..f0b4177901acf 100644 --- a/xbmc/cores/AudioEngine/AEFactory.cpp +++ b/xbmc/cores/AudioEngine/AEFactory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/AEFactory.h b/xbmc/cores/AudioEngine/AEFactory.h index 3c296efa9e7b3..99cdebd10c354 100644 --- a/xbmc/cores/AudioEngine/AEFactory.h +++ b/xbmc/cores/AudioEngine/AEFactory.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/AESinkFactory.cpp b/xbmc/cores/AudioEngine/AESinkFactory.cpp index b066ecd30e1a9..41e2298566c10 100644 --- a/xbmc/cores/AudioEngine/AESinkFactory.cpp +++ b/xbmc/cores/AudioEngine/AESinkFactory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -129,28 +129,28 @@ IAESink *CAESinkFactory::Create(std::string &device, AEAudioFormat &desiredForma return NULL; } -#define ENUMERATE_SINK(SINK) { \ +#define ENUMERATE_SINK(SINK, force) { \ AESinkInfo info; \ info.m_sinkName = #SINK; \ - CAESink ##SINK::EnumerateDevicesEx(info.m_deviceInfoList); \ + CAESink ##SINK::EnumerateDevicesEx(info.m_deviceInfoList, force); \ if(!info.m_deviceInfoList.empty()) \ list.push_back(info); \ } -void CAESinkFactory::EnumerateEx(AESinkInfoList &list) +void CAESinkFactory::EnumerateEx(AESinkInfoList &list, bool force) { #if defined(TARGET_WINDOWS) - ENUMERATE_SINK(DirectSound); + ENUMERATE_SINK(DirectSound, force); if (g_sysinfo.IsVistaOrHigher() && !g_advancedSettings.m_audioForceDirectSound) - ENUMERATE_SINK(WASAPI); + ENUMERATE_SINK(WASAPI, force); #elif defined(TARGET_ANDROID) - ENUMERATE_SINK(AUDIOTRACK); + ENUMERATE_SINK(AUDIOTRACK, force); #elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) #if defined(HAS_ALSA) - ENUMERATE_SINK(ALSA); + ENUMERATE_SINK(ALSA, force); #endif - ENUMERATE_SINK(OSS); + ENUMERATE_SINK(OSS, force); #endif } diff --git a/xbmc/cores/AudioEngine/AESinkFactory.h b/xbmc/cores/AudioEngine/AESinkFactory.h index 99c53f9be2244..5444d2ca86e98 100644 --- a/xbmc/cores/AudioEngine/AESinkFactory.h +++ b/xbmc/cores/AudioEngine/AESinkFactory.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -40,6 +40,6 @@ class CAESinkFactory public: static void ParseDevice(std::string &device, std::string &driver); static IAESink *Create(std::string &device, AEAudioFormat &desiredFormat, bool rawPassthrough); - static void EnumerateEx(AESinkInfoList &list); + static void EnumerateEx(AESinkInfoList &list, bool force = false); /* The force flag can be used to indicate the rescan devices */ }; diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp index 8ade469d6aa48..c611f15e8f5ac 100644 --- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp +++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h index b69f8c63a1ac5..0cedf3fa8c0af 100644 --- a/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h +++ b/xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.cpp b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.cpp index 410e20ce41a4f..c28da7048be62 100644 --- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.cpp +++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -59,6 +59,7 @@ CSoftAE::CSoftAE(): m_audiophile (true ), m_running (false ), m_reOpen (false ), + m_sinkIsSuspended (false ), m_isSuspended (false ), m_softSuspend (false ), m_softSuspendTimer (0 ), @@ -74,21 +75,18 @@ CSoftAE::CSoftAE(): m_outputStageFn (NULL ), m_streamStageFn (NULL ) { + unsigned int c_retry = 5; CAESinkFactory::EnumerateEx(m_sinkInfoList); - for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt) + while(m_sinkInfoList.size() == 0 && c_retry > 0) { - CLog::Log(LOGNOTICE, "Enumerated %s devices:", itt->m_sinkName.c_str()); - int count = 0; - for (AEDeviceInfoList::iterator itt2 = itt->m_deviceInfoList.begin(); itt2 != itt->m_deviceInfoList.end(); ++itt2) - { - CLog::Log(LOGNOTICE, " Device %d", ++count); - CAEDeviceInfo& info = *itt2; - std::stringstream ss((std::string)info); - std::string line; - while(std::getline(ss, line, '\n')) - CLog::Log(LOGNOTICE, " %s", line.c_str()); - } + CLog::Log(LOGNOTICE, "No Devices found - retry: %d", c_retry); + Sleep(2000); + c_retry--; + // retry the enumeration + CAESinkFactory::EnumerateEx(m_sinkInfoList, true); } + CLog::Log(LOGNOTICE, "Found %lu Lists of Devices", m_sinkInfoList.size()); + PrintSinks(); } CSoftAE::~CSoftAE() @@ -179,6 +177,18 @@ void CSoftAE::OpenSink() m_wake.Set(); } +void CSoftAE::InternalCloseSink() +{ + /* close the old sink if it was open */ + if (m_sink) + { + CExclusiveLock sinkLock(m_sinkLock); + m_sink->Drain(); + m_sink->Deinitialize(); + delete m_sink; + m_sink = NULL; + } +} /* this must NEVER be called from outside the main thread or Initialization */ void CSoftAE::InternalOpenSink() { @@ -305,15 +315,8 @@ void CSoftAE::InternalOpenSink() CExclusiveLock sinkLock(m_sinkLock); reInit = true; - - /* we are going to open, so close the old sink if it was open */ - if (m_sink) - { - m_sink->Drain(); - m_sink->Deinitialize(); - delete m_sink; - m_sink = NULL; - } + //close the sink cause it gets reinited + InternalCloseSink(); /* get the display name of the device */ GetDeviceFriendlyName(device); @@ -726,7 +729,8 @@ void CSoftAE::PauseStream(CSoftAEStream *stream) stream->m_paused = true; streamLock.Leave(); - OpenSink(); + m_reOpen = true; + m_wake.Set(); } void CSoftAE::ResumeStream(CSoftAEStream *stream) @@ -737,7 +741,8 @@ void CSoftAE::ResumeStream(CSoftAEStream *stream) streamLock.Leave(); m_streamsPlaying = true; - OpenSink(); + m_reOpen = true; + m_wake.Set(); } void CSoftAE::Stop() @@ -774,7 +779,7 @@ IAEStream *CSoftAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sample CSoftAEStream *stream = new CSoftAEStream(dataFormat, sampleRate, encodedSampleRate, channelLayout, options); m_newStreams.push_back(stream); streamLock.Leave(); - + // this is really needed here OpenSink(); return stream; } @@ -866,11 +871,9 @@ IAEStream *CSoftAE::FreeStream(IAEStream *stream) CSingleLock lock(m_streamLock); RemoveStream(m_playingStreams, (CSoftAEStream*)stream); RemoveStream(m_streams , (CSoftAEStream*)stream); - lock.Leave(); - - /* if it was the master stream we need to reopen before deletion */ - if (m_masterStream == stream) - OpenSink(); + // Reopen is old behaviour. Not opening when masterstream stops means clipping on S/PDIF. + if(!m_isSuspended && (m_masterStream == stream)) + m_reOpen = true; delete (CSoftAEStream*)stream; return NULL; @@ -976,14 +979,67 @@ bool CSoftAE::Suspend() CSoftAEStream *stream = *itt; stream->Flush(); } + streamLock.Leave(); + #if defined(TARGET_LINUX) + /*workaround sinks not playing sound after resume */ + StopAllSounds(); + bool ret = true; + if(m_sink) + { + /* Deinitialize and delete current m_sink */ + // we don't want that Run reopens our device, so we wait. + m_saveSuspend.Reset(); + // wait until we are looping in ProcessSuspend() + // this is more save to not come up unclean + // we cannot wait forever + ret = m_saveSuspend.WaitMSec(500); + if(ret) + { + CLog::Log(LOGDEBUG, "CSoftAE::Suspend - After Event"); + CExclusiveLock sinkLock(m_sinkLock); + // remove all the sinks + for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt) + { + itt->m_deviceInfoList.pop_back(); + } + InternalCloseSink(); + } + else + { + CLog::Log(LOGDEBUG, "CSoftAE::Suspend - Unload failed will continue"); + m_saveSuspend.Reset(); + } + } + // The device list is now empty and must be reenumerated afterwards. + if(ret) + m_sinkInfoList.clear(); + + // signal anybody, that we are gone now (beware of deadlocks) + // we don't unset the fields here, to care for reinit after resume + if(m_reOpen) + m_reOpenEvent.Set(); + #endif return true; } bool CSoftAE::Resume() { +#if defined(TARGET_LINUX) + // We must make sure, that we don't return empty. + if(m_sinkInfoList.empty()) + { + CLog::Log(LOGDEBUG, "CSoftAE::Resume - Re Enumerating Sinks"); + CExclusiveLock sinkLock(m_sinkLock); + // Forced enumeration - we are sure that we start completely fresh. + CAESinkFactory::EnumerateEx(m_sinkInfoList, true); + sinkLock.Leave(); // we leave here explicitly to not lock while printing new sinks + PrintSinks(); + } +#endif CLog::Log(LOGDEBUG, "CSoftAE::Resume - Resuming AE processing"); m_isSuspended = false; + // we flag reopen m_reOpen = true; return true; @@ -1000,11 +1056,12 @@ void CSoftAE::Run() { bool restart = false; - if ((this->*m_outputStageFn)(hasAudio) > 0) + /* with the new non blocking implementation - we just reOpen here, when it tells reOpen */ + if (!m_reOpen && (this->*m_outputStageFn)(hasAudio) > 0) hasAudio = false; /* taken some audio - reset our silence flag */ /* if we have enough room in the buffer */ - if (m_buffer.Free() >= m_frameSize) + if (!m_reOpen && m_buffer.Free() >= m_frameSize) { /* take some data for our use from the buffer */ uint8_t *out = (uint8_t*)m_buffer.Take(m_frameSize); @@ -1026,10 +1083,17 @@ void CSoftAE::Run() /* if we are told to restart */ if (m_reOpen || restart || !m_sink) { + if(m_sinkIsSuspended && m_sink) + { + // hint for fritsch: remember lazy evaluation + m_reOpen = !m_sink->SoftResume() || m_reOpen; + m_sinkIsSuspended = false; + CLog::Log(LOGDEBUG, "CSoftAE::Run - Sink was forgotten"); + } CLog::Log(LOGDEBUG, "CSoftAE::Run - Sink restart flagged"); InternalOpenSink(); - m_isSuspended = false; // exit Suspend state } + #if defined(TARGET_ANDROID) else if (m_playingStreams.empty() && m_playing_sounds.empty() @@ -1280,12 +1344,29 @@ int CSoftAE::RunTranscodeStage(bool hasAudio) return encodedFrames; } +void CSoftAE::PrintSinks() +{ + for (AESinkInfoList::iterator itt = m_sinkInfoList.begin(); itt != m_sinkInfoList.end(); ++itt) + { + CLog::Log(LOGNOTICE, "Enumerated %s devices:", itt->m_sinkName.c_str()); + int count = 0; + for (AEDeviceInfoList::iterator itt2 = itt->m_deviceInfoList.begin(); itt2 != itt->m_deviceInfoList.end(); ++itt2) + { + CLog::Log(LOGNOTICE, " Device %d", ++count); + CAEDeviceInfo& info = *itt2; + std::stringstream ss((std::string)info); + std::string line; + while(std::getline(ss, line, '\n')) + CLog::Log(LOGNOTICE, " %s", line.c_str()); + } + } +} + unsigned int CSoftAE::RunRawStreamStage(unsigned int channelCount, void *out, bool &restart) { StreamList resumeStreams; static StreamList::iterator itt; CSingleLock streamLock(m_streamLock); - /* handle playing streams */ for (itt = m_playingStreams.begin(); itt != m_playingStreams.end(); ++itt) { @@ -1395,10 +1476,8 @@ inline void CSoftAE::RemoveStream(StreamList &streams, CSoftAEStream *stream) inline void CSoftAE::ProcessSuspend() { - bool sinkIsSuspended = false; unsigned int curSystemClock = 0; - -#if defined(TARGET_WINDOWS) +#if defined(TARGET_WINDOWS) || defined(TARGET_LINUX) if (!m_softSuspend && m_playingStreams.empty() && m_playing_sounds.empty() && !g_advancedSettings.m_streamSilence) { @@ -1410,37 +1489,54 @@ inline void CSoftAE::ProcessSuspend() if (m_softSuspend) curSystemClock = XbmcThreads::SystemClockMillis(); #endif - /* idle while in Suspend() state until Resume() called */ /* idle if nothing to play and user hasn't enabled */ /* continuous streaming (silent stream) in as.xml */ - while ((m_isSuspended || (m_softSuspend && (curSystemClock > m_softSuspendTimer))) && - m_running && !m_reOpen) + /* In case of Suspend stay in there until Resume is called from outer thread */ + while (m_isSuspended || ((m_softSuspend && (curSystemClock > m_softSuspendTimer)) && + m_running && !m_reOpen)) { - if (m_sink && !sinkIsSuspended) + if (!m_isSuspended && m_sink && !m_sinkIsSuspended) { /* put the sink in Suspend mode */ CExclusiveLock sinkLock(m_sinkLock); - if (!m_sink->SoftSuspend()) + if (m_sink && !m_sink->SoftSuspend()) { - sinkIsSuspended = false; //sink cannot be suspended + m_sinkIsSuspended = false; //sink cannot be suspended m_softSuspend = false; //break suspend loop break; } else - sinkIsSuspended = true; //sink has suspended processing + { + CLog::Log(LOGDEBUG, "Suspended the Sink"); + m_sinkIsSuspended = true; //sink has suspended processing + } sinkLock.Leave(); } + // Signal that the Suspend can go on now. + // Idea: Outer thread calls Suspend() - but + // because of AddPackets does not care about locks, we must make + // sure, that our school bus (AE::Run) is currently driving through + // some gas station, before we move away the sink. + if(m_isSuspended) + m_saveSuspend.Set(); /* idle for platform-defined time */ m_wake.WaitMSec(SOFTAE_IDLE_WAIT_MSEC); - /* check if we need to resume for stream or sound */ + /* check if we need to resume for stream or sound or somebody wants to open us + * the suspend checks are only there to: + * a) not run out of softSuspend directly when we are sleeping + * b) nail(!) the thread during real Suspend into this method + * Note: It is not enough to check the streams buffer, cause it might not be filled yet + * We have to check after ProcessSuspending() if the sink is still in softsleep and resume it + */ if (!m_isSuspended && (!m_playingStreams.empty() || !m_playing_sounds.empty())) { - m_reOpen = !m_sink->SoftResume(); // sink returns false if it requires reinit - sinkIsSuspended = false; //sink processing data - m_softSuspend = false; //break suspend loop + m_reOpen = !m_sink->SoftResume() || m_reOpen; // sink returns false if it requires reinit (worthless with current implementation) + m_sinkIsSuspended = false; //sink processing data + m_softSuspend = false; //break suspend loop (under some conditions) + CLog::Log(LOGDEBUG, "Resumed the Sink"); break; } } diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.h b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.h index 56fb41712379f..26d5e9c72127e 100644 --- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.h +++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -116,6 +116,7 @@ class CSoftAE : public IThreadedAE void OpenSink(); void InternalOpenSink(); + void InternalCloseSink(); void ResetEncoder(); bool SetupEncoder(AEAudioFormat &format); void Deinitialize(); @@ -136,11 +137,13 @@ class CSoftAE : public IThreadedAE /* internal vars */ bool m_running, m_reOpen; + bool m_sinkIsSuspended; /* The sink is in unusable state, e.g. SoftSuspended */ bool m_isSuspended; /* engine suspended by external function to release audio context */ bool m_softSuspend; /* latches after last stream or sound played for timer below for idle */ unsigned int m_softSuspendTimer; /* time in milliseconds to hold sink open before soft suspend for idle */ CEvent m_reOpenEvent; CEvent m_wake; + CEvent m_saveSuspend; CCriticalSection m_runningLock; /* released when the thread exits */ CCriticalSection m_streamLock; /* m_streams lock */ @@ -242,5 +245,6 @@ class CSoftAE : public IThreadedAE void RunNormalizeStage (unsigned int channelCount, void *out, unsigned int mixed); void RemoveStream(StreamList &streams, CSoftAEStream *stream); + void PrintSinks(); }; diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.cpp b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.cpp index c6d50658c5d42..f9cace57f953a 100644 --- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.cpp +++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.h b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.h index 18225880a0f57..cd50c0b47565a 100644 --- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.h +++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAESound.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp index b61f46b36e476..258dcaca56b7a 100644 --- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp +++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -485,7 +485,6 @@ double CSoftAEStream::GetDelay() double delay = AE.GetDelay(); delay += (double)(m_inputBuffer.Used() / m_format.m_frameSize) / (double)m_format.m_sampleRate; delay += (double)m_framesBuffered / (double)AE.GetSampleRate(); - return delay; } @@ -494,10 +493,9 @@ double CSoftAEStream::GetCacheTime() if (m_delete) return 0.0; - double time; - time = (double)(m_inputBuffer.Used() / m_format.m_frameSize) / (double)m_format.m_sampleRate; - time += (double)(m_waterLevel - m_framesBuffered) / (double)AE.GetSampleRate(); - time += AE.GetCacheTime(); + double time = AE.GetCacheTime(); + time += (double)(m_inputBuffer.Used() / m_format.m_frameSize) / (double)m_format.m_sampleRate; + time += (double)m_framesBuffered / (double)AE.GetSampleRate(); return time; } @@ -506,10 +504,9 @@ double CSoftAEStream::GetCacheTotal() if (m_delete) return 0.0; - double total; - total = (double)(m_inputBuffer.Size() / m_format.m_frameSize) / (double)m_format.m_sampleRate; + double total = AE.GetCacheTotal(); + total += (double)(m_inputBuffer.Size() / m_format.m_frameSize) / (double)m_format.m_sampleRate; total += (double)m_waterLevel / (double)AE.GetSampleRate(); - total += AE.GetCacheTotal(); return total; } diff --git a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h index 7e93215f9161b..571dc8864747f 100644 --- a/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h +++ b/xbmc/cores/AudioEngine/Engines/SoftAE/SoftAEStream.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Interfaces/AE.h b/xbmc/cores/AudioEngine/Interfaces/AE.h index 0baba37c50c60..cbfa8ed3998e0 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AE.h +++ b/xbmc/cores/AudioEngine/Interfaces/AE.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h b/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h index 9613d029d7f1c..055f28bef87dc 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h +++ b/xbmc/cores/AudioEngine/Interfaces/AEEncoder.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Interfaces/AESink.h b/xbmc/cores/AudioEngine/Interfaces/AESink.h index a67b860b41fbe..97ea571015357 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AESink.h +++ b/xbmc/cores/AudioEngine/Interfaces/AESink.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Interfaces/AESound.h b/xbmc/cores/AudioEngine/Interfaces/AESound.h index 100efdbed425e..ebbcc0d7ee8ab 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AESound.h +++ b/xbmc/cores/AudioEngine/Interfaces/AESound.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Interfaces/AEStream.h b/xbmc/cores/AudioEngine/Interfaces/AEStream.h index 060fcb9ebf7bb..67fa5e2a0fb41 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AEStream.h +++ b/xbmc/cores/AudioEngine/Interfaces/AEStream.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h b/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h index 41053291babe0..82e4936b4491a 100644 --- a/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h +++ b/xbmc/cores/AudioEngine/Interfaces/ThreadedAE.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp index 91218a48c3d4d..3997f90874dbc 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -33,12 +33,11 @@ #include "utils/MathUtils.h" #include "threads/SingleLock.h" #include "settings/GUISettings.h" -#if defined(HAS_AMLPLAYER) -#include "cores/amlplayer/AMLUtils.h" +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) +#include "utils/AMLUtils.h" #endif #define ALSA_OPTIONS (SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_FORMAT | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_RESAMPLE) -#define ALSA_PERIODS 16 #define ALSA_MAX_CHANNELS 16 static enum AEChannel ALSAChannelMap[ALSA_MAX_CHANNELS + 1] = { @@ -144,7 +143,7 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) m_channelLayout = GetChannelLayout(format); m_passthrough = false; } -#if defined(HAS_AMLPLAYER) +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) if (aml_present()) { aml_set_audio_passthrough(m_passthrough); @@ -259,6 +258,27 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format) snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); unsigned int sampleRate = format.m_sampleRate; +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) + // alsa/kernel lies, so map everything to 44100 or 48000 + switch(sampleRate) + { + case 11025: + case 22050: + case 88200: + case 176400: + sampleRate = 44100; + break; + case 8000: + case 16000: + case 24000: + case 32000: + case 96000: + case 192000: + case 384000: + sampleRate = 48000; + break; + } +#endif unsigned int channelCount = format.m_channelLayout.Count(); snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL); snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount); @@ -328,59 +348,25 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format) } } - unsigned int periods; - snd_pcm_uframes_t periodSize, bufferSize; snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize); - bufferSize = std::min(bufferSize, (snd_pcm_uframes_t)8192); - periodSize = bufferSize / ALSA_PERIODS; - periods = ALSA_PERIODS; + bufferSize = std::min(bufferSize, (snd_pcm_uframes_t) 256 * 1024); + periodSize = 1024; - CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize); + CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, bufferSize %lu", periodSize, bufferSize); - /* work on a copy of the hw params */ - snd_pcm_hw_params_t *hw_params_copy; - snd_pcm_hw_params_alloca(&hw_params_copy); + /* try to set the period size and the buffer size*/ + snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params, &periodSize, NULL); + snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params, &bufferSize); - /* try to set the buffer size then the period size */ - snd_pcm_hw_params_copy(hw_params_copy, hw_params); - snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); - snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); - snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); - if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) + if(snd_pcm_hw_params(m_pcm, hw_params) != 0) { - /* try to set the period size then the buffer size */ - snd_pcm_hw_params_copy(hw_params_copy, hw_params); - snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); - snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); - snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); - if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) - { - /* try to just set the buffer size */ - snd_pcm_hw_params_copy(hw_params_copy, hw_params); - snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); - snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); - if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) - { - /* try to just set the period size */ - snd_pcm_hw_params_copy(hw_params_copy, hw_params); - snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); - snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); - if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) - { - CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Failed to set the parameters"); - return false; - } - } - } + CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Failed to set the parameters"); + return false; } - snd_pcm_hw_params_get_period_size(hw_params_copy, &periodSize, NULL); - snd_pcm_hw_params_get_buffer_size(hw_params_copy, &bufferSize); - - - CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize); + CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, bufferSize); /* set the format parameters */ format.m_sampleRate = sampleRate; @@ -426,7 +412,6 @@ void CAESinkALSA::Deinitialize() if (m_pcm) { - snd_pcm_drop (m_pcm); snd_pcm_close(m_pcm); m_pcm = NULL; } @@ -487,7 +472,13 @@ double CAESinkALSA::GetCacheTotal() unsigned int CAESinkALSA::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio) { if (!m_pcm) - return 0; + { + SoftResume(); + if(!m_pcm) + return 0; + + CLog::Log(LOGDEBUG, "CAESinkALSA - the grAEken is hunger, feed it (I am the downmost fallback - fix your code)"); + } int ret; @@ -676,12 +667,17 @@ bool CAESinkALSA::OpenPCMDevice(const std::string &name, const std::string ¶ return false; } -void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list) +void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { /* ensure that ALSA has been initialized */ snd_lib_error_set_handler(sndLibErrorHandler); - if(!snd_config) + if(!snd_config || force) + { + if(force) + snd_config_update_free_global(); + snd_config_update(); + } snd_config_t *config; snd_config_copy(&config, snd_config); @@ -1125,6 +1121,28 @@ bool CAESinkALSA::GetELD(snd_hctl_t *hctl, int device, CAEDeviceInfo& info, bool return true; } +bool CAESinkALSA::SoftSuspend() +{ + if(m_pcm) // it is still there + Deinitialize(); + + return true; +} +bool CAESinkALSA::SoftResume() +{ + // reinit all the clibber + bool ret = true; // all fine + if(!m_pcm) + { + if (!snd_config) + snd_config_update(); + + ret = Initialize(m_initFormat, m_initDevice); + } + //we want that AE loves us again - reinit when initialize failed + return ret; // force reinit if false +} + void CAESinkALSA::sndLibErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...) { va_list arg; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h index db1ba802901f9..fb4efb0329324 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -49,8 +49,10 @@ class CAESinkALSA : public IAESink virtual double GetCacheTotal (); virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio); virtual void Drain (); + virtual bool SoftSuspend(); + virtual bool SoftResume(); - static void EnumerateDevicesEx(AEDeviceInfoList &list); + static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false); private: CAEChannelInfo GetChannelLayout(AEAudioFormat format); void GetAESParams(const AEAudioFormat format, std::string& params); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp index b885caa99002a..48473e06634c6 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -23,8 +23,8 @@ #include "Utils/AERingBuffer.h" #include "android/activity/XBMCApp.h" #include "utils/log.h" -#if defined(HAS_AMLPLAYER) -#include "cores/amlplayer/AMLUtils.h" +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) +#include "utils/AMLUtils.h" #endif #include @@ -72,14 +72,14 @@ CAESinkAUDIOTRACK::CAESinkAUDIOTRACK() { m_sinkbuffer = NULL; m_alignedS16LE = NULL; -#if defined(HAS_AMLPLAYER) +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) aml_cpufreq_limit(true); #endif } CAESinkAUDIOTRACK::~CAESinkAUDIOTRACK() { -#if defined(HAS_AMLPLAYER) +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) aml_cpufreq_limit(false); #endif } @@ -88,6 +88,16 @@ bool CAESinkAUDIOTRACK::Initialize(AEAudioFormat &format, std::string &device) { m_format = format; + if (AE_IS_RAW(m_format.m_dataFormat)) + m_passthrough = true; + else + m_passthrough = false; + +#if defined(HAS_LIBAMCODEC) + if (aml_present()) + aml_set_audio_passthrough(m_passthrough); +#endif + // default to 44100, all android devices support it. // then check if we can support the requested rate. unsigned int sampleRate = 44100; @@ -228,13 +238,16 @@ bool CAESinkAUDIOTRACK::HasVolume() return true; } -void CAESinkAUDIOTRACK::SetVolume(float volume) +void CAESinkAUDIOTRACK::SetVolume(float scale) { - m_volume = volume; - m_volume_changed = true; + // Android uses fixed steps, reverse scale back to percent + float gain = CAEUtil::ScaleToGain(scale); + m_volume = CAEUtil::GainToPercent(gain); + if (!m_passthrough) + m_volume_changed = true; } -void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list) +void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { m_info.m_channels.Reset(); m_info.m_dataFormats.clear(); @@ -273,7 +286,6 @@ void CAESinkAUDIOTRACK::Process() jmethodID jmRelease = jenv->GetMethodID(jcAudioTrack, "release", "()V"); jmethodID jmWrite = jenv->GetMethodID(jcAudioTrack, "write", "([BII)I"); jmethodID jmPlayState = jenv->GetMethodID(jcAudioTrack, "getPlayState", "()I"); - jmethodID jmSetStereoVolume = jenv->GetMethodID(jcAudioTrack, "setStereoVolume", "(FF)I"); jmethodID jmPlayHeadPosition = jenv->GetMethodID(jcAudioTrack, "getPlaybackHeadPosition", "()I"); jmethodID jmGetMinBufferSize = jenv->GetStaticMethodID(jcAudioTrack, "getMinBufferSize", "(III)I"); @@ -302,6 +314,12 @@ void CAESinkAUDIOTRACK::Process() min_buffer_size, GetStaticIntField(jenv, "AudioTrack", "MODE_STREAM")); + // Set the initial volume + float volume = 1.0; + if (!m_passthrough) + volume = m_volume; + CXBMCApp::SetSystemVolume(jenv, volume); + // The AudioTrack object has been created and waiting to play, m_inited.Set(); // yield to give other threads a chance to do some work. @@ -318,12 +336,11 @@ void CAESinkAUDIOTRACK::Process() while (!m_bStop) { - if (m_volume_changed) + if (m_volume_changed && !m_passthrough) { // check of volume changes and make them, // do it here to keep jni calls local to this thread. - jfloat jvolume = m_volume; - jenv->CallIntMethod(joAudioTrack, jmSetStereoVolume, jvolume, jvolume); + CXBMCApp::SetSystemVolume(jenv, m_volume); m_volume_changed = false; } if (m_draining) diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h index e7ac7fce44802..901139009be89 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -42,8 +42,8 @@ class CAESinkAUDIOTRACK : public CThread, public IAESink virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio); virtual void Drain (); virtual bool HasVolume (); - virtual void SetVolume (float volume); - static void EnumerateDevicesEx(AEDeviceInfoList &list); + virtual void SetVolume (float scale); + static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false); private: virtual void Process(); @@ -62,6 +62,8 @@ class CAESinkAUDIOTRACK : public CThread, public IAESink CEvent m_wake; CEvent m_inited; volatile bool m_draining; + bool m_passthrough; + double m_audiotrackbuffer_sec; double m_audiotrack_empty_sec; }; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp index 4d3d41e205a65..ac58eb685d025 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -464,7 +464,7 @@ double CAESinkDirectSound::GetCacheTotal() return (double)m_dwBufferLen / (double)m_AvgBytesPerSec; } -void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList) +void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { CAEDeviceInfo deviceInfo; @@ -603,10 +603,10 @@ void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList) if (SUCCEEDED(hr) && varName.blob.cbSize > 0) { WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData; - deviceInfo.m_channels = layoutsByChCount[std::min(smpwfxex->nChannels, (WORD) 2)]; + deviceInfo.m_channels = layoutsByChCount[std::max(std::min(smpwfxex->nChannels, (WORD) 8), (WORD) 2)]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); - deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 96000)); + deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 192000)); } else { @@ -622,12 +622,25 @@ void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList) deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; - /* Now logged by AESinkFactory on startup */ - //CLog::Log(LOGDEBUG,"Audio Device %d: %s", i, ((std::string)deviceInfo).c_str()); - deviceInfoList.push_back(deviceInfo); } + // since AE takes the first device in deviceInfoList as default audio device we need + // to sort it in order to use the real default device + if(deviceInfoList.size() > 1) + { + std::string strDD = GetDefaultDevice(); + for (AEDeviceInfoList::iterator itt = deviceInfoList.begin(); itt != deviceInfoList.end(); ++itt) + { + CAEDeviceInfo devInfo = *itt; + if(devInfo.m_deviceName == strDD) + { + deviceInfoList.erase(itt); + deviceInfoList.insert(deviceInfoList.begin(), devInfo); + } + } + } + return; failed: @@ -825,5 +838,61 @@ const char *CAESinkDirectSound::WASAPIErrToStr(HRESULT err) return NULL; } +std::string CAESinkDirectSound::GetDefaultDevice() +{ + IMMDeviceEnumerator* pEnumerator = NULL; + IMMDevice* pDevice = NULL; + IPropertyStore* pProperty = NULL; + HRESULT hr; + PROPVARIANT varName; + std::string strDevName = "default"; + + hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %s", WASAPIErrToStr(hr)); + goto failed; + } + + hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, __FUNCTION__": Retrieval of audio endpoint enumeration failed."); + goto failed; + } + + hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed."); + goto failed; + } + + PropVariantInit(&varName); + hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed."); + goto failed; + } + AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; + PropVariantClear(&varName); + + hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed."); + goto failed; + } + strDevName = localWideToUtf(varName.pwszVal); + PropVariantClear(&varName); +failed: + + SAFE_RELEASE(pProperty); + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pEnumerator); + + return strDevName; +} diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h index 9f54090976588..dbf961862d781 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -43,7 +43,8 @@ class CAESinkDirectSound : public IAESink virtual double GetCacheTime (); virtual double GetCacheTotal (); virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio); - static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList); + static std::string GetDefaultDevice (); + static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList, bool force = false); private: void AEChannelsFromSpeakerMask(DWORD speakers); DWORD SpeakerMaskFromAEChannels(const CAEChannelInfo &channels); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp index 1468d6aa9f9ce..5f86d34e4e5f8 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h index 67e55d648d98e..e6413fcff6d8e 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkNULL.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp index 06b9a7fc4ffc7..1af9912bc27e3 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -427,7 +427,7 @@ void CAESinkOSS::Drain() // ??? } -void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list) +void CAESinkOSS::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { int mixerfd; const char * mixerdev = "/dev/mixer"; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h index aa8a9f8f9a73f..c8c1179e1e919 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkOSS.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -43,7 +43,7 @@ class CAESinkOSS : public IAESink virtual double GetCacheTotal () { return 0.0; } /* FIXME */ virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio); virtual void Drain (); - static void EnumerateDevicesEx(AEDeviceInfoList &list); + static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false); private: int m_fd; std::string m_device; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp index 200167721e6d2..56af30e60f764 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h index eb4ad0a0f8370..4718406971284 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkProfiler.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp index 8475d60f7e847..de5ed0ed2a914 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -559,7 +559,7 @@ bool CAESinkWASAPI::SoftResume() return false; } -void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList) +void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h index a0c567ae7b17d..c504892b42d2e 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.h @@ -1,6 +1,6 @@ #pragma once /* -* Copyright (C) 2010-2012 Team XBMC +* Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -45,7 +45,7 @@ class CAESinkWASAPI : public IAESink virtual unsigned int AddPackets (uint8_t *data, unsigned int frames, bool hasAudio); virtual bool SoftSuspend (); virtual bool SoftResume (); - static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList); + static void EnumerateDevicesEx (AEDeviceInfoList &deviceInfoList, bool force = false); private: bool InitializeExclusive(AEAudioFormat &format); void AEChannelsFromSpeakerMask(DWORD speakers); @@ -78,4 +78,4 @@ class CAESinkWASAPI : public IAESink unsigned int m_uiBufferLen; /* wasapi endpoint buffer size, in frames */ double m_avgTimeWaiting; /* time between next buffer of data from SoftAE and driver call for data */ double m_sinkLatency; /* time in seconds of total duration of the two WASAPI buffers */ -}; \ No newline at end of file +}; diff --git a/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp index 9eed30a099c2c..e3c790bc23dc2 100644 --- a/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h index bc40d57238dc8..69ab06775ea8b 100644 --- a/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h +++ b/xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp b/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp index b36f43464d225..a79a6949fc1d9 100644 --- a/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEBuffer.h b/xbmc/cores/AudioEngine/Utils/AEBuffer.h index aaaa69cfa0dc7..442a698557840 100644 --- a/xbmc/cores/AudioEngine/Utils/AEBuffer.h +++ b/xbmc/cores/AudioEngine/Utils/AEBuffer.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp index 0abfafd681e12..265a33917192c 100644 --- a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h index 1cd47dab53a79..438f085abf19a 100644 --- a/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h +++ b/xbmc/cores/AudioEngine/Utils/AEChannelInfo.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEConvert.cpp b/xbmc/cores/AudioEngine/Utils/AEConvert.cpp index 2e4da191c8c50..a4e47552e01a5 100644 --- a/xbmc/cores/AudioEngine/Utils/AEConvert.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEConvert.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEConvert.h b/xbmc/cores/AudioEngine/Utils/AEConvert.h index 03764ffa56df3..46472486aafc3 100644 --- a/xbmc/cores/AudioEngine/Utils/AEConvert.h +++ b/xbmc/cores/AudioEngine/Utils/AEConvert.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp index 90ab05b738077..b4a5c5ba41bc0 100644 --- a/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Team XBMC + * Copyright (C) 2012-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h index 04784fac30186..2cd80a198a062 100644 --- a/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h +++ b/xbmc/cores/AudioEngine/Utils/AEDeviceInfo.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2012 Team XBMC + * Copyright (C) 2012-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp b/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp index da5c6d320b71d..ff9f2c78d53a6 100644 --- a/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Team XBMC + * Copyright (C) 2012-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEELDParser.h b/xbmc/cores/AudioEngine/Utils/AEELDParser.h index fdbbf1f1dbf40..09984627be5ac 100644 --- a/xbmc/cores/AudioEngine/Utils/AEELDParser.h +++ b/xbmc/cores/AudioEngine/Utils/AEELDParser.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2012 Team XBMC + * Copyright (C) 2012-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AELimiter.cpp b/xbmc/cores/AudioEngine/Utils/AELimiter.cpp index 1de88bc58aa9c..77af32053239b 100644 --- a/xbmc/cores/AudioEngine/Utils/AELimiter.cpp +++ b/xbmc/cores/AudioEngine/Utils/AELimiter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AELimiter.h b/xbmc/cores/AudioEngine/Utils/AELimiter.h index decc5f4028c16..ef46d0cbb46b8 100644 --- a/xbmc/cores/AudioEngine/Utils/AELimiter.h +++ b/xbmc/cores/AudioEngine/Utils/AELimiter.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp index 27bb343e74c50..9fc0689fed2b7 100644 --- a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h index 30918027ca936..97016f65e4940 100644 --- a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h +++ b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AERemap.cpp b/xbmc/cores/AudioEngine/Utils/AERemap.cpp index 6e44bff192826..1e9da80034ea9 100644 --- a/xbmc/cores/AudioEngine/Utils/AERemap.cpp +++ b/xbmc/cores/AudioEngine/Utils/AERemap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AERemap.h b/xbmc/cores/AudioEngine/Utils/AERemap.h index 49e984b1f242a..1b35e37916896 100644 --- a/xbmc/cores/AudioEngine/Utils/AERemap.h +++ b/xbmc/cores/AudioEngine/Utils/AERemap.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AERingBuffer.h b/xbmc/cores/AudioEngine/Utils/AERingBuffer.h index b7e18fbe32087..17c9d0ca4ce50 100644 --- a/xbmc/cores/AudioEngine/Utils/AERingBuffer.h +++ b/xbmc/cores/AudioEngine/Utils/AERingBuffer.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp index bfe5f3591ac6f..876f5b6c159f7 100644 --- a/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h index c4c06c1f5378f..3c67a375804c3 100644 --- a/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h +++ b/xbmc/cores/AudioEngine/Utils/AEStreamInfo.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp index 4f1b644607c61..6de84dc5d993c 100644 --- a/xbmc/cores/AudioEngine/Utils/AEUtil.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEUtil.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.h b/xbmc/cores/AudioEngine/Utils/AEUtil.h index be52f6321c751..458dc00e39669 100644 --- a/xbmc/cores/AudioEngine/Utils/AEUtil.h +++ b/xbmc/cores/AudioEngine/Utils/AEUtil.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -71,6 +71,19 @@ class CAEUtil return (value - 1)*db_range; } + /*! \brief convert a dB gain to volume percentage (as a proportion) + We assume a dB range of 60dB, i.e. assume that 0% volume corresponds + to a reduction of 60dB. + \param the corresponding gain in dB from -60dB .. 0dB. + \return value the volume from 0..1 + \sa ScaleToGain + */ + static inline const float GainToPercent(const float gain) + { + static const float db_range = 60.0f; + return 1+(gain/db_range); + } + /*! \brief convert a dB gain to a scale factor for audio manipulation Inverts gain = 20 log_10(scale) \param dB the gain in decibels. @@ -82,6 +95,17 @@ class CAEUtil return pow(10.0f, dB/20); } + /*! \brief convert a scale factor to dB gain for audio manipulation + Inverts GainToScale result + \param the scale factor (equivalent to a voltage multiplier). + \return dB the gain in decibels. + \sa GainToScale + */ + static inline const float ScaleToGain(const float scale) + { + return 20*log10(scale); + } + #ifdef __SSE__ static void SSEMulArray (float *data, const float mul, uint32_t count); static void SSEMulAddArray (float *data, float *add, const float mul, uint32_t count); diff --git a/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp index fb24d1ac7df2f..c8a521c80da44 100644 --- a/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/AudioEngine/Utils/AEWAVLoader.h b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.h index ac4c9b5503abd..2ed33eee91c1c 100644 --- a/xbmc/cores/AudioEngine/Utils/AEWAVLoader.h +++ b/xbmc/cores/AudioEngine/Utils/AEWAVLoader.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2010-2012 Team XBMC + * Copyright (C) 2010-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoRenderers/BaseRenderer.cpp index a54ff31a62b59..2a502fcafc91f 100644 --- a/xbmc/cores/VideoRenderers/BaseRenderer.cpp +++ b/xbmc/cores/VideoRenderers/BaseRenderer.cpp @@ -66,6 +66,12 @@ void CBaseRenderer::RegisterRenderUpdateCallBack(const void *ctx, RenderUpdateCa m_RenderUpdateCallBackCtx = ctx; } +void CBaseRenderer::RegisterRenderFeaturesCallBack(const void *ctx, RenderFeaturesCallBackFn fn) +{ + m_RenderFeaturesCallBackFn = fn; + m_RenderFeaturesCallBackCtx = ctx; +} + void CBaseRenderer::ChooseBestResolution(float fps) { if (fps == 0.0) return; diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.h b/xbmc/cores/VideoRenderers/BaseRenderer.h index 81d21d86041c0..21952c8cfe6b1 100644 --- a/xbmc/cores/VideoRenderers/BaseRenderer.h +++ b/xbmc/cores/VideoRenderers/BaseRenderer.h @@ -23,6 +23,7 @@ #include "guilib/Resolution.h" #include "guilib/Geometry.h" #include "RenderFormats.h" +#include "RenderFeatures.h" #define MAX_PLANES 3 #define MAX_FIELDS 3 @@ -48,24 +49,8 @@ enum EFIELDSYNC FS_BOT }; -enum ERENDERFEATURE -{ - RENDERFEATURE_GAMMA, - RENDERFEATURE_BRIGHTNESS, - RENDERFEATURE_CONTRAST, - RENDERFEATURE_NOISE, - RENDERFEATURE_SHARPNESS, - RENDERFEATURE_NONLINSTRETCH, - RENDERFEATURE_ROTATION, - RENDERFEATURE_STRETCH, - RENDERFEATURE_CROP, - RENDERFEATURE_ZOOM, - RENDERFEATURE_VERTICAL_SHIFT, - RENDERFEATURE_PIXEL_RATIO, - RENDERFEATURE_POSTPROCESS -}; - typedef void (*RenderUpdateCallBackFn)(const void *ctx, const CRect &SrcRect, const CRect &DestRect); +typedef void (*RenderFeaturesCallBackFn)(const void *ctx, Features &renderFeatures); struct DVDVideoPicture; @@ -91,6 +76,7 @@ class CBaseRenderer std::vector SupportedFormats() { return std::vector(); } virtual void RegisterRenderUpdateCallBack(const void *ctx, RenderUpdateCallBackFn fn); + virtual void RegisterRenderFeaturesCallBack(const void *ctx, RenderFeaturesCallBackFn fn); protected: void ChooseBestResolution(float fps); @@ -131,4 +117,7 @@ class CBaseRenderer const void* m_RenderUpdateCallBackCtx; RenderUpdateCallBackFn m_RenderUpdateCallBackFn; + + const void* m_RenderFeaturesCallBackCtx; + RenderFeaturesCallBackFn m_RenderFeaturesCallBackFn; }; diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp index cb0939fef6c2a..dff74869954ed 100644 --- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp @@ -193,10 +193,22 @@ bool CLinuxRendererGLES::Configure(unsigned int width, unsigned int height, unsi m_RenderUpdateCallBackCtx = NULL; if ((m_format == RENDER_FMT_BYPASS) && g_application.GetCurrentPlayer()) { + m_renderFeatures.clear(); + m_scalingMethods.clear(); + m_deinterlaceModes.clear(); + m_deinterlaceMethods.clear(); + + if (m_RenderFeaturesCallBackFn) + { + (*m_RenderFeaturesCallBackFn)(m_RenderFeaturesCallBackCtx, m_renderFeatures); + // after setting up m_renderFeatures, we are done with the callback + m_RenderFeaturesCallBackFn = NULL; + m_RenderFeaturesCallBackCtx = NULL; + } g_application.m_pPlayer->GetRenderFeatures(m_renderFeatures); - g_application.m_pPlayer->GetDeinterlaceMethods(m_deinterlaceMethods); - g_application.m_pPlayer->GetDeinterlaceModes(m_deinterlaceModes); g_application.m_pPlayer->GetScalingMethods(m_scalingMethods); + g_application.m_pPlayer->GetDeinterlaceModes(m_deinterlaceModes); + g_application.m_pPlayer->GetDeinterlaceMethods(m_deinterlaceMethods); } return true; @@ -749,6 +761,8 @@ void CLinuxRendererGLES::UnInit() m_bConfigured = false; m_RenderUpdateCallBackFn = NULL; m_RenderUpdateCallBackCtx = NULL; + m_RenderFeaturesCallBackFn = NULL; + m_RenderFeaturesCallBackCtx = NULL; } inline void CLinuxRendererGLES::ReorderDrawPoints() @@ -1352,7 +1366,7 @@ bool CLinuxRendererGLES::RenderCapture(CRenderCapture* capture) // OpenGLES returns in RGBA order but CRenderCapture needs BGRA order // XOR Swap RGBA -> BGRA unsigned char* pixels = (unsigned char*)capture->GetRenderBuffer(); - for (int i = 0; i < capture->GetWidth() * capture->GetHeight(); i++, pixels+=4) + for (unsigned int i = 0; i < capture->GetWidth() * capture->GetHeight(); i++, pixels+=4) { std::swap(pixels[0], pixels[2]); } diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h index 76b543762b97e..8953de47d72a6 100644 --- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h +++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h @@ -29,6 +29,7 @@ #include "xbmc/guilib/Shader.h" #include "settings/VideoSettings.h" #include "RenderFlags.h" +#include "RenderFeatures.h" #include "guilib/GraphicContext.h" #include "BaseRenderer.h" #include "xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h" @@ -39,7 +40,6 @@ class CBaseTexture; namespace Shaders { class BaseYUV2RGBShader; } namespace Shaders { class BaseVideoFilterShader; } class COpenMaxVideo; -typedef std::vector Features; #define NUM_BUFFERS 3 diff --git a/xbmc/cores/VideoRenderers/OverlayRendererGL.cpp b/xbmc/cores/VideoRenderers/OverlayRendererGL.cpp index 4375d19caeb1b..28b821bdb029b 100644 --- a/xbmc/cores/VideoRenderers/OverlayRendererGL.cpp +++ b/xbmc/cores/VideoRenderers/OverlayRendererGL.cpp @@ -521,6 +521,7 @@ void COverlayTextureGL::Render(SRenderState& state) GLint posLoc = g_Windowing.GUIShaderGetPos(); GLint colLoc = g_Windowing.GUIShaderGetCol(); GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + GLint uniColLoc= g_Windowing.GUIShaderGetUniCol(); glVertexAttribPointer(posLoc, 2, GL_FLOAT, 0, 0, ver); glVertexAttribPointer(colLoc, 4, GL_FLOAT, 0, 0, col); @@ -536,6 +537,7 @@ void COverlayTextureGL::Render(SRenderState& state) col[i][0] = col[i][1] = col[i][2] = col[i][3] = 1.0f; } + glUniform4f(uniColLoc,(col[0][0]), (col[0][1]), (col[0][2]), (col[0][3])); // Setup vertex position values ver[0][0] = ver[3][0] = rd.left; ver[0][1] = ver[1][1] = rd.top; diff --git a/xbmc/cores/VideoRenderers/RenderFeatures.h b/xbmc/cores/VideoRenderers/RenderFeatures.h new file mode 100644 index 0000000000000..82c2800c70a96 --- /dev/null +++ b/xbmc/cores/VideoRenderers/RenderFeatures.h @@ -0,0 +1,39 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, see + * . + * + */ + +enum ERENDERFEATURE +{ + RENDERFEATURE_GAMMA, + RENDERFEATURE_BRIGHTNESS, + RENDERFEATURE_CONTRAST, + RENDERFEATURE_NOISE, + RENDERFEATURE_SHARPNESS, + RENDERFEATURE_NONLINSTRETCH, + RENDERFEATURE_ROTATION, + RENDERFEATURE_STRETCH, + RENDERFEATURE_CROP, + RENDERFEATURE_ZOOM, + RENDERFEATURE_VERTICAL_SHIFT, + RENDERFEATURE_PIXEL_RATIO, + RENDERFEATURE_POSTPROCESS +}; + +typedef std::vector Features; diff --git a/xbmc/cores/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoRenderers/RenderManager.cpp index d22287dbfbb1c..86c5a38ef6d15 100644 --- a/xbmc/cores/VideoRenderers/RenderManager.cpp +++ b/xbmc/cores/VideoRenderers/RenderManager.cpp @@ -256,6 +256,9 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi CApplicationMessenger::Get().SwitchToFullscreen(); lock.Enter(); } + if( format & RENDER_FMT_BYPASS ) + m_presentmethod = PRESENT_METHOD_BYPASS; + m_pRenderer->Update(false); m_bIsStarted = true; m_bReconfigured = true; @@ -266,6 +269,11 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi return result; } +bool CXBMCRenderManager::RendererHandlesPresent() +{ + return IsConfigured() && m_presentmethod != PRESENT_METHOD_BYPASS; +} + bool CXBMCRenderManager::IsConfigured() { if (!m_pRenderer) @@ -653,6 +661,12 @@ void CXBMCRenderManager::RegisterRenderUpdateCallBack(const void *ctx, RenderUpd m_pRenderer->RegisterRenderUpdateCallBack(ctx, fn); } +void CXBMCRenderManager::RegisterRenderFeaturesCallBack(const void *ctx, RenderFeaturesCallBackFn fn) +{ + if (m_pRenderer) + m_pRenderer->RegisterRenderFeaturesCallBack(ctx, fn); +} + void CXBMCRenderManager::Render(bool clear, DWORD flags, DWORD alpha) { CSharedLock lock(m_sharedSection); diff --git a/xbmc/cores/VideoRenderers/RenderManager.h b/xbmc/cores/VideoRenderers/RenderManager.h index 7fe6bb234c5df..e28001ce13ce9 100644 --- a/xbmc/cores/VideoRenderers/RenderManager.h +++ b/xbmc/cores/VideoRenderers/RenderManager.h @@ -110,6 +110,8 @@ class CXBMCRenderManager void UpdateResolution(); + bool RendererHandlesPresent(); + #ifdef HAS_GL CLinuxRendererGL *m_pRenderer; #elif HAS_GLES == 2 @@ -131,6 +133,7 @@ class CXBMCRenderManager CSharedSection& GetSection() { return m_sharedSection; }; void RegisterRenderUpdateCallBack(const void *ctx, RenderUpdateCallBackFn fn); + void RegisterRenderFeaturesCallBack(const void *ctx, RenderFeaturesCallBackFn fn); protected: void Render(bool clear, DWORD flags, DWORD alpha); @@ -164,6 +167,7 @@ class CXBMCRenderManager PRESENT_METHOD_BLEND, PRESENT_METHOD_WEAVE, PRESENT_METHOD_BOB, + PRESENT_METHOD_BYPASS, }; double m_displayLatency; diff --git a/xbmc/cores/amlplayer/AMLPlayer.cpp b/xbmc/cores/amlplayer/AMLPlayer.cpp index ebf8f1c75f515..efc79440eefc4 100644 --- a/xbmc/cores/amlplayer/AMLPlayer.cpp +++ b/xbmc/cores/amlplayer/AMLPlayer.cpp @@ -27,6 +27,8 @@ #include "GUIInfoManager.h" #include "video/VideoThumbLoader.h" #include "Util.h" +#include "cores/AudioEngine/AEFactory.h" +#include "cores/AudioEngine/Utils/AEUtil.h" #include "cores/VideoRenderers/RenderFlags.h" #include "cores/VideoRenderers/RenderFormats.h" #include "cores/VideoRenderers/RenderManager.h" @@ -54,9 +56,23 @@ #include "settings/VideoSettings.h" // amlogic libplayer -#include "AMLUtils.h" +#include "utils/AMLUtils.h" #include "DllLibamplayer.h" +static float VolumePercentToScale(float volume) +{ + float audio_volume = 0.0; + if (volume > VOLUME_MINIMUM) + { + float dB = CAEUtil::PercentToGain(volume); + audio_volume = CAEUtil::GainToScale(dB); + } + if (audio_volume >= 0.99f) + audio_volume = 1.0f; + + return audio_volume; +} + struct AMLChapterInfo { std::string name; @@ -312,6 +328,20 @@ CAMLSubTitleThread::~CAMLSubTitleThread() StopThread(); } +void CAMLSubTitleThread::Flush() +{ + CSingleLock lock(m_subtitle_csection); + if (m_subtitle_strings.size()) + { + // remove any expired subtitles + std::deque::iterator it = m_subtitle_strings.begin(); + while (it != m_subtitle_strings.end()) + { + it = m_subtitle_strings.erase(it); + } + } +} + void CAMLSubTitleThread::UpdateSubtitle(CStdString &subtitle, int64_t elapsed_ms) { CSingleLock lock(m_subtitle_csection); @@ -483,12 +513,14 @@ void CAMLSubTitleThread::Process(void) } else { - usleep(100 * 1000); + if (!m_bStop) + usleep(100 * 1000); } } else { - usleep(250 * 1000); + if (!m_bStop) + usleep(250 * 1000); } } m_subtitle_strings.clear(); @@ -519,6 +551,13 @@ CAMLPlayer::CAMLPlayer(IPlayerCallback &callback) // for external subtitles m_dvdOverlayContainer = new CDVDOverlayContainer; m_dvdPlayerSubtitle = new CDVDPlayerSubtitle(m_dvdOverlayContainer); + + // Suspend AE temporarily so exclusive or hog-mode sinks + // don't block external player's access to audio device + if (!CAEFactory::Suspend()) + { + CLog::Log(LOGNOTICE,"%s: Failed to suspend AudioEngine before launching external player", __FUNCTION__); + } } CAMLPlayer::~CAMLPlayer() @@ -528,6 +567,12 @@ CAMLPlayer::~CAMLPlayer() delete m_dvdPlayerSubtitle; delete m_dvdOverlayContainer; delete m_dll, m_dll = NULL; + + // Resume AE processing of XBMC native audio + if (!CAEFactory::Resume()) + { + CLog::Log(LOGFATAL, "%s: Failed to restart AudioEngine after return from external player",__FUNCTION__); + } } bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options) @@ -545,11 +590,14 @@ bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options) m_item = file; m_options = options; + m_pid = -1; m_elapsed_ms = 0; m_duration_ms = 0; m_audio_info = "none"; m_audio_delay = 0; + m_audio_mute = g_settings.m_bMute; + m_audio_volume = VolumePercentToScale(g_settings.m_fVolumeLevel); m_audio_passthrough_ac3 = g_guiSettings.GetBool("audiooutput.ac3passthrough"); m_audio_passthrough_dts = g_guiSettings.GetBool("audiooutput.dtspassthrough"); @@ -562,7 +610,6 @@ bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options) m_subtitle_delay = 0; m_subtitle_thread = NULL; - m_chapter_index = 0; m_chapter_count = 0; m_show_mainvideo = -1; @@ -573,6 +620,9 @@ bool CAMLPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options) ClearStreamInfos(); + if (m_item.IsDVDFile() || m_item.IsDVD()) + return false; + // setup to spin the busy dialog until we are playing m_ready.Reset(); @@ -780,7 +830,8 @@ float CAMLPlayer::GetPercentage() void CAMLPlayer::SetMute(bool bOnOff) { m_audio_mute = bOnOff; -#if !defined(TARGET_ANDROID) + +#if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME) CSingleLock lock(m_aml_csection); if (m_dll->check_pid_valid(m_pid)) { @@ -794,16 +845,10 @@ void CAMLPlayer::SetMute(bool bOnOff) void CAMLPlayer::SetVolume(float volume) { - m_audio_volume = 0.0f; - if (volume > VOLUME_MINIMUM) - { - float dB = CAEUtil::PercentToGain(volume); - m_audio_volume = CAEUtil::GainToScale(dB); - } - if (m_audio_volume >= 0.99f) - m_audio_volume = 1.0f; + // volume is a float percent from 0.0 to 1.0 + m_audio_volume = VolumePercentToScale(volume); -#if !defined(TARGET_ANDROID) +#if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME) CSingleLock lock(m_aml_csection); if (!m_audio_mute && m_dll->check_pid_valid(m_pid)) m_dll->audio_set_volume(m_pid, m_audio_volume); @@ -886,11 +931,13 @@ void CAMLPlayer::SetAVDelay(float fValue) CLog::Log(LOGDEBUG, "CAMLPlayer::SetAVDelay (%f)", fValue); m_audio_delay = fValue * 1000.0; +#if defined(HAS_AMLPLAYER_AUDIO_SETDELAY) if (m_audio_streams.size() && m_dll->check_pid_valid(m_pid)) { CSingleLock lock(m_aml_csection); m_dll->audio_set_delay(m_pid, m_audio_delay); } +#endif } float CAMLPlayer::GetAVDelay() @@ -953,9 +1000,15 @@ void CAMLPlayer::GetSubtitleName(int iStream, CStdString &strStreamName) strStreamName = g_localizeStrings.Get(13205); // Unknown } if (m_log_level > 5) - CLog::Log(LOGDEBUG, "CAMLPlayer::GetSubtitleName, iStream(%d)", iStream); + CLog::Log(LOGDEBUG, "CAMLPlayer::GetSubtitleName, iStream(%d), strStreamName(%s)", + iStream, strStreamName.c_str()); } +void CAMLPlayer::GetSubtitleLanguage(int iStream, CStdString &strStreamLang) +{ + GetSubtitleName(iStream, strStreamLang); +} + void CAMLPlayer::SetSubtitle(int iStream) { CSingleLock lock(m_aml_csection); @@ -972,7 +1025,12 @@ void CAMLPlayer::SetSubtitle(int iStream) return; if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE) + { m_dll->player_sid(m_pid, m_subtitle_streams[m_subtitle_index]->id); + aml_set_sysfs_int("/sys/class/subtitle/curr", m_subtitle_index); + if (m_subtitle_thread) + m_subtitle_thread->Flush(); + } else { m_dvdPlayerSubtitle->CloseStream(true); @@ -992,12 +1050,19 @@ void CAMLPlayer::SetSubtitleVisible(bool bVisible) if (m_subtitle_show && m_subtitle_count) { + if (g_settings.m_currentVideoSettings.m_SubtitleStream < m_subtitle_count) + m_subtitle_index = g_settings.m_currentVideoSettings.m_SubtitleStream; // on startup, if asked to show subs and SetSubtitle has not // been called, we are expected to switch/show the 1st subtitle if (m_subtitle_index < 0) m_subtitle_index = 0; if (m_dll->check_pid_valid(m_pid) && m_subtitle_streams[m_subtitle_index]->source == STREAM_SOURCE_NONE) + { m_dll->player_sid(m_pid, m_subtitle_streams[m_subtitle_index]->id); + aml_set_sysfs_int("/sys/class/subtitle/curr", m_subtitle_index); + if (m_subtitle_thread) + m_subtitle_thread->Flush(); + } else OpenSubtitleStream(m_subtitle_index); } @@ -1032,14 +1097,16 @@ int CAMLPlayer::GetChapterCount() int CAMLPlayer::GetChapter() { + // returns a one based value or zero if no chapters GetStatus(); - for (int i = 0; i < m_chapter_count - 1; i++) + int chapter_index = -1; + for (int i = 0; i < m_chapter_count; i++) { - if (m_elapsed_ms >= m_chapters[i]->seekto_ms && m_elapsed_ms < m_chapters[i + 1]->seekto_ms) - return i + 1; + if (m_elapsed_ms >= m_chapters[i]->seekto_ms) + chapter_index = i; } - return 0; + return chapter_index + 1; } void CAMLPlayer::GetChapterName(CStdString& strChapterName) @@ -1092,7 +1159,7 @@ float CAMLPlayer::GetActualFPS() return video_fps; } -void CAMLPlayer::SeekTime(__int64 seek_ms) +void CAMLPlayer::SeekTime(int64_t seek_ms) { CSingleLock lock(m_aml_csection); @@ -1112,17 +1179,15 @@ void CAMLPlayer::SeekTime(__int64 seek_ms) m_dll->player_timesearch(m_pid, (float)seek_ms/1000.0); WaitForSearchOK(5000); WaitForPlaying(5000); - // restore system volume setting. - SetVolume(m_audio_volume); } } -__int64 CAMLPlayer::GetTime() +int64_t CAMLPlayer::GetTime() { return m_elapsed_ms; } -__int64 CAMLPlayer::GetTotalTime() +int64_t CAMLPlayer::GetTotalTime() { return m_duration_ms; } @@ -1285,7 +1350,6 @@ void CAMLPlayer::OnStartup() void CAMLPlayer::OnExit() { //CLog::Log(LOGNOTICE, "CAMLPlayer::OnExit()"); - Sleep(1000); m_bStop = true; // if we didn't stop playing, advance to the next item in xbmc's playlist @@ -1387,6 +1451,33 @@ void CAMLPlayer::Process() vfs_protocol.name = http_name; url = "xb-" + url; } + else if (url.Left(strlen("sftp://")).Equals("sftp://")) + { + // the name string needs to persist + static const char *http_name = "xb-sftp"; + vfs_protocol.name = http_name; + url = "xb-" + url; + } + else if (url.Left(strlen("udp://")).Equals("udp://")) + { + std::string udp_params; + // bump up the default udp params for ffmpeg. + // ffmpeg will strip out 'dummy=10', we only add it + // to make the logic below with prpending '&' work right. + // to watch for udp errors, 'cat /proc/net/udp' + if (url.find("?") == std::string::npos) + udp_params.append("?dummy=10"); + if (url.find("pkt_size=") == std::string::npos) + udp_params.append("&pkt_size=5264"); + if (url.find("buffer_size=") == std::string::npos) + udp_params.append("&buffer_size=5390336"); + // newer ffmpeg uses fifo_size instead of buf_size + if (url.find("buf_size=") == std::string::npos) + udp_params.append("&buf_size=5390336"); + + if (udp_params.size() > 0) + url.append(udp_params); + } CLog::Log(LOGDEBUG, "CAMLPlayer::Process: URL=%s", url.c_str()); if (m_dll->player_init() != PLAYER_SUCCESS) @@ -1395,7 +1486,7 @@ void CAMLPlayer::Process() throw "CAMLPlayer::Process:player init failed"; } CLog::Log(LOGDEBUG, "player init......"); - usleep(250 * 1000); + usleep(50 * 1000); // must be after player_init m_dll->av_register_protocol2(&vfs_protocol, sizeof(vfs_protocol)); @@ -1419,13 +1510,20 @@ void CAMLPlayer::Process() play_control.need_start = 1; // if 0,you can omit player_start_play API. // just play video/audio immediately. // if 1,then need call "player_start_play" API; - //play_control.auto_buffing_enable = 1; - //play_control.buffing_min = 0.2; - //play_control.buffing_middle = 0.5; - //play_control.buffing_max = 0.8; - //play_control.byteiobufsize =; // maps to av_open_input_file buffer size - //play_control.loopbufsize =; - //play_control.enable_rw_on_pause =; + play_control.displast_frame = 0; // 0:black out when player exit 1:keep last frame when player exit + + // tweak player playback buffers for udp + if (url.Left(strlen("udp://")).Equals("udp://")) + { + play_control.auto_buffing_enable = 1; + play_control.buffing_min = 0.01; // default = 0.01 + play_control.buffing_middle = 0.02; // default = 0.02 + play_control.buffing_max = 0.20; // default = 0.80 + play_control.byteiobufsize = 1024 * 128; // maps to av_open_input_file buffer size (1024 * 32) + //play_control.loopbufsize =; + //play_control.enable_rw_on_pause =; + } + m_aml_state.clear(); m_aml_state.push_back(0); m_pid = m_dll->player_start(&play_control, 0); @@ -1464,13 +1562,7 @@ void CAMLPlayer::Process() // get our initial status. GetStatus(); - // restore mute setting. - SetMute(g_settings.m_bMute); - - // restore system volume setting. - SetVolume(g_settings.m_fVolumeLevel); - - // the default staturation is to high, drop it + // the default staturation is too high, drop it SetVideoSaturation(110); // drop CGUIDialogBusy dialog and release the hold in OpenFile. @@ -1576,13 +1668,14 @@ void CAMLPlayer::Process() case PLAYER_EXIT: if (m_log_level > 5) { - CLog::Log(LOGDEBUG, "CAMLPlayer::Process PLAYER_STOPED"); + CLog::Log(LOGDEBUG, "CAMLPlayer::Process PLAYER_STOPPED"); CLog::Log(LOGDEBUG, "CAMLPlayer::Process: %s", m_dll->player_status2str(pstatus)); } stopPlaying = true; break; } - usleep(250 * 1000); + if (!stopPlaying) + usleep(250 * 1000); } } } @@ -1620,48 +1713,43 @@ void CAMLPlayer::Process() if (m_log_level > 5) CLog::Log(LOGDEBUG, "CAMLPlayer::Process exit"); } -/* -void CAMLPlayer::GetRenderFeatures(Features* renderFeatures) + +void CAMLPlayer::GetRenderFeatures(std::vector &renderFeatures) { - renderFeatures->push_back(RENDERFEATURE_ZOOM); - renderFeatures->push_back(RENDERFEATURE_CONTRAST); - renderFeatures->push_back(RENDERFEATURE_BRIGHTNESS); - renderFeatures->push_back(RENDERFEATURE_STRETCH); - return; + renderFeatures.push_back(RENDERFEATURE_ZOOM); + renderFeatures.push_back(RENDERFEATURE_CONTRAST); + renderFeatures.push_back(RENDERFEATURE_BRIGHTNESS); + renderFeatures.push_back(RENDERFEATURE_STRETCH); } -void CAMLPlayer::GetDeinterlaceMethods(Features* deinterlaceMethods) +void CAMLPlayer::GetDeinterlaceMethods(std::vector &deinterlaceMethods) { - deinterlaceMethods->push_back(VS_INTERLACEMETHOD_DEINTERLACE); - return; + deinterlaceMethods.push_back(VS_INTERLACEMETHOD_DEINTERLACE); } -void CAMLPlayer::GetDeinterlaceModes(Features* deinterlaceModes) +void CAMLPlayer::GetDeinterlaceModes(std::vector &deinterlaceModes) { - deinterlaceModes->push_back(VS_DEINTERLACEMODE_AUTO); - return; + deinterlaceModes.push_back(VS_DEINTERLACEMODE_AUTO); } -void CAMLPlayer::GetScalingMethods(Features* scalingMethods) +void CAMLPlayer::GetScalingMethods(std::vector &scalingMethods) { - return; } -void CAMLPlayer::GetAudioCapabilities(Features* audioCaps) +void CAMLPlayer::GetAudioCapabilities(std::vector &audioCaps) { - audioCaps->push_back(IPC_AUD_OFFSET); - audioCaps->push_back(IPC_AUD_SELECT_STREAM); - return; + audioCaps.push_back(IPC_AUD_SELECT_STREAM); + audioCaps.push_back(IPC_AUD_SELECT_OUTPUT); + audioCaps.push_back(IPC_AUD_OFFSET); } -void CAMLPlayer::GetSubtitleCapabilities(Features* subCaps) +void CAMLPlayer::GetSubtitleCapabilities(std::vector &subCaps) { - subCaps->push_back(IPC_SUBS_EXTERNAL); - subCaps->push_back(IPC_SUBS_OFFSET); - subCaps->push_back(IPC_SUBS_SELECT); - return; + subCaps.push_back(IPC_SUBS_EXTERNAL); + subCaps.push_back(IPC_SUBS_SELECT); + subCaps.push_back(IPC_SUBS_OFFSET); } -*/ + //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// @@ -1795,8 +1883,8 @@ bool CAMLPlayer::WaitForStopped(int timeout_ms) switch(pstatus) { default: - usleep(100 * 1000); - timeout_ms -= 100; + usleep(20 * 1000); + timeout_ms -= 20; break; case PLAYER_PLAYEND: case PLAYER_STOPED: @@ -1821,8 +1909,8 @@ bool CAMLPlayer::WaitForSearchOK(int timeout_ms) switch(pstatus) { default: - usleep(100 * 1000); - timeout_ms -= 100; + usleep(20 * 1000); + timeout_ms -= 20; break; case PLAYER_STOPED: return false; @@ -1842,23 +1930,22 @@ bool CAMLPlayer::WaitForSearchOK(int timeout_ms) bool CAMLPlayer::WaitForPlaying(int timeout_ms) { - // force the volume off in case we are starting muted - m_audio_mute = true; while (!m_bAbortRequest && (timeout_ms > 0)) { -#if !defined(TARGET_ANDROID) - // anoying that we have to hammer audio_set_volume - // but have to catch it before any audio comes out. - m_dll->audio_set_volume(m_pid, 0.0); -#endif + // anoying that we have to hammer setting audio volume via mute + // but we have to catch it before any audio comes out. + // we cannot do this for m1 (playback will bork) so trap it out. + if (aml_get_cputype() != 1) + SetMute(m_audio_mute); + player_status pstatus = (player_status)GetPlayerSerializedState(); if (m_log_level > 5) CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForPlaying: %s", m_dll->player_status2str(pstatus)); switch(pstatus) { default: - usleep(100 * 1000); - timeout_ms -= 100; + usleep(20 * 1000); + timeout_ms -= 20; break; case PLAYER_ERROR: case PLAYER_EXIT: @@ -1866,6 +1953,8 @@ bool CAMLPlayer::WaitForPlaying(int timeout_ms) return false; break; case PLAYER_RUNNING: + // restore mute/volume settings + SetMute(m_audio_mute); return true; break; } @@ -1884,8 +1973,8 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) switch(pstatus) { default: - usleep(100 * 1000); - timeout_ms -= 100; + usleep(20 * 1000); + timeout_ms -= 20; break; case PLAYER_ERROR: case PLAYER_EXIT: @@ -1896,35 +1985,50 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) ClearStreamInfos(); - media_info_t media_info; + media_info_t media_info = {{0}}; +#if defined(TARGET_ANDROID) + // media_info_t might be different so check its size. + // player_get_media_info will memset to zero the passed + // structure so alloc more space and preset to a know value + // so we can compare the size we use to the size the lib uses. + int msize = sizeof(media_info_t) + 10240; + media_info_t *test_media_info = (media_info_t*)calloc(msize, 1); + memset(test_media_info, 0xEF, msize); + + int res = m_dll->player_get_media_info(m_pid, test_media_info); + + uint8_t *t1 = (uint8_t*)test_media_info; + for (size_t i = msize-1; i >= 0; i--) + { + if (t1[i] != 0xEF) + { + if (sizeof(media_info_t) != i+1) + { + CLog::Log(LOGERROR, "CAMLPlayer::media_info_t(%d) size changed to %d", + sizeof(media_info_t), i+1); + // size is different, we cannot trust it + free(test_media_info); + return false; + } + break; + } + } + media_info = *test_media_info; + free(test_media_info); +#else int res = m_dll->player_get_media_info(m_pid, &media_info); +#endif if (res != PLAYER_SUCCESS) return false; if (m_log_level > 5) - { media_info_dump(&media_info); - // m_video_index, m_audio_index, m_subtitle_index might be -1 eventhough - // total_video_xxx is > 0, not sure why, they should be set to zero or - // some other sensible value. - CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForFormatValid: " - "m_video_index(%d), m_audio_index(%d), m_subtitle_index(%d), m_chapter_count(%d)", - media_info.stream_info.cur_video_index, - media_info.stream_info.cur_audio_index, -#if !defined(TARGET_ANDROID) - media_info.stream_info.cur_sub_index, - media_info.stream_info.total_chapter_num); -#else - media_info.stream_info.cur_sub_index, - 0); -#endif - } - // video info if (media_info.stream_info.has_video && media_info.stream_info.total_video_num > 0) { - for (int i = 0; i < media_info.stream_info.total_video_num; i++) + for (int i = 0; i < media_info.stream_info.total_video_num && + media_info.stream_info.total_video_num < MAX_VIDEO_STREAMS; i++) { AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo; info->Clear(); @@ -1961,7 +2065,8 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) // audio info if (media_info.stream_info.has_audio && media_info.stream_info.total_audio_num > 0) { - for (int i = 0; i < media_info.stream_info.total_audio_num; i++) + for (int i = 0; i < media_info.stream_info.total_audio_num && + media_info.stream_info.total_audio_num < MAX_AUDIO_STREAMS; i++) { AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo; info->Clear(); @@ -1973,10 +2078,11 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) info->bit_rate = media_info.audio_info[i]->bit_rate; info->duration = media_info.audio_info[i]->duration; info->format = media_info.audio_info[i]->aformat; -#if !defined(TARGET_ANDROID) +#if defined(HAS_AMLPLAYER_AUDIO_LANG) if (media_info.audio_info[i]->audio_language[0] != 0) info->language = std::string(media_info.audio_info[i]->audio_language, 3); #endif + m_audio_streams.push_back(info); } @@ -1991,7 +2097,7 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) // subtitle info if (media_info.stream_info.has_sub && media_info.stream_info.total_sub_num > 0) { - for (int i = 0; i < media_info.stream_info.total_sub_num; i++) + for (int i = 0; i < media_info.stream_info.total_sub_num && i < MAX_SUB_STREAMS; i++) { AMLPlayerStreamInfo *info = new AMLPlayerStreamInfo; info->Clear(); @@ -2011,12 +2117,12 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) if (m_subtitle_count && m_subtitle_index != 0) m_subtitle_index = 0; -#if !defined(TARGET_ANDROID) +#if defined(HAS_AMLPLAYER_CHAPTERS) // chapter info if (media_info.stream_info.total_chapter_num > 0) { m_chapter_count = media_info.stream_info.total_chapter_num; - for (int i = 0; i < m_chapter_count; i++) + for (int i = 0; i < m_chapter_count && m_chapter_count < MAX_CHAPTERS; i++) { if (media_info.chapter_info[i] != NULL) { @@ -2029,6 +2135,7 @@ bool CAMLPlayer::WaitForFormatValid(int timeout_ms) } } #endif + return true; break; } @@ -2306,6 +2413,8 @@ void CAMLPlayer::SetVideoRect(const CRect &SrcRect, const CRect &DestRect) char video_axis[256] = {0}; sprintf(video_axis, "%d %d %d %d", (int)dst_rect.x1, (int)dst_rect.y1, (int)dst_rect.x2, (int)dst_rect.y2); aml_set_sysfs_str("/sys/class/video/axis", video_axis); + // make sure we are in 'full stretch' so we can stretch + aml_set_sysfs_int("/sys/class/video/screen_mode", 1); /* CStdString rectangle; rectangle.Format("%i,%i,%i,%i", @@ -2324,41 +2433,3 @@ void CAMLPlayer::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, con player->SetVideoRect(SrcRect, DestRect); } -void CAMLPlayer::GetRenderFeatures(std::vector &renderFeatures) -{ - renderFeatures.push_back(RENDERFEATURE_ZOOM); - renderFeatures.push_back(RENDERFEATURE_CONTRAST); - renderFeatures.push_back(RENDERFEATURE_BRIGHTNESS); - renderFeatures.push_back(RENDERFEATURE_STRETCH); -} - -void CAMLPlayer::GetDeinterlaceMethods(std::vector &deinterlaceMethods) -{ - deinterlaceMethods.push_back(VS_INTERLACEMETHOD_DEINTERLACE); -} - -void CAMLPlayer::GetDeinterlaceModes(std::vector &deinterlaceModes) -{ - deinterlaceModes.push_back(VS_DEINTERLACEMODE_AUTO); -} - -void CAMLPlayer::GetScalingMethods(std::vector &scalingMethods) -{ -} - -void CAMLPlayer::GetAudioCapabilities(std::vector &audioCaps) -{ - audioCaps.push_back(IPC_AUD_SELECT_STREAM); - audioCaps.push_back(IPC_AUD_SELECT_OUTPUT); -#if !defined(TARGET_ANDROID) - audioCaps.push_back(IPC_AUD_OFFSET); -#endif -} - -void CAMLPlayer::GetSubtitleCapabilities(std::vector &subCaps) -{ - subCaps.push_back(IPC_SUBS_EXTERNAL); - subCaps.push_back(IPC_SUBS_SELECT); - subCaps.push_back(IPC_SUBS_OFFSET); -} - diff --git a/xbmc/cores/amlplayer/AMLPlayer.h b/xbmc/cores/amlplayer/AMLPlayer.h index 2624b61efe784..d7aaec8a7774b 100644 --- a/xbmc/cores/amlplayer/AMLPlayer.h +++ b/xbmc/cores/amlplayer/AMLPlayer.h @@ -43,6 +43,7 @@ class CAMLSubTitleThread : public CThread CAMLSubTitleThread(DllLibAmplayer* dll); virtual ~CAMLSubTitleThread(); + void Flush(); void UpdateSubtitle(CStdString &subtitle, int64_t elapsed_ms); protected: virtual void Process(void); @@ -102,6 +103,7 @@ class CAMLPlayer : public IPlayer, public CThread virtual int GetSubtitleCount(); virtual int GetSubtitle(); virtual void GetSubtitleName(int iStream, CStdString &strStreamName); + virtual void GetSubtitleLanguage(int iStream, CStdString &strStreamLang); virtual void SetSubtitle(int iStream); virtual bool GetSubtitleVisible(); virtual void SetSubtitleVisible(bool bVisible); @@ -123,9 +125,9 @@ class CAMLPlayer : public IPlayer, public CThread virtual int SeekChapter(int iChapter); virtual float GetActualFPS(); - virtual void SeekTime(__int64 iTime = 0); - virtual __int64 GetTime(); - virtual __int64 GetTotalTime(); + virtual void SeekTime(int64_t iTime = 0); + virtual int64_t GetTime(); + virtual int64_t GetTotalTime(); virtual int GetAudioBitrate(); virtual int GetVideoBitrate(); virtual int GetSourceBitrate(); @@ -158,14 +160,6 @@ class CAMLPlayer : public IPlayer, public CThread virtual bool SetPlayerState(CStdString state) {return false;}; virtual CStdString GetPlayingTitle() {return "";}; -/* - virtual void GetRenderFeatures(Features* renderFeatures); - virtual void GetDeinterlaceMethods(Features* deinterlaceMethods); - virtual void GetDeinterlaceModes(Features* deinterlaceModes); - virtual void GetScalingMethods(Features* scalingMethods); - virtual void GetAudioCapabilities(Features* audioCaps); - virtual void GetSubtitleCapabilities(Features* subCaps); -*/ virtual void GetRenderFeatures(std::vector &renderFeatures); virtual void GetDeinterlaceMethods(std::vector &deinterlaceMethods); @@ -244,7 +238,6 @@ class CAMLPlayer : public IPlayer, public CThread CDVDPlayerSubtitle *m_dvdPlayerSubtitle; CDVDOverlayContainer *m_dvdOverlayContainer; - int m_chapter_index; int m_chapter_count; int m_show_mainvideo; diff --git a/xbmc/cores/amlplayer/AMLUtils.cpp b/xbmc/cores/amlplayer/AMLUtils.cpp deleted file mode 100644 index a127b33fa7ce3..0000000000000 --- a/xbmc/cores/amlplayer/AMLUtils.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2011-2012 Team XBMC - * http://www.xbmc.org - * - * This Program 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, or (at your option) - * any later version. - * - * This Program 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 XBMC; see the file COPYING. If not, see - * . - * - */ - -#include -#include -#include -#include -#include -#include - -int aml_set_sysfs_str(const char *path, const char *val) -{ - int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); - if (fd >= 0) - { - write(fd, val, strlen(val)); - close(fd); - return 0; - } - return -1; -} - -int aml_get_sysfs_str(const char *path, char *valstr, const int size) -{ - int fd = open(path, O_RDONLY); - if (fd >= 0) - { - read(fd, valstr, size - 1); - valstr[strlen(valstr)] = '\0'; - close(fd); - return 0; - } - - sprintf(valstr, "%s", "fail"); - return -1; -} - -int aml_set_sysfs_int(const char *path, const int val) -{ - int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); - if (fd >= 0) - { - char bcmd[16]; - sprintf(bcmd, "%d", val); - write(fd, bcmd, strlen(bcmd)); - close(fd); - return 0; - } - return -1; -} - -int aml_get_sysfs_int(const char *path) -{ - int val = 0; - int fd = open(path, O_RDONLY); - if (fd >= 0) - { - char bcmd[16]; - read(fd, bcmd, sizeof(bcmd)); - val = strtol(bcmd, NULL, 16); - close(fd); - } - return val; -} - -bool aml_present() -{ - static int has_aml = -1; - if (has_aml == -1) - { - if (aml_get_sysfs_int("/sys/class/amhdmitx/amhdmitx0/disp_cap") != -1) - has_aml = 1; - else - has_aml = 0; - } - return has_aml; -} - -void aml_cpufreq_limit(bool limit) -{ - static int audiotrack_cputype = -1; - if (audiotrack_cputype == -1) - { - // defualt to m1 SoC - audiotrack_cputype = 1; - - FILE *cpuinfo_fd = fopen("/proc/cpuinfo", "r"); - if (cpuinfo_fd) - { - char buffer[512]; - while (fgets(buffer, sizeof(buffer), cpuinfo_fd)) - { - std::string stdbuffer(buffer); - if (stdbuffer.find("MESON-M3") != std::string::npos) - { - audiotrack_cputype = 3; - break; - } - } - fclose(cpuinfo_fd); - } - } - // On M1 SoCs, when playing hw decoded audio, we cannot drop below 600MHz - // or risk hw audio dropouts. AML code does a 2X scaling based off - // /sys/class/audiodsp/codec_mips but tests show that this is - // seems risky so we just clamp to 600Mhz to be safe. - if (audiotrack_cputype == 3) - return; - - int cpufreq = 300000; - if (limit) - cpufreq = 600000; - - aml_set_sysfs_int("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", cpufreq); -} - -void aml_set_audio_passthrough(bool passthrough) -{ - if (aml_present()) - aml_set_sysfs_int("/sys/class/audiodsp/digital_raw", passthrough ? 1:0); -} diff --git a/xbmc/cores/amlplayer/DllLibamplayer.h b/xbmc/cores/amlplayer/DllLibamplayer.h index 3bde96313d4bc..95f11647decca 100644 --- a/xbmc/cores/amlplayer/DllLibamplayer.h +++ b/xbmc/cores/amlplayer/DllLibamplayer.h @@ -25,6 +25,17 @@ extern "C" { +// beware, these will alter the structs +// in player_type.h. +#if 1 + #define HAS_AMLPLAYER_CHAPTERS + #define HAS_AMLPLAYER_AUDIO_LANG + #define HAS_AMLPLAYER_AUDIO_SETDELAY + #define HAS_AMLPLAYER_AUDIO_SETVOLUME +#else + #define HAS_AMLPLAYER_VIDEO_STREAMS10 +#endif + #include #include } @@ -62,9 +73,13 @@ class DllLibAmplayerInterface virtual int player_register_update_callback(callback_t *cb,update_state_fun_t up_fn,int interval_s)=0; virtual char* player_status2str(player_status status)=0; - virtual int audio_set_volume(int pid,float val)=0; +#if defined(HAS_AMLPLAYER_AUDIO_SETDELAY) virtual int audio_set_delay(int pid, int delay)=0; - +#endif +#if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME) + virtual int audio_set_volume(int pid,float val)=0; +#endif + virtual int codec_open_sub_read(void)=0; virtual int codec_close_sub_fd(int sub_fd)=0; virtual int codec_get_sub_size_fd(int sub_fd)=0; @@ -103,8 +118,12 @@ class DllLibAmplayer : public DllDynamic, DllLibAmplayerInterface DEFINE_METHOD3(int, player_register_update_callback, (callback_t *p1, update_state_fun_t p2, int p3)) DEFINE_METHOD1(char*, player_status2str, (player_status p1)) - DEFINE_METHOD2(int, audio_set_volume, (int p1, float p2)) +#if defined(HAS_AMLPLAYER_AUDIO_SETDELAY) DEFINE_METHOD2(int, audio_set_delay, (int p1, int p2)) +#endif +#if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME) + DEFINE_METHOD2(int, audio_set_volume, (int p1, float p2)) +#endif DEFINE_METHOD0(int, codec_open_sub_read) DEFINE_METHOD1(int, codec_close_sub_fd, (int p1)) @@ -140,8 +159,12 @@ class DllLibAmplayer : public DllDynamic, DllLibAmplayerInterface RESOLVE_METHOD(player_register_update_callback) RESOLVE_METHOD(player_status2str) - RESOLVE_METHOD(audio_set_volume) +#if defined(HAS_AMLPLAYER_AUDIO_SETDELAY) RESOLVE_METHOD(audio_set_delay) +#endif +#if defined(HAS_AMLPLAYER_AUDIO_SETVOLUME) + RESOLVE_METHOD(audio_set_volume) +#endif RESOLVE_METHOD(codec_open_sub_read) RESOLVE_METHOD(codec_close_sub_fd) diff --git a/xbmc/cores/amlplayer/FileURLProtocol.cpp b/xbmc/cores/amlplayer/FileURLProtocol.cpp index ba817b0372cd3..2a7a26dca03e6 100644 --- a/xbmc/cores/amlplayer/FileURLProtocol.cpp +++ b/xbmc/cores/amlplayer/FileURLProtocol.cpp @@ -56,6 +56,10 @@ int CFileURLProtocol::Open(AML_URLContext *h, const char *filename, int flags) { url = url.Right(url.size() - strlen("xb-")); } + else if (url.Left(strlen("xb-sftp://")).Equals("xb-sftp://")) + { + url = url.Right(url.size() - strlen("xb-")); + } else if (url.Left(strlen("xb-hdhomerun://")).Equals("xb-hdhomerun://")) { url = url.Right(url.size() - strlen("xb-")); diff --git a/xbmc/cores/amlplayer/Makefile.in b/xbmc/cores/amlplayer/Makefile.in index 3d16c3daebcf7..1645cf26c2e56 100644 --- a/xbmc/cores/amlplayer/Makefile.in +++ b/xbmc/cores/amlplayer/Makefile.in @@ -3,14 +3,14 @@ INCLUDES += -I$(prefix)/include/amlplayer SRCS = AMLPlayer.cpp -SRCS+= AMLUtils.cpp SRCS+= FileURLProtocol.cpp LIB = amlplayer.a +ifneq (@USE_LIBAMCODEC@,1) @abs_top_srcdir@/system/advancedsettings.xml: $(LIB) cp -f amlplayer_advancedsettings.xml $@ +endif include @abs_top_srcdir@/Makefile.include -include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) - diff --git a/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml b/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml index 7199cd47b68fc..310ff9589147d 100644 --- a/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml +++ b/xbmc/cores/amlplayer/amlplayer_advancedsettings.xml @@ -1,6 +1,5 @@ diff --git a/xbmc/cores/dvdplayer/DVDClock.cpp b/xbmc/cores/dvdplayer/DVDClock.cpp index 9eb744f170eb9..a523d5b2928b2 100644 --- a/xbmc/cores/dvdplayer/DVDClock.cpp +++ b/xbmc/cores/dvdplayer/DVDClock.cpp @@ -28,8 +28,8 @@ int64_t CDVDClock::m_systemOffset; int64_t CDVDClock::m_systemFrequency; CCriticalSection CDVDClock::m_systemsection; - bool CDVDClock::m_ismasterclock; +CDVDClock *CDVDClock::m_playerclock = NULL;; CDVDClock::CDVDClock() { @@ -45,10 +45,15 @@ CDVDClock::CDVDClock() m_ismasterclock = true; m_startClock = 0; + + m_playerclock = this; } CDVDClock::~CDVDClock() -{} +{ + CSingleLock lock(m_systemsection); + m_playerclock = NULL; +} // Returns the current absolute clock in units of DVD_TIME_BASE (usually microseconds). double CDVDClock::GetAbsoluteClock(bool interpolated /*= true*/) @@ -90,6 +95,12 @@ double CDVDClock::WaitAbsoluteClock(double target) return (double)systemtarget / freq * DVD_TIME_BASE; } +CDVDClock* CDVDClock::GetMasterClock() +{ + CSingleLock lock(m_systemsection); + return m_playerclock; +} + double CDVDClock::GetClock(bool interpolated /*= true*/) { CSharedLock lock(m_critSection); diff --git a/xbmc/cores/dvdplayer/DVDClock.h b/xbmc/cores/dvdplayer/DVDClock.h index dbf431b94d51a..27b9fc5385e41 100644 --- a/xbmc/cores/dvdplayer/DVDClock.h +++ b/xbmc/cores/dvdplayer/DVDClock.h @@ -66,7 +66,7 @@ class CDVDClock //the rendermanager needs to know about that because it can synchronize the videoreferenceclock to the video timestamps static void SetMasterClock(bool ismasterclock) { m_ismasterclock = ismasterclock; } static bool IsMasterClock() { return m_ismasterclock; } - + static CDVDClock* GetMasterClock(); protected: static void CheckSystemClock(); static double SystemToAbsolute(int64_t system); @@ -87,4 +87,5 @@ class CDVDClock bool m_speedadjust; CCriticalSection m_speedsection; static bool m_ismasterclock; + static CDVDClock *m_playerclock; }; diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp index 0cea7a99c7e29..76891558e9b48 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp @@ -36,6 +36,9 @@ #if defined(HAVE_LIBCRYSTALHD) #include "Video/DVDVideoCodecCrystalHD.h" #endif +#if defined(HAS_LIBAMCODEC) +#include "Video/DVDVideoCodecAmlogic.h" +#endif #include "Audio/DVDAudioCodecFFmpeg.h" #include "Audio/DVDAudioCodecLibMad.h" #include "Audio/DVDAudioCodecPcm.h" @@ -149,6 +152,11 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne #else hwSupport += "CrystalHD:no "; #endif +#if defined(HAS_LIBAMCODEC) + hwSupport += "AMCodec:yes "; +#else + hwSupport += "AMCodec:no "; +#endif #if defined(HAVE_LIBOPENMAX) && defined(_LINUX) hwSupport += "OpenMax:yes "; #elif defined(_LINUX) @@ -171,12 +179,14 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne #endif CLog::Log(LOGDEBUG, "CDVDFactoryCodec: compiled in hardware support: %s", hwSupport.c_str()); - +#if !defined(HAS_LIBAMCODEC) // dvd's have weird still-frames in it, which is not fully supported in ffmpeg if(hint.stills && (hint.codec == CODEC_ID_MPEG2VIDEO || hint.codec == CODEC_ID_MPEG1VIDEO)) { if( (pCodec = OpenCodec(new CDVDVideoCodecLibMpeg2(), hint, options)) ) return pCodec; } +#endif + #if defined(HAVE_LIBVDADECODER) if (!hint.software && g_guiSettings.GetBool("videoplayer.usevda")) { @@ -233,6 +243,14 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne } #endif +#if defined(HAS_LIBAMCODEC) + if (!hint.software /* && g_guiSettings.GetBool("videoplayer.useamcodec")*/) + { + CLog::Log(LOGINFO, "Amlogic Video Decoder..."); + if ( (pCodec = OpenCodec(new CDVDVideoCodecAmlogic(), hint, options)) ) return pCodec; + } +#endif + #if defined(HAVE_LIBOPENMAX) if (g_guiSettings.GetBool("videoplayer.useomx") && !hint.software ) { diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/AMLCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/AMLCodec.cpp new file mode 100644 index 0000000000000..c813e6b28bc13 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/AMLCodec.cpp @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" + +#include "AMLCodec.h" +#include "DynamicDll.h" + +#include "cores/dvdplayer/DVDClock.h" +#include "cores/VideoRenderers/RenderManager.h" +#include "settings/Settings.h" +#include "utils/AMLUtils.h" +#include "utils/log.h" +#include "utils/TimeUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// amcodec include +extern "C" { +#include +} // extern "C" + +class DllLibamCodecInterface +{ +public: + virtual ~DllLibamCodecInterface() {}; + + virtual int codec_init(codec_para_t *pcodec)=0; + virtual int codec_close(codec_para_t *pcodec)=0; + virtual int codec_reset(codec_para_t *pcodec)=0; + virtual int codec_pause(codec_para_t *pcodec)=0; + virtual int codec_resume(codec_para_t *pcodec)=0; + virtual int codec_write(codec_para_t *pcodec, void *buffer, int len)=0; + virtual int codec_checkin_pts(codec_para_t *pcodec, unsigned long pts)=0; + virtual int codec_get_vbuf_state(codec_para_t *pcodec, struct buf_status *)=0; + + virtual int codec_init_cntl(codec_para_t *pcodec)=0; + virtual int codec_poll_cntl(codec_para_t *pcodec)=0; + virtual int codec_set_cntl_mode(codec_para_t *pcodec, unsigned int mode)=0; + virtual int codec_set_cntl_avthresh(codec_para_t *pcodec, unsigned int)=0; + virtual int codec_set_cntl_syncthresh(codec_para_t *pcodec, unsigned int syncthresh)=0; + + // grab these from libamplayer + virtual int h263vld(unsigned char *inbuf, unsigned char *outbuf, int inbuf_len, int s263)=0; + virtual int decodeble_h263(unsigned char *buf)=0; + + // grab this from amffmpeg so we do not have to load DllAvUtil + virtual AVRational av_d2q(double d, int max)=0; +}; + +class DllLibAmCodec : public DllDynamic, DllLibamCodecInterface +{ + // libamcodec is static linked into libamplayer.so + DECLARE_DLL_WRAPPER(DllLibAmCodec, "libamplayer.so") + + DEFINE_METHOD1(int, codec_init, (codec_para_t *p1)) + DEFINE_METHOD1(int, codec_close, (codec_para_t *p1)) + DEFINE_METHOD1(int, codec_reset, (codec_para_t *p1)) + DEFINE_METHOD1(int, codec_pause, (codec_para_t *p1)) + DEFINE_METHOD1(int, codec_resume, (codec_para_t *p1)) + DEFINE_METHOD3(int, codec_write, (codec_para_t *p1, void *p2, int p3)) + DEFINE_METHOD2(int, codec_checkin_pts, (codec_para_t *p1, unsigned long p2)) + DEFINE_METHOD2(int, codec_get_vbuf_state, (codec_para_t *p1, struct buf_status * p2)) + + DEFINE_METHOD1(int, codec_init_cntl, (codec_para_t *p1)) + DEFINE_METHOD1(int, codec_poll_cntl, (codec_para_t *p1)) + DEFINE_METHOD2(int, codec_set_cntl_mode, (codec_para_t *p1, unsigned int p2)) + DEFINE_METHOD2(int, codec_set_cntl_avthresh, (codec_para_t *p1, unsigned int p2)) + DEFINE_METHOD2(int, codec_set_cntl_syncthresh,(codec_para_t *p1, unsigned int p2)) + + DEFINE_METHOD4(int, h263vld, (unsigned char *p1, unsigned char *p2, int p3, int p4)) + DEFINE_METHOD1(int, decodeble_h263, (unsigned char *p1)) + + DEFINE_METHOD2(AVRational, av_d2q, (double p1, int p2)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD(codec_init) + RESOLVE_METHOD(codec_close) + RESOLVE_METHOD(codec_reset) + RESOLVE_METHOD(codec_pause) + RESOLVE_METHOD(codec_resume) + RESOLVE_METHOD(codec_write) + RESOLVE_METHOD(codec_checkin_pts) + RESOLVE_METHOD(codec_get_vbuf_state) + + RESOLVE_METHOD(codec_init_cntl) + RESOLVE_METHOD(codec_poll_cntl) + RESOLVE_METHOD(codec_set_cntl_mode) + RESOLVE_METHOD(codec_set_cntl_avthresh) + RESOLVE_METHOD(codec_set_cntl_syncthresh) + + RESOLVE_METHOD(h263vld) + RESOLVE_METHOD(decodeble_h263) + + RESOLVE_METHOD(av_d2q) + END_METHOD_RESOLVE() +}; + +//----------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------- +// AppContext - Application state +#define PTS_FREQ 90000 +#define UNIT_FREQ 96000 +#define AV_SYNC_THRESH PTS_FREQ*30 + +#define TRICKMODE_NONE 0x00 +#define TRICKMODE_I 0x01 +#define TRICKMODE_FFFB 0x02 + +// same as AV_NOPTS_VALUE +#define INT64_0 INT64_C(0x8000000000000000) + +#define EXTERNAL_PTS (1) +#define SYNC_OUTSIDE (2) + +// missing tags +#define CODEC_TAG_VC_1 (0x312D4356) +#define CODEC_TAG_RV30 (0x30335652) +#define CODEC_TAG_RV40 (0x30345652) +#define CODEC_TAG_MJPEG (0x47504a4d) +#define CODEC_TAG_mjpeg (0x47504a4c) +#define CODEC_TAG_jpeg (0x6765706a) +#define CODEC_TAG_mjpa (0x61706a6d) + +#define RW_WAIT_TIME (20 * 1000) // 20ms + +#define P_PRE (0x02000000) +#define F_PRE (0x03000000) +#define PLAYER_SUCCESS (0) +#define PLAYER_FAILED (-(P_PRE|0x01)) +#define PLAYER_NOMEM (-(P_PRE|0x02)) +#define PLAYER_EMPTY_P (-(P_PRE|0x03)) + +#define PLAYER_WR_FAILED (-(P_PRE|0x21)) +#define PLAYER_WR_EMPTYP (-(P_PRE|0x22)) +#define PLAYER_WR_FINISH (P_PRE|0x1) + +#define PLAYER_PTS_ERROR (-(P_PRE|0x31)) +#define PLAYER_UNSUPPORT (-(P_PRE|0x35)) +#define PLAYER_CHECK_CODEC_ERROR (-(P_PRE|0x39)) + +#define HDR_BUF_SIZE 1024 +typedef struct hdr_buf { + char *data; + int size; +} hdr_buf_t; + +typedef struct am_packet { + AVPacket avpkt; + int64_t avpts; + int64_t avdts; + int avduration; + int isvalid; + int newflag; + int64_t lastpts; + unsigned char *data; + unsigned char *buf; + int data_size; + int buf_size; + hdr_buf_t *hdr; + codec_para_t *codec; +} am_packet_t; + +typedef enum { + AM_STREAM_UNKNOWN = 0, + AM_STREAM_TS, + AM_STREAM_PS, + AM_STREAM_ES, + AM_STREAM_RM, + AM_STREAM_AUDIO, + AM_STREAM_VIDEO, +} pstream_type; + +typedef union { + int64_t total_bytes; + unsigned int vpkt_num; + unsigned int spkt_num; +} read_write_size; + +typedef struct { + unsigned int read_end_flag: 1; + unsigned int end_flag: 1; + unsigned int reset_flag: 1; + int check_lowlevel_eagain_cnt; +} p_ctrl_info_t; + +typedef struct am_private_t +{ + am_packet_t am_pkt; + codec_para_t vcodec; + + pstream_type stream_type; + p_ctrl_info_t playctrl_info; + + read_write_size read_size; + read_write_size write_size; + int check_first_pts; + + vformat_t video_format; + int video_pid; + unsigned int video_codec_id; + unsigned int video_codec_tag; + vdec_type_t video_codec_type; + unsigned int video_width; + unsigned int video_height; + unsigned int video_ratio; + unsigned int video_ratio64; + unsigned int video_rate; + unsigned int video_rotation_degree; + int flv_flag; + int h263_decodable; + int extrasize; + uint8_t *extradata; + DllLibAmCodec *m_dll; +} am_private_t; + +/*************************************************************************/ +static int64_t get_pts_video() +{ + int fd = open("/sys/class/tsync/pts_video", O_RDONLY); + if (fd >= 0) + { + char pts_str[16]; + int size = read(fd, pts_str, sizeof(pts_str)); + close(fd); + if (size > 0) + { + unsigned long pts = strtoul(pts_str, NULL, 16); + return pts; + } + } + + CLog::Log(LOGERROR, "get_pts_video: open /tsync/event error"); + return -1; +} + +static int set_pts_pcrscr(int64_t value) +{ + int fd = open("/sys/class/tsync/pts_pcrscr", O_WRONLY); + if (fd >= 0) + { + char pts_str[64]; + unsigned long pts = (unsigned long)value; + sprintf(pts_str, "0x%lx", pts); + write(fd, pts_str, strlen(pts_str)); + close(fd); + return 0; + } + + CLog::Log(LOGERROR, "set_pts_pcrscr: open pts_pcrscr error"); + return -1; +} + +static vformat_t codecid_to_vformat(enum CodecID id) +{ + vformat_t format; + switch (id) + { + case CODEC_ID_MPEG1VIDEO: + case CODEC_ID_MPEG2VIDEO: + case CODEC_ID_MPEG2VIDEO_XVMC: + format = VFORMAT_MPEG12; + break; + case CODEC_ID_H263: + case CODEC_ID_MPEG4: + case CODEC_ID_H263P: + case CODEC_ID_H263I: + case CODEC_ID_MSMPEG4V2: + case CODEC_ID_MSMPEG4V3: + case CODEC_ID_FLV1: + format = VFORMAT_MPEG4; + break; + case CODEC_ID_RV10: + case CODEC_ID_RV20: + case CODEC_ID_RV30: + case CODEC_ID_RV40: + format = VFORMAT_REAL; + break; + case CODEC_ID_H264: + format = VFORMAT_H264; + break; + /* + case CODEC_ID_H264MVC: + // H264 Multiview Video Coding (3d blurays) + format = VFORMAT_H264MVC; + break; + */ + case CODEC_ID_MJPEG: + format = VFORMAT_MJPEG; + break; + case CODEC_ID_VC1: + case CODEC_ID_WMV3: + format = VFORMAT_VC1; + break; + case CODEC_ID_AVS: + case CODEC_ID_CAVS: + format = VFORMAT_AVS; + break; + + default: + format = VFORMAT_UNSUPPORT; + break; + } + + CLog::Log(LOGDEBUG, "codecid_to_vformat, id(%d) -> vformat(%d)", (int)id, format); + return format; +} + +static vdec_type_t codec_tag_to_vdec_type(unsigned int codec_tag) +{ + vdec_type_t dec_type; + switch (codec_tag) + { + case CODEC_TAG_MJPEG: + case CODEC_TAG_mjpeg: + case CODEC_TAG_jpeg: + case CODEC_TAG_mjpa: + dec_type = VIDEO_DEC_FORMAT_MJPEG; + break; + case CODEC_TAG_XVID: + case CODEC_TAG_xvid: + case CODEC_TAG_XVIX: + dec_type = VIDEO_DEC_FORMAT_MPEG4_5; + break; + case CODEC_TAG_COL1: + case CODEC_TAG_DIV3: + case CODEC_TAG_MP43: + dec_type = VIDEO_DEC_FORMAT_MPEG4_3; + break; + case CODEC_TAG_DIV4: + case CODEC_TAG_DIVX: + dec_type = VIDEO_DEC_FORMAT_MPEG4_4; + break; + case CODEC_TAG_DIV5: + case CODEC_TAG_DX50: + case CODEC_TAG_M4S2: + case CODEC_TAG_FMP4: + dec_type = VIDEO_DEC_FORMAT_MPEG4_5; + break; + case CODEC_TAG_DIV6: + dec_type = VIDEO_DEC_FORMAT_MPEG4_5; + break; + case CODEC_TAG_MP4V: + case CODEC_TAG_RMP4: + case CODEC_TAG_MPG4: + case CODEC_TAG_mp4v: + case CODEC_ID_MPEG4: + dec_type = VIDEO_DEC_FORMAT_MPEG4_5; + break; + case CODEC_ID_H263: + case CODEC_TAG_H263: + case CODEC_TAG_h263: + case CODEC_TAG_s263: + case CODEC_TAG_F263: + dec_type = VIDEO_DEC_FORMAT_H263; + break; + case CODEC_TAG_AVC1: + case CODEC_TAG_avc1: + case CODEC_TAG_H264: + case CODEC_TAG_h264: + dec_type = VIDEO_DEC_FORMAT_H264; + break; + case CODEC_ID_RV30: + case CODEC_TAG_RV30: + dec_type = VIDEO_DEC_FORMAT_REAL_8; + break; + case CODEC_ID_RV40: + case CODEC_TAG_RV40: + dec_type = VIDEO_DEC_FORMAT_REAL_9; + break; + case CODEC_ID_H264: + dec_type = VIDEO_DEC_FORMAT_H264; + break; + /* + case CODEC_ID_H264MVC: + dec_type = VIDEO_DEC_FORMAT_H264; + break; + */ + case CODEC_TAG_WMV3: + dec_type = VIDEO_DEC_FORMAT_WMV3; + break; + case CODEC_ID_VC1: + case CODEC_TAG_VC_1: + case CODEC_TAG_WVC1: + case CODEC_TAG_WMVA: + dec_type = VIDEO_DEC_FORMAT_WVC1; + break; + case CODEC_ID_VP6F: + dec_type = VIDEO_DEC_FORMAT_SW; + break; + default: + dec_type = VIDEO_DEC_FORMAT_UNKNOW; + break; + } + + CLog::Log(LOGDEBUG, "codec_tag_to_vdec_type, codec_tag(%d) -> vdec_type(%d)", codec_tag, dec_type); + return dec_type; +} + +static void am_packet_init(am_packet_t *pkt) +{ + memset(&pkt->avpkt, 0, sizeof(AVPacket)); + pkt->avpts = 0; + pkt->avdts = 0; + pkt->avduration = 0; + pkt->isvalid = 0; + pkt->newflag = 0; + pkt->lastpts = 0; + pkt->data = NULL; + pkt->buf = NULL; + pkt->data_size = 0; + pkt->buf_size = 0; + pkt->hdr = NULL; + pkt->codec = NULL; +} + +void am_packet_release(am_packet_t *pkt) +{ + if (pkt->buf != NULL) + { + free(pkt->buf); + pkt->buf= NULL; + } + if (pkt->hdr != NULL) + { + free(pkt->hdr->data); + pkt->hdr->data = NULL; + free(pkt->hdr); + pkt->hdr = NULL; + } + pkt->codec = NULL; +} + +int check_in_pts(am_private_t *para, am_packet_t *pkt) +{ + int last_duration = 0; + static int last_v_duration = 0; + int64_t pts = 0; + + last_duration = last_v_duration; + + if (para->stream_type == AM_STREAM_ES) { + if ((int64_t)INT64_0 != pkt->avpts) { + pts = pkt->avpts; + + if (para->m_dll->codec_checkin_pts(pkt->codec, pts) != 0) { + CLog::Log(LOGDEBUG, "ERROR check in pts error!"); + return PLAYER_PTS_ERROR; + } + + } else if ((int64_t)INT64_0 != pkt->avdts) { + pts = pkt->avdts * last_duration; + + if (para->m_dll->codec_checkin_pts(pkt->codec, pts) != 0) { + CLog::Log(LOGDEBUG, "ERROR check in dts error!"); + return PLAYER_PTS_ERROR; + } + + last_v_duration = pkt->avduration ? pkt->avduration : 1; + } else { + if (!para->check_first_pts) { + if (para->m_dll->codec_checkin_pts(pkt->codec, 0) != 0) { + CLog::Log(LOGDEBUG, "ERROR check in 0 to video pts error!"); + return PLAYER_PTS_ERROR; + } + } + } + if (!para->check_first_pts) { + para->check_first_pts = 1; + } + } + if (pts > 0) + pkt->lastpts = pts; + + return PLAYER_SUCCESS; +} + +static int check_write_finish(am_private_t *para, am_packet_t *pkt) +{ + if (para->playctrl_info.read_end_flag) { + if ((para->write_size.vpkt_num == para->read_size.vpkt_num)) { + return PLAYER_WR_FINISH; + } + } + return PLAYER_WR_FAILED; +} + +static int write_header(am_private_t *para, am_packet_t *pkt) +{ + int write_bytes = 0, len = 0; + + if (pkt->hdr && pkt->hdr->size > 0) { + if ((NULL == pkt->codec) || (NULL == pkt->hdr->data)) { + CLog::Log(LOGDEBUG, "[write_header]codec null!"); + return PLAYER_EMPTY_P; + } + //some wvc1 es data not need to add header + if (para->video_format == VFORMAT_VC1 && para->video_codec_type == VIDEO_DEC_FORMAT_WVC1) { + if ((pkt->data) && (pkt->data_size >= 4) + && (pkt->data[0] == 0) && (pkt->data[1] == 0) + && (pkt->data[2] == 1) && (pkt->data[3] == 0xd || pkt->data[3] == 0xf)) { + return PLAYER_SUCCESS; + } + } + while (1) { + write_bytes = para->m_dll->codec_write(pkt->codec, pkt->hdr->data + len, pkt->hdr->size - len); + if (write_bytes < 0 || write_bytes > (pkt->hdr->size - len)) { + if (-errno != AVERROR(EAGAIN)) { + CLog::Log(LOGDEBUG, "ERROR:write header failed!"); + return PLAYER_WR_FAILED; + } else { + continue; + } + } else { + len += write_bytes; + if (len == pkt->hdr->size) { + break; + } + } + } + } + return PLAYER_SUCCESS; +} + +int check_avbuffer_enough(am_private_t *para, am_packet_t *pkt) +{ + return 1; +} + +int write_av_packet(am_private_t *para, am_packet_t *pkt) +{ + //CLog::Log(LOGDEBUG, "write_av_packet, pkt->isvalid(%d), pkt->data(%p), pkt->data_size(%d)", + // pkt->isvalid, pkt->data, pkt->data_size); + + int write_bytes = 0, len = 0, ret; + unsigned char *buf; + int size; + + if (pkt->newflag) { + if (pkt->isvalid) { + ret = check_in_pts(para, pkt); + if (ret != PLAYER_SUCCESS) { + CLog::Log(LOGDEBUG, "check in pts failed"); + return PLAYER_WR_FAILED; + } + } + if (write_header(para, pkt) == PLAYER_WR_FAILED) { + CLog::Log(LOGDEBUG, "[%s]write header failed!", __FUNCTION__); + return PLAYER_WR_FAILED; + } + pkt->newflag = 0; + } + + buf = pkt->data; + size = pkt->data_size ; + if (size == 0 && pkt->isvalid) { + para->write_size.vpkt_num++; + pkt->isvalid = 0; + } + while (size > 0 && pkt->isvalid) { + write_bytes = para->m_dll->codec_write(pkt->codec, (char *)buf, size); + if (write_bytes < 0 || write_bytes > size) { + if (-errno != AVERROR(EAGAIN)) { + para->playctrl_info.check_lowlevel_eagain_cnt = 0; + CLog::Log(LOGDEBUG, "write codec data failed!"); + return PLAYER_WR_FAILED; + } else { + // EAGAIN to see if buffer full or write time out too much + if (check_avbuffer_enough(para, pkt)) { + para->playctrl_info.check_lowlevel_eagain_cnt++; + } else { + para->playctrl_info.check_lowlevel_eagain_cnt = 0; + } + + if (para->playctrl_info.check_lowlevel_eagain_cnt > 50) { + // reset decoder + para->playctrl_info.check_lowlevel_eagain_cnt = 0; + para->playctrl_info.reset_flag = 1; + para->playctrl_info.end_flag = 1; + CLog::Log(LOGDEBUG, "$$$$$$ write blocked, need reset decoder!$$$$$$"); + } + //pkt->data += len; + //pkt->data_size -= len; + usleep(RW_WAIT_TIME); + CLog::Log(LOGDEBUG, "usleep(RW_WAIT_TIME), len(%d)", len); + return PLAYER_SUCCESS; + } + } else { + para->playctrl_info.check_lowlevel_eagain_cnt = 0; + len += write_bytes; + if (len == pkt->data_size) { + para->write_size.vpkt_num++; + pkt->isvalid = 0; + pkt->data_size = 0; + break; + } else if (len < pkt->data_size) { + buf += write_bytes; + size -= write_bytes; + } else { + return PLAYER_WR_FAILED; + } + } + } + if (check_write_finish(para, pkt) == PLAYER_WR_FINISH) { + return PLAYER_WR_FINISH; + } + return PLAYER_SUCCESS; +} + +static int check_size_in_buffer(unsigned char *p, int len) +{ + unsigned int size; + unsigned char *q = p; + while ((q + 4) < (p + len)) { + size = (*q << 24) | (*(q + 1) << 16) | (*(q + 2) << 8) | (*(q + 3)); + if (size & 0xff000000) { + return 0; + } + + if (q + size + 4 == p + len) { + return 1; + } + + q += size + 4; + } + return 0; +} + +static int check_size_in_buffer3(unsigned char *p, int len) +{ + unsigned int size; + unsigned char *q = p; + while ((q + 3) < (p + len)) { + size = (*q << 16) | (*(q + 1) << 8) | (*(q + 2)); + + if (q + size + 3 == p + len) { + return 1; + } + + q += size + 3; + } + return 0; +} + +static int check_size_in_buffer2(unsigned char *p, int len) +{ + unsigned int size; + unsigned char *q = p; + while ((q + 2) < (p + len)) { + size = (*q << 8) | (*(q + 1)); + + if (q + size + 2 == p + len) { + return 1; + } + + q += size + 2; + } + return 0; +} + +/*************************************************************************/ +static int m4s2_dx50_mp4v_add_header(unsigned char *buf, int size, am_packet_t *pkt) +{ + if (size > pkt->hdr->size) { + free(pkt->hdr->data), pkt->hdr->data = NULL; + pkt->hdr->size = 0; + + pkt->hdr->data = (char*)malloc(size); + if (!pkt->hdr->data) { + CLog::Log(LOGDEBUG, "[m4s2_dx50_add_header] NOMEM!"); + return PLAYER_FAILED; + } + } + + pkt->hdr->size = size; + memcpy(pkt->hdr->data, buf, size); + + return PLAYER_SUCCESS; +} + +static int m4s2_dx50_mp4v_write_header(am_private_t *para, am_packet_t *pkt) +{ + CLog::Log(LOGDEBUG, "m4s2_dx50_mp4v_write_header"); + int ret = m4s2_dx50_mp4v_add_header(para->extradata, para->extrasize, pkt); + if (ret == PLAYER_SUCCESS) { + if (1) { + pkt->codec = ¶->vcodec; + } else { + CLog::Log(LOGDEBUG, "[m4s2_dx50_mp4v_add_header]invalid video codec!"); + return PLAYER_EMPTY_P; + } + pkt->newflag = 1; + ret = write_av_packet(para, pkt); + } + return ret; +} + +static int mjpeg_data_prefeeding(am_packet_t *pkt) +{ + const unsigned char mjpeg_addon_data[] = { + 0xff, 0xd8, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, + 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, + 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, + 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, + 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, + 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, + 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, + 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, + 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, + 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, + 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa + }; + + if (pkt->hdr->data) { + memcpy(pkt->hdr->data, &mjpeg_addon_data, sizeof(mjpeg_addon_data)); + pkt->hdr->size = sizeof(mjpeg_addon_data); + } else { + CLog::Log(LOGDEBUG, "[mjpeg_data_prefeeding]No enough memory!"); + return PLAYER_FAILED; + } + return PLAYER_SUCCESS; +} + +static int mjpeg_write_header(am_private_t *para, am_packet_t *pkt) +{ + mjpeg_data_prefeeding(pkt); + if (1) { + pkt->codec = ¶->vcodec; + } else { + CLog::Log(LOGDEBUG, "[mjpeg_write_header]invalid codec!"); + return PLAYER_EMPTY_P; + } + pkt->newflag = 1; + write_av_packet(para, pkt); + return PLAYER_SUCCESS; +} + +static int divx3_data_prefeeding(am_packet_t *pkt, unsigned w, unsigned h) +{ + unsigned i = (w << 12) | (h & 0xfff); + unsigned char divx311_add[10] = { + 0x00, 0x00, 0x00, 0x01, + 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + divx311_add[5] = (i >> 16) & 0xff; + divx311_add[6] = (i >> 8) & 0xff; + divx311_add[7] = i & 0xff; + + if (pkt->hdr->data) { + memcpy(pkt->hdr->data, divx311_add, sizeof(divx311_add)); + pkt->hdr->size = sizeof(divx311_add); + } else { + CLog::Log(LOGDEBUG, "[divx3_data_prefeeding]No enough memory!"); + return PLAYER_FAILED; + } + return PLAYER_SUCCESS; +} + +static int divx3_write_header(am_private_t *para, am_packet_t *pkt) +{ + CLog::Log(LOGDEBUG, "divx3_write_header"); + divx3_data_prefeeding(pkt, para->video_width, para->video_height); + if (1) { + pkt->codec = ¶->vcodec; + } else { + CLog::Log(LOGDEBUG, "[divx3_write_header]invalid codec!"); + return PLAYER_EMPTY_P; + } + pkt->newflag = 1; + write_av_packet(para, pkt); + return PLAYER_SUCCESS; +} + +static int h264_add_header(unsigned char *buf, int size, am_packet_t *pkt) +{ + char nal_start_code[] = {0x0, 0x0, 0x0, 0x1}; + int nalsize; + unsigned char* p; + int tmpi; + unsigned char* extradata = buf; + int header_len = 0; + char* buffer = pkt->hdr->data; + + p = extradata; + + // h264 annex-b + if ((p[0]==0 && p[1]==0 && p[2]==0 && p[3]==1) && size < HDR_BUF_SIZE) { + CLog::Log(LOGDEBUG, "add 264 header in stream before header len=%d",size); + memcpy(buffer, buf, size); + pkt->hdr->size = size; + return PLAYER_SUCCESS; + } + + if (size < 4) { + return PLAYER_FAILED; + } + + if (size < 10) { + CLog::Log(LOGDEBUG, "avcC too short"); + return PLAYER_FAILED; + } + + // h264 avcC + if (*p != 1) { + CLog::Log(LOGDEBUG, "Unknown avcC version %d", *p); + return PLAYER_FAILED; + } + + int cnt = *(p + 5) & 0x1f; //number of sps + // CLog::Log(LOGDEBUG, "number of sps :%d", cnt); + p += 6; + for (tmpi = 0; tmpi < cnt; tmpi++) { + nalsize = (*p << 8) | (*(p + 1)); + memcpy(&(buffer[header_len]), nal_start_code, 4); + header_len += 4; + memcpy(&(buffer[header_len]), p + 2, nalsize); + header_len += nalsize; + p += (nalsize + 2); + } + + cnt = *(p++); //Number of pps + // CLog::Log(LOGDEBUG, "number of pps :%d", cnt); + for (tmpi = 0; tmpi < cnt; tmpi++) { + nalsize = (*p << 8) | (*(p + 1)); + memcpy(&(buffer[header_len]), nal_start_code, 4); + header_len += 4; + memcpy(&(buffer[header_len]), p + 2, nalsize); + header_len += nalsize; + p += (nalsize + 2); + } + if (header_len >= HDR_BUF_SIZE) { + CLog::Log(LOGDEBUG, "header_len %d is larger than max length", header_len); + return 0; + } + pkt->hdr->size = header_len; + + return PLAYER_SUCCESS; +} +static int h264_write_header(am_private_t *para, am_packet_t *pkt) +{ + // CLog::Log(LOGDEBUG, "h264_write_header"); + int ret = -1; + + ret = h264_add_header(para->extradata, para->extrasize, pkt); + if (ret == PLAYER_SUCCESS) { + //if (ctx->vcodec) { + if (1) { + pkt->codec = ¶->vcodec; + } else { + //CLog::Log(LOGDEBUG, "[pre_header_feeding]invalid video codec!"); + return PLAYER_EMPTY_P; + } + + pkt->newflag = 1; + ret = write_av_packet(para, pkt); + } + return ret; +} + +static int wmv3_write_header(am_private_t *para, am_packet_t *pkt) +{ + CLog::Log(LOGDEBUG, "wmv3_write_header"); + unsigned i, check_sum = 0; + unsigned data_len = para->extrasize + 4; + + pkt->hdr->data[0] = 0; + pkt->hdr->data[1] = 0; + pkt->hdr->data[2] = 1; + pkt->hdr->data[3] = 0x10; + + pkt->hdr->data[4] = 0; + pkt->hdr->data[5] = (data_len >> 16) & 0xff; + pkt->hdr->data[6] = 0x88; + pkt->hdr->data[7] = (data_len >> 8) & 0xff; + pkt->hdr->data[8] = data_len & 0xff; + pkt->hdr->data[9] = 0x88; + + pkt->hdr->data[10] = 0xff; + pkt->hdr->data[11] = 0xff; + pkt->hdr->data[12] = 0x88; + pkt->hdr->data[13] = 0xff; + pkt->hdr->data[14] = 0xff; + pkt->hdr->data[15] = 0x88; + + for (i = 4 ; i < 16 ; i++) { + check_sum += pkt->hdr->data[i]; + } + + pkt->hdr->data[16] = (check_sum >> 8) & 0xff; + pkt->hdr->data[17] = check_sum & 0xff; + pkt->hdr->data[18] = 0x88; + pkt->hdr->data[19] = (check_sum >> 8) & 0xff; + pkt->hdr->data[20] = check_sum & 0xff; + pkt->hdr->data[21] = 0x88; + + pkt->hdr->data[22] = (para->video_width >> 8) & 0xff; + pkt->hdr->data[23] = para->video_width & 0xff; + pkt->hdr->data[24] = (para->video_height >> 8) & 0xff; + pkt->hdr->data[25] = para->video_height & 0xff; + + memcpy(pkt->hdr->data + 26, para->extradata, para->extrasize); + pkt->hdr->size = para->extrasize + 26; + if (1) { + pkt->codec = ¶->vcodec; + } else { + CLog::Log(LOGDEBUG, "[wmv3_write_header]invalid codec!"); + return PLAYER_EMPTY_P; + } + pkt->newflag = 1; + return write_av_packet(para, pkt); +} + +static int wvc1_write_header(am_private_t *para, am_packet_t *pkt) +{ + CLog::Log(LOGDEBUG, "wvc1_write_header"); + memcpy(pkt->hdr->data, para->extradata + 1, para->extrasize - 1); + pkt->hdr->size = para->extrasize - 1; + if (1) { + pkt->codec = ¶->vcodec; + } else { + CLog::Log(LOGDEBUG, "[wvc1_write_header]invalid codec!"); + return PLAYER_EMPTY_P; + } + pkt->newflag = 1; + return write_av_packet(para, pkt); +} + +static int mpeg_add_header(am_private_t *para, am_packet_t *pkt) +{ + CLog::Log(LOGDEBUG, "mpeg_add_header"); +#define STUFF_BYTES_LENGTH (256) + int size; + unsigned char packet_wrapper[] = { + 0x00, 0x00, 0x01, 0xe0, + 0x00, 0x00, /* pes packet length */ + 0x81, 0xc0, 0x0d, + 0x20, 0x00, 0x00, 0x00, 0x00, /* PTS */ + 0x1f, 0xff, 0xff, 0xff, 0xff, /* DTS */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + size = para->extrasize + sizeof(packet_wrapper); + packet_wrapper[4] = size >> 8 ; + packet_wrapper[5] = size & 0xff ; + memcpy(pkt->hdr->data, packet_wrapper, sizeof(packet_wrapper)); + size = sizeof(packet_wrapper); + //CLog::Log(LOGDEBUG, "[mpeg_add_header:%d]wrapper size=%d\n",__LINE__,size); + memcpy(pkt->hdr->data + size, para->extradata, para->extrasize); + size += para->extrasize; + //CLog::Log(LOGDEBUG, "[mpeg_add_header:%d]wrapper+seq size=%d\n",__LINE__,size); + memset(pkt->hdr->data + size, 0xff, STUFF_BYTES_LENGTH); + size += STUFF_BYTES_LENGTH; + pkt->hdr->size = size; + //CLog::Log(LOGDEBUG, "[mpeg_add_header:%d]hdr_size=%d\n",__LINE__,size); + if (1) { + pkt->codec = ¶->vcodec; + } else { + CLog::Log(LOGDEBUG, "[mpeg_add_header]invalid codec!"); + return PLAYER_EMPTY_P; + } + + pkt->newflag = 1; + return write_av_packet(para, pkt); +} + +int pre_header_feeding(am_private_t *para, am_packet_t *pkt) +{ + int ret; + if (para->stream_type == AM_STREAM_ES) { + if (pkt->hdr == NULL) { + pkt->hdr = (hdr_buf_t*)malloc(sizeof(hdr_buf_t)); + pkt->hdr->data = (char *)malloc(HDR_BUF_SIZE); + if (!pkt->hdr->data) { + //CLog::Log(LOGDEBUG, "[pre_header_feeding] NOMEM!"); + return PLAYER_NOMEM; + } + } + + if (VFORMAT_H264 == para->video_format /*|| VFORMAT_H264MVC == para->video_format*/) { + ret = h264_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + } else if ((VFORMAT_MPEG4 == para->video_format) && (VIDEO_DEC_FORMAT_MPEG4_3 == para->video_codec_type)) { + ret = divx3_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + } else if ((CODEC_TAG_M4S2 == para->video_codec_tag) + || (CODEC_TAG_DX50 == para->video_codec_tag) + || (CODEC_TAG_mp4v == para->video_codec_tag)) { + ret = m4s2_dx50_mp4v_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + /* + } else if ((AVI_FILE == para->file_type) + && (VIDEO_DEC_FORMAT_MPEG4_3 != para->vstream_info.video_codec_type) + && (VFORMAT_H264 != para->vstream_info.video_format) + && (VFORMAT_VC1 != para->vstream_info.video_format)) { + ret = avi_write_header(para); + if (ret != PLAYER_SUCCESS) { + return ret; + } + */ + } else if (CODEC_TAG_WMV3 == para->video_codec_tag) { + CLog::Log(LOGDEBUG, "CODEC_TAG_WMV3 == para->video_codec_tag"); + ret = wmv3_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + } else if ((CODEC_TAG_WVC1 == para->video_codec_tag) + || (CODEC_TAG_VC_1 == para->video_codec_tag) + || (CODEC_TAG_WMVA == para->video_codec_tag)) { + CLog::Log(LOGDEBUG, "CODEC_TAG_WVC1 == para->video_codec_tag"); + ret = wvc1_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + /* + } else if ((MKV_FILE == para->file_type) && + ((VFORMAT_MPEG4 == para->vstream_info.video_format) + || (VFORMAT_MPEG12 == para->vstream_info.video_format))) { + ret = mkv_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + */ + } else if (VFORMAT_MJPEG == para->video_format) { + ret = mjpeg_write_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + } + + if (pkt->hdr) { + if (pkt->hdr->data) { + free(pkt->hdr->data); + pkt->hdr->data = NULL; + } + free(pkt->hdr); + pkt->hdr = NULL; + } + } + else if (para->stream_type == AM_STREAM_PS) { + if (pkt->hdr == NULL) { + pkt->hdr = (hdr_buf_t*)malloc(sizeof(hdr_buf_t)); + pkt->hdr->data = (char*)malloc(HDR_BUF_SIZE); + if (!pkt->hdr->data) { + CLog::Log(LOGDEBUG, "[pre_header_feeding] NOMEM!"); + return PLAYER_NOMEM; + } + } + if (( CODEC_ID_MPEG1VIDEO == para->video_codec_id) + || (CODEC_ID_MPEG2VIDEO == para->video_codec_id) + || (CODEC_ID_MPEG2VIDEO_XVMC == para->video_codec_id)) { + ret = mpeg_add_header(para, pkt); + if (ret != PLAYER_SUCCESS) { + return ret; + } + } + if (pkt->hdr) { + if (pkt->hdr->data) { + free(pkt->hdr->data); + pkt->hdr->data = NULL; + } + free(pkt->hdr); + pkt->hdr = NULL; + } + } + return PLAYER_SUCCESS; +} + +/*************************************************************************/ +int h264_update_frame_header(am_packet_t *pkt) +{ + int nalsize, size = pkt->data_size; + unsigned char *data = pkt->data; + unsigned char *p = data; + if (p != NULL) { + if (check_size_in_buffer(p, size)) { + while ((p + 4) < (data + size)) { + nalsize = (*p << 24) | (*(p + 1) << 16) | (*(p + 2) << 8) | (*(p + 3)); + *p = 0; + *(p + 1) = 0; + *(p + 2) = 0; + *(p + 3) = 1; + p += (nalsize + 4); + } + return PLAYER_SUCCESS; + } else if (check_size_in_buffer3(p, size)) { + while ((p + 3) < (data + size)) { + nalsize = (*p << 16) | (*(p + 1) << 8) | (*(p + 2)); + *p = 0; + *(p + 1) = 0; + *(p + 2) = 1; + p += (nalsize + 3); + } + return PLAYER_SUCCESS; + } else if (check_size_in_buffer2(p, size)) { + unsigned char *new_data; + int new_len = 0; + + new_data = (unsigned char *)malloc(size + 2 * 1024); + if (!new_data) { + return PLAYER_NOMEM; + } + + while ((p + 2) < (data + size)) { + nalsize = (*p << 8) | (*(p + 1)); + *(new_data + new_len) = 0; + *(new_data + new_len + 1) = 0; + *(new_data + new_len + 2) = 0; + *(new_data + new_len + 3) = 1; + memcpy(new_data + new_len + 4, p + 2, nalsize); + p += (nalsize + 2); + new_len += nalsize + 4; + } + + free(pkt->buf); + + pkt->buf = new_data; + pkt->buf_size = size + 2 * 1024; + pkt->data = pkt->buf; + pkt->data_size = new_len; + } + } else { + CLog::Log(LOGDEBUG, "[%s]invalid pointer!", __FUNCTION__); + return PLAYER_FAILED; + } + return PLAYER_SUCCESS; +} + +int divx3_prefix(am_packet_t *pkt) +{ +#define DIVX311_CHUNK_HEAD_SIZE 13 + const unsigned char divx311_chunk_prefix[DIVX311_CHUNK_HEAD_SIZE] = { + 0x00, 0x00, 0x00, 0x01, 0xb6, 'D', 'I', 'V', 'X', '3', '.', '1', '1' + }; + if ((pkt->hdr != NULL) && (pkt->hdr->data != NULL)) { + free(pkt->hdr->data); + pkt->hdr->data = NULL; + } + + if (pkt->hdr == NULL) { + pkt->hdr = (hdr_buf_t*)malloc(sizeof(hdr_buf_t)); + if (!pkt->hdr) { + CLog::Log(LOGDEBUG, "[divx3_prefix] NOMEM!"); + return PLAYER_FAILED; + } + + pkt->hdr->data = NULL; + pkt->hdr->size = 0; + } + + pkt->hdr->data = (char*)malloc(DIVX311_CHUNK_HEAD_SIZE + 4); + if (pkt->hdr->data == NULL) { + CLog::Log(LOGDEBUG, "[divx3_prefix] NOMEM!"); + return PLAYER_FAILED; + } + + memcpy(pkt->hdr->data, divx311_chunk_prefix, DIVX311_CHUNK_HEAD_SIZE); + + pkt->hdr->data[DIVX311_CHUNK_HEAD_SIZE + 0] = (pkt->data_size >> 24) & 0xff; + pkt->hdr->data[DIVX311_CHUNK_HEAD_SIZE + 1] = (pkt->data_size >> 16) & 0xff; + pkt->hdr->data[DIVX311_CHUNK_HEAD_SIZE + 2] = (pkt->data_size >> 8) & 0xff; + pkt->hdr->data[DIVX311_CHUNK_HEAD_SIZE + 3] = pkt->data_size & 0xff; + + pkt->hdr->size = DIVX311_CHUNK_HEAD_SIZE + 4; + pkt->newflag = 1; + + return PLAYER_SUCCESS; +} + +int set_header_info(am_private_t *para) +{ + am_packet_t *pkt = ¶->am_pkt; + + //if (pkt->newflag) + { + //if (pkt->hdr) + // pkt->hdr->size = 0; + + if ((para->video_format == VFORMAT_H264) /*|| (am_private->video_format == VFORMAT_H264MVC)*/) + { + return h264_update_frame_header(pkt); + } + else if (para->video_format == VFORMAT_MPEG4) + { + if (para->video_codec_type == VIDEO_DEC_FORMAT_MPEG4_3) + { + return divx3_prefix(pkt); + } + else if (para->video_codec_type == VIDEO_DEC_FORMAT_H263) + { + return PLAYER_UNSUPPORT; + unsigned char *vld_buf; + int vld_len, vld_buf_size = para->video_width * para->video_height * 2; + + if (!pkt->data_size) { + return PLAYER_SUCCESS; + } + + if ((pkt->data[0] == 0) && (pkt->data[1] == 0) && (pkt->data[2] == 1) && (pkt->data[3] == 0xb6)) { + return PLAYER_SUCCESS; + } + + vld_buf = (unsigned char*)malloc(vld_buf_size); + if (!vld_buf) { + return PLAYER_NOMEM; + } + + if (para->flv_flag) { + vld_len = para->m_dll->h263vld(pkt->data, vld_buf, pkt->data_size, 1); + } else { + if (0 == para->h263_decodable) { + para->h263_decodable = para->m_dll->decodeble_h263(pkt->data); + if (0 == para->h263_decodable) { + CLog::Log(LOGDEBUG, "[%s]h263 unsupport video and audio, exit", __FUNCTION__); + return PLAYER_UNSUPPORT; + } + } + vld_len = para->m_dll->h263vld(pkt->data, vld_buf, pkt->data_size, 0); + } + + if (vld_len > 0) { + if (pkt->buf) { + free(pkt->buf); + } + pkt->buf = vld_buf; + pkt->buf_size = vld_buf_size; + pkt->data = pkt->buf; + pkt->data_size = vld_len; + } else { + free(vld_buf); + pkt->data_size = 0; + } + } + } else if (para->video_format == VFORMAT_VC1) { + if (para->video_codec_type == VIDEO_DEC_FORMAT_WMV3) { + unsigned i, check_sum = 0, data_len = 0; + + if ((pkt->hdr != NULL) && (pkt->hdr->data != NULL)) { + free(pkt->hdr->data); + pkt->hdr->data = NULL; + } + + if (pkt->hdr == NULL) { + pkt->hdr = (hdr_buf_t*)malloc(sizeof(hdr_buf_t)); + if (!pkt->hdr) { + return PLAYER_FAILED; + } + + pkt->hdr->data = NULL; + pkt->hdr->size = 0; + } + + if (pkt->avpkt.flags) { + pkt->hdr->data = (char*)malloc(para->extrasize + 26 + 22); + if (pkt->hdr->data == NULL) { + return PLAYER_FAILED; + } + + pkt->hdr->data[0] = 0; + pkt->hdr->data[1] = 0; + pkt->hdr->data[2] = 1; + pkt->hdr->data[3] = 0x10; + + data_len = para->extrasize + 4; + pkt->hdr->data[4] = 0; + pkt->hdr->data[5] = (data_len >> 16) & 0xff; + pkt->hdr->data[6] = 0x88; + pkt->hdr->data[7] = (data_len >> 8) & 0xff; + pkt->hdr->data[8] = data_len & 0xff; + pkt->hdr->data[9] = 0x88; + + pkt->hdr->data[10] = 0xff; + pkt->hdr->data[11] = 0xff; + pkt->hdr->data[12] = 0x88; + pkt->hdr->data[13] = 0xff; + pkt->hdr->data[14] = 0xff; + pkt->hdr->data[15] = 0x88; + + for (i = 4 ; i < 16 ; i++) { + check_sum += pkt->hdr->data[i]; + } + + pkt->hdr->data[16] = (check_sum >> 8) & 0xff; + pkt->hdr->data[17] = check_sum & 0xff; + pkt->hdr->data[18] = 0x88; + pkt->hdr->data[19] = (check_sum >> 8) & 0xff; + pkt->hdr->data[20] = check_sum & 0xff; + pkt->hdr->data[21] = 0x88; + + pkt->hdr->data[22] = (para->video_width >> 8) & 0xff; + pkt->hdr->data[23] = para->video_width & 0xff; + pkt->hdr->data[24] = (para->video_height >> 8) & 0xff; + pkt->hdr->data[25] = para->video_height & 0xff; + + memcpy(pkt->hdr->data + 26, para->extradata, para->extrasize); + + check_sum = 0; + data_len = para->extrasize + 26; + } else { + pkt->hdr->data = (char*)malloc(22); + if (pkt->hdr->data == NULL) { + return PLAYER_FAILED; + } + } + + pkt->hdr->data[data_len + 0] = 0; + pkt->hdr->data[data_len + 1] = 0; + pkt->hdr->data[data_len + 2] = 1; + pkt->hdr->data[data_len + 3] = 0xd; + + pkt->hdr->data[data_len + 4] = 0; + pkt->hdr->data[data_len + 5] = (pkt->data_size >> 16) & 0xff; + pkt->hdr->data[data_len + 6] = 0x88; + pkt->hdr->data[data_len + 7] = (pkt->data_size >> 8) & 0xff; + pkt->hdr->data[data_len + 8] = pkt->data_size & 0xff; + pkt->hdr->data[data_len + 9] = 0x88; + + pkt->hdr->data[data_len + 10] = 0xff; + pkt->hdr->data[data_len + 11] = 0xff; + pkt->hdr->data[data_len + 12] = 0x88; + pkt->hdr->data[data_len + 13] = 0xff; + pkt->hdr->data[data_len + 14] = 0xff; + pkt->hdr->data[data_len + 15] = 0x88; + + for (i = data_len + 4 ; i < data_len + 16 ; i++) { + check_sum += pkt->hdr->data[i]; + } + + pkt->hdr->data[data_len + 16] = (check_sum >> 8) & 0xff; + pkt->hdr->data[data_len + 17] = check_sum & 0xff; + pkt->hdr->data[data_len + 18] = 0x88; + pkt->hdr->data[data_len + 19] = (check_sum >> 8) & 0xff; + pkt->hdr->data[data_len + 20] = check_sum & 0xff; + pkt->hdr->data[data_len + 21] = 0x88; + + pkt->hdr->size = data_len + 22; + pkt->newflag = 1; + } else if (para->video_codec_type == VIDEO_DEC_FORMAT_WVC1) { + if ((pkt->hdr != NULL) && (pkt->hdr->data != NULL)) { + free(pkt->hdr->data); + pkt->hdr->data = NULL; + } + + if (pkt->hdr == NULL) { + pkt->hdr = (hdr_buf_t*)malloc(sizeof(hdr_buf_t)); + if (!pkt->hdr) { + CLog::Log(LOGDEBUG, "[wvc1_prefix] NOMEM!"); + return PLAYER_FAILED; + } + + pkt->hdr->data = NULL; + pkt->hdr->size = 0; + } + + pkt->hdr->data = (char*)malloc(4); + if (pkt->hdr->data == NULL) { + CLog::Log(LOGDEBUG, "[wvc1_prefix] NOMEM!"); + return PLAYER_FAILED; + } + + pkt->hdr->data[0] = 0; + pkt->hdr->data[1] = 0; + pkt->hdr->data[2] = 1; + pkt->hdr->data[3] = 0xd; + pkt->hdr->size = 4; + pkt->newflag = 1; + } + } + } + return PLAYER_SUCCESS; +} + +/*************************************************************************/ +CAMLCodec::CAMLCodec() : CThread("CAMLCodec") +{ + m_opened = false; + am_private = new am_private_t; + memset(am_private, 0, sizeof(am_private_t)); + m_dll = new DllLibAmCodec; + m_dll->Load(); + am_private->m_dll = m_dll; +} + + +CAMLCodec::~CAMLCodec() +{ + StopThread(); + delete am_private; + am_private = NULL; + delete m_dll, m_dll = NULL; +} + +bool CAMLCodec::OpenDecoder(CDVDStreamInfo &hints) +{ + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder"); + m_speed = DVD_PLAYSPEED_NORMAL; + m_1st_pts = 0; + m_cur_pts = 0; + m_cur_pictcnt = 0; + m_old_pictcnt = 0; + m_dst_rect.SetRect(0, 0, 0, 0); + m_zoom = -1; + m_contrast = -1; + m_brightness = -1; + m_vbufsize = 500000 * 2; + + ShowMainVideo(false); + + am_packet_init(&am_private->am_pkt); + // default stream type + am_private->stream_type = AM_STREAM_ES; + // handle hints. + am_private->video_width = hints.width; + am_private->video_height = hints.height; + am_private->video_codec_id = hints.codec; + am_private->video_codec_tag = hints.codec_tag; + am_private->video_pid = hints.pid; + + // handle video ratio + AVRational video_ratio = m_dll->av_d2q(1, SHRT_MAX); + //if (!hints.forced_aspect) + // video_ratio = m_dll->av_d2q(hints.aspect, SHRT_MAX); + am_private->video_ratio = ((int32_t)video_ratio.num << 16) | video_ratio.den; + am_private->video_ratio64 = ((int64_t)video_ratio.num << 32) | video_ratio.den; + + // handle video rate + if (hints.rfpsrate > 0 && hints.rfpsscale != 0) + { + // check ffmpeg r_frame_rate 1st + am_private->video_rate = 0.5 + (float)UNIT_FREQ * hints.rfpsscale / hints.rfpsrate; + } + else if (hints.fpsrate > 0 && hints.fpsscale != 0) + { + // then ffmpeg avg_frame_rate next + am_private->video_rate = 0.5 + (float)UNIT_FREQ * hints.fpsscale / hints.fpsrate; + } + else + { + // stupid PVR hacks because it does not fill in all of hints. + if (hints.codec == CODEC_ID_MPEG2VIDEO) + { + am_private->video_rate = 0.5 + (float)UNIT_FREQ * 1001 / 30000; + if (hints.width == 1280) + am_private->video_rate = 0.5 + (float)UNIT_FREQ * 1001 / 60000;; + } + } + // check for 1920x1080, interlaced, 25 fps + // incorrectly reported as 50 fps (yes, video_rate == 1920) + if (hints.width == 1920 && am_private->video_rate == 1920) + { + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder video_rate exception"); + am_private->video_rate = 0.5 + (float)UNIT_FREQ * 1001 / 25000; + } + + // check for SD h264 content incorrectly reported as 60 fsp + // mp4/avi containers :( + if (hints.codec == CODEC_ID_H264 && hints.width <= 720 && am_private->video_rate == 1602) + { + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder video_rate exception"); + am_private->video_rate = 0.5 + (float)UNIT_FREQ * 1001 / 24000; + } + + // check for SD h264 content incorrectly reported as some form of 30 fsp + // mp4/avi containers :( + if (hints.codec == CODEC_ID_H264 && hints.width <= 720) + { + if (am_private->video_rate >= 3200 && am_private->video_rate <= 3210) + { + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder video_rate exception"); + am_private->video_rate = 0.5 + (float)UNIT_FREQ * 1001 / 24000; + } + } + + // handle orientation + am_private->video_rotation_degree = 0; + if (hints.orientation == 90) + am_private->video_rotation_degree = 1; + else if (hints.orientation == 180) + am_private->video_rotation_degree = 2; + else if (hints.orientation == 270) + am_private->video_rotation_degree = 3; + // handle extradata + am_private->video_format = codecid_to_vformat(hints.codec); + switch (am_private->video_format) + { + default: + am_private->extrasize = hints.extrasize; + am_private->extradata = (uint8_t*)malloc(hints.extrasize); + memcpy(am_private->extradata, hints.extradata, hints.extrasize); + break; + case VFORMAT_REAL: + case VFORMAT_MPEG12: + break; + } + + if (am_private->stream_type == AM_STREAM_ES && am_private->video_codec_tag != 0) + am_private->video_codec_type = codec_tag_to_vdec_type(am_private->video_codec_tag); + else + am_private->video_codec_type = codec_tag_to_vdec_type(am_private->video_codec_id); + + am_private->flv_flag = 0; + if (am_private->video_codec_id == CODEC_ID_FLV1) + { + am_private->video_codec_tag = CODEC_TAG_F263; + am_private->flv_flag = 1; + } + + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder hints.fpsrate(%d), hints.fpsscale(%d), hints.rfpsrate(%d), hints.rfpsscale(%d), video_rate(%d)", + hints.fpsrate, hints.fpsscale, hints.rfpsrate, hints.rfpsscale, am_private->video_rate); + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder hints.aspect(%f), video_ratio.num(%d), video_ratio.den(%d)", + hints.aspect, video_ratio.num, video_ratio.den); + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder hints.orientation(%d), hints.forced_aspect(%d)", + hints.orientation, hints.forced_aspect); + + // default video codec params + am_private->vcodec.has_video = 1; + am_private->vcodec.noblock = 0; + am_private->vcodec.video_pid = am_private->video_pid; + am_private->vcodec.video_type = am_private->video_format; + am_private->vcodec.stream_type = STREAM_TYPE_ES_VIDEO; + am_private->vcodec.am_sysinfo.format = am_private->video_codec_type; + am_private->vcodec.am_sysinfo.width = am_private->video_width; + am_private->vcodec.am_sysinfo.height = am_private->video_height; + am_private->vcodec.am_sysinfo.rate = am_private->video_rate; + am_private->vcodec.am_sysinfo.ratio = am_private->video_ratio; + am_private->vcodec.am_sysinfo.ratio64 = am_private->video_ratio64; + am_private->vcodec.am_sysinfo.param = NULL; + + switch(am_private->video_format) + { + default: + break; + case VFORMAT_MPEG4: + am_private->vcodec.am_sysinfo.param = (void*)EXTERNAL_PTS; + break; + case VFORMAT_H264: + case VFORMAT_H264MVC: + am_private->vcodec.am_sysinfo.format = VIDEO_DEC_FORMAT_H264; + am_private->vcodec.am_sysinfo.param = (void*)EXTERNAL_PTS; + // h264 in an avi file + if (hints.ptsinvalid) + am_private->vcodec.am_sysinfo.param = (void*)(EXTERNAL_PTS | SYNC_OUTSIDE); + break; + case VFORMAT_REAL: + am_private->stream_type = AM_STREAM_RM; + am_private->vcodec.stream_type = STREAM_TYPE_RM; + am_private->vcodec.am_sysinfo.ratio = 0x100; + am_private->vcodec.am_sysinfo.ratio64 = 0; + { + static unsigned short tbl[9] = {0}; + if (VIDEO_DEC_FORMAT_REAL_8 == am_private->video_codec_type) + { + am_private->vcodec.am_sysinfo.extra = am_private->extradata[1] & 7; + tbl[0] = (((am_private->vcodec.am_sysinfo.width >> 2) - 1) << 8) + | (((am_private->vcodec.am_sysinfo.height >> 2) - 1) & 0xff); + unsigned int j; + for (unsigned int i = 1; i <= am_private->vcodec.am_sysinfo.extra; i++) + { + j = 2 * (i - 1); + tbl[i] = ((am_private->extradata[8 + j] - 1) << 8) | ((am_private->extradata[8 + j + 1] - 1) & 0xff); + } + } + am_private->vcodec.am_sysinfo.param = &tbl; + } + break; + case VFORMAT_VC1: + // vc1 in an avi file + if (hints.ptsinvalid) + am_private->vcodec.am_sysinfo.param = (void*)EXTERNAL_PTS; + break; + } + am_private->vcodec.am_sysinfo.param = (void *)((unsigned int)am_private->vcodec.am_sysinfo.param | (am_private->video_rotation_degree << 16)); + + int ret = m_dll->codec_init(&am_private->vcodec); + if (ret != CODEC_ERROR_NONE) + { + CLog::Log(LOGDEBUG, "CAMLCodec::OpenDecoder codec init failed, ret=0x%x", -ret); + return false; + } + // make sure we are not stuck in pause (amcodec bug) + m_dll->codec_resume(&am_private->vcodec); + m_dll->codec_set_cntl_mode(&am_private->vcodec, TRICKMODE_NONE); + + m_dll->codec_set_cntl_avthresh(&am_private->vcodec, AV_SYNC_THRESH); + m_dll->codec_set_cntl_syncthresh(&am_private->vcodec, 0); + // disable tsync, we are playing video disconnected from audio. + aml_set_sysfs_int("/sys/class/tsync/enable", 0); + + am_private->am_pkt.codec = &am_private->vcodec; + pre_header_feeding(am_private, &am_private->am_pkt); + + Create(); + + g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack); + g_renderManager.RegisterRenderFeaturesCallBack((const void*)this, RenderFeaturesCallBack); + + m_opened = true; + + return true; +} + +void CAMLCodec::CloseDecoder() +{ + CLog::Log(LOGDEBUG, "CAMLCodec::CloseDecoder"); + StopThread(); + + g_renderManager.RegisterRenderUpdateCallBack((const void*)NULL, NULL); + g_renderManager.RegisterRenderFeaturesCallBack((const void*)NULL, NULL); + + m_dll->codec_close(&am_private->vcodec); + m_opened = false; + + am_packet_release(&am_private->am_pkt); + free(am_private->extradata); + am_private->extradata = NULL; + // return tsync to default so external apps work + aml_set_sysfs_int("/sys/class/tsync/enable", 1); + + ShowMainVideo(false); + +} + +void CAMLCodec::Reset() +{ + CLog::Log(LOGDEBUG, "CAMLCodec::Reset"); + + if (!m_opened) + return; + + // set the system blackout_policy to leave the last frame showing + int blackout_policy = aml_get_sysfs_int("/sys/class/video/blackout_policy"); + aml_set_sysfs_int("/sys/class/video/blackout_policy", 0); + + // restore the speed (some amcodec versions require this) + if (m_speed != DVD_PLAYSPEED_NORMAL) + { + m_dll->codec_resume(&am_private->vcodec); + m_dll->codec_set_cntl_mode(&am_private->vcodec, TRICKMODE_NONE); + } + // reset the decoder + m_dll->codec_reset(&am_private->vcodec); + + // re-init our am_pkt + am_packet_release(&am_private->am_pkt); + am_packet_init(&am_private->am_pkt); + am_private->am_pkt.codec = &am_private->vcodec; + pre_header_feeding(am_private, &am_private->am_pkt); + + // reset some interal vars + m_speed = DVD_PLAYSPEED_NORMAL; + m_1st_pts = 0; + m_cur_pts = 0; + m_cur_pictcnt = 0; + m_old_pictcnt = 0; + + // restore the saved system blackout_policy value + aml_set_sysfs_int("/sys/class/video/blackout_policy", blackout_policy); +} + +int CAMLCodec::Decode(unsigned char *pData, size_t size, double dts, double pts) +{ + if (!m_opened) + return VC_BUFFER; + + // grr, m_RenderUpdateCallBackFn in g_renderManager is NULL'ed during + // g_renderManager.Configure call by player, which happens after the codec + // OpenDecoder call. So we need to restore it but it does not seem to stick :) + g_renderManager.RegisterRenderUpdateCallBack((const void*)this, RenderUpdateCallBack); + + if (pData) + { + am_private->am_pkt.data = pData; + am_private->am_pkt.data_size = size; + am_private->am_pkt.newflag = 1; + am_private->am_pkt.isvalid = 1; + am_private->am_pkt.avduration = 0; + + if (pts == DVD_NOPTS_VALUE) + am_private->am_pkt.avpts = AV_NOPTS_VALUE; + else + am_private->am_pkt.avpts = 0.5 + (pts * PTS_FREQ) / DVD_TIME_BASE; + + if (dts == DVD_NOPTS_VALUE) + am_private->am_pkt.avdts = AV_NOPTS_VALUE; + else + am_private->am_pkt.avdts = 0.5 + (dts * PTS_FREQ) / DVD_TIME_BASE; + + //CLog::Log(LOGDEBUG, "CAMLCodec::Decode: dts(%f), pts(%f), avdts(%llx), avpts(%llx)", + // dts, pts, am_private->am_pkt.avdts, am_private->am_pkt.avpts); + + set_header_info(am_private); + write_av_packet(am_private, &am_private->am_pkt); + + // if we seek, then GetTimeSize is wrong as + // reports lastpts - cur_pts and hw decoder has + // not started outputing new pts values yet. + // so we grab the 1st pts sent into driver and + // use that to calc GetTimeSize. + if (m_1st_pts == 0) + m_1st_pts = am_private->am_pkt.lastpts; + } + + // if we have still frames, demux size will be small + // and we need to pre-buffer more. + double target_timesize = 1.0; + if (size < 20) + target_timesize = 2.0; + + // keep hw buffered demux above 1 second + if (GetTimeSize() < target_timesize && m_speed == DVD_PLAYSPEED_NORMAL) + { + return VC_BUFFER; + } + + // wait until we get a new frame or 100ms, + if (m_old_pictcnt == m_cur_pictcnt) + m_ready_event.WaitMSec(100); + + // we must return VC_BUFFER or VC_PICTURE, + // default to VC_BUFFER. + int rtn = VC_BUFFER; + if (m_old_pictcnt != m_cur_pictcnt) + { + m_old_pictcnt = m_cur_pictcnt; + rtn = VC_PICTURE; + // we got a new pict, try and keep hw buffered demux above 2 seconds. + // this, combined with the above 1 second check, keeps hw buffered demux between 1 and 2 seconds. + // we also check to make sure we keep from filling hw buffer. + if (GetTimeSize() < 2.0 && GetDataSize() < m_vbufsize/3) + rtn |= VC_BUFFER; + } +/* + CLog::Log(LOGDEBUG, "CAMLCodec::Decode: " + "rtn(%d), m_cur_pictcnt(%lld), m_cur_pts(%f), lastpts(%f), GetTimeSize(%f), GetDataSize(%d)", + rtn, m_cur_pictcnt, (float)m_cur_pts/PTS_FREQ, (float)am_private->am_pkt.lastpts/PTS_FREQ, GetTimeSize(), GetDataSize()); +*/ + return rtn; +} + +bool CAMLCodec::GetPicture(DVDVideoPicture *pDvdVideoPicture) +{ + if (!m_opened) + return false; + + pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED; + pDvdVideoPicture->format = RENDER_FMT_BYPASS; + pDvdVideoPicture->iDuration = (double)(am_private->video_rate * DVD_TIME_BASE) / UNIT_FREQ; + + pDvdVideoPicture->dts = DVD_NOPTS_VALUE; + pDvdVideoPicture->pts = GetPlayerPtsSeconds() * (double)DVD_TIME_BASE; + // video pts cannot be late or dvdplayer goes nuts, + // so run it one frame ahead + pDvdVideoPicture->pts += 1 * pDvdVideoPicture->iDuration; + + return true; +} + +void CAMLCodec::SetSpeed(int speed) +{ + CLog::Log(LOGDEBUG, "CAMLCodec::SetSpeed, speed(%d)", speed); + + if (!m_opened) + return; + + if (m_speed != speed) + { + switch(speed) + { + case DVD_PLAYSPEED_PAUSE: + m_dll->codec_pause(&am_private->vcodec); + m_dll->codec_set_cntl_mode(&am_private->vcodec, TRICKMODE_NONE); + break; + case DVD_PLAYSPEED_NORMAL: + m_dll->codec_resume(&am_private->vcodec); + m_dll->codec_set_cntl_mode(&am_private->vcodec, TRICKMODE_NONE); + break; + default: + Reset(); + m_dll->codec_resume(&am_private->vcodec); + m_dll->codec_set_cntl_mode(&am_private->vcodec, TRICKMODE_I); + //m_dll->codec_set_cntl_mode(&am_private->vcodec, TRICKMODE_FFFB); + break; + } + m_speed = speed; + } +} + +int CAMLCodec::GetDataSize() +{ + if (!m_opened) + return 0; + + struct buf_status vbuf ={0}; + if (m_dll->codec_get_vbuf_state(&am_private->vcodec, &vbuf) >= 0) + m_vbufsize = vbuf.size; + + return vbuf.data_len; +} + +double CAMLCodec::GetTimeSize() +{ + if (!m_opened) + return 0; + + // if m_cur_pts is zero, hw decoder was not started yet + // so we use the pts of the 1st demux packet that was send + // to hw decoder to calc timesize. + if (m_cur_pts == 0) + m_timesize = (double)(am_private->am_pkt.lastpts - m_1st_pts) / PTS_FREQ; + else + m_timesize = (double)(am_private->am_pkt.lastpts - m_cur_pts) / PTS_FREQ; + + // lie to DVDPlayer, it is hardcoded to a max of 8 seconds, + // if you buffer more than 8 seconds, it goes nuts. + double timesize = m_timesize; + if (timesize < 0.0) + timesize = 0.0; + else if (timesize > 7.0) + timesize = 7.0; + + return timesize; +} + +void CAMLCodec::Process() +{ + CLog::Log(LOGDEBUG, "CAMLCodec::Process Started"); + + // bump our priority to be level with SoftAE + SetPriority(THREAD_PRIORITY_ABOVE_NORMAL); + while (!m_bStop) + { + int64_t pts_video = 0; + if (am_private->am_pkt.lastpts > 0) + { + // this is a blocking poll that returns every vsync. + // since we are running at a higher priority, make sure + // we sleep if the call fails or does a timeout. + if (m_dll->codec_poll_cntl(&am_private->vcodec) < 0) + { + CLog::Log(LOGDEBUG, "CAMLCodec::Process: codec_poll_cntl failed"); + Sleep(10); + } + + pts_video = get_pts_video(); + if (m_cur_pts != pts_video) + { + //CLog::Log(LOGDEBUG, "CAMLCodec::Process: pts_video(%lld), pts_video/PTS_FREQ(%f), duration(%f)", + // pts_video, (double)pts_video/PTS_FREQ, 1.0/((double)(pts_video - m_cur_pts)/PTS_FREQ)); + // other threads look at these, do them first + m_cur_pts = pts_video; + m_cur_pictcnt++; + m_ready_event.Set(); + + double app_pts = GetPlayerPtsSeconds(); + // add in audio delay/display latency contribution + double offset = g_renderManager.GetDisplayLatency() - g_settings.m_currentVideoSettings.m_AudioDelay; + // correct video pts by user set delay and rendering delay + app_pts += offset; + + double error = app_pts - (double)pts_video/PTS_FREQ; + double abs_error = fabs(error); + if (abs_error > 0.040) + { + //CLog::Log(LOGDEBUG, "CAMLCodec::Process pts diff = %f", error); + if (abs_error > 0.125) + { + // big error so try to reset pts_pcrscr + SetVideoPtsSeconds(app_pts); + } + else + { + // small error so try to avoid a frame jump + SetVideoPtsSeconds((double)pts_video/PTS_FREQ + error/4); + } + } + } + } + else + { + Sleep(10); + } + } + SetPriority(THREAD_PRIORITY_NORMAL); + CLog::Log(LOGDEBUG, "CAMLCodec::Process Stopped"); +} + +double CAMLCodec::GetPlayerPtsSeconds() +{ + double clock_pts = 0.0; + CDVDClock *playerclock = CDVDClock::GetMasterClock(); + if (playerclock) + clock_pts = playerclock->GetClock() / DVD_TIME_BASE; + + return clock_pts; +} + +void CAMLCodec::SetVideoPtsSeconds(const double pts) +{ + //CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoPtsSeconds: pts(%f)", pts); + if (pts >= 0.0) + set_pts_pcrscr((int64_t)(pts * PTS_FREQ)); +} + +void CAMLCodec::ShowMainVideo(const bool show) +{ + static int saved_disable_video = -1; + + int disable_video = show ? 0:1; + if (saved_disable_video == disable_video) + return; + + aml_set_sysfs_int("/sys/class/video/disable_video", disable_video); + saved_disable_video = disable_video; +} + +void CAMLCodec::SetVideoZoom(const float zoom) +{ + // input zoom range is 0.5 to 2.0 with a default of 1.0. + // output zoom range is 2 to 300 with default of 100. + // we limit that to a range of 50 to 200 with default of 100. + aml_set_sysfs_int("/sys/class/video/zoom", (int)(100 * zoom)); +} + +void CAMLCodec::SetVideoContrast(const int contrast) +{ + // input contrast range is 0 to 100 with default of 50. + // output contrast range is -255 to 255 with default of 0. + int aml_contrast = (255 * (contrast - 50)) / 50; + aml_set_sysfs_int("/sys/class/video/contrast", aml_contrast); +} +void CAMLCodec::SetVideoBrightness(const int brightness) +{ + // input brightness range is 0 to 100 with default of 50. + // output brightness range is -127 to 127 with default of 0. + int aml_brightness = (127 * (brightness - 50)) / 50; + aml_set_sysfs_int("/sys/class/video/brightness", aml_brightness); +} +void CAMLCodec::SetVideoSaturation(const int saturation) +{ + // output saturation range is -127 to 127 with default of 127. + aml_set_sysfs_int("/sys/class/video/saturation", saturation); +} + +void CAMLCodec::GetRenderFeatures(Features &renderFeatures) +{ + renderFeatures.push_back(RENDERFEATURE_ZOOM); + renderFeatures.push_back(RENDERFEATURE_CONTRAST); + renderFeatures.push_back(RENDERFEATURE_BRIGHTNESS); + renderFeatures.push_back(RENDERFEATURE_STRETCH); + renderFeatures.push_back(RENDERFEATURE_PIXEL_RATIO); + return; +} + +void CAMLCodec::RenderFeaturesCallBack(const void *ctx, Features &renderFeatures) +{ + CAMLCodec *codec = (CAMLCodec*)ctx; + if (codec) + codec->GetRenderFeatures(renderFeatures); +} + +void CAMLCodec::SetVideoRect(const CRect &SrcRect, const CRect &DestRect) +{ + // this routine gets called every video frame + // and is in the context of the renderer thread so + // do not do anything stupid here. + + // video zoom adjustment. + float zoom = g_settings.m_currentVideoSettings.m_CustomZoomAmount; + if ((int)(zoom * 1000) != (int)(m_zoom * 1000)) + { + m_zoom = zoom; + } + // video contrast adjustment. + int contrast = g_settings.m_currentVideoSettings.m_Contrast; + if (contrast != m_contrast) + { + SetVideoContrast(contrast); + m_contrast = contrast; + } + // video brightness adjustment. + int brightness = g_settings.m_currentVideoSettings.m_Brightness; + if (brightness != m_brightness) + { + SetVideoBrightness(brightness); + m_brightness = brightness; + } + + // check if destination rect or video view mode has changed + if ((m_dst_rect != DestRect) || (m_view_mode != g_settings.m_currentVideoSettings.m_ViewMode)) + { + m_dst_rect = DestRect; + m_view_mode = g_settings.m_currentVideoSettings.m_ViewMode; + } + else + { + // mainvideo 'should' be showing already if we get here, make sure. + ShowMainVideo(true); + return; + } + + CRect gui, display, dst_rect; + gui = g_graphicsContext.GetViewWindow(); + // when display is at 1080p, we have freescale enabled + // and that scales all layers into 1080p display including video, + // so we have to setup video axis for 720p instead of 1080p... Boooo. + display = g_graphicsContext.GetViewWindow(); + //RESOLUTION res = g_graphicsContext.GetVideoResolution(); + //display.SetRect(0, 0, g_settings.m_ResInfo[res].iScreenWidth, g_settings.m_ResInfo[res].iScreenHeight); + dst_rect = m_dst_rect; + if (gui != display) + { + float xscale = display.Width() / gui.Width(); + float yscale = display.Height() / gui.Height(); + dst_rect.x1 *= xscale; + dst_rect.x2 *= xscale; + dst_rect.y1 *= yscale; + dst_rect.y2 *= yscale; + } + + ShowMainVideo(false); + + // goofy 0/1 based difference in aml axis coordinates. + // fix them. + dst_rect.x2--; + dst_rect.y2--; + + char video_axis[256] = {0}; + sprintf(video_axis, "%d %d %d %d", (int)dst_rect.x1, (int)dst_rect.y1, (int)dst_rect.x2, (int)dst_rect.y2); + aml_set_sysfs_str("/sys/class/video/axis", video_axis); + // make sure we are in 'full stretch' so we can stretch + aml_set_sysfs_int("/sys/class/video/screen_mode", 1); + +/* + CStdString rectangle; + rectangle.Format("%i,%i,%i,%i", + (int)dst_rect.x1, (int)dst_rect.y1, + (int)dst_rect.Width(), (int)dst_rect.Height()); + CLog::Log(LOGDEBUG, "CAMLCodec::SetVideoRect:dst_rect(%s)", rectangle.c_str()); +*/ + + // we only get called once gui has changed to something + // that would show video playback, so show it. + ShowMainVideo(true); +} + +void CAMLCodec::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect) +{ + CAMLCodec *codec = (CAMLCodec*)ctx; + codec->SetVideoRect(SrcRect, DestRect); +} diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/AMLCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/AMLCodec.h new file mode 100644 index 0000000000000..9da94e9ec3ea9 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/AMLCodec.h @@ -0,0 +1,83 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DVDVideoCodec.h" +#include "cores/dvdplayer/DVDStreamInfo.h" +#include "cores/VideoRenderers/RenderFeatures.h" +#include "guilib/Geometry.h" +#include "threads/Thread.h" + +typedef struct am_private_t am_private_t; + +class DllLibAmCodec; + +class CAMLCodec : public CThread +{ +public: + CAMLCodec(); + virtual ~CAMLCodec(); + + bool OpenDecoder(CDVDStreamInfo &hints); + void CloseDecoder(); + void Reset(); + + int Decode(unsigned char *pData, size_t size, double dts, double pts); + + bool GetPicture(DVDVideoPicture* pDvdVideoPicture); + void SetSpeed(int speed); + int GetDataSize(); + double GetTimeSize(); + +protected: + virtual void Process(); + +private: + double GetPlayerPtsSeconds(); + void SetVideoPtsSeconds(double pts); + void ShowMainVideo(const bool show); + void SetVideoZoom(const float zoom); + void SetVideoContrast(const int contrast); + void SetVideoBrightness(const int brightness); + void SetVideoSaturation(const int saturation); + void GetRenderFeatures(Features &renderFeatures); + static void RenderFeaturesCallBack(const void *ctx, Features &renderFeatures); + void SetVideoRect(const CRect &SrcRect, const CRect &DestRect); + static void RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect); + + DllLibAmCodec *m_dll; + bool m_opened; + am_private_t *am_private; + volatile int m_speed; + volatile int64_t m_1st_pts; + volatile int64_t m_cur_pts; + volatile int64_t m_cur_pictcnt; + volatile int64_t m_old_pictcnt; + volatile double m_timesize; + volatile int64_t m_vbufsize; + CEvent m_ready_event; + + CRect m_dst_rect; + int m_view_mode; + float m_zoom; + int m_contrast; + int m_brightness; +}; diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h index 1d8bad38525cd..104cbbf58cffe 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h @@ -191,6 +191,12 @@ class CDVDVideoCodec */ virtual void SetDropState(bool bDrop) = 0; + /* + * will be called by video player indicating the playback speed. see DVD_PLAYSPEED_NORMAL, + * DVD_PLAYSPEED_PAUSE and friends. + */ + virtual void SetSpeed(int iSpeed) {}; + /* * returns the number of demuxer bytes in any internal buffers */ diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp new file mode 100644 index 0000000000000..2c8fb03e0d0bc --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include + +#include "DVDVideoCodecAmlogic.h" +#include "DVDClock.h" +#include "DVDStreamInfo.h" +#include "AMLCodec.h" +#include "video/VideoThumbLoader.h" +#include "utils/log.h" + +#define __MODULE_NAME__ "DVDVideoCodecAmlogic" + +typedef struct pts_queue { + double dts; + double pts; + double sort_time; + struct pts_queue *nextpts; +} pts_queue; + +CDVDVideoCodecAmlogic::CDVDVideoCodecAmlogic() : + m_Codec(NULL), + m_pFormatName("amcodec"), + m_last_pts(0.0), + m_pts_queue(NULL), + m_queue_depth(0), + m_framerate(0.0), + m_video_rate(0) +{ + pthread_mutex_init(&m_queue_mutex, NULL); +} + +CDVDVideoCodecAmlogic::~CDVDVideoCodecAmlogic() +{ + Dispose(); + pthread_mutex_destroy(&m_queue_mutex); +} + +bool CDVDVideoCodecAmlogic::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) +{ + switch(hints.codec) + { + case CODEC_ID_MJPEG: + m_pFormatName = "am-mjpeg"; + break; + case CODEC_ID_MPEG1VIDEO: + case CODEC_ID_MPEG2VIDEO: + case CODEC_ID_MPEG2VIDEO_XVMC: + m_pFormatName = "am-mpeg2"; + break; + case CODEC_ID_H264: + m_pFormatName = "am-h264"; + break; + case CODEC_ID_MPEG4: + case CODEC_ID_MSMPEG4V2: + case CODEC_ID_MSMPEG4V3: + m_pFormatName = "am-mpeg4"; + break; + case CODEC_ID_H263: + case CODEC_ID_H263P: + case CODEC_ID_H263I: + m_pFormatName = "am-h263"; + break; + case CODEC_ID_FLV1: + m_pFormatName = "am-flv1"; + break; + case CODEC_ID_RV10: + case CODEC_ID_RV20: + case CODEC_ID_RV30: + case CODEC_ID_RV40: + m_pFormatName = "am-rv"; + break; + case CODEC_ID_VC1: + m_pFormatName = "am-vc1"; + break; + case CODEC_ID_WMV3: + m_pFormatName = "am-wmv3"; + break; + case CODEC_ID_AVS: + case CODEC_ID_CAVS: + m_pFormatName = "am-avs"; + break; + default: + CLog::Log(LOGDEBUG, "%s: Unknown hints.codec(%d", __MODULE_NAME__, hints.codec); + return false; + break; + } + + m_hints = hints; + m_Codec = new CAMLCodec(); + if (!m_Codec) + { + CLog::Log(LOGERROR, "%s: Failed to create Amlogic Codec", __MODULE_NAME__); + return false; + } + m_opened = false; + + // allocate a dummy DVDVideoPicture buffer. + // first make sure all properties are reset. + memset(&m_videobuffer, 0, sizeof(DVDVideoPicture)); + + m_videobuffer.dts = DVD_NOPTS_VALUE; + m_videobuffer.pts = DVD_NOPTS_VALUE; + m_videobuffer.format = RENDER_FMT_BYPASS; + m_videobuffer.color_range = 0; + m_videobuffer.color_matrix = 4; + m_videobuffer.iFlags = DVP_FLAG_ALLOCATED; + m_videobuffer.iWidth = hints.width; + m_videobuffer.iHeight = hints.height; + + m_videobuffer.iDisplayWidth = m_videobuffer.iWidth; + m_videobuffer.iDisplayHeight = m_videobuffer.iHeight; + if (hints.aspect > 0.0 && !hints.forced_aspect) + { + m_videobuffer.iDisplayWidth = ((int)lrint(m_videobuffer.iHeight * hints.aspect)) & -3; + if (m_videobuffer.iDisplayWidth > m_videobuffer.iWidth) + { + m_videobuffer.iDisplayWidth = m_videobuffer.iWidth; + m_videobuffer.iDisplayHeight = ((int)lrint(m_videobuffer.iWidth / hints.aspect)) & -3; + } + } + + CJobManager::GetInstance().Pause(kJobTypeMediaFlags); + if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0) + { + int timeout_ms = 5000; + while (timeout_ms > 0) + { + if (CJobManager::GetInstance().IsProcessing(kJobTypeMediaFlags) > 0) + { + Sleep(100); + timeout_ms -= 100; + } + else + break; + } + } + + CLog::Log(LOGINFO, "%s: Opened Amlogic Codec", __MODULE_NAME__); + return true; +} + +void CDVDVideoCodecAmlogic::Dispose(void) +{ + if (m_Codec) + m_Codec->CloseDecoder(), m_Codec = NULL; + if (m_videobuffer.iFlags) + m_videobuffer.iFlags = 0; + while (m_queue_depth) + PtsQueuePop(); + + // let thumbgen jobs resume. + CJobManager::GetInstance().UnPause(kJobTypeMediaFlags); +} + +int CDVDVideoCodecAmlogic::Decode(BYTE *pData, int iSize, double dts, double pts) +{ + // Handle Input, add demuxer packet to input queue, we must accept it or + // it will be discarded as DVDPlayerVideo has no concept of "try again". + + if (!m_opened) + { + if (m_Codec && !m_Codec->OpenDecoder(m_hints)) + CLog::Log(LOGERROR, "%s: Failed to open Amlogic Codec", __MODULE_NAME__); + m_opened = true; + } + + if (m_hints.ptsinvalid) + pts = DVD_NOPTS_VALUE; + + if (pData) + FrameRateTracking(dts, pts); + + return m_Codec->Decode(pData, iSize, dts, pts); +} + +void CDVDVideoCodecAmlogic::Reset(void) +{ + while (m_queue_depth) + PtsQueuePop(); + + m_Codec->Reset(); +} + +bool CDVDVideoCodecAmlogic::GetPicture(DVDVideoPicture* pDvdVideoPicture) +{ + if (m_Codec) + m_Codec->GetPicture(&m_videobuffer); + *pDvdVideoPicture = m_videobuffer; + + return true; +} + +void CDVDVideoCodecAmlogic::SetDropState(bool bDrop) +{ +} + +void CDVDVideoCodecAmlogic::SetSpeed(int iSpeed) +{ + if (m_Codec) + m_Codec->SetSpeed(iSpeed); +} + +int CDVDVideoCodecAmlogic::GetDataSize(void) +{ + if (m_Codec) + return m_Codec->GetDataSize(); + else + return 0; +} + +double CDVDVideoCodecAmlogic::GetTimeSize(void) +{ + if (m_Codec) + return m_Codec->GetTimeSize(); + else + return 0; +} + +void CDVDVideoCodecAmlogic::PtsQueuePop(void) +{ + if (!m_pts_queue || m_queue_depth == 0) + return; + + pthread_mutex_lock(&m_queue_mutex); + // pop the top frame off the queue + pts_queue *top_pts = m_pts_queue; + m_pts_queue = top_pts->nextpts; + m_queue_depth--; + pthread_mutex_unlock(&m_queue_mutex); + + // and release it + free(top_pts); +} + +void CDVDVideoCodecAmlogic::PtsQueuePush(double dts, double pts) +{ + pts_queue *newpts = (pts_queue*)calloc(sizeof(pts_queue), 1); + newpts->dts = dts; + newpts->pts = pts; + // if both dts or pts are good we use those, else use decoder insert time for frame sort + if ((newpts->pts != DVD_NOPTS_VALUE) || (newpts->dts != DVD_NOPTS_VALUE)) + { + // if pts is borked (stupid avi's), use dts for frame sort + if (newpts->pts == DVD_NOPTS_VALUE) + newpts->sort_time = newpts->dts; + else + newpts->sort_time = newpts->pts; + } + + pthread_mutex_lock(&m_queue_mutex); + pts_queue *queueWalker = m_pts_queue; + if (!queueWalker || (newpts->sort_time < queueWalker->sort_time)) + { + // we have an empty queue, or this frame earlier than the current queue head. + newpts->nextpts = queueWalker; + m_pts_queue = newpts; + } else { + // walk the queue and insert this frame where it belongs in display order. + bool ptrInserted = false; + pts_queue *nextpts = NULL; + // + while (!ptrInserted) + { + nextpts = queueWalker->nextpts; + if (!nextpts || (newpts->sort_time < nextpts->sort_time)) + { + // if the next frame is the tail of the queue, or our new frame is earlier. + newpts->nextpts = nextpts; + queueWalker->nextpts = newpts; + ptrInserted = true; + } + queueWalker = nextpts; + } + } + m_queue_depth++; + pthread_mutex_unlock(&m_queue_mutex); +} + +void CDVDVideoCodecAmlogic::FrameRateTracking(double dts, double pts) +{ + PtsQueuePush(dts, pts); + + // we might have out-of-order pts, + // so make sure we wait for at least 16 values in sorted queue. + if (m_queue_depth > 16) + { + pthread_mutex_lock(&m_queue_mutex); + + float cur_pts = m_pts_queue->pts; + if (cur_pts == DVD_NOPTS_VALUE) + cur_pts = m_pts_queue->dts; + + pthread_mutex_unlock(&m_queue_mutex); + + float duration = cur_pts - m_last_pts; + m_last_pts = cur_pts; + + // clamp duration to sensible range, + // 66 fsp to 20 fsp + if (duration >= 15000.0 && duration <= 50000.0) + { + double framerate; + switch((int)(0.5 + duration)) + { + // 59.940 (16683.333333) + case 16000 ... 17000: + framerate = 60000.0 / 1001.0; + break; + + // 50.000 (20000.000000) + case 20000: + framerate = 50000.0 / 1000.0; + break; + + // 49.950 (20020.000000) + case 20020: + framerate = 50000.0 / 1001.0; + break; + + // 29.970 (33366.666656) + case 32000 ... 35000: + framerate = 30000.0 / 1001.0; + break; + + // 25.000 (40000.000000) + case 40000: + framerate = 25000.0 / 1000.0; + break; + + // 24.975 (40040.000000) + case 40040: + framerate = 25000.0 / 1001.0; + break; + + /* + // 24.000 (41666.666666) + case 41667: + framerate = 24000.0 / 1000.0; + break; + */ + + // 23.976 (41708.33333) + case 40200 ... 43200: + // 23.976 seems to have the crappiest encodings :) + framerate = 24000.0 / 1001.0; + break; + + default: + framerate = 0.0; + CLog::Log(LOGDEBUG, "%s: unknown duration(%f), cur_pts(%f)", + __MODULE_NAME__, duration, cur_pts); + break; + } + + if (framerate > 0.0 && (int)m_framerate != (int)framerate) + { + m_framerate = framerate; + m_video_rate = (int)(0.5 + (96000.0 / framerate)); + CLog::Log(LOGDEBUG, "%s: detected new framerate(%f), video_rate(%d)", + __MODULE_NAME__, m_framerate, m_video_rate); + } + } + + PtsQueuePop(); + } +} diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h new file mode 100644 index 0000000000000..04c4eac90adbc --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h @@ -0,0 +1,63 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DVDVideoCodec.h" +#include "DVDStreamInfo.h" + +class CAMLCodec; +typedef struct pts_queue pts_queue; + +class CDVDVideoCodecAmlogic : public CDVDVideoCodec +{ +public: + CDVDVideoCodecAmlogic(); + virtual ~CDVDVideoCodecAmlogic(); + + // Required overrides + virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options); + virtual void Dispose(void); + virtual int Decode(BYTE *pData, int iSize, double dts, double pts); + virtual void Reset(void); + virtual bool GetPicture(DVDVideoPicture *pDvdVideoPicture); + virtual void SetSpeed(int iSpeed); + virtual void SetDropState(bool bDrop); + virtual int GetDataSize(void); + virtual double GetTimeSize(void); + virtual const char* GetName(void) { return (const char*)m_pFormatName; } + +protected: + void PtsQueuePop(void); + void PtsQueuePush(double dts, double pts); + void FrameRateTracking(double dts, double pts); + + CAMLCodec *m_Codec; + const char *m_pFormatName; + DVDVideoPicture m_videobuffer; + bool m_opened; + CDVDStreamInfo m_hints; + double m_last_pts; + pts_queue *m_pts_queue; + int32_t m_queue_depth; + pthread_mutex_t m_queue_mutex; + double m_framerate; + int m_video_rate; +}; diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in index 176ceff899b83..157b6b325efe8 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in @@ -23,6 +23,12 @@ SRCS += OpenMaxVideo.cpp SRCS += DVDVideoCodecOpenMax.cpp endif +ifeq (@USE_LIBAMCODEC@,1) +SRCS += AMLCodec.cpp +SRCS += DVDVideoCodecAmlogic.cpp +INCLUDES += -I$(prefix)/include/amlplayer +endif + LIB=Video.a include @abs_top_srcdir@/Makefile.include diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h index aac0ccec7b2d6..a48a902b25692 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h @@ -154,6 +154,8 @@ class CDemuxStreamVideo : public CDemuxStream { iFpsScale = 0; iFpsRate = 0; + irFpsScale = 0; + irFpsRate = 0; iHeight = 0; iWidth = 0; fAspect = 0.0; @@ -168,6 +170,8 @@ class CDemuxStreamVideo : public CDemuxStream virtual ~CDemuxStreamVideo() {} int iFpsScale; // scale of 1000 and a rate of 29970 will result in 29.97 fps int iFpsRate; + int irFpsScale; + int irFpsRate; int iHeight; // height of the stream reported by the demuxer int iWidth; // width of the stream reported by the demuxer float fAspect; // display aspect of stream diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp index 7c0ab03e0052f..7847947295bfd 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp @@ -974,8 +974,8 @@ void CDVDDemuxFFmpeg::AddStream(int iId) else st->bVFR = false; - // never trust pts in avi files with h264. - if (m_bAVI && pStream->codec->codec_id == CODEC_ID_H264) + // never trust pts in avi files + if (m_bAVI) st->bPTSInvalid = true; //average fps is more accurate for mkv files @@ -995,6 +995,18 @@ void CDVDDemuxFFmpeg::AddStream(int iId) st->iFpsScale = 0; } + // added for aml hw decoder, mkv frame-rate can be wrong. + if (pStream->r_frame_rate.den && pStream->r_frame_rate.num) + { + st->irFpsRate = pStream->r_frame_rate.num; + st->irFpsScale = pStream->r_frame_rate.den; + } + else + { + st->irFpsRate = 0; + st->irFpsScale = 0; + } + if (pStream->codec_info_nb_frames > 0 && pStream->codec_info_nb_frames <= 2 && m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)) diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp index 0cd2510056a82..191df88afc233 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp @@ -2862,7 +2862,9 @@ bool CDVDPlayer::OpenVideoStream(int iStream, int source, bool reset) hint.aspect = aspect; hint.forced_aspect = true; } +#if !defined(HAS_LIBAMCODEC) hint.software = true; +#endif } boost::shared_ptr client; diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp index 23899d20c9e90..a25cdc61f2ea0 100644 --- a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp @@ -645,7 +645,12 @@ void CDVDPlayerAudio::HandleSyncError(double duration) double error = m_dvdAudio.GetPlayingPts() - clock; int64_t now; +//#if defined(TARGET_ANDROID) +#if 1 + if( fabs(error) > DVD_MSEC_TO_TIME(250) || m_syncclock ) +#else if( fabs(error) > DVD_MSEC_TO_TIME(100) || m_syncclock ) +#endif { m_pClock->Discontinuity(clock+error); if(m_speed == DVD_PLAYSPEED_NORMAL) diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp index 3008c25f845b8..346223505b60b 100644 --- a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp @@ -465,6 +465,8 @@ void CDVDPlayerVideo::Process() m_speed = static_cast(pMsg)->m_value; if(m_speed == DVD_PLAYSPEED_PAUSE) m_iNrOfPicturesNotToSkip = 0; + if (m_pVideoCodec) + m_pVideoCodec->SetSpeed(m_speed); } else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) { @@ -1174,8 +1176,11 @@ int CDVDPlayerVideo::OutputPicture(const DVDVideoPicture* src, double pts) limited = true; //correct any pattern in the timestamps - m_pullupCorrection.Add(pts); - pts += m_pullupCorrection.GetCorrection(); + if (m_output.color_format != RENDER_FMT_BYPASS) + { + m_pullupCorrection.Add(pts); + pts += m_pullupCorrection.GetCorrection(); + } //try to calculate the framerate CalcFrameRate(); @@ -1189,8 +1194,11 @@ int CDVDPlayerVideo::OutputPicture(const DVDVideoPicture* src, double pts) pts -= DVD_TIME_BASE * interval; } - // Correct pts by user set delay and rendering delay - pts += m_iVideoDelay - DVD_SEC_TO_TIME(g_renderManager.GetDisplayLatency()); + if (m_output.color_format != RENDER_FMT_BYPASS) + { + // Correct pts by user set delay and rendering delay + pts += m_iVideoDelay - DVD_SEC_TO_TIME(g_renderManager.GetDisplayLatency()); + } // calculate the time we need to delay this picture before displaying double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; diff --git a/xbmc/cores/dvdplayer/DVDStreamInfo.cpp b/xbmc/cores/dvdplayer/DVDStreamInfo.cpp index 36cf79852ff67..6d6c6fa42b4ce 100644 --- a/xbmc/cores/dvdplayer/DVDStreamInfo.cpp +++ b/xbmc/cores/dvdplayer/DVDStreamInfo.cpp @@ -50,6 +50,8 @@ void CDVDStreamInfo::Clear() fpsscale = 0; fpsrate = 0; + rfpsscale= 0; + rfpsrate = 0; height = 0; width = 0; aspect = 0.0; @@ -60,6 +62,7 @@ void CDVDStreamInfo::Clear() ptsinvalid = false; forced_aspect = false; bitsperpixel = 0; + pid = 0; channels = 0; samplerate = 0; @@ -90,6 +93,8 @@ bool CDVDStreamInfo::Equal(const CDVDStreamInfo& right, bool withextradata) // VIDEO if( fpsscale != right.fpsscale || fpsrate != right.fpsrate + || rfpsscale!= right.rfpsscale + || rfpsrate != right.rfpsrate || height != right.height || width != right.width || stills != right.stills @@ -98,6 +103,7 @@ bool CDVDStreamInfo::Equal(const CDVDStreamInfo& right, bool withextradata) || ptsinvalid != right.ptsinvalid || forced_aspect != right.forced_aspect || bitsperpixel != right.bitsperpixel + || pid != right.pid || vfr != right.vfr) return false; // AUDIO @@ -145,6 +151,8 @@ void CDVDStreamInfo::Assign(const CDVDStreamInfo& right, bool withextradata) // VIDEO fpsscale = right.fpsscale; fpsrate = right.fpsrate; + rfpsscale= right.rfpsscale; + rfpsrate = right.rfpsrate; height = right.height; width = right.width; aspect = right.aspect; @@ -155,6 +163,7 @@ void CDVDStreamInfo::Assign(const CDVDStreamInfo& right, bool withextradata) forced_aspect = right.forced_aspect; orientation = right.orientation; bitsperpixel = right.bitsperpixel; + pid = right.pid; // AUDIO channels = right.channels; @@ -198,6 +207,8 @@ void CDVDStreamInfo::Assign(const CDemuxStream& right, bool withextradata) const CDemuxStreamVideo *stream = static_cast(&right); fpsscale = stream->iFpsScale; fpsrate = stream->iFpsRate; + rfpsscale = stream->irFpsScale; + rfpsrate = stream->irFpsRate; height = stream->iHeight; width = stream->iWidth; aspect = stream->fAspect; @@ -206,6 +217,7 @@ void CDVDStreamInfo::Assign(const CDemuxStream& right, bool withextradata) forced_aspect = stream->bForcedAspect; orientation = stream->iOrientation; bitsperpixel = stream->iBitsPerPixel; + pid = stream->iPhysicalId; } else if( right.type == STREAM_SUBTITLE ) { diff --git a/xbmc/cores/dvdplayer/DVDStreamInfo.h b/xbmc/cores/dvdplayer/DVDStreamInfo.h index e4fec0f047f98..47e5f73c460de 100644 --- a/xbmc/cores/dvdplayer/DVDStreamInfo.h +++ b/xbmc/cores/dvdplayer/DVDStreamInfo.h @@ -67,6 +67,8 @@ class CDVDStreamInfo // VIDEO int fpsscale; // scale of 1000 and a rate of 29970 will result in 29.97 fps int fpsrate; + int rfpsscale; + int rfpsrate; int height; // height of the stream reported by the demuxer int width; // width of the stream reported by the demuxer float aspect; // display aspect as reported by demuxer @@ -78,6 +80,7 @@ class CDVDStreamInfo bool forced_aspect; // aspect is forced from container int orientation; // orientation of the video in degress counter clockwise int bitsperpixel; + int pid; // AUDIO int channels; diff --git a/xbmc/cores/dvdplayer/Edl.cpp b/xbmc/cores/dvdplayer/Edl.cpp index a07164b09243b..3c27256f3bcd5 100644 --- a/xbmc/cores/dvdplayer/Edl.cpp +++ b/xbmc/cores/dvdplayer/Edl.cpp @@ -79,7 +79,7 @@ bool CEdl::ReadEditDecisionLists(const CStdString& strMovie, const float fFrameR * back frame markers. However, this doesn't seem possible for MythTV. */ float fFramesPerSecond; - if (int(fFrameRate * 100) == 5994) // 59.940 fps = NTSC or 60i content + if (iHeight != 720 && int(fFrameRate * 100) == 5994) // 59.940 fps = NTSC or 60i content except for 1280x720/60 { fFramesPerSecond = fFrameRate / 2; // ~29.97f - division used to retain accuracy of original. CLog::Log(LOGDEBUG, "%s - Assuming NTSC or 60i interlaced content. Adjusted frames per second from %.3f (~59.940 fps) to %.3f", diff --git a/xbmc/dialogs/GUIDialogFileBrowser.cpp b/xbmc/dialogs/GUIDialogFileBrowser.cpp index e4149d1f4b18a..8fe1de39ecd1a 100644 --- a/xbmc/dialogs/GUIDialogFileBrowser.cpp +++ b/xbmc/dialogs/GUIDialogFileBrowser.cpp @@ -26,7 +26,6 @@ #include "GUIDialogContextMenu.h" #include "storage/MediaManager.h" #include "AutoSwitch.h" -#include "network/Network.h" #include "GUIPassword.h" #include "guilib/GUIWindowManager.h" #include "Application.h" @@ -572,7 +571,7 @@ bool CGUIDialogFileBrowser::HaveDiscOrConnection( int iDriveType ) else if ( iDriveType == CMediaSource::SOURCE_TYPE_REMOTE ) { // TODO: Handle not connected to a remote share - if ( !g_application.getNetwork().IsConnected() ) + if ( !g_application.getNetworkManager().IsConnected() ) { CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0); return false; diff --git a/xbmc/epg/EpgContainer.cpp b/xbmc/epg/EpgContainer.cpp index 23122a463b747..fb9fba0b85120 100644 --- a/xbmc/epg/EpgContainer.cpp +++ b/xbmc/epg/EpgContainer.cpp @@ -444,12 +444,13 @@ bool CEpgContainer::InterruptUpdate(void) const bool bReturn(false); CSingleLock lock(m_critSection); bReturn = g_application.m_bStop || m_bStop || m_bPreventUpdates; + const bool isVideoPlaying = g_application.m_pPlayer && g_application.m_pPlayer->IsPlaying(); + const bool isPVRPlaying = g_PVRManager.IsStarted() && g_PVRManager.IsPlaying(); lock.Leave(); return bReturn || (g_guiSettings.GetBool("epg.preventupdateswhileplayingtv") && - g_PVRManager.IsStarted() && - g_PVRManager.IsPlaying()); + ( isPVRPlaying || isVideoPlaying )); } void CEpgContainer::WaitForUpdateFinish(bool bInterrupt /* = true */) diff --git a/xbmc/filesystem/DirectoryFactory.cpp b/xbmc/filesystem/DirectoryFactory.cpp index 9fb21698a8dad..1fcf1afd317db 100644 --- a/xbmc/filesystem/DirectoryFactory.cpp +++ b/xbmc/filesystem/DirectoryFactory.cpp @@ -21,7 +21,6 @@ #if (defined HAVE_CONFIG_H) && (!defined WIN32) #include "config.h" #endif -#include "network/Network.h" #include "system.h" #include "DirectoryFactory.h" #include "HDDirectory.h" @@ -167,7 +166,7 @@ IDirectory* CDirectoryFactory::Create(const CStdString& strPath) if (strProtocol == "filereader") return CDirectoryFactory::Create(url.GetFileName()); - if( g_application.getNetwork().IsAvailable(true) ) // true to wait for the network (if possible) + if( g_application.getNetworkManager().IsAvailable(true) ) // true to wait for the network (if possible) { if (strProtocol == "lastfm") return new CLastFMDirectory(); if (strProtocol == "tuxbox") return new CTuxBoxDirectory(); diff --git a/xbmc/filesystem/FileFactory.cpp b/xbmc/filesystem/FileFactory.cpp index a042d39e7ddce..41493fbb68af7 100644 --- a/xbmc/filesystem/FileFactory.cpp +++ b/xbmc/filesystem/FileFactory.cpp @@ -22,7 +22,6 @@ #if (defined HAVE_CONFIG_H) && (!defined WIN32) #include "config.h" #endif -#include "network/Network.h" #include "system.h" #include "FileFactory.h" #include "HDFile.h" @@ -144,7 +143,7 @@ IFile* CFileFactory::CreateLoader(const CURL& url) #endif else if(strProtocol == "udf") return new CUDFFile(); - if( g_application.getNetwork().IsAvailable() ) + if( g_application.getNetworkManager().IsAvailable() ) { if (strProtocol == "dav" || strProtocol == "davs" diff --git a/xbmc/filesystem/ISOFile.cpp b/xbmc/filesystem/ISOFile.cpp index 9baa95d81e01a..52a556cc85c71 100644 --- a/xbmc/filesystem/ISOFile.cpp +++ b/xbmc/filesystem/ISOFile.cpp @@ -87,7 +87,7 @@ unsigned int CISOFile::Read(void *lpBuf, int64_t uiBufSize) if (m_cache.getMaxWriteSize() > 5000) { - byte buffer[5000]; + BYTE buffer[5000]; long lBytesRead = m_isoReader.ReadFile( m_hFile, buffer, sizeof(buffer)); if (lBytesRead > 0) m_cache.WriteData((char*)buffer, lBytesRead); @@ -97,7 +97,7 @@ unsigned int CISOFile::Read(void *lpBuf, int64_t uiBufSize) } return lTotalBytesRead; } - int iResult = m_isoReader.ReadFile( m_hFile, (byte*)pData, (long)uiBufSize); + int iResult = m_isoReader.ReadFile( m_hFile, (BYTE*)pData, (long)uiBufSize); if (iResult == -1) return 0; return iResult; diff --git a/xbmc/filesystem/NFSFile.cpp b/xbmc/filesystem/NFSFile.cpp index a7b49b3b57845..8f61878b8556e 100644 --- a/xbmc/filesystem/NFSFile.cpp +++ b/xbmc/filesystem/NFSFile.cpp @@ -140,6 +140,17 @@ void CNfsConnection::destroyOpenContexts() m_openContextMap.clear(); } +void CNfsConnection::destroyContext(const CStdString &exportName) +{ + CSingleLock lock(openContextLock); + tOpenContextMap::iterator it = m_openContextMap.find(exportName.c_str()); + if(it != m_openContextMap.end()) + { + m_pLibNfs->nfs_destroy_context(it->second.pContext); + m_openContextMap.erase(it); + } +} + struct nfs_context *CNfsConnection::getContextFromMap(const CStdString &exportname, bool forceCacheHit/* = false*/) { struct nfs_context *pRet = NULL; @@ -166,6 +177,7 @@ struct nfs_context *CNfsConnection::getContextFromMap(const CStdString &exportna //destroy it and return NULL CLog::Log(LOGDEBUG, "NFS: Old context timed out - destroying it"); m_pLibNfs->nfs_destroy_context(it->second.pContext); + m_openContextMap.erase(it); } } return pRet; @@ -288,6 +300,7 @@ bool CNfsConnection::Connect(const CURL& url, CStdString &relativePath) if(nfsRet != 0) { CLog::Log(LOGERROR,"NFS: Failed to mount nfs share: %s (%s)\n", exportPath.c_str(), m_pLibNfs->nfs_get_error(m_pNfsContext)); + destroyContext(url.GetHostName() + exportPath); return false; } CLog::Log(LOGDEBUG,"NFS: Connected to server %s and export %s\n", url.GetHostName().c_str(), exportPath.c_str()); diff --git a/xbmc/filesystem/NFSFile.h b/xbmc/filesystem/NFSFile.h index 7e2db95aa7c19..bd758e7117b2b 100644 --- a/xbmc/filesystem/NFSFile.h +++ b/xbmc/filesystem/NFSFile.h @@ -117,6 +117,7 @@ class CNfsConnection : public CCriticalSection struct nfs_context *getContextFromMap(const CStdString &exportname, bool forceCacheHit = false); int getContextForExport(const CStdString &exportname);//get context for given export and add to open contexts map - sets m_pNfsContext (my return a already mounted cached context) void destroyOpenContexts(); + void destroyContext(const CStdString &exportName); void resolveHost(const CURL &url);//resolve hostname by dnslookup void keepAlive(std::string _exportPath, struct nfsfh *_pFileHandle); }; diff --git a/xbmc/filesystem/RarFile.h b/xbmc/filesystem/RarFile.h index 5163c49e2be7f..de47391fbc4c2 100644 --- a/xbmc/filesystem/RarFile.h +++ b/xbmc/filesystem/RarFile.h @@ -104,8 +104,8 @@ namespace XFILE CmdExtract* m_pExtract; CRarFileExtractThread* m_pExtractThread; #endif - byte* m_szBuffer; - byte* m_szStartOfBuffer; + BYTE* m_szBuffer; + BYTE* m_szStartOfBuffer; int64_t m_iDataInBuffer; int64_t m_iBufferStart; }; diff --git a/xbmc/filesystem/SFTPFile.cpp b/xbmc/filesystem/SFTPFile.cpp index d176969835a37..a6212dca1fb33 100644 --- a/xbmc/filesystem/SFTPFile.cpp +++ b/xbmc/filesystem/SFTPFile.cpp @@ -275,12 +275,14 @@ bool CSFTPSession::VerifyKnownHost(ssh_session session) CLog::Log(LOGINFO, "SFTPSession: Server file was not found, creating a new one"); case SSH_SERVER_NOT_KNOWN: CLog::Log(LOGINFO, "SFTPSession: Server unkown, we trust it for now"); +#if !defined(TARGET_ANDROID) +//TODO: use a writable path and remove this. if (ssh_write_knownhost(session) < 0) { CLog::Log(LOGERROR, "CSFTPSession: Failed to save host '%s'", strerror(errno)); return false; } - +#endif return true; case SSH_SERVER_ERROR: CLog::Log(LOGERROR, "SFTPSession: Failed to verify host '%s'", ssh_get_error(session)); diff --git a/xbmc/filesystem/iso9660.cpp b/xbmc/filesystem/iso9660.cpp index 7f2ad2f8dd0d2..d2417cd8901ad 100644 --- a/xbmc/filesystem/iso9660.cpp +++ b/xbmc/filesystem/iso9660.cpp @@ -752,7 +752,7 @@ HANDLE iso9660::OpenFile(const char *filename) pContext->m_dwCurrentBlock = m_searchpointer->Location; pContext->m_dwFileSize = m_info.curr_filesize = fileinfo.nFileSizeLow; - pContext->m_pBuffer = new byte[CIRC_BUFFER_SIZE * BUFFER_SIZE]; + pContext->m_pBuffer = new BYTE[CIRC_BUFFER_SIZE * BUFFER_SIZE]; pContext->m_dwStartBlock = pContext->m_dwCurrentBlock; pContext->m_dwFilePos = 0; pContext->m_dwCircBuffBegin = 0; @@ -791,7 +791,7 @@ void iso9660::CloseFile(HANDLE hFile) FreeFileContext(hFile); } //************************************************************************************ -bool iso9660::ReadSectorFromCache(iso9660::isofile* pContext, DWORD sector, byte** ppBuffer) +bool iso9660::ReadSectorFromCache(iso9660::isofile* pContext, DWORD sector, BYTE** ppBuffer) { DWORD StartSectorInCircBuff = pContext->m_dwCircBuffSectorStart; @@ -891,7 +891,7 @@ void iso9660::ReleaseSectorFromCache(iso9660::isofile* pContext, DWORD sector) } } //************************************************************************************ -long iso9660::ReadFile(HANDLE hFile, byte *pBuffer, long lSize) +long iso9660::ReadFile(HANDLE hFile, BYTE *pBuffer, long lSize) { bool bError; long iBytesRead = 0; @@ -912,7 +912,7 @@ long iso9660::ReadFile(HANDLE hFile, byte *pBuffer, long lSize) //sprintf(szBuf,"pos:%i cblk:%i sblk:%i off:%i",(long)m_dwFilePos, (long)m_dwCurrentBlock,(long)m_dwStartBlock,(long)iOffsetInBuffer); //DBG(szBuf); - byte* pSector; + BYTE* pSector; bError = !ReadSectorFromCache(pContext, pContext->m_dwCurrentBlock, &pSector); if (!bError) { diff --git a/xbmc/filesystem/iso9660.h b/xbmc/filesystem/iso9660.h index e2989c8dfb787..bd3dbbc2000ca 100644 --- a/xbmc/filesystem/iso9660.h +++ b/xbmc/filesystem/iso9660.h @@ -185,7 +185,7 @@ class iso9660 int64_t GetFilePosition(HANDLE hFile); int64_t Seek(HANDLE hFile, int64_t lOffset, int whence); HANDLE OpenFile( const char* filename ); - long ReadFile(HANDLE fd, byte *pBuffer, long lSize); + long ReadFile(HANDLE fd, BYTE *pBuffer, long lSize); void CloseFile(HANDLE hFile); void Reset(); void Scan(); @@ -196,7 +196,7 @@ class iso9660 struct iso_dirtree* ReadRecursiveDirFromSector( DWORD sector, const char * ); struct iso_dirtree* FindFolder( char *Folder ); std::string GetThinText(BYTE* strTxt, int iLen ); - bool ReadSectorFromCache(iso9660::isofile* pContext, DWORD sector, byte** ppBuffer); + bool ReadSectorFromCache(iso9660::isofile* pContext, DWORD sector, BYTE** ppBuffer); void ReleaseSectorFromCache(iso9660::isofile* pContext, DWORD sector); const std::string ParseName(struct iso9660_Directory& isodir); HANDLE AllocFileContext(); diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp index 8d04313047eee..7a96095f5903f 100644 --- a/xbmc/guilib/GUIShader.cpp +++ b/xbmc/guilib/GUIShader.cpp @@ -38,6 +38,7 @@ CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_ver m_hCol = 0; m_hCord0 = 0; m_hCord1 = 0; + m_hUniCol = 0; m_proj = NULL; m_model = NULL; @@ -50,6 +51,8 @@ void CGUIShader::OnCompiledAndLinked() // Variables passed directly to the Fragment shader m_hTex0 = glGetUniformLocation(ProgramHandle(), "m_samp0"); m_hTex1 = glGetUniformLocation(ProgramHandle(), "m_samp1"); + m_hUniCol = glGetUniformLocation(ProgramHandle(), "m_unicol"); + // Variables passed directly to the Vertex shader m_hProj = glGetUniformLocation(ProgramHandle(), "m_proj"); m_hModel = glGetUniformLocation(ProgramHandle(), "m_model"); @@ -62,6 +65,7 @@ void CGUIShader::OnCompiledAndLinked() glUseProgram( ProgramHandle() ); glUniform1i(m_hTex0, 0); glUniform1i(m_hTex1, 1); + glUniform4f(m_hUniCol, 1.0, 1.0, 1.0, 1.0); glUseProgram( 0 ); } diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h index d84e25158a0b2..95de7585e6656 100644 --- a/xbmc/guilib/GUIShader.h +++ b/xbmc/guilib/GUIShader.h @@ -39,10 +39,12 @@ class CGUIShader : public CGLSLShaderProgram GLint GetColLoc() { return m_hCol; } GLint GetCord0Loc() { return m_hCord0; } GLint GetCord1Loc() { return m_hCord1; } + GLint GetUniColLoc() { return m_hUniCol; } protected: GLint m_hTex0; GLint m_hTex1; + GLint m_hUniCol; GLint m_hProj; GLint m_hModel; GLint m_hPos; diff --git a/xbmc/guilib/GUITextureGLES.cpp b/xbmc/guilib/GUITextureGLES.cpp index 86fb6bb3aaf1d..9621a89b56d6b 100644 --- a/xbmc/guilib/GUITextureGLES.cpp +++ b/xbmc/guilib/GUITextureGLES.cpp @@ -72,9 +72,6 @@ void CGUITextureGLES::Begin(color_t color) m_diffuse.m_textures[0]->BindToUnit(1); - GLint tex1Loc = g_Windowing.GUIShaderGetCoord1(); - glVertexAttribPointer(tex1Loc, 2, GL_FLOAT, 0, 0, m_tex1); - glEnableVertexAttribArray(tex1Loc); } else { @@ -88,20 +85,6 @@ void CGUITextureGLES::Begin(color_t color) } } - GLint posLoc = g_Windowing.GUIShaderGetPos(); - GLint colLoc = g_Windowing.GUIShaderGetCol(); - GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); - - glVertexAttribPointer(posLoc, 3, GL_FLOAT, 0, 0, m_vert); - if(colLoc >= 0) - glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, m_col); - glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, 0, 0, m_tex0); - - glEnableVertexAttribArray(posLoc); - if(colLoc >= 0) - glEnableVertexAttribArray(colLoc); - glEnableVertexAttribArray(tex0Loc); - if ( hasAlpha ) { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE); @@ -111,101 +94,136 @@ void CGUITextureGLES::Begin(color_t color) { glDisable(GL_BLEND); } + m_packedVertices.clear(); } void CGUITextureGLES::End() { + GLint posLoc = g_Windowing.GUIShaderGetPos(); + GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + GLint tex1Loc = g_Windowing.GUIShaderGetCoord1(); + GLint uniColLoc = g_Windowing.GUIShaderGetUniCol(); + + if(uniColLoc >= 0) + { + glUniform4f(uniColLoc,(m_col[0][0] / 255.0), (m_col[0][1] / 255.0), (m_col[0][2] / 255.0), (m_col[0][3] / 255.0)); + } + + if(m_diffuse.size()) + { + glVertexAttribPointer(tex1Loc, 2, GL_FLOAT, 0, sizeof(PackedVertex), (char*)&m_packedVertices[0] + offsetof(PackedVertex, u2)); + glEnableVertexAttribArray(tex1Loc); + } + glVertexAttribPointer(posLoc, 3, GL_FLOAT, 0, sizeof(PackedVertex), (char*)&m_packedVertices[0] + offsetof(PackedVertex, x)); + glEnableVertexAttribArray(posLoc); + glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, 0, sizeof(PackedVertex), (char*)&m_packedVertices[0] + offsetof(PackedVertex, u1)); + glEnableVertexAttribArray(tex0Loc); + + GLushort *idx; + idx = new GLushort[m_packedVertices.size()*6 / 4]; + for (unsigned int i=0, size=0; i < m_packedVertices.size(); i+=4, size+=6) + { + idx[size+0] = i+0; + idx[size+1] = i+1; + idx[size+2] = i+2; + idx[size+3] = i+2; + idx[size+4] = i+3; + idx[size+5] = i+0; + } + + glDrawElements(GL_TRIANGLES, m_packedVertices.size()*6 / 4, GL_UNSIGNED_SHORT, idx); + if (m_diffuse.size()) { - glDisableVertexAttribArray(g_Windowing.GUIShaderGetCoord1()); + glDisableVertexAttribArray(tex1Loc); glActiveTexture(GL_TEXTURE0); } - glDisableVertexAttribArray(g_Windowing.GUIShaderGetPos()); - GLint colLoc = g_Windowing.GUIShaderGetCol(); - if(colLoc >= 0) - glDisableVertexAttribArray(g_Windowing.GUIShaderGetCol()); - glDisableVertexAttribArray(g_Windowing.GUIShaderGetCoord0()); + glDisableVertexAttribArray(posLoc); + glDisableVertexAttribArray(tex0Loc); glEnable(GL_BLEND); g_Windowing.DisableGUIShader(); + delete [] idx; } void CGUITextureGLES::Draw(float *x, float *y, float *z, const CRect &texture, const CRect &diffuse, int orientation) { - GLubyte idx[4] = {0, 1, 3, 2}; //determines order of triangle strip - - // Setup vertex position values - for (int i=0; i<4; i++) - { - m_vert[i][0] = x[i]; - m_vert[i][1] = y[i]; - m_vert[i][2] = z[i]; - } + PackedVertex vertices[4]; // Setup texture coordinates //TopLeft - m_tex0[0][0] = texture.x1; - m_tex0[0][1] = texture.y1; + vertices[0].u1 = texture.x1; + vertices[0].v1 = texture.y1; //TopRight if (orientation & 4) { - m_tex0[1][0] = texture.x1; - m_tex0[1][1] = texture.y2; + vertices[1].u1 = texture.x1; + vertices[1].v1 = texture.y2; } else { - m_tex0[1][0] = texture.x2; - m_tex0[1][1] = texture.y1; + vertices[1].u1 = texture.x2; + vertices[1].v1 = texture.y1; } //BottomRight - m_tex0[2][0] = texture.x2; - m_tex0[2][1] = texture.y2; + vertices[2].u1 = texture.x2; + vertices[2].v1 = texture.y2; //BottomLeft if (orientation & 4) { - m_tex0[3][0] = texture.x2; - m_tex0[3][1] = texture.y1; + vertices[3].u1 = texture.x2; + vertices[3].v1 = texture.y1; } else { - m_tex0[3][0] = texture.x1; - m_tex0[3][1] = texture.y2; + vertices[3].u1 = texture.x1; + vertices[3].v1 = texture.y2; } if (m_diffuse.size()) { //TopLeft - m_tex1[0][0] = diffuse.x1; - m_tex1[0][1] = diffuse.y1; + vertices[0].u2 = diffuse.x1; + vertices[0].v2 = diffuse.y1; //TopRight if (m_info.orientation & 4) { - m_tex1[1][0] = diffuse.x1; - m_tex1[1][1] = diffuse.y2; + vertices[1].u2 = diffuse.x1; + vertices[1].v2 = diffuse.y2; } else { - m_tex1[1][0] = diffuse.x2; - m_tex1[1][1] = diffuse.y1; + vertices[1].u2 = diffuse.x2; + vertices[1].v2 = diffuse.y1; } //BottomRight - m_tex1[2][0] = diffuse.x2; - m_tex1[2][1] = diffuse.y2; + vertices[2].u2 = diffuse.x2; + vertices[2].v2 = diffuse.y2; //BottomLeft if (m_info.orientation & 4) { - m_tex1[3][0] = diffuse.x2; - m_tex1[3][1] = diffuse.y1; + vertices[3].u2 = diffuse.x2; + vertices[3].v2 = diffuse.y1; } else { - m_tex1[3][0] = diffuse.x1; - m_tex1[3][1] = diffuse.y2; + vertices[3].u2 = diffuse.x1; + vertices[3].v2 = diffuse.y2; } } - glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); + for (int i=0; i<4; i++) + { + vertices[i].x = x[i]; + vertices[i].y = y[i]; + vertices[i].z = z[i]; + vertices[i].r = m_col[i][0]; + vertices[i].g = m_col[i][1]; + vertices[i].b = m_col[i][2]; + vertices[i].a = m_col[i][3]; + m_packedVertices.push_back(vertices[i]); + } } void CGUITextureGLES::DrawQuad(const CRect &rect, color_t color, CBaseTexture *texture, const CRect *texCoords) @@ -234,6 +252,7 @@ void CGUITextureGLES::DrawQuad(const CRect &rect, color_t color, CBaseTexture *t GLint posLoc = g_Windowing.GUIShaderGetPos(); GLint colLoc = g_Windowing.GUIShaderGetCol(); GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + GLint uniColLoc= g_Windowing.GUIShaderGetUniCol(); glVertexAttribPointer(posLoc, 3, GL_FLOAT, 0, 0, ver); if(colLoc >= 0) @@ -256,6 +275,8 @@ void CGUITextureGLES::DrawQuad(const CRect &rect, color_t color, CBaseTexture *t col[i][3] = (GLubyte)GET_A(color); } + glUniform4f(uniColLoc,col[0][0] / 255, col[0][1] / 255, col[0][2] / 255, col[0][3] / 255); + // Setup vertex position values #define ROUND_TO_PIXEL(x) (float)(MathUtils::round_int(x)) ver[0][0] = ROUND_TO_PIXEL(g_graphicsContext.ScaleFinalXCoord(rect.x1, rect.y1)); diff --git a/xbmc/guilib/GUITextureGLES.h b/xbmc/guilib/GUITextureGLES.h index d0d38dfc08a4a..206d9d6427280 100644 --- a/xbmc/guilib/GUITextureGLES.h +++ b/xbmc/guilib/GUITextureGLES.h @@ -32,6 +32,15 @@ #include "system_gl.h" +struct PackedVertex +{ + float x, y, z; + unsigned char r, g, b, a; + float u1, v1; + float u2, v2; +}; +typedef std::vector PackedVertices; + class CGUITextureGLES : public CGUITextureBase { public: @@ -43,9 +52,8 @@ class CGUITextureGLES : public CGUITextureBase void End(); GLubyte m_col [4][4]; - GLfloat m_vert[4][3]; - GLfloat m_tex0[4][2]; - GLfloat m_tex1[4][2]; + + PackedVertices m_packedVertices; }; #endif diff --git a/xbmc/guilib/GUIVideoControl.cpp b/xbmc/guilib/GUIVideoControl.cpp index f9c7090729785..49e1a0fe7b69d 100644 --- a/xbmc/guilib/GUIVideoControl.cpp +++ b/xbmc/guilib/GUIVideoControl.cpp @@ -40,7 +40,8 @@ CGUIVideoControl::~CGUIVideoControl(void) void CGUIVideoControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) { // TODO Proper processing which marks when its actually changed. Just mark always for now. - MarkDirtyRegion(); + if (g_renderManager.RendererHandlesPresent()) + MarkDirtyRegion(); CGUIControl::Process(currentTime, dirtyregions); } diff --git a/xbmc/guilib/Key.h b/xbmc/guilib/Key.h index 10aa356f06f73..be24f7fad7ea4 100644 --- a/xbmc/guilib/Key.h +++ b/xbmc/guilib/Key.h @@ -314,7 +314,7 @@ #define ACTION_SUBTITLE_ALIGN 232 // toggle vertical alignment of subtitles #define ACTION_FILTER 233 - +#define ACTION_CONNECTIONS_REFRESH 300 // Window ID defines to make the code a bit more readable #define WINDOW_INVALID 9999 #define WINDOW_HOME 10000 diff --git a/xbmc/input/linux/LinuxInputDevices.cpp b/xbmc/input/linux/LinuxInputDevices.cpp index f4331658c46af..c43fd2bb4283f 100644 --- a/xbmc/input/linux/LinuxInputDevices.cpp +++ b/xbmc/input/linux/LinuxInputDevices.cpp @@ -782,7 +782,7 @@ void CLinuxInputDevice::SetupKeyboardAutoRepeat(int fd) { bool enable = true; -#if defined(HAS_AMLPLAYER) +#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) // ignore the native aml driver named 'key_input', // it is the dedicated power key handler (am_key_input) if (strncmp(m_deviceName, "key_input", strlen("key_input")) == 0) @@ -967,7 +967,6 @@ bool CLinuxInputDevices::CheckDevice(const char *device) { int fd; - CLog::Log(LOGDEBUG, "Checking device: %s\n", device); /* Check if we are able to open the device */ fd = open(device, O_RDWR); if (fd < 0) diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp index fae25240a3e55..066d142a40924 100644 --- a/xbmc/interfaces/Builtins.cpp +++ b/xbmc/interfaces/Builtins.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "system.h" #include "utils/AlarmClock.h" #include "utils/Screenshot.h" @@ -1261,8 +1260,8 @@ int CBuiltins::Execute(const CStdString& execString) g_application.StopVideoScan(); ADDON::CAddonMgr::Get().StopServices(true); + g_application.getNetworkManager().NetworkMessage(CNetworkManager::SERVICES_DOWN,1); - g_application.getNetwork().NetworkMessage(CNetwork::SERVICES_DOWN,1); g_settings.LoadMasterForLogin(); g_passwordManager.bMasterUser = false; g_windowManager.ActivateWindow(WINDOW_LOGIN_SCREEN); @@ -1517,7 +1516,7 @@ int CBuiltins::Execute(const CStdString& execString) } else if (execute.Equals("wakeonlan")) { - g_application.getNetwork().WakeOnLan((char*)params[0].c_str()); + g_application.getNetworkManager().WakeOnLan((char*)params[0].c_str()); } else if (execute.Equals("addon.default.opensettings") && params.size() == 1) { diff --git a/xbmc/interfaces/legacy/ModuleXbmc.cpp b/xbmc/interfaces/legacy/ModuleXbmc.cpp index f995a2f6ce607..2009a0ab90281 100644 --- a/xbmc/interfaces/legacy/ModuleXbmc.cpp +++ b/xbmc/interfaces/legacy/ModuleXbmc.cpp @@ -24,7 +24,6 @@ #if (defined HAVE_CONFIG_H) && (!defined WIN32) #include "config.h" #endif -#include "network/Network.h" #include "ModuleXbmc.h" @@ -189,13 +188,7 @@ namespace XBMCAddon String getIPAddress() { TRACE; - char cTitleIP[32]; - sprintf(cTitleIP, "127.0.0.1"); - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface) - return iface->GetCurrentIPAddress(); - - return cTitleIP; + return g_application.getNetworkManager().GetDefaultConnectionAddress().c_str(); } long getDVDState() diff --git a/xbmc/interfaces/python/CallbackHandler.cpp b/xbmc/interfaces/python/CallbackHandler.cpp index 01707b964a68d..1bb3997a4c76f 100644 --- a/xbmc/interfaces/python/CallbackHandler.cpp +++ b/xbmc/interfaces/python/CallbackHandler.cpp @@ -36,7 +36,6 @@ namespace XBMCAddon PythonCallbackHandler::PythonCallbackHandler() : RetardedAsynchCallbackHandler("PythonCallbackHandler") { objectThreadState = PyThreadState_Get(); - CLog::Log(LOGDEBUG,"NEWADDON PythonCallbackHandler construction with PyThreadState 0x%lx",(long)objectThreadState); } /** diff --git a/xbmc/linux/PlatformDefs.h b/xbmc/linux/PlatformDefs.h index a66ef0adee49e..07f91e1bba67e 100644 --- a/xbmc/linux/PlatformDefs.h +++ b/xbmc/linux/PlatformDefs.h @@ -155,7 +155,6 @@ #define ZeroMemory(dst,size) memset(dst, 0, size) #define VOID void -#define byte unsigned char #define __int8 char #define __int16 short #define __int32 int diff --git a/xbmc/linux/PosixMountProvider.cpp b/xbmc/linux/PosixMountProvider.cpp index c91a76e403a7a..27d639eee538a 100644 --- a/xbmc/linux/PosixMountProvider.cpp +++ b/xbmc/linux/PosixMountProvider.cpp @@ -65,6 +65,7 @@ void CPosixMountProvider::GetDrives(VECSOURCES &drives) || strcmp(fs, "ext2") == 0 || strcmp(fs, "ext3") == 0 || strcmp(fs, "reiserfs") == 0 || strcmp(fs, "xfs") == 0 || strcmp(fs, "ntfs-3g") == 0 || strcmp(fs, "iso9660") == 0 + || strcmp(fs, "exfat") == 0 || strcmp(fs, "fusefs") == 0 || strcmp(fs, "hfs") == 0) accepted = true; @@ -126,6 +127,16 @@ std::vector CPosixMountProvider::GetDiskUsage() return result; } +bool CPosixMountProvider::Eject(CStdString mountpath) +{ + // just go ahead and try to umount the disk + // if it does umount, life is good, if not, no loss. + std::string cmd = "umount " + mountpath; + system(cmd.c_str()); + + return true; +} + bool CPosixMountProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback) { VECSOURCES drives; diff --git a/xbmc/linux/PosixMountProvider.h b/xbmc/linux/PosixMountProvider.h index da0506cdf4316..02ff302172957 100644 --- a/xbmc/linux/PosixMountProvider.h +++ b/xbmc/linux/PosixMountProvider.h @@ -34,7 +34,7 @@ class CPosixMountProvider : public IStorageProvider virtual std::vector GetDiskUsage(); - virtual bool Eject(CStdString mountpath) { return false; } + virtual bool Eject(CStdString mountpath); virtual bool PumpDriveChangeEvents(IStorageEventsCallback *callback); private: diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp index bdee61aca872d..c387ddea5eeab 100644 --- a/xbmc/music/MusicDatabase.cpp +++ b/xbmc/music/MusicDatabase.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "threads/SystemClock.h" #include "system.h" #include "MusicDatabase.h" @@ -2390,7 +2389,7 @@ bool CMusicDatabase::LookupCDDBInfo(bool bRequery/*=false*/) return false; // check network connectivity - if (!g_application.getNetwork().IsAvailable()) + if (!g_application.getNetworkManager().IsAvailable()) return false; // Get information for the inserted disc diff --git a/xbmc/network/AirPlayServer.cpp b/xbmc/network/AirPlayServer.cpp index fd7f755a558a2..866728c7b120a 100644 --- a/xbmc/network/AirPlayServer.cpp +++ b/xbmc/network/AirPlayServer.cpp @@ -17,7 +17,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "network/Network.h" #include "AirPlayServer.h" #ifdef HAS_AIRPLAY @@ -119,7 +118,7 @@ const char *eventStrings[] = {"playing", "paused", "loading", "stopped"}; "features\r\n"\ "119\r\n"\ "model\r\n"\ -"AppleTV2,1\r\n"\ +"Xbmc,1\r\n"\ "protovers\r\n"\ "1.0\r\n"\ "srcvers\r\n"\ @@ -960,7 +959,7 @@ int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader, else if (uri == "/server-info") { CLog::Log(LOGDEBUG, "AIRPLAY: got request %s", uri.c_str()); - responseBody.Format(SERVER_INFO, g_application.getNetwork().GetFirstConnectedInterface()->GetMacAddress()); + responseBody.Format(SERVER_INFO, g_application.getNetworkManager().GetDefaultConnectionMacAddress()); responseHeader = "Content-Type: text/x-apple-plist+xml\r\n"; } diff --git a/xbmc/network/AirTunesServer.cpp b/xbmc/network/AirTunesServer.cpp index 8af8319ee5a7c..95a43c1bf0be8 100644 --- a/xbmc/network/AirTunesServer.cpp +++ b/xbmc/network/AirTunesServer.cpp @@ -17,7 +17,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "network/Network.h" #if !defined(TARGET_WINDOWS) #pragma GCC diagnostic ignored "-Wwrite-strings" #endif @@ -148,6 +147,7 @@ LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\ void CAirTunesServer::AudioOutputFunctions::audio_set_metadata(void *cls, void *session, const void *buffer, int buflen) { + return; CAirTunesServer::SetMetadataFromBuffer((char *)buffer, buflen); } @@ -339,7 +339,11 @@ ao_device* CAirTunesServer::AudioOutputFunctions::ao_open_live(int driver_id, ao header.durationMs = 0; if (device->pipe->Write(&header, sizeof(header)) == 0) + { + delete device->pipe; + delete device; return 0; + } ThreadMessage tMsg = { TMSG_MEDIA_STOP }; CApplicationMessenger::Get().SendMessage(tMsg, true); @@ -468,21 +472,13 @@ bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, con { bool success = false; CStdString pw = password; - CNetworkInterface *net = g_application.getNetwork().GetFirstConnectedInterface(); StopServer(true); - if (net) - { - m_macAddress = net->GetMacAddress(); - m_macAddress.Replace(":",""); - while (m_macAddress.size() < 12) - { - m_macAddress = CStdString("0") + m_macAddress; - } - } - else + m_macAddress = g_application.getNetworkManager().GetDefaultConnectionMacAddress(); + m_macAddress.Replace(":",""); + while (m_macAddress.size() < 12) { - m_macAddress = "000102030405"; + m_macAddress = CStdString("0") + m_macAddress; } if (!usePassword) @@ -504,22 +500,23 @@ bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, con CStdString appName; appName.Format("%s@%s", m_macAddress.c_str(), g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME).c_str()); - std::map txt; - txt["cn"] = "0,1"; - txt["ch"] = "2"; - txt["ek"] = "1"; - txt["et"] = "0,1"; - txt["sv"] = "false"; - txt["tp"] = "UDP"; - txt["sm"] = "false"; - txt["ss"] = "16"; - txt["sr"] = "44100"; - txt["pw"] = "false"; - txt["vn"] = "3"; - txt["da"] = "true"; - txt["vs"] = "130.14"; - txt["md"] = "0,1,2"; - txt["txtvers"] = "1"; + std::vector > txt; + txt.push_back(std::make_pair("txtvers", "1")); + txt.push_back(std::make_pair("cn", "0,1")); + txt.push_back(std::make_pair("ch", "2")); + txt.push_back(std::make_pair("ek", "1")); + txt.push_back(std::make_pair("et", "0,1")); + txt.push_back(std::make_pair("sv", "false")); + txt.push_back(std::make_pair("tp", "UDP")); + txt.push_back(std::make_pair("sm", "false")); + txt.push_back(std::make_pair("ss", "16")); + txt.push_back(std::make_pair("sr", "44100")); + txt.push_back(std::make_pair("pw", "false")); + txt.push_back(std::make_pair("vn", "3")); + txt.push_back(std::make_pair("da", "true")); + txt.push_back(std::make_pair("vs", "130.14")); + txt.push_back(std::make_pair("md", "0,1,2")); + txt.push_back(std::make_pair("am", "Xbmc,1")); CZeroconf::GetInstance()->PublishService("servers.airtunes", "_raop._tcp", appName, port, txt); } @@ -658,7 +655,7 @@ bool CAirTunesServer::Initialize(const CStdString &password) if (m_pLibShairport->Load()) { - struct AudioOutput ao; + struct AudioOutput ao = {0}; ao.ao_initialize = AudioOutputFunctions::ao_initialize; ao.ao_play = AudioOutputFunctions::ao_play; ao.ao_default_driver_id = AudioOutputFunctions::ao_default_driver_id; diff --git a/xbmc/network/ConnectionJob.cpp b/xbmc/network/ConnectionJob.cpp new file mode 100644 index 0000000000000..c46ac336cb761 --- /dev/null +++ b/xbmc/network/ConnectionJob.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "ConnectionJob.h" +#include "Application.h" +#include "utils/log.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogBusy.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "security/KeyringManager.h" +#include "settings/GUISettings.h" +#include "settings/AdvancedSettings.h" + +CConnectionJob::CConnectionJob(CConnectionPtr connection, const CIPConfig &ipconfig, CKeyringManager *keyringManager) +{ + m_ipconfig = ipconfig; + m_connection = connection; + m_keyringManager = keyringManager; +} + +bool CConnectionJob::DoWork() +{ + bool result; + + CGUIDialogBusy *busy_dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY); + busy_dialog->Show(); + + // we need to shutdown network services before changing the connection. + // The Network Manager's PumpNetworkEvents will take care of starting them back up. + g_application.getNetworkManager().OnConnectionStateChange(NETWORK_CONNECTION_STATE_DISCONNECTED); + result = m_connection->Connect((IPassphraseStorage*)this, m_ipconfig); + if (!result) + { + // the connect failed, pop a failed dialog + // and revert to the previous connection. + // Not connected. Check network settings. + CGUIDialogOK::ShowAndGetInput(0, 13297, 0, 0); + } + + busy_dialog->Close(); + return result; +} + +void CConnectionJob::InvalidatePassphrase(const std::string &uuid) +{ + m_keyringManager->EraseSecret("network", uuid); + g_guiSettings.SetString("network.passphrase", ""); +} + +bool CConnectionJob::GetPassphrase(const std::string &uuid, std::string &passphrase) +{ + passphrase = g_guiSettings.GetString("network.passphrase"); + if (passphrase.size() > 0) + return true; + /* + CVariant secret; + if (m_keyringManager->FindSecret("network", uuid, secret) && secret.isString()) + { + passphrase = secret.asString(); + return true; + } + */ + else + { + bool result; + CStdString utf8; + if (g_advancedSettings.m_showNetworkPassPhrase) + result = CGUIKeyboardFactory::ShowAndGetInput(utf8, 12340, false); + else + result = CGUIKeyboardFactory::ShowAndGetNewPassword(utf8); + + passphrase = utf8; + StorePassphrase(uuid, passphrase); + return result; + } +} + +void CConnectionJob::StorePassphrase(const std::string &uuid, const std::string &passphrase) +{ + m_keyringManager->StoreSecret("network", uuid, CVariant(passphrase)); + // hack until we get keyring storage working + g_guiSettings.SetString("network.passphrase", passphrase.c_str()); +} diff --git a/xbmc/network/ConnectionJob.h b/xbmc/network/ConnectionJob.h new file mode 100644 index 0000000000000..687a4fa681510 --- /dev/null +++ b/xbmc/network/ConnectionJob.h @@ -0,0 +1,43 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#include "IConnection.h" +#include "utils/Job.h" + +class CKeyringManager; + +class CConnectionJob : public CJob, public IPassphraseStorage +{ +public: + CConnectionJob(CConnectionPtr connection, const CIPConfig &ipconfig, CKeyringManager *keyringManager); + + virtual bool DoWork(); + + virtual void InvalidatePassphrase(const std::string &uuid); + virtual bool GetPassphrase(const std::string &uuid, std::string &passphrase); + virtual void StorePassphrase(const std::string &uuid, const std::string &passphrase); +private: + CIPConfig m_ipconfig; + CConnectionPtr m_connection; + CKeyringManager *m_keyringManager; +}; diff --git a/xbmc/network/EventServer.cpp b/xbmc/network/EventServer.cpp index b4fd8c437157c..beb6c79bafc01 100644 --- a/xbmc/network/EventServer.cpp +++ b/xbmc/network/EventServer.cpp @@ -155,7 +155,7 @@ void CEventServer::Run() CAddress any_addr; CSocketListener listener; int packetSize = 0; - std::map txt; + std::vector > txt; CLog::Log(LOGNOTICE, "ES: Starting UDP Event server on %s:%d", any_addr.Address(), m_iPort); diff --git a/xbmc/network/GUIDialogAccessPoints.cpp b/xbmc/network/GUIDialogAccessPoints.cpp index e04d276357a81..e454e73bc3791 100644 --- a/xbmc/network/GUIDialogAccessPoints.cpp +++ b/xbmc/network/GUIDialogAccessPoints.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Team XBMC + * Copyright (C) 2011-2012 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -13,120 +13,298 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html * */ #include "GUIDialogAccessPoints.h" -#include "guilib/GUIKeyboardFactory.h" -#ifdef _LINUX -#include "linux/NetworkLinux.h" -#endif +#include "dialogs/GUIDialogKeyboardGeneric.h" #include "Application.h" +#include "ApplicationMessenger.h" #include "FileItem.h" #include "guilib/LocalizeStrings.h" +#include "guilib/GUIWindowManager.h" +#include "utils/JobManager.h" +#include "ConnectionJob.h" -#define CONTROL_ACCESS_POINTS 3 +// defines for the controls +#define ACCESS_POINT_LABEL 1 +#define ACCESS_POINT_LIST 3 +//-------------------------------------------------------------- +const std::string EncodeAccessPointParam(const std::string name, const CIPConfig &ipconfig) +{ + // encode CIPConfig structure into a string based ip connect param. + std::string method("dhcp"); + if (ipconfig.m_method == IP_CONFIG_STATIC) + method = "static"; + + // '+' and '\t' are invalid essid characters, + // a ' ' is valid so watch out for those in the name. + const std::string param("name+" + name + "\t" + + "method+" + method + "\t" + + "address+" + ipconfig.m_address + "\t" + + "netmask+" + ipconfig.m_netmask + "\t" + + "gateway+" + ipconfig.m_gateway + "\t" + + "nameserver+" + ipconfig.m_nameserver + "\t"); + return param; +} + +//-------------------------------------------------------------- +//-------------------------------------------------------------- CGUIDialogAccessPoints::CGUIDialogAccessPoints(void) : CGUIDialog(WINDOW_DIALOG_ACCESS_POINTS, "DialogAccessPoints.xml") { - m_accessPoints = new CFileItemList; + m_connectionsFileList = new CFileItemList; } CGUIDialogAccessPoints::~CGUIDialogAccessPoints(void) { - delete m_accessPoints; + delete m_connectionsFileList; } bool CGUIDialogAccessPoints::OnAction(const CAction &action) { if (action.GetID() == ACTION_SELECT_ITEM) { - CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_ACCESS_POINTS); - OnMessage(msg); - int iItem = msg.GetParam1(); + // block users from doing another connection + // while we are already trying to connect. + if (!m_doing_connection) + { + // fetch the current selected item (access point) + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), ACCESS_POINT_LIST); + OnMessage(msg); + int iItem = msg.GetParam1(); + + ConnectionList connections = g_application.getNetworkManager().GetConnections(); + CConnectionJob *connection = new CConnectionJob(connections[iItem], + m_ipconfig, &g_application.getKeyringManager()); + CJobManager::GetInstance().AddJob(connection, this); + m_doing_connection = true; + } + return true; + } + else if (action.GetID() == ACTION_CONNECTIONS_REFRESH) + { + // msg from Network Manager when the network connection changes. + // this is for future support for scanning for new access points. + UpdateConnectionList(); + return true; + } - if (iItem == (int) m_aps.size()) + return CGUIDialog::OnAction(action); +} + +bool CGUIDialogAccessPoints::OnMessage(CGUIMessage& message) +{ + bool result = CGUIDialog::OnMessage(message); + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_INIT: { - m_selectedAPEssId = ""; - if (CGUIKeyboardFactory::ShowAndGetInput(m_selectedAPEssId, g_localizeStrings.Get(789), false)) - { - m_selectedAPEncMode = m_aps[iItem].getEncryptionMode(); - m_wasItemSelected = true; - Close(); - return true; - } + m_use_ipconfig = false; + m_doing_connection = false; + // fetch the param list + std::string param(message.GetStringParam()); + + // network apply vs network connect + if (param.find("name+") != std::string::npos) + { + m_use_ipconfig = true; + // network apply, param contains a description for connecting + // to an access point. we want to find this access point, + // disable the others, then inject a OnAction msg to select it. + DecodeAccessPointParam(param); + // change the label to show we are doing a connection. + CGUIMessage msg(GUI_MSG_LABEL_SET, GetID(), ACCESS_POINT_LABEL); + // Connecting + msg.SetLabel(g_localizeStrings.Get(33203)); + OnMessage(msg); + } + UpdateConnectionList(); + // if we are doing an 'apply', then inject a click on the "selected" item. + if (m_use_ipconfig) + CApplicationMessenger::Get().SendAction(CAction(ACTION_SELECT_ITEM), GetID()); + break; } - else + case GUI_MSG_WINDOW_DEINIT: { - m_selectedAPEssId = m_aps[iItem].getEssId(); - m_selectedAPEncMode = m_aps[iItem].getEncryptionMode(); - m_wasItemSelected = true; - Close(); - return true; } } - return CGUIDialog::OnAction(action); + return result; } -void CGUIDialogAccessPoints::OnInitWindow() +bool CGUIDialogAccessPoints::OnBack(int actionID) { - m_wasItemSelected = false; + // block the user from closing us if we are trying to connect. + if (m_doing_connection) + return false; + else + return CGUIDialog::OnBack(actionID); +} - CGUIDialog::OnInitWindow(); +//-------------------------------------------------------------- +//-------------------------------------------------------------- +void CGUIDialogAccessPoints::OnJobComplete(unsigned int jobID, bool success, CJob *job) +{ + // auto-close when connection job completes + m_doing_connection = false; + if (success) + { + Close(true); + } +} - CGUIMessage msgReset(GUI_MSG_LABEL_RESET, GetID(), CONTROL_ACCESS_POINTS); - OnMessage(msgReset); +void CGUIDialogAccessPoints::UpdateConnectionList() +{ + m_connectionsFileList->Clear(); - m_accessPoints->Clear(); + CGUIMessage msgReset(GUI_MSG_LABEL_RESET, GetID(), ACCESS_POINT_LIST); + OnMessage(msgReset); - CStdString ifaceName(m_interfaceName); - CNetworkInterface* iface = g_application.getNetwork().GetInterfaceByName(ifaceName); - m_aps = iface->GetAccessPoints(); + int connectedItem = 0; + ConnectionList connections = g_application.getNetworkManager().GetConnections(); - for (int i = 0; i < (int) m_aps.size(); i++) + std::string connection_name; + for (size_t i = 0; i < connections.size(); i++) { - CFileItemPtr item(new CFileItem(m_aps[i].getEssId())); + connection_name = connections[i]->GetName(); + CFileItemPtr item(new CFileItem(connection_name)); - int q = m_aps[i].getQuality(); - if (q <= 20) item->SetArt("thumb", "ap-signal1.png"); - else if (q <= 40) item->SetArt("thumb", "ap-signal2.png"); - else if (q <= 60) item->SetArt("thumb", "ap-signal3.png"); - else if (q <= 80) item->SetArt("thumb", "ap-signal4.png"); - else if (q <= 100) item->SetArt("thumb", "ap-signal5.png"); + if (m_use_ipconfig) + { + if (connection_name.find(m_ipname) != std::string::npos) + connectedItem = i; + } + else + { + if (connections[i]->GetState() == NETWORK_CONNECTION_STATE_CONNECTED) + connectedItem = i; + } + if (connections[i]->GetType() == NETWORK_CONNECTION_TYPE_WIFI) + { + int signal, strength = connections[i]->GetStrength(); + if (strength == 0 || strength < 20) + signal = 1; + else if (strength == 20 || strength < 40) + signal = 2; + else if (strength == 40 || strength < 60) + signal = 3; + else if (strength == 60 || strength < 80) + signal = 4; + else + signal = 5; - if (m_aps[i].getEncryptionMode() != ENC_NONE) - item->SetIconImage("ap-lock.png"); + if (strength <= 20) item->SetArt("thumb", "ap-signal1.png"); + else if (strength <= 40) item->SetArt("thumb", "ap-signal2.png"); + else if (strength <= 60) item->SetArt("thumb", "ap-signal3.png"); + else if (strength <= 80) item->SetArt("thumb", "ap-signal4.png"); + else if (strength <= 100) item->SetArt("thumb", "ap-signal5.png"); - m_accessPoints->Add(item); - } + item->SetProperty("signal", signal); + item->SetProperty("encryption", EncryptionToString(connections[i]->GetEncryption())); + } - CFileItemPtr item(new CFileItem(g_localizeStrings.Get(1047))); - m_accessPoints->Add(item); + item->SetProperty("type", ConnectionTypeToString(connections[i]->GetType())); + item->SetProperty("state", ConnectionStateToString(connections[i]->GetState())); + + m_connectionsFileList->Add(item); + } - CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_ACCESS_POINTS, 0, 0, m_accessPoints); + CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), ACCESS_POINT_LIST, connectedItem, 0, m_connectionsFileList); OnMessage(msg); } -void CGUIDialogAccessPoints::SetInterfaceName(CStdString interfaceName) +void CGUIDialogAccessPoints::DecodeAccessPointParam(const std::string ¶m) { - m_interfaceName = interfaceName; + // decode a string based ip connect param into a CIPConfig structure. + std::string::size_type start; + std::string::size_type end; + + start = param.find("name+") + sizeof("name"); + end = param.find("\t", start); + m_ipname = param.substr(start, end - start); + // + start = param.find("method+") + sizeof("method"); + end = param.find("\t", start); + m_ipconfig.m_method = IP_CONFIG_DHCP; + if (param.find("static", start) != std::string::npos) + m_ipconfig.m_method = IP_CONFIG_STATIC; + // + start = param.find("address+") + sizeof("address"); + end = param.find("\t", start); + m_ipconfig.m_address = param.substr(start, end - start); + // + start = param.find("netmask+") + sizeof("netmask"); + end = param.find("\t", start); + m_ipconfig.m_netmask = param.substr(start, end - start); + // + start = param.find("gateway+") + sizeof("gateway"); + end = param.find("\t", start); + m_ipconfig.m_gateway = param.substr(start, end - start); + // + start = param.find("nameserver+") + sizeof("nameserver"); + end = param.find("\t", start); + m_ipconfig.m_nameserver = param.substr(start, end - start); } -CStdString CGUIDialogAccessPoints::GetSelectedAccessPointEssId() +const char *CGUIDialogAccessPoints::ConnectionStateToString(ConnectionState state) { - return m_selectedAPEssId; + switch (state) + { + case NETWORK_CONNECTION_STATE_DISCONNECTED: + return "disconnected"; + case NETWORK_CONNECTION_STATE_CONNECTING: + return "connecting"; + case NETWORK_CONNECTION_STATE_CONNECTED: + return "connected"; + case NETWORK_CONNECTION_STATE_FAILURE: + return "failure"; + case NETWORK_CONNECTION_STATE_UNKNOWN: + default: + return "unknown"; + } + + return ""; } -EncMode CGUIDialogAccessPoints::GetSelectedAccessPointEncMode() +const char *CGUIDialogAccessPoints::ConnectionTypeToString(ConnectionType type) { - return m_selectedAPEncMode; + switch (type) + { + case NETWORK_CONNECTION_TYPE_WIRED: + return "wired"; + case NETWORK_CONNECTION_TYPE_WIFI: + return "wifi"; + case NETWORK_CONNECTION_TYPE_UNKNOWN: + default: + return "unknown"; + } + + return ""; } -bool CGUIDialogAccessPoints::WasItemSelected() +const char *CGUIDialogAccessPoints::EncryptionToString(EncryptionType type) { - return m_wasItemSelected; + switch (type) + { + case NETWORK_CONNECTION_ENCRYPTION_NONE: + return ""; + case NETWORK_CONNECTION_ENCRYPTION_WEP: + return "wep"; + case NETWORK_CONNECTION_ENCRYPTION_WPA: + return "wpa"; + case NETWORK_CONNECTION_ENCRYPTION_WPA2: + return "wpa2"; + case NETWORK_CONNECTION_ENCRYPTION_IEEE8021x: + return "wpa-rsn"; + case NETWORK_CONNECTION_ENCRYPTION_UNKNOWN: + default: + return "unknown"; + } + + return ""; } diff --git a/xbmc/network/GUIDialogAccessPoints.h b/xbmc/network/GUIDialogAccessPoints.h index f76d8b1bf311c..5ada50280b325 100644 --- a/xbmc/network/GUIDialogAccessPoints.h +++ b/xbmc/network/GUIDialogAccessPoints.h @@ -1,8 +1,6 @@ -#ifndef GUI_DIALOG_ACCES_POINTS -#define GUI_DIALOG_ACCES_POINTS - +#pragma once /* - * Copyright (C) 2005-2012 Team XBMC + * Copyright (C) 2005-2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -16,8 +14,9 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html * */ @@ -25,29 +24,35 @@ #include #include "guilib/GUIDialog.h" -#include "Network.h" +#include "IConnection.h" +#include "utils/Job.h" + + +const std::string EncodeAccessPointParam(const std::string name, const CIPConfig &ipconfig); class CFileItemList; -class CGUIDialogAccessPoints : public CGUIDialog +class CGUIDialogAccessPoints : public CGUIDialog, public IJobCallback { public: CGUIDialogAccessPoints(void); virtual ~CGUIDialogAccessPoints(void); - virtual void OnInitWindow(); virtual bool OnAction(const CAction &action); - void SetInterfaceName(CStdString interfaceName); - CStdString GetSelectedAccessPointEssId(); - EncMode GetSelectedAccessPointEncMode(); - bool WasItemSelected(); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnBack(int actionID); + virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job); private: - std::vector m_aps; - CStdString m_interfaceName; - CStdString m_selectedAPEssId; - EncMode m_selectedAPEncMode; - bool m_wasItemSelected; - CFileItemList *m_accessPoints; -}; + void UpdateConnectionList(); + void DecodeAccessPointParam(const std::string ¶m); + + static const char *ConnectionStateToString(ConnectionState state); + static const char *ConnectionTypeToString(ConnectionType type); + static const char *EncryptionToString(EncryptionType type); -#endif + std::string m_ipname; + CIPConfig m_ipconfig; + bool m_use_ipconfig; + bool m_doing_connection; + CFileItemList *m_connectionsFileList; +}; diff --git a/xbmc/network/IConnection.h b/xbmc/network/IConnection.h new file mode 100644 index 0000000000000..fd337e98854c9 --- /dev/null +++ b/xbmc/network/IConnection.h @@ -0,0 +1,209 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ +#include +#include +#include "IPassphraseStorage.h" +#include + +enum ConnectionType +{ + NETWORK_CONNECTION_TYPE_UNKNOWN = 0, + NETWORK_CONNECTION_TYPE_WIRED, + NETWORK_CONNECTION_TYPE_WIFI +}; + +enum ConnectionState +{ + NETWORK_CONNECTION_STATE_UNKNOWN = 0, + NETWORK_CONNECTION_STATE_FAILURE, + NETWORK_CONNECTION_STATE_DISCONNECTED, + NETWORK_CONNECTION_STATE_CONNECTING, + NETWORK_CONNECTION_STATE_CONNECTED +}; + +enum EncryptionType +{ + NETWORK_CONNECTION_ENCRYPTION_UNKNOWN = 0, // This should be used to flag accesspoints which have some encryption which we cannot connect to. + NETWORK_CONNECTION_ENCRYPTION_NONE, + NETWORK_CONNECTION_ENCRYPTION_WEP, + NETWORK_CONNECTION_ENCRYPTION_WPA, + NETWORK_CONNECTION_ENCRYPTION_WPA2, + NETWORK_CONNECTION_ENCRYPTION_IEEE8021x +}; + +enum IPConfigMethod +{ + IP_CONFIG_DHCP, + IP_CONFIG_STATIC, + IP_CONFIG_DISABLED +}; + +class CIPConfig +{ +public: + CIPConfig() + { + reset(); + } + + CIPConfig(IPConfigMethod method, + const std::string &address, const std::string &netmask, + const std::string &gateway, const std::string &nameserver) + { + m_method = method; + m_address = address; + m_netmask = netmask; + m_gateway = gateway; + m_nameserver = nameserver; + } + + void reset() + { + m_method = IP_CONFIG_DISABLED; + m_address = ""; + m_netmask = ""; + m_gateway = ""; + m_nameserver = ""; + } + + IPConfigMethod m_method; + std::string m_address; + std::string m_netmask; + std::string m_gateway; + std::string m_nameserver; +}; + +class IConnection +{ +public: + virtual ~IConnection() { } + + /*! + \brief Get the name of the connection + + \return The name of the connection + \sa IConnection + */ + virtual std::string GetName() const = 0; + + /*! + \brief Get the IP of the connection + + \return The IP of the connection + \sa IConnection + */ + virtual std::string GetAddress() const = 0; + + /*! + \brief Get the netmask of the connection + + \return The netmask of the connection + \sa IConnection + */ + virtual std::string GetNetmask() const = 0; + + /*! + \brief Get the gateway address of the connection + + \return The gateway address of the connection + \sa IConnection + */ + virtual std::string GetGateway() const = 0; + + /*! + \brief Get the name server of the connection + + \return The name server of the connection + \sa IConnection + */ + + virtual std::string GetNameServer() const = 0; + + /*! + \brief Get the mac address of the connection + + \return The mac address of the connection + \sa IConnection + */ + virtual std::string GetMacAddress() const = 0; + + /*! + \brief Get the connection type + + \return The connection type + \sa ConnectionType + */ + virtual ConnectionType GetType() const = 0; + + /*! + \brief Get the state of the connection + + \return The state the connection is currently in. + \sa ConnectionState + */ + virtual ConnectionState GetState() const = 0; + + /*! + \brief Get the speed of the connection + + \return The speed of the connection + \sa IConnection + */ + virtual unsigned int GetSpeed() const = 0; + + /*! + \brief Get the connection type + + \return The connection method + \sa IPConfigMethod + */ + virtual IPConfigMethod GetMethod() const = 0; + + /*! + \brief The signal strength of the connection + + \return The signal strength of the connection + \sa IConnection + */ + virtual unsigned int GetStrength() const = 0; + + /*! + \brief Get the encryption used by the connection + + \return The encryption used by the connection + \sa EncryptionType + */ + virtual EncryptionType GetEncryption() const = 0; + + /*! + \brief Connect to connection + + \param storage a passphrase provider + \param ipconfig a network configuration + \returns true if connected, false if not. + \sa IPassphraseStorage CIPConfig + */ + virtual bool Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig) = 0; +}; + +typedef boost::shared_ptr CConnectionPtr; +typedef std::vector ConnectionList; diff --git a/xbmc/network/INetworkManager.h b/xbmc/network/INetworkManager.h new file mode 100644 index 0000000000000..fafb459e2450d --- /dev/null +++ b/xbmc/network/INetworkManager.h @@ -0,0 +1,68 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IConnection.h" + +/*! + \ingroup network + \brief Callback interface for network events. + + Used by subsystems of the network manager to receive events about the subsystem. + It will be called relatively often from application thread to allow thread safe updates + of the network states. + + \sa INetworkManager + */ +class INetworkEventsCallback +{ +public: + virtual ~INetworkEventsCallback() { } + + virtual void OnConnectionStateChange(ConnectionState state) = 0; + virtual void OnConnectionChange(CConnectionPtr connection) = 0; + virtual void OnConnectionListChange(ConnectionList list) = 0; +}; + + +/*! + \ingroup network + \brief Interface for the network subsystems context + + Used by subsystems of the network manager to receive events about the subsystem. + It will be called relatively often from application thread to allow thread safe updates + of the network states. + + \sa INetworkManager + */ +class INetworkManager +{ +public: + virtual ~INetworkManager() { } + + virtual bool CanManageConnections() = 0; + + virtual ConnectionList GetConnections() = 0; + + virtual bool PumpNetworkEvents(INetworkEventsCallback *callback) = 0; + + virtual bool SendWakeOnLan(const char *mac) {return false;}; +}; diff --git a/xbmc/network/IPassphraseStorage.h b/xbmc/network/IPassphraseStorage.h new file mode 100644 index 0000000000000..894b7ee66a714 --- /dev/null +++ b/xbmc/network/IPassphraseStorage.h @@ -0,0 +1,63 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +class IPassphraseStorage +{ +public: + virtual ~IPassphraseStorage() { } + + /*! + \brief Invalidate passphrase + + While a connection is connecting it may invalidate any stored passphrase. This is used + if the subsystem is not able to store passwords but the connection may have changed passphrase + + \param uuid the unique id of the connection associated with the passphrase + \sa IConnection + */ + virtual void InvalidatePassphrase(const std::string &uuid) = 0; + + /*! + \brief Get passphrase + + While a connection is connecting it may need to acquire a passphrase. This is used + if the subsystem has no stored passphrase. + + \param uuid the unique id of the connection. + \param passphrase a string which the passphrase storage will fill in the passphrase. + \return true if the passphrase was filled and false if it was some form of failure acquiring it. + \sa IConnection + */ + virtual bool GetPassphrase(const std::string &uuid, std::string &passphrase) = 0; + + /*! + \brief Store passphrase + + While a connection is connecting it may need to store a passphrase. This is used + if the subsystem is not capable of storing the passphrase itself. + + \param uuid the unique id of the connection. + \param passphrase is the passphrase to be stored + \sa IConnection + */ + virtual void StorePassphrase(const std::string &uuid, const std::string &passphrase) = 0; +}; diff --git a/xbmc/network/Makefile.in b/xbmc/network/Makefile.in index e2ffb0bce9454..ecef3a8bec4b6 100644 --- a/xbmc/network/Makefile.in +++ b/xbmc/network/Makefile.in @@ -5,13 +5,16 @@ SRCS += EventPacket.cpp SRCS += EventServer.cpp SRCS += GUIDialogAccessPoints.cpp SRCS += GUIDialogNetworkSetup.cpp -SRCS += Network.cpp SRCS += Socket.cpp SRCS += TCPServer.cpp SRCS += UdpClient.cpp SRCS += WebServer.cpp SRCS += ZeroconfBrowser.cpp SRCS += Zeroconf.cpp +SRCS += NetworkManager.cpp +SRCS += NetworkUtils.cpp +SRCS += NullNetworkManager.cpp +SRCS += ConnectionJob.cpp ifeq (@USE_AIRPLAY@, 1) SRCS += AirPlayServer.cpp diff --git a/xbmc/network/Network.cpp b/xbmc/network/Network.cpp deleted file mode 100644 index 9c569582b99d6..0000000000000 --- a/xbmc/network/Network.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2005-2012 Team XBMC - * http://www.xbmc.org - * - * This Program 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, or (at your option) - * any later version. - * - * This Program 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 XBMC; see the file COPYING. If not, see - * . - * - */ - -#include "system.h" -#include "Network.h" -#include "Application.h" -#include "ApplicationMessenger.h" -#include "libscrobbler/lastfmscrobbler.h" -#include "libscrobbler/librefmscrobbler.h" -#include "utils/RssReader.h" -#include "utils/log.h" -#include "guilib/LocalizeStrings.h" -#include "dialogs/GUIDialogKaiToast.h" - -#include -#include -#include - -using namespace std; - -/* slightly modified in_ether taken from the etherboot project (http://sourceforge.net/projects/etherboot) */ -bool in_ether (const char *bufp, unsigned char *addr) -{ - if (strlen(bufp) != 17) - return false; - - char c; - const char *orig; - unsigned char *ptr = addr; - unsigned val; - - int i = 0; - orig = bufp; - - while ((*bufp != '\0') && (i < 6)) - { - val = 0; - c = *bufp++; - - if (isdigit(c)) - val = c - '0'; - else if (c >= 'a' && c <= 'f') - val = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - val = c - 'A' + 10; - else - return false; - - val <<= 4; - c = *bufp; - if (isdigit(c)) - val |= c - '0'; - else if (c >= 'a' && c <= 'f') - val |= c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - val |= c - 'A' + 10; - else if (c == ':' || c == '-' || c == 0) - val >>= 4; - else - return false; - - if (c != 0) - bufp++; - - *ptr++ = (unsigned char) (val & 0377); - i++; - - if (*bufp == ':' || *bufp == '-') - bufp++; - } - - if (bufp - orig != 17) - return false; - - return true; -} - -CNetwork::CNetwork() -{ - CApplicationMessenger::Get().NetworkMessage(SERVICES_UP, 0); -} - -CNetwork::~CNetwork() -{ - CApplicationMessenger::Get().NetworkMessage(SERVICES_DOWN, 0); -} - -int CNetwork::ParseHex(char *str, unsigned char *addr) -{ - int len = 0; - - while (*str) - { - int tmp; - if (str[1] == 0) - return -1; - if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1) - return -1; - addr[len] = tmp; - len++; - str += 2; - } - - return len; -} - -CStdString CNetwork::GetHostName(void) -{ - char hostName[128]; - if (gethostname(hostName, sizeof(hostName))) - return CStdString("unknown"); - else - return CStdString(hostName); -} - - -CNetworkInterface* CNetwork::GetFirstConnectedInterface() -{ - vector& ifaces = GetInterfaceList(); - vector::const_iterator iter = ifaces.begin(); - while (iter != ifaces.end()) - { - CNetworkInterface* iface = *iter; - if (iface && iface->IsConnected()) - return iface; - ++iter; - } - - return NULL; -} - -bool CNetwork::HasInterfaceForIP(unsigned long address) -{ - unsigned long subnet; - unsigned long local; - vector& ifaces = GetInterfaceList(); - vector::const_iterator iter = ifaces.begin(); - while (iter != ifaces.end()) - { - CNetworkInterface* iface = *iter; - if (iface && iface->IsConnected()) - { - subnet = ntohl(inet_addr(iface->GetCurrentNetmask())); - local = ntohl(inet_addr(iface->GetCurrentIPAddress())); - if( (address & subnet) == (local & subnet) ) - return true; - } - ++iter; - } - - return false; -} - -bool CNetwork::IsAvailable(bool wait /*= false*/) -{ - if (wait) - { - // NOTE: Not implemented in linuxport branch as 99.9% of the time - // we have the network setup already. Trunk code has a busy - // wait for 5 seconds here. - } - - vector& ifaces = GetInterfaceList(); - return (ifaces.size() != 0); -} - -bool CNetwork::IsConnected() -{ - return GetFirstConnectedInterface() != NULL; -} - -CNetworkInterface* CNetwork::GetInterfaceByName(CStdString& name) -{ - vector& ifaces = GetInterfaceList(); - vector::const_iterator iter = ifaces.begin(); - while (iter != ifaces.end()) - { - CNetworkInterface* iface = *iter; - if (iface && iface->GetName().Equals(name)) - return iface; - ++iter; - } - - return NULL; -} - -void CNetwork::NetworkMessage(EMESSAGE message, int param) -{ - switch( message ) - { - case SERVICES_UP: - { - CLog::Log(LOGDEBUG, "%s - Starting network services",__FUNCTION__); - StartServices(); - } - break; - case SERVICES_DOWN: - { - CLog::Log(LOGDEBUG, "%s - Signaling network services to stop",__FUNCTION__); - StopServices(false); //tell network services to stop, but don't wait for them yet - CLog::Log(LOGDEBUG, "%s - Waiting for network services to stop",__FUNCTION__); - StopServices(true); //wait for network services to stop - } - break; - } -} - -bool CNetwork::WakeOnLan(const char* mac) -{ - int i, j, packet; - unsigned char ethaddr[8]; - unsigned char buf [128]; - unsigned char *ptr; - - // Fetch the hardware address - if (!in_ether(mac, ethaddr)) - { - CLog::Log(LOGERROR, "%s - Invalid hardware address specified (%s)", __FUNCTION__, mac); - return false; - } - - // Setup the socket - if ((packet = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) - { - CLog::Log(LOGERROR, "%s - Unable to create socket (%s)", __FUNCTION__, strerror (errno)); - return false; - } - - // Set socket options - struct sockaddr_in saddr; - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); - saddr.sin_port = htons(9); - - unsigned int value = 1; - if (setsockopt (packet, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR) - { - CLog::Log(LOGERROR, "%s - Unable to set socket options (%s)", __FUNCTION__, strerror (errno)); - closesocket(packet); - return false; - } - - // Build the magic packet (6 x 0xff + 16 x MAC address) - ptr = buf; - for (i = 0; i < 6; i++) - *ptr++ = 0xff; - - for (j = 0; j < 16; j++) - for (i = 0; i < 6; i++) - *ptr++ = ethaddr[i]; - - // Send the magic packet - if (sendto (packet, (char *)buf, 102, 0, (struct sockaddr *)&saddr, sizeof (saddr)) < 0) - { - CLog::Log(LOGERROR, "%s - Unable to send magic packet (%s)", __FUNCTION__, strerror (errno)); - closesocket(packet); - return false; - } - - closesocket(packet); - CLog::Log(LOGINFO, "%s - Magic packet send to '%s'", __FUNCTION__, mac); - return true; -} - -void CNetwork::StartServices() -{ -#ifdef HAS_TIME_SERVER - g_application.StartTimeServer(); -#endif -#ifdef HAS_WEB_SERVER - if (!g_application.StartWebServer()) - CGUIDialogKaiToast::QueueNotification("DefaultIconWarning.png", g_localizeStrings.Get(33101), g_localizeStrings.Get(33100)); -#endif -#ifdef HAS_UPNP - g_application.StartUPnP(); -#endif -#ifdef HAS_EVENT_SERVER - if (!g_application.StartEventServer()) - CGUIDialogKaiToast::QueueNotification("DefaultIconWarning.png", g_localizeStrings.Get(33102), g_localizeStrings.Get(33100)); -#endif -#ifdef HAS_JSONRPC - if (!g_application.StartJSONRPCServer()) - CGUIDialogKaiToast::QueueNotification("DefaultIconWarning.png", g_localizeStrings.Get(33103), g_localizeStrings.Get(33100)); -#endif -#ifdef HAS_ZEROCONF - g_application.StartZeroconf(); -#endif -#ifdef HAS_AIRPLAY - g_application.StartAirplayServer(); -#endif - CLastfmScrobbler::GetInstance()->Init(); - CLibrefmScrobbler::GetInstance()->Init(); - g_rssManager.Start(); -} - -void CNetwork::StopServices(bool bWait) -{ - if (bWait) - { -#ifdef HAS_TIME_SERVER - g_application.StopTimeServer(); -#endif -#ifdef HAS_UPNP - g_application.StopUPnP(bWait); -#endif -#ifdef HAS_ZEROCONF - g_application.StopZeroconf(); -#endif -#ifdef HAS_WEB_SERVER - g_application.StopWebServer(); -#endif - CLastfmScrobbler::GetInstance()->Term(); - CLibrefmScrobbler::GetInstance()->Term(); - // smb.Deinit(); if any file is open over samba this will break. - - g_rssManager.Stop(); - } - -#ifdef HAS_EVENT_SERVER - g_application.StopEventServer(bWait, false); -#endif -#ifdef HAS_JSONRPC - g_application.StopJSONRPCServer(bWait); -#endif -#if defined(HAS_AIRPLAY) || defined(HAS_AIRTUNES) - g_application.StopAirplayServer(bWait); -#endif -} diff --git a/xbmc/network/Network.h b/xbmc/network/Network.h deleted file mode 100644 index 1aaa764cea179..0000000000000 --- a/xbmc/network/Network.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef NETWORK_H_ -#define NETWORK_H_ - -/* - * Copyright (C) 2005-2012 Team XBMC - * http://www.xbmc.org - * - * This Program 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, or (at your option) - * any later version. - * - * This Program 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 XBMC; see the file COPYING. If not, see - * . - * - */ - -#include -#include "utils/StdString.h" -#include "system.h" - -enum EncMode { ENC_NONE = 0, ENC_WEP = 1, ENC_WPA = 2, ENC_WPA2 = 3 }; -enum NetworkAssignment { NETWORK_DASH = 0, NETWORK_DHCP = 1, NETWORK_STATIC = 2, NETWORK_DISABLED = 3 }; - -class NetworkAccessPoint -{ -public: - NetworkAccessPoint(CStdString& essId, int quality, EncMode encryption) - { - m_essId = essId; - m_quality = quality; - m_encryptionMode = encryption; - } - - CStdString getEssId() { return m_essId; } - int getQuality() { return m_quality; } - EncMode getEncryptionMode() { return m_encryptionMode; } - -private: - CStdString m_essId; - int m_quality; - EncMode m_encryptionMode; -}; - -class CNetworkInterface -{ -public: - virtual ~CNetworkInterface() {}; - - virtual CStdString& GetName(void) = 0; - - virtual bool IsEnabled(void) = 0; - virtual bool IsConnected(void) = 0; - virtual bool IsWireless(void) = 0; - - virtual CStdString GetMacAddress(void) = 0; - virtual void GetMacAddressRaw(char rawMac[6]) = 0; - - virtual CStdString GetCurrentIPAddress() = 0; - virtual CStdString GetCurrentNetmask() = 0; - virtual CStdString GetCurrentDefaultGateway(void) = 0; - virtual CStdString GetCurrentWirelessEssId(void) = 0; - - // Returns the list of access points in the area - virtual std::vector GetAccessPoints(void) = 0; - - virtual void GetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode) = 0; - virtual void SetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode) = 0; -}; - - - -class CNetwork -{ -public: - enum EMESSAGE - { - SERVICES_UP, - SERVICES_DOWN - }; - - CNetwork(); - virtual ~CNetwork(); - - // Return our hostname - virtual CStdString GetHostName(void); - - // Return the list of interfaces - virtual std::vector& GetInterfaceList(void) = 0; - CNetworkInterface* GetInterfaceByName(CStdString& name); - - // Return the first interface which is active - virtual CNetworkInterface* GetFirstConnectedInterface(void); - - // Return true if there is a interface for the same network as address - bool HasInterfaceForIP(unsigned long address); - - // Return true if there's at least one defined network interface - bool IsAvailable(bool wait = false); - - // Return true if there's at least one interface which is connected - bool IsConnected(void); - - // Return true if the magic packet was send - bool WakeOnLan(const char *mac); - - // Get/set the nameserver(s) - virtual std::vector GetNameServers(void) = 0; - virtual void SetNameServers(std::vector nameServers) = 0; - - // callback from application controlled thread to handle any setup - void NetworkMessage(EMESSAGE message, int param); - - void StartServices(); - void StopServices(bool bWait); - - static int ParseHex(char *str, unsigned char *addr); -}; -#ifdef HAS_LINUX_NETWORK -#include "linux/NetworkLinux.h" -#else -#include "windows/NetworkWin32.h" -#endif -#endif diff --git a/xbmc/network/NetworkManager.cpp b/xbmc/network/NetworkManager.cpp new file mode 100644 index 0000000000000..275692cb6ebe3 --- /dev/null +++ b/xbmc/network/NetworkManager.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "NetworkManager.h" +#include "NullNetworkManager.h" +#include "Application.h" +#include "ApplicationMessenger.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/GUIWindowManager.h" +#include "libscrobbler/lastfmscrobbler.h" +#include "libscrobbler/librefmscrobbler.h" +#include "linux/ConnmanNetworkManager.h" +#include "linux/PosixNetworkManager.h" +#include "windows/WinNetworkManager.h" +#include "utils/log.h" +#include "utils/RssReader.h" + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +CNetworkManager::CNetworkManager() +{ + m_timer = NULL; + m_instance = NULL; + m_state = NETWORK_CONNECTION_STATE_UNKNOWN; +} + +CNetworkManager::~CNetworkManager() +{ + delete m_instance; +} + +void CNetworkManager::Initialize() +{ +#ifdef HAS_DBUS +// if (CConnmanNetworkManager::HasConnman()) +// m_instance = new CConnmanNetworkManager(); +#endif + +#if defined(TARGET_POSIX) + if (m_instance == NULL) + m_instance = new CPosixNetworkManager(); +#endif + +#ifdef TARGET_WINDOWS + if (m_instance == NULL) + m_instance = new CWinNetworkManager(); +#endif + + if (m_instance == NULL) + m_instance = new CNullNetworkManager(); + + m_defaultConnection = CConnectionPtr(new CNullConnection()); + OnConnectionListChange(m_instance->GetConnections()); +} + +bool CNetworkManager::PumpNetworkEvents() +{ +#if defined(TARGET_ANDROID) + if (!g_application.m_pPlayer) + { + if (!m_timer && !IsConnected()) + { + CLog::Log(LOGDEBUG, "NetworkManager: not connected, bgn timout"); + m_timer = new CStopWatch(); + m_timer->StartZero(); + } + } + if (m_timer && m_timer->GetElapsedSeconds() > 15) + { + CLog::Log(LOGDEBUG, "NetworkManager: not connected, end timout"); + OnConnectionListChange(m_instance->GetConnections()); + delete m_timer, m_timer = NULL; + } +#endif + + return m_instance->PumpNetworkEvents(this); +} + +std::string CNetworkManager::GetDefaultConnectionName() +{ + if (m_defaultConnection) + return m_defaultConnection->GetName(); + else + return std::string("opps"); +} + +std::string CNetworkManager::GetDefaultConnectionAddress() +{ + if (m_defaultConnection) + return m_defaultConnection->GetAddress(); + else + return std::string("opps"); +} + +std::string CNetworkManager::GetDefaultConnectionNetmask() +{ + if (m_defaultConnection) + return m_defaultConnection->GetNetmask(); + else + return std::string("opps"); +} + +std::string CNetworkManager::GetDefaultConnectionMacAddress() +{ + if (m_defaultConnection) + return m_defaultConnection->GetMacAddress(); + else + return std::string("opps"); +} + +std::string CNetworkManager::GetDefaultConnectionGateway() +{ + if (m_defaultConnection) + return m_defaultConnection->GetGateway(); + else + return std::string("opps"); +} + +std::string CNetworkManager::GetDefaultConnectionNameServer() +{ + if (m_defaultConnection) + return m_defaultConnection->GetNameServer(); + else + return std::string("127.0.0.1"); +} + +ConnectionType CNetworkManager::GetDefaultConnectionType() +{ + if (m_defaultConnection) + return m_defaultConnection->GetType(); + else + return NETWORK_CONNECTION_TYPE_UNKNOWN; +} + +IPConfigMethod CNetworkManager::GetDefaultConnectionMethod() +{ + if (m_defaultConnection) + return m_defaultConnection->GetMethod(); + else + return IP_CONFIG_DISABLED; +} + +ConnectionState CNetworkManager::GetDefaultConnectionState() +{ + return m_state; +} + +bool CNetworkManager::IsConnected() +{ + return GetDefaultConnectionState() == NETWORK_CONNECTION_STATE_CONNECTED; +} + +bool CNetworkManager::IsAvailable(bool wait) +{ + return true; +} + +bool CNetworkManager::CanManageConnections() +{ + return m_instance->CanManageConnections(); +} + +ConnectionList CNetworkManager::GetConnections() +{ + return m_connections; +} + +void CNetworkManager::OnConnectionStateChange(ConnectionState state) +{ + ConnectionState oldState = m_state; + m_state = state; + + if (m_state != oldState) + CLog::Log(LOGDEBUG, "NetworkManager: State changed to %s", ConnectionStateToString(m_state)); + + if (oldState != NETWORK_CONNECTION_STATE_CONNECTED && m_state == NETWORK_CONNECTION_STATE_CONNECTED) + StartServices(); + else if (oldState == NETWORK_CONNECTION_STATE_CONNECTED && oldState != m_state) + StopServices(); +} + +void CNetworkManager::OnConnectionChange(CConnectionPtr connection) +{ + if (connection->GetState() == NETWORK_CONNECTION_STATE_CONNECTED) + m_defaultConnection = connection; + + if (g_windowManager.GetWindow(WINDOW_DIALOG_ACCESS_POINTS)) + { + CAction action(ACTION_CONNECTIONS_REFRESH); + CApplicationMessenger::Get().SendAction(action, WINDOW_DIALOG_ACCESS_POINTS); + } +} + +void CNetworkManager::OnConnectionListChange(ConnectionList list) +{ + m_connections = list; + + for (unsigned int i = 0; i < m_connections.size(); i++) + { + if (m_connections[i]->GetState() == NETWORK_CONNECTION_STATE_CONNECTED) + { + m_defaultConnection = m_connections[i]; + OnConnectionStateChange(NETWORK_CONNECTION_STATE_CONNECTED); + break; + } + } + + if (g_windowManager.GetWindow(WINDOW_DIALOG_ACCESS_POINTS)) + { + CAction action(ACTION_CONNECTIONS_REFRESH); + CApplicationMessenger::Get().SendAction(action, WINDOW_DIALOG_ACCESS_POINTS); + } +} + +void CNetworkManager::NetworkMessage(EMESSAGE message, int param) +{ + switch( message ) + { + case SERVICES_UP: + { + CLog::Log(LOGDEBUG, "%s - Starting network services",__FUNCTION__); + StartServices(); + } + break; + case SERVICES_DOWN: + { + CLog::Log(LOGDEBUG, "%s - Signaling network services to stop",__FUNCTION__); + StopServices(false); //tell network services to stop, but don't wait for them yet + CLog::Log(LOGDEBUG, "%s - Waiting for network services to stop",__FUNCTION__); + StopServices(true); //wait for network services to stop + } + break; + } +} + +bool CNetworkManager::WakeOnLan(const char *mac) +{ + return m_instance->SendWakeOnLan(mac); +} + +void CNetworkManager::StartServices() +{ + CLog::Log(LOGDEBUG, "NetworkManager: Starting network services"); + + // TODO: fix properly + system("/etc/init.d/S49ntp restart"); + +#ifdef HAS_TIME_SERVER + g_application.StartTimeServer(); +#endif +#ifdef HAS_WEB_SERVER + if (!g_application.StartWebServer()) + CGUIDialogKaiToast::QueueNotification("DefaultIconWarning.png", g_localizeStrings.Get(33101), g_localizeStrings.Get(33100)); +#endif +#ifdef HAS_UPNP + g_application.StartUPnP(); +#endif +#ifdef HAS_EVENT_SERVER + if (!g_application.StartEventServer()) + CGUIDialogKaiToast::QueueNotification("DefaultIconWarning.png", g_localizeStrings.Get(33102), g_localizeStrings.Get(33100)); +#endif +#ifdef HAS_DBUS_SERVER + g_application.StartDbusServer(); +#endif +#ifdef HAS_JSONRPC + if (!g_application.StartJSONRPCServer()) + CGUIDialogKaiToast::QueueNotification("DefaultIconWarning.png", g_localizeStrings.Get(33103), g_localizeStrings.Get(33100)); +#endif +#ifdef HAS_ZEROCONF + g_application.StartZeroconf(); +#endif +#ifdef HAS_AIRPLAY + g_application.StartAirplayServer(); +#endif + CLastfmScrobbler::GetInstance()->Init(); + CLibrefmScrobbler::GetInstance()->Init(); + g_rssManager.Start(); +} + +void CNetworkManager::StopServices() +{ + CLog::Log(LOGDEBUG, "NetworkManager: Stopping network services"); + StopServices(false); + CLog::Log(LOGDEBUG, "NetworkManager: Waiting for network services to stop"); + StopServices(true); +} + +void CNetworkManager::StopServices(bool wait) +{ + if (wait) + { +#ifdef HAS_TIME_SERVER + g_application.StopTimeServer(); +#endif +#ifdef HAS_UPNP + g_application.StopUPnP(wait); +#endif +#ifdef HAS_ZEROCONF + g_application.StopZeroconf(); +#endif +#ifdef HAS_WEB_SERVER + g_application.StopWebServer(); +#endif + CLastfmScrobbler::GetInstance()->Term(); + CLibrefmScrobbler::GetInstance()->Term(); + // smb.Deinit(); if any file is open over samba this will break. + + g_rssManager.Stop(); + } + +#ifdef HAS_EVENT_SERVER + g_application.StopEventServer(wait, false); +#endif +#ifdef HAS_DBUS_SERVER + g_application.StopDbusServer(wait); +#endif +#ifdef HAS_JSONRPC + g_application.StopJSONRPCServer(wait); +#endif +#if defined(HAS_AIRPLAY) || defined(HAS_AIRTUNES) + g_application.StopAirplayServer(wait); +#endif +} + +const char *CNetworkManager::ConnectionStateToString(ConnectionState state) +{ + switch (state) + { + case NETWORK_CONNECTION_STATE_FAILURE: + return "failure"; + case NETWORK_CONNECTION_STATE_DISCONNECTED: + return "disconnect"; + case NETWORK_CONNECTION_STATE_CONNECTING: + return "connecting"; + case NETWORK_CONNECTION_STATE_CONNECTED: + return "connected"; + case NETWORK_CONNECTION_STATE_UNKNOWN: + default: + return "unknown"; + } +} diff --git a/xbmc/network/NetworkManager.h b/xbmc/network/NetworkManager.h new file mode 100644 index 0000000000000..010eb6034e89d --- /dev/null +++ b/xbmc/network/NetworkManager.h @@ -0,0 +1,83 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "INetworkManager.h" + +class CStopWatch; + +class CNetworkManager : public INetworkEventsCallback +{ +public: + enum EMESSAGE + { + SERVICES_UP, + SERVICES_DOWN + }; + + CNetworkManager(); + virtual ~CNetworkManager(); + + void Initialize(); + + bool PumpNetworkEvents(); + + std::string GetDefaultConnectionName(); + std::string GetDefaultConnectionAddress(); + std::string GetDefaultConnectionNetmask(); + std::string GetDefaultConnectionGateway(); + std::string GetDefaultConnectionNameServer(); + std::string GetDefaultConnectionMacAddress(); + + ConnectionType GetDefaultConnectionType(); + ConnectionState GetDefaultConnectionState(); + IPConfigMethod GetDefaultConnectionMethod(); + + bool IsConnected(); + + bool IsAvailable(bool wait = false); + + bool CanManageConnections(); + + ConnectionList GetConnections(); + + virtual void OnConnectionStateChange(ConnectionState state); + virtual void OnConnectionChange(CConnectionPtr connection); + virtual void OnConnectionListChange(ConnectionList list); + + // Return true if the magic packet was send + bool WakeOnLan(const char *mac); + + // callback from application controlled thread to handle any setup + void NetworkMessage(EMESSAGE message, int param); + + void StartServices(); + void StopServices(); +private: + void StopServices(bool wait); + const char* ConnectionStateToString(ConnectionState state); + + INetworkManager *m_instance; + CConnectionPtr m_defaultConnection; + ConnectionList m_connections; + ConnectionState m_state; + CStopWatch *m_timer; +}; diff --git a/xbmc/network/NetworkUtils.cpp b/xbmc/network/NetworkUtils.cpp new file mode 100644 index 0000000000000..810533d41ae28 --- /dev/null +++ b/xbmc/network/NetworkUtils.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "NetworkUtils.h" +#include +#include + +std::string CNetworkUtils::IPTotring(unsigned int ip) +{ + char buffer[16]; + sprintf(buffer, "%i:%i:%i:%i", ip & 0xff, (ip & (0xff << 8)) >> 8, (ip & (0xff << 16)) >> 16, (ip & (0xff << 24)) >> 24); + std::string returnString = buffer; + return returnString; +} + +// slightly modified in_ether taken from the etherboot project (http://sourceforge.net/projects/etherboot) +bool CNetworkUtils::in_ether(const char *bufp, unsigned char *addr) +{ + if (strlen(bufp) != 17) + return false; + + char c; + const char *orig; + unsigned char *ptr = addr; + unsigned val; + + int i = 0; + orig = bufp; + + while ((*bufp != '\0') && (i < 6)) + { + val = 0; + c = *bufp++; + + if (isdigit(c)) + val = c - '0'; + else if (c >= 'a' && c <= 'f') + val = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = c - 'A' + 10; + else + return false; + + val <<= 4; + c = *bufp; + if (isdigit(c)) + val |= c - '0'; + else if (c >= 'a' && c <= 'f') + val |= c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val |= c - 'A' + 10; + else if (c == ':' || c == '-' || c == 0) + val >>= 4; + else + return false; + + if (c != 0) + bufp++; + + *ptr++ = (unsigned char) (val & 0377); + i++; + + if (*bufp == ':' || *bufp == '-') + bufp++; + } + + if (bufp - orig != 17) + return false; + + return true; +} diff --git a/xbmc/network/NetworkUtils.h b/xbmc/network/NetworkUtils.h new file mode 100644 index 0000000000000..bad4f4eafdd62 --- /dev/null +++ b/xbmc/network/NetworkUtils.h @@ -0,0 +1,31 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include + +class CNetworkUtils +{ +public: + static std::string IPTotring(unsigned int ip); + static bool in_ether(const char *bufp, unsigned char *addr); + +}; diff --git a/xbmc/network/NullNetworkManager.cpp b/xbmc/network/NullNetworkManager.cpp new file mode 100644 index 0000000000000..98c84b33671f1 --- /dev/null +++ b/xbmc/network/NullNetworkManager.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "NullNetworkManager.h" + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +CNullConnection::~CNullConnection() +{ +} + +std::string CNullConnection::GetName() const +{ + return "no connection"; +} + +std::string CNullConnection::GetAddress() const +{ + return "127.0.0.1"; +} + +std::string CNullConnection::GetNetmask() const +{ + return "255.255.255.0"; +} + +std::string CNullConnection::GetGateway() const +{ + return "127.0.0.1"; +} + +std::string CNullConnection::GetNameServer() const +{ + return "127.0.0.1"; +} + +std::string CNullConnection::GetMacAddress() const +{ + return "00:00:00:00:00:00"; +} + +ConnectionType CNullConnection::GetType() const +{ + return NETWORK_CONNECTION_TYPE_UNKNOWN; +} + +unsigned int CNullConnection::GetSpeed() const +{ + return 100; +} + +ConnectionState CNullConnection::GetState() const +{ + return NETWORK_CONNECTION_STATE_DISCONNECTED; +} + +IPConfigMethod CNullConnection::GetMethod() const +{ + return IP_CONFIG_DISABLED; +} + +unsigned int CNullConnection::GetStrength() const +{ + return 100; +} + +EncryptionType CNullConnection::GetEncryption() const +{ + return NETWORK_CONNECTION_ENCRYPTION_NONE; +} + +bool CNullConnection::Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig) +{ + return false; +} + + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +CNullNetworkManager::~CNullNetworkManager() +{ +} + +bool CNullNetworkManager::CanManageConnections() +{ + return false; +} + +ConnectionList CNullNetworkManager::GetConnections() +{ + ConnectionList list; + list.push_back(CConnectionPtr(new CNullConnection())); + return list; +} + +bool CNullNetworkManager::PumpNetworkEvents(INetworkEventsCallback *callback) +{ + return true; +} diff --git a/xbmc/network/NullNetworkManager.h b/xbmc/network/NullNetworkManager.h new file mode 100644 index 0000000000000..7268bd4e1da92 --- /dev/null +++ b/xbmc/network/NullNetworkManager.h @@ -0,0 +1,55 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "INetworkManager.h" + +class CNullConnection : public IConnection +{ +public: + virtual ~CNullConnection(); + + virtual std::string GetName() const; + virtual std::string GetAddress() const; + virtual std::string GetNetmask() const; + virtual std::string GetGateway() const; + virtual std::string GetNameServer() const; + virtual std::string GetMacAddress() const; + + virtual ConnectionType GetType() const; + virtual ConnectionState GetState() const; + virtual unsigned int GetSpeed() const; + virtual IPConfigMethod GetMethod() const; + virtual unsigned int GetStrength() const; + virtual EncryptionType GetEncryption() const; + + virtual bool Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig); +}; + +class CNullNetworkManager : public INetworkManager +{ + virtual ~CNullNetworkManager(); + + virtual bool CanManageConnections(); + + virtual ConnectionList GetConnections(); + virtual bool PumpNetworkEvents(INetworkEventsCallback *callback); +}; diff --git a/xbmc/network/UdpClient.cpp b/xbmc/network/UdpClient.cpp index 8fad6579df6e5..0aa581d61aea9 100644 --- a/xbmc/network/UdpClient.cpp +++ b/xbmc/network/UdpClient.cpp @@ -23,7 +23,6 @@ #ifdef _LINUX #include #endif -#include "Network.h" #include "guilib/GraphicContext.h" #include "utils/log.h" #include "utils/TimeUtils.h" diff --git a/xbmc/network/Zeroconf.cpp b/xbmc/network/Zeroconf.cpp index e83eced22ac4e..ec69f70a3a14c 100644 --- a/xbmc/network/Zeroconf.cpp +++ b/xbmc/network/Zeroconf.cpp @@ -43,7 +43,7 @@ //should be optimized away class CZeroconfDummy : public CZeroconf { - virtual bool doPublishService(const std::string&, const std::string&, const std::string&, unsigned int, std::map) + virtual bool doPublishService(const std::string&, const std::string&, const std::string&, unsigned int, const std::vector >&) { return false; } @@ -68,7 +68,7 @@ bool CZeroconf::PublishService(const std::string& fcr_identifier, const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt) + const std::vector >& txt) { CSingleLock lock(*mp_crit_sec); CZeroconf::PublishInfo info = {fcr_type, fcr_name, f_port, txt}; diff --git a/xbmc/network/Zeroconf.h b/xbmc/network/Zeroconf.h index 52075a5b07ee6..377f817d0f4f3 100644 --- a/xbmc/network/Zeroconf.h +++ b/xbmc/network/Zeroconf.h @@ -22,6 +22,7 @@ #include #include +#include #include "utils/Job.h" class CCriticalSection; @@ -49,7 +50,7 @@ class CZeroconf const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt); + const std::vector >& txt); ///removes the specified service ///returns false if fcr_identifier does not exist @@ -86,7 +87,7 @@ class CZeroconf const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt) = 0; + const std::vector >& txt) = 0; //removes the service if published virtual bool doRemoveService(const std::string& fcr_ident) = 0; @@ -107,7 +108,7 @@ class CZeroconf std::string type; std::string name; unsigned int port; - std::map txt; + std::vector > txt; }; //protects data diff --git a/xbmc/network/linux/ConnmanConnection.cpp b/xbmc/network/linux/ConnmanConnection.cpp new file mode 100644 index 0000000000000..9f1dc9aac6c44 --- /dev/null +++ b/xbmc/network/linux/ConnmanConnection.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "ConnmanConnection.h" + +#ifdef HAS_DBUS +#include "linux/DBusUtil.h" +#include "linux/DBusMessage.h" +#include "utils/log.h" + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +CConnmanConnection::CConnmanConnection(const char *serviceObject) +{ + m_serviceObject = serviceObject; + + // should use CONNMAN_SERVICE here (in connman/dbus.h) + CDBusMessage message("net.connman", serviceObject, "net.connman.Service", "GetProperties"); + CDBusReplyPtr reply = message.SendSystem(); + m_properties = reply->GetNextArgument(); + + UpdateConnection(); + + dbus_error_init (&m_error); + + // TODO: do not use dbus_connection_pop_message() that requires the use of a + // private connection + m_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &m_error); + if (m_connection) + { + dbus_connection_set_exit_on_disconnect(m_connection, false); + + dbus_bus_add_match(m_connection, "type='signal',interface='net.connman.Service'", &m_error); + dbus_connection_flush(m_connection); + if (dbus_error_is_set(&m_error)) + { + CLog::Log(LOGERROR, "ConnmanConnection: %s Failed to attach to signal %s", serviceObject, m_error.message); + dbus_connection_close(m_connection); + dbus_connection_unref(m_connection); + m_connection = NULL; + } + } + else + CLog::Log(LOGERROR, "ConnmanConnection: %s Failed to get a DBus connection %s", serviceObject, m_error.message); +} + +CConnmanConnection::~CConnmanConnection() +{ + if (m_connection) + { + dbus_connection_close(m_connection); + dbus_connection_unref(m_connection); + m_connection = NULL; + } + + dbus_error_free (&m_error); +} + +std::string CConnmanConnection::GetName() const +{ + return m_name; +} + +std::string CConnmanConnection::GetAddress() const +{ + return m_address; +} + +std::string CConnmanConnection::GetNetmask() const +{ + return m_netmask; +} + +std::string CConnmanConnection::GetGateway() const +{ + return m_gateway; +} + +std::string CConnmanConnection::GetNameServer() const +{ + return "127.0.0.1"; +} + +std::string CConnmanConnection::GetMacAddress() const +{ + return m_macaddress; +} + +ConnectionType CConnmanConnection::GetType() const +{ + return m_type; +} + +ConnectionState CConnmanConnection::GetState() const +{ + return m_state; +} + +unsigned int CConnmanConnection::GetSpeed() const +{ + return m_speed; +} + +IPConfigMethod CConnmanConnection::GetMethod() const +{ + if (m_method.find("dhcp") != std::string::npos) + return IP_CONFIG_DHCP; + else if (m_method.find("manual") != std::string::npos) + return IP_CONFIG_STATIC; + else + return IP_CONFIG_DISABLED; +} + +unsigned int CConnmanConnection::GetStrength() const +{ + return m_strength;; +} + +EncryptionType CConnmanConnection::GetEncryption() const +{ + return m_encryption; +} + +bool CConnmanConnection::Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig) +{ + if (m_encryption != NETWORK_CONNECTION_ENCRYPTION_NONE) + { + if (!storage->GetPassphrase(m_serviceObject, m_passphrase)) + return false; + + CDBusMessage message("net.connman", m_serviceObject.c_str(), "net.connman.Service", "SetProperty"); + message.AppendArgument("Passphrase"); + message.AppendArgument(m_passphrase.c_str()); + + CDBusReplyPtr reply = message.SendSystem(); + if (reply->IsErrorSet()) + { + CLog::Log(LOGERROR, "ConnmanConnection: Failed to set passphrase"); + return false; + } + } + + CLog::Log(LOGDEBUG, "CConnmanConnection::Connect:m_serviceObject(%s)", m_serviceObject.c_str()); + CDBusMessage message("net.connman", m_serviceObject.c_str(), "net.connman.Service", "Connect"); + return message.SendAsyncSystem(); +} + +//----------------------------------------------------------------------- +bool CConnmanConnection::PumpNetworkEvents() +{ + bool result = false; + + if (m_connection) + { + dbus_connection_read_write(m_connection, 0); + DBusMessage *msg = dbus_connection_pop_message(m_connection); + + if (msg) + { + CDBusReplyPtr reply = CDBusReplyPtr(new CDBusReply(msg)); + + if (dbus_message_is_signal(msg, "net.connman.Service", "PropertyChanged")) + { + CVariant key = reply->GetNextArgument(); + m_properties[key.asString()] = reply->GetNextArgument(); + + UpdateConnection(); + result = true; + } + + dbus_message_unref(msg); + } + } + + return result; +} + +ConnectionState CConnmanConnection::ParseConnectionState(const char *stateString) +{ + if (strcmp(stateString, "online") == 0) + return NETWORK_CONNECTION_STATE_CONNECTED; + else if (strcmp(stateString, "association") == 0) + return NETWORK_CONNECTION_STATE_CONNECTING; + else if (strcmp(stateString, "configuration") == 0) + return NETWORK_CONNECTION_STATE_CONNECTING; + else if (strcmp(stateString, "failure") == 0) + return NETWORK_CONNECTION_STATE_FAILURE; + else + { + // The state can be ready which means that the connection have been setup and is ready to be used. + // Perhaps we want to differentiate this in GUI? Its not used but might be in case the active connection is lost. + return NETWORK_CONNECTION_STATE_DISCONNECTED; + } +} + +void CConnmanConnection::UpdateConnection() +{ + m_name = m_properties["Name"].asString(); + + m_state = ParseConnectionState(m_properties["State"].asString().c_str()); + + if (strcmp(m_properties["Type"].asString().c_str(), "ethernet") == 0) + m_type = NETWORK_CONNECTION_TYPE_WIRED; + else if (strcmp(m_properties["Type"].asString().c_str(), "wifi") == 0) + m_type = NETWORK_CONNECTION_TYPE_WIFI; + else + m_type = NETWORK_CONNECTION_TYPE_UNKNOWN; + + m_address = m_properties["IPv4"]["Address"].asString(); + m_netmask = m_properties["IPv4"]["Netmask"].asString(); + m_macaddress = m_properties["Ethernet"]["Address"].asString(); + m_gateway = m_properties["IPv4"]["Gateway"].asString(); + m_method = m_properties["IPv4"]["Method"].asString(); + + if (m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + m_strength = m_properties["Strength"].asInteger(); + m_speed = m_properties["Ethernet"]["Speed"].asInteger(); + + m_encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + if (strcmp(m_properties["PassphraseRequired"].asString().c_str(), "true") == 0) + { + if (m_properties["Security"].asString().empty()) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + else if (strcmp(m_properties["Security"].asString().c_str(), "none") == 0) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + else if (strcmp(m_properties["Security"].asString().c_str(), "wep") == 0) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_WEP; + else if (strcmp(m_properties["Security"].asString().c_str(), "wpa") == 0) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_WPA; + else if (strcmp(m_properties["Security"].asString().c_str(), "psk") == 0) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_WPA; + else if (strcmp(m_properties["Security"].asString().c_str(), "rsn") == 0) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_WPA; + else if (strcmp(m_properties["Security"].asString().c_str(), "ieee8021x") == 0) + m_encryption = NETWORK_CONNECTION_ENCRYPTION_IEEE8021x; + else + { + CLog::Log(LOGWARNING, "Connman: unknown connection encryption %s", m_properties["Security"].asString().c_str()); + m_encryption = NETWORK_CONNECTION_ENCRYPTION_UNKNOWN; + } + } + } + else + { + m_strength = 100; + m_speed = m_properties["Ethernet"]["Speed"].asInteger(); + m_encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + } +} +#endif diff --git a/xbmc/network/linux/ConnmanConnection.h b/xbmc/network/linux/ConnmanConnection.h new file mode 100644 index 0000000000000..9da5e615ba24f --- /dev/null +++ b/xbmc/network/linux/ConnmanConnection.h @@ -0,0 +1,77 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#ifdef HAS_DBUS +#include "network/IConnection.h" +#include "linux/DBusUtil.h" +#include + +class CConnmanConnection : public IConnection +{ +public: + CConnmanConnection(const char *serviceObject); + virtual ~CConnmanConnection(); + + virtual std::string GetName() const; + virtual std::string GetAddress() const; + virtual std::string GetNetmask() const; + virtual std::string GetGateway() const; + virtual std::string GetNameServer() const; + virtual std::string GetMacAddress() const; + + virtual ConnectionType GetType() const; + virtual ConnectionState GetState() const; + virtual unsigned int GetSpeed() const; + virtual IPConfigMethod GetMethod() const; + virtual unsigned int GetStrength() const; + virtual EncryptionType GetEncryption() const; + + virtual bool Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig); + + bool PumpNetworkEvents(); + static ConnectionState ParseConnectionState(const char *stateString); + +private: + void UpdateConnection(); + + std::string m_name; + std::string m_address; + std::string m_netmask; + std::string m_gateway; + std::string m_macaddress; + + ConnectionType m_type; + ConnectionState m_state; + unsigned int m_speed; + std::string m_method; + unsigned int m_strength; + EncryptionType m_encryption; + std::string m_passphrase; + + CVariant m_properties; + std::string m_serviceObject; + + DBusConnection *m_connection; + DBusError m_error; +}; +#endif diff --git a/xbmc/network/linux/ConnmanNetworkManager.cpp b/xbmc/network/linux/ConnmanNetworkManager.cpp new file mode 100644 index 0000000000000..0b6d22b21d85e --- /dev/null +++ b/xbmc/network/linux/ConnmanNetworkManager.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "ConnmanNetworkManager.h" + +#ifdef HAS_DBUS +#include "ConnmanConnection.h" +#include "linux/DBusUtil.h" +#include "linux/DBusMessage.h" +#include "utils/log.h" + +using namespace std; + +CConnmanNetworkManager::CConnmanNetworkManager() +{ + // should use CONNMAN_SERVICE here (in connman/dbus.h) + CDBusMessage message("net.connman", "/", "net.connman.Manager", "GetProperties"); + CDBusReplyPtr reply = message.SendSystem(); + m_properties = reply->GetNextArgument(); + + UpdateNetworkManager(); + + dbus_error_init (&m_error); + // TODO: do not use dbus_connection_pop_message() that requires the use of a + // private connection + m_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &m_error); + if (m_connection) + { + dbus_connection_set_exit_on_disconnect(m_connection, false); + + dbus_bus_add_match(m_connection, "type='signal',interface='net.connman.Manager'", &m_error); + dbus_connection_flush(m_connection); + if (dbus_error_is_set(&m_error)) + { + CLog::Log(LOGERROR, "ConnmanNetworkManager: Failed to attach to signal %s", m_error.message); + dbus_connection_close(m_connection); + dbus_connection_unref(m_connection); + m_connection = NULL; + } + } + else + CLog::Log(LOGERROR, "ConnmanNetworkManager: Failed to get a DBus connection %s", m_error.message); +} + +CConnmanNetworkManager::~CConnmanNetworkManager() +{ + if (m_connection) + { + dbus_connection_close(m_connection); + dbus_connection_unref(m_connection); + m_connection = NULL; + } + + dbus_error_free (&m_error); +} + +bool CConnmanNetworkManager::CanManageConnections() +{ + // TODO Only return true if we are registered as agent + return true; +} + +ConnectionList CConnmanNetworkManager::GetConnections() +{ + return m_connections; +} + +bool CConnmanNetworkManager::PumpNetworkEvents(INetworkEventsCallback *callback) +{ + bool result = false; + + if (m_connection) + { + dbus_connection_read_write(m_connection, 0); + DBusMessage *msg = dbus_connection_pop_message(m_connection); + + if (msg) + { + CDBusReplyPtr reply = CDBusReplyPtr(new CDBusReply(msg)); + + if (dbus_message_is_signal(msg, "net.connman.Manager", "PropertyChanged")) + { + CVariant key = reply->GetNextArgument(); + m_properties[key.asString()] = reply->GetNextArgument(); + + UpdateNetworkManager(); + + if (strcmp(key.asString().c_str(), "Services") == 0) + callback->OnConnectionListChange(m_connections); + + result = true; + } + else if (dbus_message_is_signal(msg, "net.connman.Manager", "StateChanged")) + { + CVariant stateString = reply->GetNextArgument(); + result = true; + callback->OnConnectionStateChange(CConnmanConnection::ParseConnectionState(stateString.asString().c_str())); + } + else if (dbus_message_is_signal(msg, "net.connman.Manager", "NameAcquired")) + { + } + else + CLog::Log(LOGINFO, "ConnmanNetworkManager: Recieved an unknown signal %s", dbus_message_get_member(msg)); + + dbus_message_unref(msg); + } + } + + for (size_t i = 0; i < m_connections.size(); i++) + { + if (((CConnmanConnection *)m_connections[i].get())->PumpNetworkEvents()) + { + callback->OnConnectionChange(m_connections[i]); + result = true; + } + } + + return result; +} + +bool CConnmanNetworkManager::HasConnman() +{ + CDBusMessage message("net.connman", "/", "net.connman.Manager", "GetProperties"); + + DBusError error; + dbus_error_init (&error); + DBusConnection *connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + + message.Send(connection, &error); + + if (!dbus_error_is_set(&error)) + return true; + else + { + CLog::Log(LOGDEBUG, "ConnmanNetworkManager: %s - %s", error.name, error.message); + return false; + } +} + +void CConnmanNetworkManager::UpdateNetworkManager() +{ + m_connections.clear(); + + CVariant services = m_properties["Services"]; + + for (unsigned int i = 0; i < services.size(); i++) + { + if (strcmp(services[i].asString().c_str(), "") == 0) + continue; + + IConnection *connection = new CConnmanConnection(services[i].asString().c_str()); + m_connections.push_back(CConnectionPtr(connection)); + } +} +#endif diff --git a/xbmc/network/linux/ConnmanNetworkManager.h b/xbmc/network/linux/ConnmanNetworkManager.h new file mode 100644 index 0000000000000..a7a9388c0498d --- /dev/null +++ b/xbmc/network/linux/ConnmanNetworkManager.h @@ -0,0 +1,51 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#ifdef HAS_DBUS +#include "network/INetworkManager.h" +#include "utils/Variant.h" +#include + +class CConnmanNetworkManager : public INetworkManager +{ +public: + CConnmanNetworkManager(); + virtual ~CConnmanNetworkManager(); + + + virtual bool CanManageConnections(); + + virtual ConnectionList GetConnections(); + + virtual bool PumpNetworkEvents(INetworkEventsCallback *callback); + + static bool HasConnman(); +private: + void UpdateNetworkManager(); + + ConnectionList m_connections; + CVariant m_properties; + DBusConnection *m_connection; + DBusError m_error; +}; +#endif diff --git a/xbmc/network/linux/Makefile b/xbmc/network/linux/Makefile deleted file mode 100644 index 207ecda33b307..0000000000000 --- a/xbmc/network/linux/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -SRCS=NetworkLinux.cpp \ - ZeroconfAvahi.cpp \ - ZeroconfBrowserAvahi.cpp \ - -LIB=network_linux.a - -include ../../../Makefile.include --include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/network/linux/Makefile.in b/xbmc/network/linux/Makefile.in new file mode 100644 index 0000000000000..a4b76f8a90336 --- /dev/null +++ b/xbmc/network/linux/Makefile.in @@ -0,0 +1,11 @@ +#SRCS = ConnmanConnection.cpp +#SRCS += ConnmanNetworkManager.cpp +SRCS += PosixConnection.cpp +SRCS += PosixNetworkManager.cpp +SRCS += ZeroconfAvahi.cpp +SRCS += ZeroconfBrowserAvahi.cpp + +LIB = network_linux.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) \ No newline at end of file diff --git a/xbmc/network/linux/NetworkLinux.cpp b/xbmc/network/linux/NetworkLinux.cpp deleted file mode 100644 index 582d3e3852182..0000000000000 --- a/xbmc/network/linux/NetworkLinux.cpp +++ /dev/null @@ -1,930 +0,0 @@ -/* - * Copyright (C) 2005-2012 Team XBMC - * http://www.xbmc.org - * - * This Program 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, or (at your option) - * any later version. - * - * This Program 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 XBMC; see the file COPYING. If not, see - * . - * - */ - -#include -#include -#include -#include -#if defined(TARGET_LINUX) - #include - #include - #include -#endif -#ifdef TARGET_ANDROID -#include "android/bionic_supplement/bionic_supplement.h" -#include "sys/system_properties.h" -#endif -#include -#include -#if defined(TARGET_DARWIN) - #include - #include - #include - #include -#elif defined(TARGET_FREEBSD) - #include - #include - #include - #include - #include -#else - #include -#endif -#include "PlatformDefs.h" -#include "NetworkLinux.h" -#include "Util.h" -#include "utils/log.h" - -using namespace std; - -CNetworkInterfaceLinux::CNetworkInterfaceLinux(CNetworkLinux* network, CStdString interfaceName, char interfaceMacAddrRaw[6]) - -{ - m_network = network; - m_interfaceName = interfaceName; - m_interfaceMacAdr.Format("%02X:%02X:%02X:%02X:%02X:%02X", - (uint8_t)interfaceMacAddrRaw[0], - (uint8_t)interfaceMacAddrRaw[1], - (uint8_t)interfaceMacAddrRaw[2], - (uint8_t)interfaceMacAddrRaw[3], - (uint8_t)interfaceMacAddrRaw[4], - (uint8_t)interfaceMacAddrRaw[5]); - memcpy(m_interfaceMacAddrRaw, interfaceMacAddrRaw, sizeof(m_interfaceMacAddrRaw)); -} - -CNetworkInterfaceLinux::~CNetworkInterfaceLinux(void) -{ -} - -CStdString& CNetworkInterfaceLinux::GetName(void) -{ - return m_interfaceName; -} - -bool CNetworkInterfaceLinux::IsWireless() -{ -#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) - return false; -#else - struct iwreq wrq; - strcpy(wrq.ifr_name, m_interfaceName.c_str()); - if (ioctl(m_network->GetSocket(), SIOCGIWNAME, &wrq) < 0) - return false; -#endif - - return true; -} - -bool CNetworkInterfaceLinux::IsEnabled() -{ - struct ifreq ifr; - strcpy(ifr.ifr_name, m_interfaceName.c_str()); - if (ioctl(m_network->GetSocket(), SIOCGIFFLAGS, &ifr) < 0) - return false; - - return ((ifr.ifr_flags & IFF_UP) == IFF_UP); -} - -bool CNetworkInterfaceLinux::IsConnected() -{ - struct ifreq ifr; - int zero = 0; - memset(&ifr,0,sizeof(struct ifreq)); - strcpy(ifr.ifr_name, m_interfaceName.c_str()); - if (ioctl(m_network->GetSocket(), SIOCGIFFLAGS, &ifr) < 0) - return false; - - // ignore loopback - int iRunning = ( (ifr.ifr_flags & IFF_RUNNING) && (!(ifr.ifr_flags & IFF_LOOPBACK))); - - if (ioctl(m_network->GetSocket(), SIOCGIFADDR, &ifr) < 0) - return false; - - // return only interfaces which has ip address - return iRunning && (0 != memcmp(ifr.ifr_addr.sa_data+sizeof(short), &zero, sizeof(int))); -} - -CStdString CNetworkInterfaceLinux::GetMacAddress() -{ - return m_interfaceMacAdr; -} - -void CNetworkInterfaceLinux::GetMacAddressRaw(char rawMac[6]) -{ - memcpy(rawMac, m_interfaceMacAddrRaw, 6); -} - -CStdString CNetworkInterfaceLinux::GetCurrentIPAddress(void) -{ - CStdString result = ""; - - struct ifreq ifr; - strcpy(ifr.ifr_name, m_interfaceName.c_str()); - ifr.ifr_addr.sa_family = AF_INET; - if (ioctl(m_network->GetSocket(), SIOCGIFADDR, &ifr) >= 0) - { - result = inet_ntoa((*((struct sockaddr_in *)&ifr.ifr_addr)).sin_addr); - } - - return result; -} - -CStdString CNetworkInterfaceLinux::GetCurrentNetmask(void) -{ - CStdString result = ""; - - struct ifreq ifr; - strcpy(ifr.ifr_name, m_interfaceName.c_str()); - ifr.ifr_addr.sa_family = AF_INET; - if (ioctl(m_network->GetSocket(), SIOCGIFNETMASK, &ifr) >= 0) - { - result = inet_ntoa((*((struct sockaddr_in*)&ifr.ifr_addr)).sin_addr); - } - - return result; -} - -CStdString CNetworkInterfaceLinux::GetCurrentWirelessEssId(void) -{ - CStdString result = ""; - -#if defined(TARGET_LINUX) - char essid[IW_ESSID_MAX_SIZE + 1]; - memset(&essid, 0, sizeof(essid)); - - struct iwreq wrq; - strcpy(wrq.ifr_name, m_interfaceName.c_str()); - wrq.u.essid.pointer = (caddr_t) essid; - wrq.u.essid.length = IW_ESSID_MAX_SIZE; - wrq.u.essid.flags = 0; - if (ioctl(m_network->GetSocket(), SIOCGIWESSID, &wrq) >= 0) - { - result = essid; - } -#endif - - return result; -} - -CStdString CNetworkInterfaceLinux::GetCurrentDefaultGateway(void) -{ - CStdString result = ""; - -#if defined(TARGET_DARWIN) - FILE* pipe = popen("echo \"show State:/Network/Global/IPv4\" | scutil | grep Router", "r"); - if (pipe) - { - CStdString tmpStr; - char buffer[256] = {'\0'}; - if (fread(buffer, sizeof(char), sizeof(buffer), pipe) > 0 && !ferror(pipe)) - { - tmpStr = buffer; - result = tmpStr.Mid(11); - } - else - { - CLog::Log(LOGWARNING, "Unable to determine gateway"); - } - pclose(pipe); - } -#elif defined(TARGET_FREEBSD) - size_t needed; - int mib[6]; - char *buf, *next, *lim; - char line[16]; - struct rt_msghdr *rtm; - struct sockaddr *sa; - struct sockaddr_in *sockin; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; - mib[3] = 0; - mib[4] = NET_RT_DUMP; - mib[5] = 0; - if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) - return result; - - if ((buf = (char *)malloc(needed)) == NULL) - return result; - - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { - free(buf); - return result; - } - - lim = buf + needed; - for (next = buf; next < lim; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)next; - sa = (struct sockaddr *)(rtm + 1); - sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa); - sockin = (struct sockaddr_in *)sa; - if (inet_ntop(AF_INET, &sockin->sin_addr.s_addr, - line, sizeof(line)) == NULL) { - free(buf); - return result; - } - result = line; - break; - } - free(buf); -#else - FILE* fp = fopen("/proc/net/route", "r"); - if (!fp) - { - // TBD: Error - return result; - } - - char* line = NULL; - char iface[16]; - char dst[128]; - char gateway[128]; - size_t linel = 0; - int n; - int linenum = 0; - while (getdelim(&line, &linel, '\n', fp) > 0) - { - // skip first two lines - if (linenum++ < 1) - continue; - - // search where the word begins - n = sscanf(line, "%16s %128s %128s", - iface, dst, gateway); - - if (n < 3) - continue; - - if (strcmp(iface, m_interfaceName.c_str()) == 0 && - strcmp(dst, "00000000") == 0 && - strcmp(gateway, "00000000") != 0) - { - unsigned char gatewayAddr[4]; - int len = CNetwork::ParseHex(gateway, gatewayAddr); - if (len == 4) - { - struct in_addr in; - in.s_addr = (gatewayAddr[0] << 24) | (gatewayAddr[1] << 16) | - (gatewayAddr[2] << 8) | (gatewayAddr[3]); - result = inet_ntoa(in); - break; - } - } - } - free(line); - fclose(fp); -#endif - - return result; -} - -CNetworkLinux::CNetworkLinux(void) -{ - m_sock = socket(AF_INET, SOCK_DGRAM, 0); - queryInterfaceList(); -} - -CNetworkLinux::~CNetworkLinux(void) -{ - if (m_sock != -1) - close(CNetworkLinux::m_sock); - - vector::iterator it = m_interfaces.begin(); - while(it != m_interfaces.end()) - { - CNetworkInterface* nInt = *it; - delete nInt; - it = m_interfaces.erase(it); - } -} - -std::vector& CNetworkLinux::GetInterfaceList(void) -{ - return m_interfaces; -} - -// Overwrite the GetFirstConnectedInterface and requery -// the interface list if no connected device is found -// this fixes a bug when no network is available after first start of xbmc -// and the interface comes up during runtime -CNetworkInterface* CNetworkLinux::GetFirstConnectedInterface(void) -{ - CNetworkInterface *pNetIf=CNetwork::GetFirstConnectedInterface(); - - // no connected Interfaces found? - requeryInterfaceList - if (!pNetIf) - { - CLog::Log(LOGDEBUG,"%s no connected interface found - requery list",__FUNCTION__); - queryInterfaceList(); - //retry finding a connected if - pNetIf = CNetwork::GetFirstConnectedInterface(); - } - - return pNetIf; -} - - -void CNetworkLinux::GetMacAddress(CStdString interfaceName, char rawMac[6]) -{ - memset(rawMac, 0, 6); -#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) - -#if !defined(IFT_ETHER) -#define IFT_ETHER 0x6/* Ethernet CSMACD */ -#endif - const struct sockaddr_dl* dlAddr = NULL; - const uint8_t * base = NULL; - // Query the list of interfaces. - struct ifaddrs *list; - struct ifaddrs *interface; - - if( getifaddrs(&list) < 0 ) - { - return; - } - - for(interface = list; interface != NULL; interface = interface->ifa_next) - { - if(CStdString(interface->ifa_name).Equals(interfaceName)) - { - if ( (interface->ifa_addr->sa_family == AF_LINK) && (((const struct sockaddr_dl *) interface->ifa_addr)->sdl_type == IFT_ETHER) ) - { - dlAddr = (const struct sockaddr_dl *) interface->ifa_addr; - base = (const uint8_t *) &dlAddr->sdl_data[dlAddr->sdl_nlen]; - - if( dlAddr->sdl_alen > 5 ) - { - memcpy(rawMac, base, 6); - } - } - break; - } - } - - freeifaddrs(list); - -#else - - struct ifreq ifr; - strcpy(ifr.ifr_name, interfaceName.c_str()); - if (ioctl(GetSocket(), SIOCGIFHWADDR, &ifr) >= 0) - { - memcpy(rawMac, ifr.ifr_hwaddr.sa_data, 6); - } -#endif -} - -void CNetworkLinux::queryInterfaceList() -{ - char macAddrRaw[6]; - m_interfaces.clear(); - -#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) - - // Query the list of interfaces. - struct ifaddrs *list; - if (getifaddrs(&list) < 0) - return; - - struct ifaddrs *cur; - for(cur = list; cur != NULL; cur = cur->ifa_next) - { - if(cur->ifa_addr->sa_family != AF_INET) - continue; - - GetMacAddress(cur->ifa_name, macAddrRaw); - // Add the interface. - m_interfaces.push_back(new CNetworkInterfaceLinux(this, cur->ifa_name, macAddrRaw)); - } - - freeifaddrs(list); - -#else - FILE* fp = fopen("/proc/net/dev", "r"); - if (!fp) - { - // TBD: Error - return; - } - - char* line = NULL; - size_t linel = 0; - int n; - char* p; - int linenum = 0; - while (getdelim(&line, &linel, '\n', fp) > 0) - { - // skip first two lines - if (linenum++ < 2) - continue; - - // search where the word begins - p = line; - while (isspace(*p)) - ++p; - - // read word until : - n = strcspn(p, ": \t"); - p[n] = 0; - - // save the result - CStdString interfaceName = p; - GetMacAddress(interfaceName, macAddrRaw); - m_interfaces.push_back(new CNetworkInterfaceLinux(this, interfaceName, macAddrRaw)); - } - free(line); - fclose(fp); -#endif -} - -std::vector CNetworkLinux::GetNameServers(void) -{ - std::vector result; - -#if defined(TARGET_DARWIN) - //only finds the primary dns (0 :) - FILE* pipe = popen("scutil --dns | grep \"nameserver\\[0\\]\" | tail -n1", "r"); - if (pipe) - { - CStdString tmpStr; - char buffer[256] = {'\0'}; - if (fread(buffer, sizeof(char), sizeof(buffer), pipe) > 0 && !ferror(pipe)) - { - tmpStr = buffer; - result.push_back(tmpStr.Mid(17)); - } - else - { - CLog::Log(LOGWARNING, "Unable to determine nameserver"); - } - pclose(pipe); - } -#elif defined(TARGET_ANDROID) - char nameserver[PROP_VALUE_MAX]; - - if (__system_property_get("net.dns1",nameserver)) - result.push_back(nameserver); - if (__system_property_get("net.dns2",nameserver)) - result.push_back(nameserver); - if (__system_property_get("net.dns3",nameserver)) - result.push_back(nameserver); - - if (!result.size()) - CLog::Log(LOGWARNING, "Unable to determine nameserver"); -#else - res_init(); - - for (int i = 0; i < _res.nscount; i ++) - { - CStdString ns = inet_ntoa(((struct sockaddr_in *)&_res.nsaddr_list[i])->sin_addr); - result.push_back(ns); - } -#endif - return result; -} - -void CNetworkLinux::SetNameServers(std::vector nameServers) -{ -#if !defined(__ANDROID__) - FILE* fp = fopen("/etc/resolv.conf", "w"); - if (fp != NULL) - { - for (unsigned int i = 0; i < nameServers.size(); i++) - { - fprintf(fp, "nameserver %s\n", nameServers[i].c_str()); - } - fclose(fp); - } - else - { - // TODO: - } -#endif -} - -std::vector CNetworkInterfaceLinux::GetAccessPoints(void) -{ - std::vector result; - - if (!IsWireless()) - return result; - -#if defined(TARGET_LINUX) - // Query the wireless extentsions version number. It will help us when we - // parse the resulting events - struct iwreq iwr; - char rangebuffer[sizeof(iw_range) * 2]; /* Large enough */ - struct iw_range* range = (struct iw_range*) rangebuffer; - - memset(rangebuffer, 0, sizeof(rangebuffer)); - iwr.u.data.pointer = (caddr_t) rangebuffer; - iwr.u.data.length = sizeof(rangebuffer); - iwr.u.data.flags = 0; - strncpy(iwr.ifr_name, GetName().c_str(), IFNAMSIZ); - if (ioctl(m_network->GetSocket(), SIOCGIWRANGE, &iwr) < 0) - { - CLog::Log(LOGWARNING, "%-8.16s Driver has no Wireless Extension version information.", - GetName().c_str()); - return result; - } - - // Scan for wireless access points - memset(&iwr, 0, sizeof(iwr)); - strncpy(iwr.ifr_name, GetName().c_str(), IFNAMSIZ); - if (ioctl(m_network->GetSocket(), SIOCSIWSCAN, &iwr) < 0) - { - CLog::Log(LOGWARNING, "Cannot initiate wireless scan: ioctl[SIOCSIWSCAN]: %s", strerror(errno)); - return result; - } - - // Get the results of the scanning. Three scenarios: - // 1. There's not enough room in the result buffer (E2BIG) - // 2. The scanning is not complete (EAGAIN) and we need to try again. We cap this with 15 seconds. - // 3. Were'e good. - int duration = 0; // ms - unsigned char* res_buf = NULL; - int res_buf_len = IW_SCAN_MAX_DATA; - while (duration < 15000) - { - if (!res_buf) - res_buf = (unsigned char*) malloc(res_buf_len); - - if (res_buf == NULL) - { - CLog::Log(LOGWARNING, "Cannot alloc memory for wireless scanning"); - return result; - } - - strncpy(iwr.ifr_name, GetName().c_str(), IFNAMSIZ); - iwr.u.data.pointer = res_buf; - iwr.u.data.length = res_buf_len; - iwr.u.data.flags = 0; - int x = ioctl(m_network->GetSocket(), SIOCGIWSCAN, &iwr); - if (x == 0) - break; - - if (errno == E2BIG && res_buf_len < 100000) - { - free(res_buf); - res_buf = NULL; - res_buf_len *= 2; - CLog::Log(LOGDEBUG, "Scan results did not fit - trying larger buffer (%lu bytes)", - (unsigned long) res_buf_len); - } - else if (errno == EAGAIN) - { - usleep(250000); // sleep for 250ms - duration += 250; - } - else - { - CLog::Log(LOGWARNING, "Cannot get wireless scan results: ioctl[SIOCGIWSCAN]: %s", strerror(errno)); - free(res_buf); - return result; - } - } - - size_t len = iwr.u.data.length; - char* pos = (char *) res_buf; - char* end = (char *) res_buf + len; - char* custom; - struct iw_event iwe_buf, *iwe = &iwe_buf; - - CStdString essId; - int quality = 0; - EncMode encryption = ENC_NONE; - bool first = true; - - while (pos + IW_EV_LCP_LEN <= end) - { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - if (iwe->len <= IW_EV_LCP_LEN) - break; - - custom = pos + IW_EV_POINT_LEN; - if (range->we_version_compiled > 18 && - (iwe->cmd == SIOCGIWESSID || - iwe->cmd == SIOCGIWENCODE || - iwe->cmd == IWEVGENIE || - iwe->cmd == IWEVCUSTOM)) - { - /* Wireless extentsions v19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen); - } - else - { - memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; - } - - switch (iwe->cmd) - { - case SIOCGIWAP: - if (first) - first = false; - else - result.push_back(NetworkAccessPoint(essId, quality, encryption)); - encryption = ENC_NONE; - break; - - case SIOCGIWESSID: - { - char essid[IW_ESSID_MAX_SIZE+1]; - memset(essid, '\0', sizeof(essid)); - if ((custom) && (iwe->u.essid.length)) - { - memcpy(essid, custom, iwe->u.essid.length); - essId = essid; - } - break; - } - - case IWEVQUAL: - quality = iwe->u.qual.qual; - break; - - case SIOCGIWENCODE: - if (!(iwe->u.data.flags & IW_ENCODE_DISABLED) && encryption == ENC_NONE) - encryption = ENC_WEP; - break; - - case IWEVGENIE: - { - int offset = 0; - while (offset <= iwe_buf.u.data.length) - { - switch ((unsigned char)custom[offset]) - { - case 0xdd: /* WPA1 */ - if (encryption != ENC_WPA2) - encryption = ENC_WPA; - break; - case 0x30: /* WPA2 */ - encryption = ENC_WPA2; - } - - offset += custom[offset+1] + 2; - } - } - } - - pos += iwe->len; - } - - if (!first) - result.push_back(NetworkAccessPoint(essId, quality, encryption)); - - free(res_buf); - res_buf = NULL; -#endif - - return result; -} - -void CNetworkInterfaceLinux::GetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode) -{ - ipAddress = "0.0.0.0"; - networkMask = "0.0.0.0"; - defaultGateway = "0.0.0.0"; - essId = ""; - key = ""; - encryptionMode = ENC_NONE; - assignment = NETWORK_DISABLED; - -#if defined(TARGET_LINUX) - FILE* fp = fopen("/etc/network/interfaces", "r"); - if (!fp) - { - // TODO - return; - } - - char* line = NULL; - size_t linel = 0; - CStdString s; - bool foundInterface = false; - - while (getdelim(&line, &linel, '\n', fp) > 0) - { - vector tokens; - - s = line; - s.TrimLeft(" \t").TrimRight(" \n"); - - // skip comments - if (s.length() == 0 || s.GetAt(0) == '#') - continue; - - // look for "iface inet" - CUtil::Tokenize(s, tokens, " "); - if (!foundInterface && - tokens.size() >=3 && - tokens[0].Equals("iface") && - tokens[1].Equals(GetName()) && - tokens[2].Equals("inet")) - { - if (tokens[3].Equals("dhcp")) - { - assignment = NETWORK_DHCP; - foundInterface = true; - } - if (tokens[3].Equals("static")) - { - assignment = NETWORK_STATIC; - foundInterface = true; - } - } - - if (foundInterface && tokens.size() == 2) - { - if (tokens[0].Equals("address")) ipAddress = tokens[1]; - else if (tokens[0].Equals("netmask")) networkMask = tokens[1]; - else if (tokens[0].Equals("gateway")) defaultGateway = tokens[1]; - else if (tokens[0].Equals("wireless-essid")) essId = tokens[1]; - else if (tokens[0].Equals("wireless-key")) - { - key = tokens[1]; - if (key.length() > 2 && key[0] == 's' && key[1] == ':') - key.erase(0, 2); - encryptionMode = ENC_WEP; - } - else if (tokens[0].Equals("wpa-ssid")) essId = tokens[1]; - else if (tokens[0].Equals("wpa-proto") && tokens[1].Equals("WPA")) encryptionMode = ENC_WPA; - else if (tokens[0].Equals("wpa-proto") && tokens[1].Equals("WPA2")) encryptionMode = ENC_WPA2; - else if (tokens[0].Equals("wpa-psk")) key = tokens[1]; - else if (tokens[0].Equals("auto") || tokens[0].Equals("iface") || tokens[0].Equals("mapping")) break; - } - } - free(line); - - // Fallback in case wpa-proto is not set - if (key != "" && encryptionMode == ENC_NONE) - encryptionMode = ENC_WPA; - - fclose(fp); -#endif -} - -void CNetworkInterfaceLinux::SetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode) -{ -#if defined(TARGET_LINUX) - FILE* fr = fopen("/etc/network/interfaces", "r"); - if (!fr) - { - // TODO - return; - } - - FILE* fw = fopen("/tmp/interfaces.temp", "w"); - if (!fw) - { - // TODO - fclose(fr); - return; - } - - char* line = NULL; - size_t linel = 0; - CStdString s; - bool foundInterface = false; - bool dataWritten = false; - - while (getdelim(&line, &linel, '\n', fr) > 0) - { - vector tokens; - - s = line; - s.TrimLeft(" \t").TrimRight(" \n"); - - // skip comments - if (!foundInterface && (s.length() == 0 || s.GetAt(0) == '#')) - { - fprintf(fw, "%s", line); - continue; - } - - // look for "iface inet" - CUtil::Tokenize(s, tokens, " "); - if (tokens.size() == 2 && - tokens[0].Equals("auto") && - tokens[1].Equals(GetName())) - { - continue; - } - else if (!foundInterface && - tokens.size() == 4 && - tokens[0].Equals("iface") && - tokens[1].Equals(GetName()) && - tokens[2].Equals("inet")) - { - foundInterface = true; - WriteSettings(fw, assignment, ipAddress, networkMask, defaultGateway, essId, key, encryptionMode); - dataWritten = true; - } - else if (foundInterface && - tokens.size() == 4 && - tokens[0].Equals("iface")) - { - foundInterface = false; - fprintf(fw, "%s", line); - } - else if (!foundInterface) - { - fprintf(fw, "%s", line); - } - } - free(line); - - if (!dataWritten && assignment != NETWORK_DISABLED) - { - fprintf(fw, "\n"); - WriteSettings(fw, assignment, ipAddress, networkMask, defaultGateway, essId, key, encryptionMode); - } - - fclose(fr); - fclose(fw); - - // Rename the file - if (rename("/tmp/interfaces.temp", "/etc/network/interfaces") < 0) - { - // TODO - return; - } - - std::string cmd = "/sbin/ifdown " + GetName(); - if (system(cmd.c_str()) != 0) - CLog::Log(LOGERROR, "Unable to stop interface %s", GetName().c_str()); - else - CLog::Log(LOGINFO, "Stopped interface %s", GetName().c_str()); - - if (assignment != NETWORK_DISABLED) - { - cmd = "/sbin/ifup " + GetName(); - if (system(cmd.c_str()) != 0) - CLog::Log(LOGERROR, "Unable to start interface %s", GetName().c_str()); - else - CLog::Log(LOGINFO, "Started interface %s", GetName().c_str()); - } -#endif -} - -void CNetworkInterfaceLinux::WriteSettings(FILE* fw, NetworkAssignment assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode) -{ - if (assignment == NETWORK_DHCP) - { - fprintf(fw, "iface %s inet dhcp\n", GetName().c_str()); - } - else if (assignment == NETWORK_STATIC) - { - fprintf(fw, "iface %s inet static\n", GetName().c_str()); - fprintf(fw, " address %s\n", ipAddress.c_str()); - fprintf(fw, " netmask %s\n", networkMask.c_str()); - fprintf(fw, " gateway %s\n", defaultGateway.c_str()); - } - - if (assignment != NETWORK_DISABLED && IsWireless()) - { - if (encryptionMode == ENC_NONE) - { - fprintf(fw, " wireless-essid %s\n", essId.c_str()); - } - else if (encryptionMode == ENC_WEP) - { - fprintf(fw, " wireless-essid %s\n", essId.c_str()); - fprintf(fw, " wireless-key s:%s\n", key.c_str()); - } - else if (encryptionMode == ENC_WPA || encryptionMode == ENC_WPA2) - { - fprintf(fw, " wpa-ssid %s\n", essId.c_str()); - fprintf(fw, " wpa-psk %s\n", key.c_str()); - fprintf(fw, " wpa-proto %s\n", encryptionMode == ENC_WPA ? "WPA" : "WPA2"); - } - } - - if (assignment != NETWORK_DISABLED) - fprintf(fw, "auto %s\n\n", GetName().c_str()); -} - - diff --git a/xbmc/network/linux/NetworkLinux.h b/xbmc/network/linux/NetworkLinux.h deleted file mode 100644 index df094983a84b5..0000000000000 --- a/xbmc/network/linux/NetworkLinux.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef NETWORK_LINUX_H_ -#define NETWORK_LINUX_H_ - -/* - * Copyright (C) 2005-2012 Team XBMC - * http://www.xbmc.org - * - * This Program 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, or (at your option) - * any later version. - * - * This Program 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 XBMC; see the file COPYING. If not, see - * . - * - */ - -#include -#include "utils/StdString.h" -#include "network/Network.h" - -class CNetworkLinux; - -class CNetworkInterfaceLinux : public CNetworkInterface -{ -public: - CNetworkInterfaceLinux(CNetworkLinux* network, CStdString interfaceName, char interfaceMacAddrRaw[6]); - ~CNetworkInterfaceLinux(void); - - virtual CStdString& GetName(void); - - virtual bool IsEnabled(void); - virtual bool IsConnected(void); - virtual bool IsWireless(void); - - virtual CStdString GetMacAddress(void); - virtual void GetMacAddressRaw(char rawMac[6]); - - virtual CStdString GetCurrentIPAddress(); - virtual CStdString GetCurrentNetmask(); - virtual CStdString GetCurrentDefaultGateway(void); - virtual CStdString GetCurrentWirelessEssId(void); - - virtual void GetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode); - virtual void SetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode); - - // Returns the list of access points in the area - virtual std::vector GetAccessPoints(void); - -private: - void WriteSettings(FILE* fw, NetworkAssignment assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode); - CStdString m_interfaceName; - CStdString m_interfaceMacAdr; - char m_interfaceMacAddrRaw[6]; - CNetworkLinux* m_network; -}; - -class CNetworkLinux : public CNetwork -{ -public: - CNetworkLinux(void); - virtual ~CNetworkLinux(void); - - // Return the list of interfaces - virtual std::vector& GetInterfaceList(void); - virtual CNetworkInterface* GetFirstConnectedInterface(void); - - // Get/set the nameserver(s) - virtual std::vector GetNameServers(void); - virtual void SetNameServers(std::vector nameServers); - - friend class CNetworkInterfaceLinux; - -private: - int GetSocket() { return m_sock; } - void GetMacAddress(CStdString interfaceName, char macAddrRaw[6]); - void queryInterfaceList(); - std::vector m_interfaces; - int m_sock; -}; - -#endif - diff --git a/xbmc/network/linux/PosixConnection.cpp b/xbmc/network/linux/PosixConnection.cpp new file mode 100644 index 0000000000000..03e618683559b --- /dev/null +++ b/xbmc/network/linux/PosixConnection.cpp @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2011-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PosixConnection.h" +#include "Util.h" +#include "linux/XTimeUtils.h" +#include "utils/StdString.h" +#include "utils/log.h" + +// temp until keychainManager is working +#include "settings/GUISettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(TARGET_LINUX) + #include + #include + #include + #include +#endif + +#if defined(TARGET_ANDROID) + #include "android/bionic_supplement/bionic_supplement.h" + #include "sys/system_properties.h" +#endif + +//----------------------------------------------------------------------- +int PosixParseHex(char *str, unsigned char *addr) +{ + int len = 0; + + while (*str) + { + int tmp; + if (str[1] == 0) + return -1; + if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1) + return -1; + addr[len] = tmp; + len++; + str += 2; + } + + return len; +} + +bool PosixCheckHex(const std::string &passphrase) +{ + // we could get fooled by strings that only + // have 0-9, A, B, C, D, E, F in them :) + for (size_t i = 0; i < passphrase.size(); i++) + { + switch (passphrase[i]) + { + default: + return false; + break; + case '0':case '1':case '2':case '3':case '4': + case '5':case '6':case '7':case '8':case '9': + case 'a':case 'A':case 'b':case 'B': + case 'c':case 'C':case 'd':case 'D': + case 'e':case 'E':case 'f':case 'F': + break; + } + } + + return true; +} + +bool PosixGuessIsHexPassPhrase(const std::string &passphrase, EncryptionType encryption) +{ + if (encryption == NETWORK_CONNECTION_ENCRYPTION_WEP) + { + // wep hex 256-bit is 58 characters. + if (passphrase.size() == 58) + return true; + + // wep hex 152-bit is 32 characters. + if (passphrase.size() == 32) + return true; + + // wep hex 128-bit is 26 characters. + if (passphrase.size() == 26) + return true; + + // wep hex 64-bit is 10 characters. + if (passphrase.size() == 10) + return true; + } + else + { + // wap/wpa2 hex has a length of 64. + if (passphrase.size() == 64) + return true; + } + + // anthing else is wep/wap/wpa2 ascii + return false; +} + +bool IsWireless(int socket, const char *interface) +{ + struct iwreq wrq; + memset(&wrq, 0x00, sizeof(iwreq)); + + strcpy(wrq.ifr_name, interface); + if (ioctl(socket, SIOCGIWNAME, &wrq) < 0) + return false; + + return true; +} + +bool PosixCheckInterfaceUp(const std::string &interface) +{ + std::string iface_state; + std::string sysclasspath("/sys/class/net/" + interface + "/operstate"); + + int fd = open(sysclasspath.c_str(), O_RDONLY); + if (fd >= 0) + { + char buffer[256] = {0}; + read(fd, buffer, 255); + close(fd); + // make sure we can treat this as a c-str. + buffer[255] = 0; + iface_state = buffer; + } + if (iface_state.find("up") != std::string::npos) + return true; + else + return false; +} + +std::string PosixGetDefaultGateway(const std::string &interface) +{ + std::string result = ""; + + FILE* fp = fopen("/proc/net/route", "r"); + if (!fp) + return result; + + char* line = NULL; + size_t linel = 0; + int n, linenum = 0; + char dst[128], iface[16], gateway[128]; + while (getdelim(&line, &linel, '\n', fp) > 0) + { + // skip first two lines + if (linenum++ < 1) + continue; + + // search where the word begins + n = sscanf(line, "%16s %128s %128s", iface, dst, gateway); + + if (n < 3) + continue; + + if (strcmp(iface, interface.c_str()) == 0 && + strcmp(dst, "00000000") == 0 && + strcmp(gateway, "00000000") != 0) + { + unsigned char gatewayAddr[4]; + int len = PosixParseHex(gateway, gatewayAddr); + if (len == 4) + { + struct in_addr in; + in.s_addr = (gatewayAddr[0] << 24) | + (gatewayAddr[1] << 16) | + (gatewayAddr[2] << 8) | + (gatewayAddr[3]); + result = inet_ntoa(in); + break; + } + } + } + free(line); + fclose(fp); + + return result; +} + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +CPosixConnection::CPosixConnection(bool managed, + int socket, const char *interface, const char *macaddress, + const char *essid, ConnectionType type, EncryptionType encryption, int signal) +{ + m_managed = managed; + m_socket = socket; + + m_type = type; + m_essid = essid; + m_signal = signal; + m_interface = interface; + m_macaddress = macaddress; + m_encryption = encryption; + + m_state = GetState(); +} + +CPosixConnection::~CPosixConnection() +{ +} + +std::string CPosixConnection::GetName() const +{ + return m_essid; +} + +std::string CPosixConnection::GetAddress() const +{ + struct ifreq ifr; + strcpy(ifr.ifr_name, m_interface.c_str()); + ifr.ifr_addr.sa_family = AF_INET; + + if (ioctl(m_socket, SIOCGIFADDR, &ifr) >= 0) + return inet_ntoa((*((struct sockaddr_in*)&ifr.ifr_addr)).sin_addr); + else + return ""; +} + +std::string CPosixConnection::GetNetmask() const +{ + struct ifreq ifr; + strcpy(ifr.ifr_name, m_interface.c_str()); + ifr.ifr_addr.sa_family = AF_INET; + + if (ioctl(m_socket, SIOCGIFNETMASK, &ifr) >= 0) + return inet_ntoa((*((struct sockaddr_in*)&ifr.ifr_addr)).sin_addr); + else + return ""; +} + +std::string CPosixConnection::GetGateway() const +{ + return PosixGetDefaultGateway(m_interface); +} + +std::string CPosixConnection::GetNameServer() const +{ + std::string nameserver("127.0.0.1"); + +#if defined(TARGET_ANDROID) + char ns[PROP_VALUE_MAX]; + + if (__system_property_get("net.dns1", ns)) + nameserver = ns; +#else + res_init(); + for (int i = 0; i < _res.nscount; i ++) + { + nameserver = inet_ntoa(((struct sockaddr_in *)&_res.nsaddr_list[0])->sin_addr); + break; + } +#endif + return nameserver; +} + +std::string CPosixConnection::GetMacAddress() const +{ + CStdString result; + result.Format("00:00:00:00:00:00"); + + struct ifreq ifr; + strcpy(ifr.ifr_name, m_interface.c_str()); + if (ioctl(m_socket, SIOCGIFHWADDR, &ifr) >= 0) + { + result.Format("%02X:%02X:%02X:%02X:%02X:%02X", + ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], + ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], + ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); + } + + return result.c_str(); +} + +ConnectionType CPosixConnection::GetType() const +{ + return m_type; +} + +ConnectionState CPosixConnection::GetState() const +{ + int zero = 0; + struct ifreq ifr; + + memset(&ifr, 0x00, sizeof(struct ifreq)); + // check if the interface is up. + strcpy(ifr.ifr_name, m_interface.c_str()); + if (ioctl(m_socket, SIOCGIFFLAGS, &ifr) < 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + // check for running and not loopback + if (!(ifr.ifr_flags & IFF_RUNNING) || (ifr.ifr_flags & IFF_LOOPBACK)) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + // check for an ip address + if (ioctl(m_socket, SIOCGIFADDR, &ifr) < 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + if (ifr.ifr_addr.sa_data == NULL) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + // return only interfaces which have an ip address + if (memcmp(ifr.ifr_addr.sa_data + sizeof(short), &zero, sizeof(int)) == 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + if (m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + // for wifi, we need to check we have a wifi driver name. + struct iwreq wrq; + strcpy(wrq.ifr_name, m_interface.c_str()); + if (ioctl(m_socket, SIOCGIWNAME, &wrq) < 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + +#if !defined(TARGET_ANDROID) + // since the wifi interface can be connected to + // any wifi access point, we need to compare the assigned + // essid to our connection essid. If they match, then + // this connection is up. + char essid[IFNAMSIZ]; + memset(&wrq, 0x00, sizeof(struct iwreq)); + wrq.u.essid.pointer = (caddr_t)essid; + wrq.u.essid.length = sizeof(essid); + strncpy(wrq.ifr_name, m_interface.c_str(), IFNAMSIZ); + if (ioctl(m_socket, SIOCGIWESSID, &wrq) < 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + if (wrq.u.essid.length <= 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + std::string test_essid(essid, wrq.u.essid.length); + // Since Android cannot SIOCSIWSCAN (permissions error), + // m_essid was defaulted to 'Wifi'. So ignore this check. + if (m_essid.find(test_essid) == std::string::npos) + return NETWORK_CONNECTION_STATE_DISCONNECTED; +#endif + } + + // finally, we need to see if we have a gateway assigned to our interface. + std::string default_gateway = PosixGetDefaultGateway(m_interface); + if (default_gateway.size() <= 0) + return NETWORK_CONNECTION_STATE_DISCONNECTED; + + // passing the above tests means we are connected. + return NETWORK_CONNECTION_STATE_CONNECTED; +} + +unsigned int CPosixConnection::GetSpeed() const +{ + int speed = 100; + return speed; +} + +IPConfigMethod CPosixConnection::GetMethod() const +{ + return m_method; +} + +unsigned int CPosixConnection::GetStrength() const +{ + /* + int strength = 100; + if (m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + struct iwreq wreq; + // wireless tools says this is large enough + char buffer[sizeof(struct iw_range) * 2]; + int max_qual_level = 0; + double max_qual = 92.0; + + // Fetch the range + memset(buffer, 0x00, sizeof(iw_range) * 2); + memset(&wreq, 0x00, sizeof(struct iwreq)); + wreq.u.data.pointer = (caddr_t)buffer; + wreq.u.data.length = sizeof(buffer); + wreq.u.data.flags = 0; + strncpy(wreq.ifr_name, m_interface.c_str(), IFNAMSIZ); + if (ioctl(m_socket, SIOCGIWRANGE, &wreq) >= 0) + { + struct iw_range *range = (struct iw_range*)buffer; + if (range->max_qual.qual > 0) + max_qual = range->max_qual.qual; + if (range->max_qual.level > 0) + max_qual_level = range->max_qual.level; + } + printf("CPosixConnection::GetStrength, max_qual(%d), qual(%d)\n", max_qual_level, max_qual_level); + + struct iw_statistics stats; + memset(&wreq, 0x00, sizeof(struct iwreq)); + // Fetch the stats + wreq.u.data.pointer = (caddr_t)&stats; + wreq.u.data.length = sizeof(stats); + wreq.u.data.flags = 1; // Clear updated flag + strncpy(wreq.ifr_name, m_interface.c_str(), IFNAMSIZ); + if (ioctl(m_socket, SIOCGIWSTATS, &wreq) < 0) { + return strength; + } + + // this is not correct :) + strength = (100 * wreq.u.qual.qual)/256; + + printf("CPosixConnection::GetStrength, strength(%d), qual(%d)\n", strength, wreq.u.qual.qual); + } + return strength; + */ + return m_signal; +} + +EncryptionType CPosixConnection::GetEncryption() const +{ + return m_encryption; +} + +bool CPosixConnection::Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig) +{ + if (!m_managed) + return true; + + std::string passphrase(""); + + if (storage && m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + if (m_encryption != NETWORK_CONNECTION_ENCRYPTION_NONE) + { + if (!storage->GetPassphrase(m_essid, passphrase)) + return false; + if (passphrase.size() <= 0) + return false; + } + } + else + { + passphrase = g_guiSettings.GetString("network.passphrase"); + /* + CVariant secret; + if (m_keyringManager->FindSecret("network", m_essid, secret) && secret.isString()) + { + passphrase = secret.asString(); + return true; + } + */ + } + + if (DoConnection(ipconfig, passphrase) && GetState() == NETWORK_CONNECTION_STATE_CONNECTED) + { + // if we connect, save out the essid + g_guiSettings.SetString("network.essid", m_essid.c_str()); + // quick update of some internal vars + m_method = ipconfig.m_method; + m_address = ipconfig.m_address; + m_netmask = ipconfig.m_netmask; + m_gateway = ipconfig.m_gateway; + return true; + } + else + { + if (storage) + storage->InvalidatePassphrase(m_essid); + } + + return false; +} + +//----------------------------------------------------------------------- +bool CPosixConnection::PumpNetworkEvents() +{ + bool state_changed = false; + + ConnectionState state = GetState(); + if (m_state != state) + { + m_state = state; + state_changed = true; + } + + return state_changed; +} + +bool CPosixConnection::DoConnection(const CIPConfig &ipconfig, std::string passphrase) +{ + FILE *fr = fopen("/etc/network/interfaces", "r"); + if (!fr) + return false; + + char *line = NULL; + size_t line_length = 0; + std::vector interfaces_lines; + while (getdelim(&line, &line_length, '\n', fr) > 0) + interfaces_lines.push_back(line); + fclose(fr); + + std::vector new_interfaces_lines; + std::vector ifdown_interfaces; + for (size_t i = 0; i < interfaces_lines.size(); i++) + { + //printf("CPosixConnection::SetSettings, interfaces_lines:%s", interfaces_lines[i].c_str()); + + // comments are always skipped and copied over + if (interfaces_lines[i].find("#") != std::string::npos) + { + new_interfaces_lines.push_back(interfaces_lines[i]); + continue; + } + + // always copy the auto section over + if (interfaces_lines[i].find("auto") != std::string::npos) + { + new_interfaces_lines.push_back(interfaces_lines[i]); + continue; + } + + // always copy the loopback iface section over + if (interfaces_lines[i].find("iface lo") != std::string::npos) + { + new_interfaces_lines.push_back(interfaces_lines[i]); + continue; + } + + // look for "iface inet" + if (interfaces_lines[i].find("iface") != std::string::npos) + { + // we will take all interfaces down, then bring up ours. + // so remember all iface names. This could be more robust :) + std::string ifdown_interface = interfaces_lines[i]; + std::string::size_type start = ifdown_interface.find("iface") + sizeof("iface"); + std::string::size_type end = ifdown_interface.find(" inet", start); + ifdown_interfaces.push_back(ifdown_interface.substr(start, end - start)); + + if (interfaces_lines[i].find(m_interface) == std::string::npos) + { + // this is not our interface section (ethX or wlanX). + // we always copy the iface line over. + new_interfaces_lines.push_back(interfaces_lines[i]); + for (size_t j = i + 1; j < interfaces_lines.size(); j++) + { + // if next line is an 'iface', we are done. + if (interfaces_lines[j].find("iface") != std::string::npos) + { + // back up by one so next pass in + // loop starts at correct place. + i = j - 1; + break; + } + else + { + // copy these iface details over, we do not care about them. + new_interfaces_lines.push_back(interfaces_lines[j]); + } + } + continue; + } + else + { + std::string tmp; + // is this our interface section (ethX or wlanX) + // check requested method first. + if (ipconfig.m_method == IP_CONFIG_STATIC) + { + std::string method("static"); + tmp = "iface " + m_interface + " inet " + method + "\n"; + new_interfaces_lines.push_back(tmp); + tmp = " address " + ipconfig.m_address + "\n"; + new_interfaces_lines.push_back(tmp); + tmp = " netmask " + ipconfig.m_netmask + "\n"; + new_interfaces_lines.push_back(tmp); + tmp = " gateway " + ipconfig.m_gateway + "\n"; + new_interfaces_lines.push_back(tmp); + } + else + { + std::string method("dhcp"); + if (m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + // the wpa_action script will take care of + // launching udhcpc after the AP connects. + method = "manual"; + } + tmp = "iface " + m_interface + " inet " + method + "\n"; + new_interfaces_lines.push_back(tmp); + } + + // fill in the wifi details if needed + if (m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + // quote the essid, spaces are legal characters + tmp = " wpa-ssid \"" + m_essid + "\"\n"; + new_interfaces_lines.push_back(tmp); + + tmp = " wpa-ap-scan 1\n"; + new_interfaces_lines.push_back(tmp); + + tmp = " wpa-scan-ssid 1\n"; + new_interfaces_lines.push_back(tmp); + + if (m_encryption == NETWORK_CONNECTION_ENCRYPTION_NONE) + { + tmp = " wpa-key-mgmt NONE\n"; + new_interfaces_lines.push_back(tmp); + } + else if (m_encryption == NETWORK_CONNECTION_ENCRYPTION_WEP) + { + tmp = " wpa-key-mgmt NONE\n"; + new_interfaces_lines.push_back(tmp); + + // if ascii, then quote it, if hex, no quotes + if (PosixGuessIsHexPassPhrase(passphrase, m_encryption)) + tmp = " wpa-wep-key0 " + passphrase + "\n"; + else + tmp = " wpa-wep-key0 \"" + passphrase + "\"\n"; + new_interfaces_lines.push_back(tmp); + tmp = " wpa-wep-tx-keyidx 0\n"; + new_interfaces_lines.push_back(tmp); + } + else if (m_encryption == NETWORK_CONNECTION_ENCRYPTION_WPA || + m_encryption == NETWORK_CONNECTION_ENCRYPTION_WPA2) + { + // if ascii, then quote it, if hex, no quotes + if (PosixGuessIsHexPassPhrase(passphrase, m_encryption)) + tmp = " wpa-psk " + passphrase + "\n"; + else + tmp = " wpa-psk \"" + passphrase + "\"\n"; + new_interfaces_lines.push_back(tmp); + if (m_encryption == NETWORK_CONNECTION_ENCRYPTION_WPA) + tmp = " wpa-proto WPA\n"; + else + tmp = " wpa-proto WPA2\n"; + new_interfaces_lines.push_back(tmp); + } + } + } + } + } + + // write out the new /etc/network/interfaces as a temp file. + // we do a rename on it later as that is atomic. + FILE* fw = fopen("/etc/network/interfaces.temp", "w"); + if (!fw) + return false; + for (size_t i = 0; i < new_interfaces_lines.size(); i++) + { + //printf("CPosixConnection::SetSettings, new_interfaces_lines:%s", new_interfaces_lines[i].c_str()); + fwrite(new_interfaces_lines[i].c_str(), new_interfaces_lines[i].size(), 1, fw); + } + fclose(fw); + + // take down all interfaces using the current /etc/network/interfaces + int rtn_error; + std::string cmd; + for (size_t i = 0; i < ifdown_interfaces.size(); i++) + { + // check if interface is actually 'up' + //if (PosixCheckInterfaceUp(ifdown_interfaces[i])) + { + cmd = "/sbin/ifdown " + ifdown_interfaces[i]; + rtn_error = system(cmd.c_str()); + // ifdown was succesful but we got 'No child processes' which is harmless + if (rtn_error != 0 && errno != ECHILD) + CLog::Log(LOGERROR, "NetworkManager: Unable to stop interface %s, %s", ifdown_interfaces[i].c_str(), strerror(errno)); + else + CLog::Log(LOGDEBUG, "NetworkManager: Stopped interface %s", ifdown_interfaces[i].c_str()); + } + } + + // Rename the file (remember, you can not rename across devices) + if (rename("/etc/network/interfaces.temp", "/etc/network/interfaces") < 0) + return false; + + // bring up this interface using the new /etc/network/interfaces + cmd = "/sbin/ifup " + m_interface; + rtn_error = system(cmd.c_str()); + // ifup was succesful but we got 'No child processes' which is harmless + if (rtn_error != 0 && errno != ECHILD) + CLog::Log(LOGERROR, "NetworkManager: Unable to start interface %s, %s", m_interface.c_str(), strerror(errno)); + else + CLog::Log(LOGDEBUG, "NetworkManager: Started interface %s", m_interface.c_str()); + + // wait for wap to connect to the AP and udhcp to fetch an IP + if (m_type == NETWORK_CONNECTION_TYPE_WIFI) + { + for (int i = 0; i < 60; ++i) + { + if (GetState() == NETWORK_CONNECTION_STATE_CONNECTED) + break; + Sleep(1000); + } + } + + return true; +} diff --git a/xbmc/network/linux/PosixConnection.h b/xbmc/network/linux/PosixConnection.h new file mode 100644 index 0000000000000..8dbd96e031b3d --- /dev/null +++ b/xbmc/network/linux/PosixConnection.h @@ -0,0 +1,78 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "xbmc/network/IConnection.h" + +int PosixParseHex(char *str, unsigned char *addr); +bool IsWireless(int socket, const char *interface); +bool PosixCheckInterfaceUp(const std::string &interface); +std::string PosixGetDefaultGateway(const std::string &interface); + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +class CPosixConnection : public IConnection +{ +public: + CPosixConnection(bool managed, + int socket, const char *interface, const char *macaddress, + const char *essid, ConnectionType type, EncryptionType encryption, int signal); + virtual ~CPosixConnection(); + + virtual std::string GetName() const; + virtual std::string GetAddress() const; + virtual std::string GetNetmask() const; + virtual std::string GetGateway() const; + virtual std::string GetNameServer() const; + virtual std::string GetMacAddress() const; + + virtual ConnectionType GetType() const; + virtual ConnectionState GetState() const; + virtual unsigned int GetSpeed() const; + virtual IPConfigMethod GetMethod() const; + virtual unsigned int GetStrength() const; + virtual EncryptionType GetEncryption() const; + + virtual bool Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig); + + bool PumpNetworkEvents(); + + void SetCanManage(bool can_manange); +private: + bool DoConnection(const CIPConfig &ipconfig, std::string passphrase); + + bool m_managed; + + std::string m_essid; + std::string m_address; + std::string m_netmask; + std::string m_gateway; + std::string m_macaddress; + + ConnectionType m_type; + ConnectionState m_state; + IPConfigMethod m_method; + EncryptionType m_encryption; + + int m_signal; + int m_socket; + std::string m_interface; +}; diff --git a/xbmc/network/linux/PosixNetworkManager.cpp b/xbmc/network/linux/PosixNetworkManager.cpp new file mode 100644 index 0000000000000..098cc6ab61a99 --- /dev/null +++ b/xbmc/network/linux/PosixNetworkManager.cpp @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PosixNetworkManager.h" +#include "PosixConnection.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "guilib/Key.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "network/NetworkUtils.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "threads/Thread.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(TARGET_ANDROID) +#include "android/bionic_supplement/bionic_supplement.h" +#endif + +#define ARPHRD_80211 801 // wifi + +// CPosixNetworkManager and CPosixConnection rely on the debian/ubuntu method of using +// /etc/network/interfaces and pre-up/post-down scripts to handle bringing connection +// to wired/wireless networks. The pre-up/post-down scripts handle wireless/wpa though +// /etc/network/interfaces extensions "wireless-" and "wpa-". Basically, ifup/ifdown will +// tokenize these as shell vars and passes them to the pre-up/post-down scripts for handling. +// Note: This method,of course, requires root permissions. +// +// /etc/network/interfaces examples: +// auto wlan0 +// iface wlan0 inet dhcp +// wireless-essid [ESSID] +// wireless-mode [MODE] +// +// or +// auto wlan0 +// iface wlan0 inet dhcp +// wpa-ssid mynetworkname +// wpa-psk mysecretpassphrase +// +// Then, 'ifup wlan0' will bring up wlan0 with the proper wifi setup and dhdp fetch. +// +// NOTE: BusyBox will call pre-up/post-down scripts BUT it does not pass $PHASE so the +// if the script is the same for pre-up/post-down it will not be able to tell what to do. +// The simple fix is to add the following to /etc/wpa_supplicant/ifupdown.sh +// +// case $0 in +// *if-up.d*) PHASE="up";; +// *if-down.d*) PHASE="down";; +// *if-pre-up.d*) PHASE="pre-up";; +// *if-post-down.d*) PHASE="post-down";; +// esac +// +// Also you need to merge post-up -> pre-up and pre-down -> post-down as those phases +// do not exist under Busybox. +// +// +// CPosixNetworkManager and CPosixConnection work by detecting the avaliable network +// interfaces, then for wlan0, doing a wifi scan for access points using ioctl calls. +// A CPosixConnection object is created for each wired interface and each wifi access point. +// after creation, the 1st two fields are retained as the internal connection name for +// passphrase look up which is only relevent for wifi conections. +// +// Switching connections is performed by a CPosixConnection method in three steps. +// 1) use ifdown take down every interface except loopback. +// 2) if the desired connection is wifi, then +// rewrite /etc/network/interfaces, only changing "wireless-" or "wpa-" items. +// 3) use ifup to bring up the desired active connection. +// +// TODO: handle static in addition to dhcp settings. +// + +//----------------------------------------------------------------------- +//----------------------------------------------------------------------- +CPosixNetworkManager::CPosixNetworkManager() +{ + CLog::Log(LOGDEBUG, "NetworkManager: PosixNetworkManager created"); + m_socket = socket(AF_INET, SOCK_DGRAM, 0); + m_post_failed = false; +} + +CPosixNetworkManager::~CPosixNetworkManager() +{ + if (m_socket != -1) + close(m_socket); +} + +bool CPosixNetworkManager::CanManageConnections() +{ + return g_advancedSettings.m_enableNetworkManager; +} + +ConnectionList CPosixNetworkManager::GetConnections() +{ + FindNetworkInterfaces(); + if (CanManageConnections()) + RestoreSavedConnection(); + else + RestoreSystemConnection(); + return m_connections; +} + +bool CPosixNetworkManager::PumpNetworkEvents(INetworkEventsCallback *callback) +{ + bool result = false; + + // check for a failed startup connection + if (m_post_failed) + { + // since network startup happens before windowing and rendering are up + // wait until we can retrive WINDOW_DIALOG_KAI_TOAST before posting. + CGUIDialogKaiToast *toast = (CGUIDialogKaiToast*)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); + if (toast) + { + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(13279), g_localizeStrings.Get(1001)); + m_post_failed = false; + } + } + + // pump our connection + for (size_t i = 0; i < m_connections.size(); i++) + { + if (((CPosixConnection*)m_connections[i].get())->PumpNetworkEvents()) + { + //some connection state changed (connected or disconnected) + if (((CPosixConnection*)m_connections[i].get())->GetState() == NETWORK_CONNECTION_STATE_CONNECTED) + { + // callback to CNetworkManager to setup the + // m_defaultConnection and update GUI state if showing. + callback->OnConnectionChange(m_connections[i]); + // callback to start services + callback->OnConnectionStateChange(NETWORK_CONNECTION_STATE_CONNECTED); + result = true; + } + } + } + + return result; +} + +bool CPosixNetworkManager::SendWakeOnLan(const char *mac) +{ + // Fetch the hardware address + unsigned char ethaddr[8] = {0}; + if (!CNetworkUtils::in_ether(mac, ethaddr)) + { + CLog::Log(LOGERROR, "%s - Invalid hardware address specified (%s)", __FUNCTION__, mac); + return false; + } + + // Setup the socket + int packet = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (packet < 0) + { + CLog::Log(LOGERROR, "%s - Unable to create socket (%s)", __FUNCTION__, strerror (errno)); + return false; + } + + // Set socket options + struct sockaddr_in saddr = {0}; + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + saddr.sin_port = htons(9); + + unsigned int value = 1; + if (setsockopt(packet, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof(unsigned int)) == SOCKET_ERROR) + { + CLog::Log(LOGERROR, "%s - Unable to set socket options (%s)", __FUNCTION__, strerror (errno)); + closesocket(packet); + return false; + } + + // Build the magic packet (6 x 0xff + 16 x MAC address) + unsigned char buf[128] = {0}; + unsigned char *ptr = buf; + + for (int i = 0; i < 6; i++) + *ptr++ = 0xff; + + for (int j = 0; j < 16; j++) + { + for (int i = 0; i < 6; i++) + *ptr++ = ethaddr[i]; + } + + // Send the magic packet + if (sendto(packet, (char*)buf, 102, 0, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) + { + CLog::Log(LOGERROR, "%s - Unable to send magic packet (%s)", __FUNCTION__, strerror (errno)); + closesocket(packet); + return false; + } + + closesocket(packet); + CLog::Log(LOGINFO, "%s - Magic packet send to '%s'", __FUNCTION__, mac); + + return true; +} + +//----------------------------------------------------------------------- +void CPosixNetworkManager::RestoreSavedConnection() +{ + CLog::Log(LOGDEBUG, "NetworkManager: Restoring saved connection"); + + std::string saved_name = g_guiSettings.GetString("network.essid"); + + CIPConfig saved_ipconfig; + saved_ipconfig.m_method = (IPConfigMethod)g_guiSettings.GetInt("network.method"); + saved_ipconfig.m_address = g_guiSettings.GetString("network.address"); + saved_ipconfig.m_netmask = g_guiSettings.GetString("network.netmask"); + saved_ipconfig.m_gateway = g_guiSettings.GetString("network.gateway"); + saved_ipconfig.m_nameserver = g_guiSettings.GetString("network.nameserver"); + + for (size_t i = 0; i < m_connections.size(); i++) + { + std::string connection_name = ((CPosixConnection*)m_connections[i].get())->GetName(); + if (connection_name.find(saved_name) != std::string::npos) + { + if (!((CPosixConnection*)m_connections[i].get())->Connect(NULL, saved_ipconfig)) + { + // best we can do is try an existing system connection + RestoreSystemConnection(); + } + break; + } + } +} + +void CPosixNetworkManager::RestoreSystemConnection() +{ + CLog::Log(LOGDEBUG, "NetworkManager: Defaulting to system connection"); + // nothing to do here, CNetworkManager will activate the first + // connection with a state of NETWORK_CONNECTION_STATE_CONNECTED; + + // humm, how do we check ? + // m_post_failed = true; + +} + +void CPosixNetworkManager::FindNetworkInterfaces() +{ + m_connections.clear(); + + FILE *fp = fopen("/proc/net/dev", "r"); + if (!fp) + return; + + int n, linenum = 0; + char *line = NULL; + size_t linel = 0; + char *interfaceName; + bool managed = CanManageConnections(); + + while (getdelim(&line, &linel, '\n', fp) > 0) + { + // skip first two lines + if (linenum++ < 2) + continue; + + // search where the word begins + interfaceName = line; + while (isspace(*interfaceName)) + ++interfaceName; + + // read word until : + n = strcspn(interfaceName, ": \t"); + interfaceName[n] = 0; + +#if defined(TARGET_ANDROID) + // only test ethX and wlanX interfaces, + // anything else is non-standard and we do not care about it. + if (strncmp(interfaceName, "eth", 3) != 0 && strncmp(interfaceName, "wlan", 4) != 0) + continue; +#endif + + // make sure the device has ethernet encapsulation + struct ifreq ifr; + memset(&ifr, 0x00, sizeof(ifr)); + strcpy(ifr.ifr_name, interfaceName); + std::string essid = "Wired"; + ConnectionType connection = NETWORK_CONNECTION_TYPE_WIRED; + EncryptionType encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + + if (ioctl(m_socket, SIOCGIFHWADDR, &ifr) >= 0) + { +#if defined(TARGET_ANDROID) + // Android cannot SIOCSIWSCAN (permissions error) + // So just flag as wifi with unknown encryption and use it. + if (IsWireless(m_socket, interfaceName)) + { + essid = "Wifi"; + connection = NETWORK_CONNECTION_TYPE_WIFI; + encryption = NETWORK_CONNECTION_ENCRYPTION_UNKNOWN; + } +#else + if (IsWireless(m_socket, interfaceName)) + { + // get the list of access points on this interface, try this 3 times + int retryCount = 0; + while (!FindWifiConnections(interfaceName) && retryCount < 3) + retryCount++; + } + else +#endif + { + // and ignore loopback, we also include ARPHRD_80211 but that will only + // apply if we are running on android. + if ((ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER || ifr.ifr_hwaddr.sa_family == ARPHRD_80211) + && !(ifr.ifr_flags & IFF_LOOPBACK)) + { + char macaddress[1024] = {0}; + if (ioctl(m_socket, SIOCGIFHWADDR, &ifr) >= 0) + { + // format up 'wire.. + sprintf(macaddress, "%02X:%02X:%02X:%02X:%02X:%02X", + ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], + ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], + ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); + } + /* + CLog::Log(LOGDEBUG, "CPosixNetworkManager::FindNetworkInterfaces, " + "interfaceName(%s), macaddress(%s), essid(%s)", + interfaceName, macaddress, essid.c_str()); + */ + m_connections.push_back(CConnectionPtr(new CPosixConnection(managed, + m_socket, interfaceName, macaddress, essid.c_str(), connection, encryption, 100))); + } + } + } + } + + free(line); + fclose(fp); +} + +bool CPosixNetworkManager::FindWifiConnections(const char *interfaceName) +{ + bool managed = CanManageConnections(); + // Query the wireless extentsions version number. It will help us when we + // parse the resulting events + struct iwreq iwr; + char rangebuffer[sizeof(iw_range) * 2]; /* Large enough */ + struct iw_range* range = (struct iw_range*) rangebuffer; + + memset(rangebuffer, 0x00, sizeof(rangebuffer)); + iwr.u.data.pointer = (caddr_t) rangebuffer; + iwr.u.data.length = sizeof(rangebuffer); + iwr.u.data.flags = 0; + strncpy(iwr.ifr_name, interfaceName, IFNAMSIZ); + if (ioctl(m_socket, SIOCGIWRANGE, &iwr) < 0) + { + CLog::Log(LOGWARNING, "%-8.16s Driver has no Wireless Extension version information.", + interfaceName); + return false; + } + + // Scan for wireless access points + memset(&iwr, 0x00, sizeof(iwr)); + strncpy(iwr.ifr_name, interfaceName, IFNAMSIZ); + if (ioctl(m_socket, SIOCSIWSCAN, &iwr) < 0) + { + CLog::Log(LOGWARNING, "Cannot initiate wireless scan: ioctl[SIOCSIWSCAN]: %s", strerror(errno)); + return false; + } + + // Get the results of the scanning. Three scenarios: + // 1. There's not enough room in the result buffer (E2BIG) + // 2. The scanning is not complete (EAGAIN) and we need to try again. We cap this with 15 seconds. + // 3. Were'e good. + int duration = 0; // ms + unsigned char* res_buf = NULL; + int res_buf_len = IW_SCAN_MAX_DATA; + while (duration < 15000) + { + if (!res_buf) + res_buf = (unsigned char*) malloc(res_buf_len); + + if (res_buf == NULL) + { + CLog::Log(LOGWARNING, "Cannot alloc memory for wireless scanning"); + return false; + } + + strncpy(iwr.ifr_name, interfaceName, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + iwr.u.data.flags = 0; + int x = ioctl(m_socket, SIOCGIWSCAN, &iwr); + if (x == 0) + break; + + if (errno == E2BIG && res_buf_len < 100000) + { + free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + CLog::Log(LOGDEBUG, "Scan results did not fit - trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } + else if (errno == EAGAIN) + { + usleep(250000); // sleep for 250ms + duration += 250; + } + else + { + CLog::Log(LOGWARNING, "Cannot get wireless scan results: ioctl[SIOCGIWSCAN]: %s", strerror(errno)); + free(res_buf); + return false; + } + } + + size_t len = iwr.u.data.length; // total length of the wireless events from the scan results + unsigned char* pos = res_buf; // pointer to the current event (about 10 per wireless network) + unsigned char* end = res_buf + len; // marks the end of the scan results + unsigned char* custom; // pointer to the event payload + struct iw_event iwe_buf, *iwe = &iwe_buf; // buffer to hold individual events + + bool first = true; + char essid[IW_ESSID_MAX_SIZE+1]; + char macaddress[256]; + int quality = 0, signalLevel = 0; + EncryptionType encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + + while (pos + IW_EV_LCP_LEN <= end) + { + // Event data may be unaligned, so make a local, aligned copy before processing. + + // copy event prefix (size of event minus IOCTL fixed payload) + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + // if the payload is nontrivial (i.e. > 16 octets) assume it comes after a pointer + custom = pos + IW_EV_POINT_LEN; + if (range->we_version_compiled > 18 && + (iwe->cmd == SIOCGIWESSID || + iwe->cmd == SIOCGIWENCODE || + iwe->cmd == IWEVGENIE || + iwe->cmd == IWEVCUSTOM)) + { + // Wireless extentsions v19 removed the pointer from struct iw_point + char *data_pos = (char*)&iwe_buf.u.data.length; + int data_len = data_pos - (char*)&iwe_buf; + memcpy(data_pos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - data_len); + } + else + { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) + { + + // Get the access point MAC addresses + case SIOCGIWAP: + { + // this is the 1st cmp we get, so we have to play games + // and push back our parsed results on the next one, but + // we need to save the macaddress so we push the right one. + char cur_macaddress[256] = {0}; + // macAddress is big-endian, write in byte chunks + sprintf(cur_macaddress, "%02X:%02X:%02X:%02X:%02X:%02X", + iwe->u.ap_addr.sa_data[0], iwe->u.ap_addr.sa_data[1], + iwe->u.ap_addr.sa_data[2], iwe->u.ap_addr.sa_data[3], + iwe->u.ap_addr.sa_data[4], iwe->u.ap_addr.sa_data[5]); + + if (first) + { + first = false; + memcpy(macaddress, cur_macaddress, sizeof(macaddress)); + } + else + { + m_connections.push_back(CConnectionPtr(new CPosixConnection(managed, + m_socket, interfaceName, macaddress, essid, + NETWORK_CONNECTION_TYPE_WIFI, encryption, quality))); + memcpy(macaddress, cur_macaddress, sizeof(macaddress)); + } + // reset encryption for parsing next access point + encryption = NETWORK_CONNECTION_ENCRYPTION_NONE; + signalLevel = 0; + break; + } + + // Get ESSID + case SIOCGIWESSID: + { + memset(essid, 0x00, sizeof(essid)); + if ((custom) && (iwe->u.essid.length)) + memcpy(essid, custom, iwe->u.essid.length); + break; + } + + // Quality part of statistics + case IWEVQUAL: + { + quality = iwe->u.qual.qual; + signalLevel = iwe->u.qual.level; + break; + } + + // Get encoding token & mode + case SIOCGIWENCODE: + { + if (!(iwe->u.data.flags & IW_ENCODE_DISABLED) && encryption == NETWORK_CONNECTION_ENCRYPTION_NONE) + encryption = NETWORK_CONNECTION_ENCRYPTION_WEP; + break; + } + + // Generic IEEE 802.11 information element (IE) for WPA, RSN, WMM, ... + case IWEVGENIE: + { + int offset = 0; + // Loop on each IE, each IE is minimum 2 bytes + while (offset <= iwe_buf.u.data.length - 2) + { + switch (custom[offset]) + { + case 0xdd: // WPA1 + if (encryption != NETWORK_CONNECTION_ENCRYPTION_WPA2) + encryption = NETWORK_CONNECTION_ENCRYPTION_WPA; + break; + case 0x30: // WPA2 + encryption = NETWORK_CONNECTION_ENCRYPTION_WPA2; + break; + } + offset += custom[offset+1] + 2; + } + } + } + pos += iwe->len; + } + + if (!first) + { + m_connections.push_back(CConnectionPtr(new CPosixConnection(managed, + m_socket, interfaceName, macaddress, essid, + NETWORK_CONNECTION_TYPE_WIFI, encryption, quality))); + } + + free(res_buf); + res_buf = NULL; + return true; +} diff --git a/xbmc/network/linux/PosixNetworkManager.h b/xbmc/network/linux/PosixNetworkManager.h new file mode 100644 index 0000000000000..a3ab34cb6f520 --- /dev/null +++ b/xbmc/network/linux/PosixNetworkManager.h @@ -0,0 +1,47 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "xbmc/network/INetworkManager.h" + +class CPosixNetworkManager : public INetworkManager +{ +public: + CPosixNetworkManager(); + virtual ~CPosixNetworkManager(); + + virtual bool CanManageConnections(); + + virtual ConnectionList GetConnections(); + + virtual bool PumpNetworkEvents(INetworkEventsCallback *callback); + virtual bool SendWakeOnLan(const char *mac); +private: + void RestoreSavedConnection(); + void RestoreSystemConnection(); + + void FindNetworkInterfaces(); + bool FindWifiConnections(const char *interfaceName); + + int m_socket; + ConnectionList m_connections; + bool m_post_failed; +}; diff --git a/xbmc/network/linux/ZeroconfAvahi.cpp b/xbmc/network/linux/ZeroconfAvahi.cpp index 8214bb4021349..158f7b21bf6e2 100644 --- a/xbmc/network/linux/ZeroconfAvahi.cpp +++ b/xbmc/network/linux/ZeroconfAvahi.cpp @@ -131,7 +131,7 @@ bool CZeroconfAvahi::doPublishService(const std::string& fcr_identifier, const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt) + const std::vector >& txt) { CLog::Log(LOGDEBUG, "CZeroconfAvahi::doPublishService identifier: %s type: %s name:%s port:%i", fcr_identifier.c_str(), fcr_type.c_str(), fcr_name.c_str(), f_port); @@ -145,7 +145,7 @@ bool CZeroconfAvahi::doPublishService(const std::string& fcr_identifier, //txt records to AvahiStringList AvahiStringList *txtList = NULL; - for(std::map::iterator it=txt.begin(); it!=txt.end(); it++) + for(std::vector >::const_iterator it=txt.begin(); it!=txt.end(); it++) { txtList = avahi_string_list_add_pair(txtList, it->first.c_str(), it->second.c_str()); } diff --git a/xbmc/network/linux/ZeroconfAvahi.h b/xbmc/network/linux/ZeroconfAvahi.h index b6881c14f62ad..704f2a2f7c22d 100644 --- a/xbmc/network/linux/ZeroconfAvahi.h +++ b/xbmc/network/linux/ZeroconfAvahi.h @@ -25,6 +25,7 @@ #include #include +#include #include #include "network/Zeroconf.h" @@ -47,7 +48,7 @@ class CZeroconfAvahi : public CZeroconf const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt); + const std::vector >& txt); virtual bool doRemoveService(const std::string& fcr_ident); diff --git a/xbmc/network/osx/ZeroconfOSX.cpp b/xbmc/network/osx/ZeroconfOSX.cpp index b50d310aacf98..10291e37849e3 100644 --- a/xbmc/network/osx/ZeroconfOSX.cpp +++ b/xbmc/network/osx/ZeroconfOSX.cpp @@ -44,7 +44,7 @@ bool CZeroconfOSX::doPublishService(const std::string& fcr_identifier, const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt) + const std::vector >& txt) { CLog::Log(LOGDEBUG, "CZeroconfOSX::doPublishService identifier: %s type: %s name:%s port:%i", fcr_identifier.c_str(), fcr_type.c_str(), fcr_name.c_str(), f_port); @@ -74,7 +74,7 @@ bool CZeroconfOSX::doPublishService(const std::string& fcr_identifier, //txt map to dictionary CFDataRef txtData = NULL; CFMutableDictionaryRef txtDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - for(std::map::const_iterator it = txt.begin(); it != txt.end(); ++it) + for(std::vector >::const_iterator it = txt.begin(); it != txt.end(); ++it) { CFStringRef key = CFStringCreateWithCString (NULL, it->first.c_str(), diff --git a/xbmc/network/osx/ZeroconfOSX.h b/xbmc/network/osx/ZeroconfOSX.h index fb4409dcf617b..34aeb6dec013d 100644 --- a/xbmc/network/osx/ZeroconfOSX.h +++ b/xbmc/network/osx/ZeroconfOSX.h @@ -20,6 +20,7 @@ */ #include +#include #include "network/Zeroconf.h" #include "threads/CriticalSection.h" @@ -42,7 +43,7 @@ class CZeroconfOSX : public CZeroconf const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt); + const std::vector >& txt); bool doRemoveService(const std::string& fcr_ident); diff --git a/xbmc/network/upnp/UPnP.cpp b/xbmc/network/upnp/UPnP.cpp index 03fb92d3229e2..71a0d916ae7d5 100644 --- a/xbmc/network/upnp/UPnP.cpp +++ b/xbmc/network/upnp/UPnP.cpp @@ -28,7 +28,6 @@ #include "utils/URIUtils.h" #include "Application.h" #include "ApplicationMessenger.h" -#include "network/Network.h" #include "utils/log.h" #include "Platinum.h" #include "URL.h" @@ -204,9 +203,7 @@ CUPnP::CUPnP() : m_UPnP = new PLT_UPnP(); // keep main IP around - if (g_application.getNetwork().GetFirstConnectedInterface()) { - m_IP = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str(); - } + m_IP = g_application.getNetworkManager().GetDefaultConnectionAddress().c_str(); NPT_List list; if (NPT_SUCCEEDED(PLT_UPnPMessageHelper::GetIPAddresses(list)) && list.GetItemCount()) { m_IP = (*(list.GetFirstItem())).ToString(); diff --git a/xbmc/network/upnp/UPnPRenderer.cpp b/xbmc/network/upnp/UPnPRenderer.cpp index adde44ca2b782..3e0dc3edcea9f 100644 --- a/xbmc/network/upnp/UPnPRenderer.cpp +++ b/xbmc/network/upnp/UPnPRenderer.cpp @@ -1,4 +1,3 @@ -#include "network/Network.h" #include "UPnPRenderer.h" #include "UPnP.h" #include "UPnPInternal.h" @@ -364,10 +363,7 @@ CUPnPRenderer::GetMetadata(NPT_String& meta) thumb = CTextureCache::GetWrappedImageURL(thumb); - NPT_String ip; - if (g_application.getNetwork().GetFirstConnectedInterface()) { - ip = g_application.getNetwork().GetFirstConnectedInterface()->GetCurrentIPAddress().c_str(); - } + NPT_String ip = g_application.getNetworkManager().GetDefaultConnectionAddress().c_str(); // build url, use the internal device http server to serv the image NPT_HttpUrlQuery query; query.AddField("path", thumb.c_str()); diff --git a/xbmc/network/windows/WinConnection.cpp b/xbmc/network/windows/WinConnection.cpp new file mode 100644 index 0000000000000..c304e11746f44 --- /dev/null +++ b/xbmc/network/windows/WinConnection.cpp @@ -0,0 +1,104 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "WinConnection.h" +#include "xbmc/utils/StdString.h" + +CWinConnection::CWinConnection(IP_ADAPTER_INFO adapter) +{ + m_adapter = adapter; +} + +CWinConnection::~CWinConnection() +{ +} + +std::string CWinConnection::GetName() const +{ + return adapter.Description; +} + +std::string CWinConnection::GetAddress() const +{ + return m_adapter.IpAddressList.IpAddress.String; +} + +std::string CWinConnection::GetNetmask() const +{ + return m_adapter.IpAddressList.IpMask.String; +} + +std::string CWinConnection::GetGateway() const +{ + return m_adapter.GatewayList.IpAddress.String; +} + +std::string CPosixConnection::GetNameServer() const +{ + std::string nameserver("127.0.0.1"); + return nameserver; +} + +std::string CWinConnection::GetMacAddress() const +{ + return std::string((char*)m_adapter.Address); +} + +ConnectionType CWinConnection::GetType() const +{ + return NETWORK_CONNECTION_TYPE_WIRED; +} + +ConnectionState CWinConnection::GetState() const +{ + CStdString strIP = m_adapter.IpAddressList.IpAddress.String; + + if (strIP != "0.0.0.0") + return NETWORK_CONNECTION_STATE_CONNECTED; + else + return NETWORK_CONNECTION_STATE_DISCONNECTED; +} + +IPConfigMethod CWinConnection::GetMethod() const +{ + return IP_CONFIG_DHCP; +} + +unsigned int CWinConnection::GetStrength() const +{ + return 0; +} + +EncryptionType CWinConnection::GetEncryption() const +{ + return NETWORK_CONNECTION_ENCRYPTION_NONE; +} + +unsigned int CWinConnection::GetConnectionSpeed() const +{ + return 100; +} + +bool CWinConnection::Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig) +{ + return false; +} diff --git a/xbmc/network/windows/WinConnection.h b/xbmc/network/windows/WinConnection.h new file mode 100644 index 0000000000000..013e333d33b06 --- /dev/null +++ b/xbmc/network/windows/WinConnection.h @@ -0,0 +1,51 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#ifdef _WIN32 +#include "xbmc/network/IConnection.h" +#include "Iphlpapi.h" + +class CWinConnection : public IConnection +{ +public: + CWinConnection(IP_ADAPTER_INFO adapter); + virtual ~CWinConnection(); + + virtual std::string GetName() const; + virtual std::string GetAddress() const; + virtual std::string GetNetmask() const; + virtual std::string GetGateway() const; + virtual std::string GetNameServer() const; + virtual std::string GetMacAddress() const; + + virtual ConnectionType GetType() const; + virtual unsigned int GetSpeed() const; + virtual ConnectionState GetState() const; + virtual IPConfigMethod GetMethod() const; + virtual unsigned int GetStrength() const; + virtual EncryptionType GetEncryption() const; + virtual bool Connect(IPassphraseStorage *storage, const CIPConfig &ipconfig); +private: + IP_ADAPTER_INFO m_adapter; +}; +#endif diff --git a/xbmc/network/windows/WinNetworkManager.cpp b/xbmc/network/windows/WinNetworkManager.cpp new file mode 100644 index 0000000000000..073f2a19f1f6a --- /dev/null +++ b/xbmc/network/windows/WinNetworkManager.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "WinNetworkManager.h" +#ifdef _WIN32 +#include "WinConnection.h" + +CWinNetworkManager::CWinNetworkManager() +{ +} + +CWinNetworkManager::~CWinNetworkManager() +{ +} + +bool CWinNetworkManager::Connect() +{ + return false; +} + +bool CWinNetworkManager::CanManageConnections() +{ + return false; +} + +ConnectionList CWinNetworkManager::GetConnections() +{ + ConnectionList connections; + + PIP_ADAPTER_INFO adapterInfo; + PIP_ADAPTER_INFO adapter = NULL; + + ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); + + adapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof (IP_ADAPTER_INFO)); + if (adapterInfo == NULL) + return; + + if (GetAdaptersInfo(adapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) + { + free(adapterInfo); + adapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + if (adapterInfo == NULL) + { + OutputDebugString("Error allocating memory needed to call GetAdaptersinfo\n"); + return; + } + } + + if ((GetAdaptersInfo(adapterInfo, &ulOutBufLen)) == NO_ERROR) + { + adapter = adapterInfo; + while (adapter) + { + connections.push_back(CConnectionPtr(new CWinConnection(*adapter))); + + adapter = adapter->Next; + } + } + + free(adapterInfo); + + return connections; +} + +bool CWinNetworkManager::PumpNetworkEvents(INetworkEventsCallback *callback) +{ + return false; +} +#endif diff --git a/xbmc/network/windows/WinNetworkManager.h b/xbmc/network/windows/WinNetworkManager.h new file mode 100644 index 0000000000000..48bd027bb47af --- /dev/null +++ b/xbmc/network/windows/WinNetworkManager.h @@ -0,0 +1,41 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "system.h" +#ifdef _WIN32 +#include "xbmc/network/INetworkManager.h" + +class CWinNetworkManager : public INetworkManager +{ +public: + CWinNetworkManager(); + virtual ~CWinNetworkManager(); + + virtual bool Connect(); + + virtual bool CanManageConnections(); + + virtual ConnectionList GetConnections(); + + virtual bool PumpNetworkEvents(INetworkEventsCallback *callback); +}; +#endif diff --git a/xbmc/network/windows/ZeroconfWIN.cpp b/xbmc/network/windows/ZeroconfWIN.cpp index e879bc8bbbdba..eddc98afd047a 100644 --- a/xbmc/network/windows/ZeroconfWIN.cpp +++ b/xbmc/network/windows/ZeroconfWIN.cpp @@ -62,7 +62,7 @@ bool CZeroconfWIN::doPublishService(const std::string& fcr_identifier, const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt) + const std::vector >& txt) { DNSServiceRef netService = NULL; TXTRecordRef txtRecord; @@ -87,7 +87,7 @@ bool CZeroconfWIN::doPublishService(const std::string& fcr_identifier, //add txt records if(!txt.empty()) { - for(std::map::const_iterator it = txt.begin(); it != txt.end(); ++it) + for(std::vector >::const_iterator it = txt.begin(); it != txt.end(); ++it) { CLog::Log(LOGDEBUG, "ZeroconfWIN: key:%s, value:%s",it->first.c_str(),it->second.c_str()); uint8_t txtLen = (uint8_t)strlen(it->second.c_str()); diff --git a/xbmc/network/windows/ZeroconfWIN.h b/xbmc/network/windows/ZeroconfWIN.h index 3b9e3d6616d45..d8d4f2f59d40a 100644 --- a/xbmc/network/windows/ZeroconfWIN.h +++ b/xbmc/network/windows/ZeroconfWIN.h @@ -36,7 +36,7 @@ class CZeroconfWIN : public CZeroconf const std::string& fcr_type, const std::string& fcr_name, unsigned int f_port, - std::map txt); + const std::vector >& txt); bool doRemoveService(const std::string& fcr_ident); diff --git a/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp b/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp index b7a00612696ba..d25a4b01b0116 100644 --- a/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp +++ b/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp @@ -20,6 +20,11 @@ #include "PeripheralBusUSBLibUdev.h" #include "peripherals/Peripherals.h" + +#if defined(HAS_LINUX_EVENTS) +#include "windowing/WinEventsLinux.h" +#endif + extern "C" { #include } @@ -107,6 +112,7 @@ CPeripheralBusUSB::~CPeripheralBusUSB(void) bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results) { + bool rtn = false; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev(NULL), *parent(NULL); @@ -173,7 +179,32 @@ bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results) /* Free the enumerator object */ udev_enumerate_unref(enumerate); - return true; + // quick check if number of entries changed. + if (m_results.m_results.size() != results.m_results.size()) + { + m_results = results; + rtn = true; + } + else + { + // number of entried are the same, so we have to compare each one. + for (unsigned int iDevicePtr = 0; iDevicePtr < m_results.m_results.size(); iDevicePtr++) + { + if (!results.ContainsResult(m_results.m_results.at(iDevicePtr))) + { + // if anything changes, we flag a new PeripheralScanResults + m_results = results; + rtn = true; + break; + } + } + } +#if defined(HAS_LINUX_EVENTS) + if (rtn) + CWinEvents::RefreshDevices(); +#endif + + return rtn; } const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass) @@ -221,9 +252,9 @@ void CPeripheralBusUSB::Clear(void) bool CPeripheralBusUSB::WaitForUpdate() { - int m_udevFd = udev_monitor_get_fd(m_udevMon); + int udevFd = udev_monitor_get_fd(m_udevMon); - if (m_udevFd < 0) + if (udevFd < 0) { CLog::Log(LOGERROR, "%s - get udev monitor", __FUNCTION__); return false; @@ -231,7 +262,7 @@ bool CPeripheralBusUSB::WaitForUpdate() /* poll for udev changes */ struct pollfd pollFd; - pollFd.fd = m_udevFd; + pollFd.fd = udevFd; pollFd.events = POLLIN; int iPollResult; while (!m_bStop && ((iPollResult = poll(&pollFd, 1, 100)) <= 0)) diff --git a/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.h b/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.h index b93cebee5402d..e5c43de050d7c 100644 --- a/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.h +++ b/xbmc/peripherals/bus/linux/PeripheralBusUSBLibUdev.h @@ -50,5 +50,6 @@ namespace PERIPHERALS struct udev * m_udev; struct udev_monitor *m_udevMon; + PeripheralScanResults m_results; }; } diff --git a/xbmc/pictures/SlideShowPicture.cpp b/xbmc/pictures/SlideShowPicture.cpp index 055806b4f6f54..a8aa160bb6e5b 100644 --- a/xbmc/pictures/SlideShowPicture.cpp +++ b/xbmc/pictures/SlideShowPicture.cpp @@ -859,6 +859,7 @@ void CSlideShowPic::Render(float *x, float *y, CBaseTexture* pTexture, color_t c GLint posLoc = g_Windowing.GUIShaderGetPos(); GLint colLoc = g_Windowing.GUIShaderGetCol(); GLint tex0Loc = g_Windowing.GUIShaderGetCoord0(); + GLint uniColLoc= g_Windowing.GUIShaderGetUniCol(); glVertexAttribPointer(posLoc, 3, GL_FLOAT, 0, 0, ver); glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, col); @@ -887,6 +888,7 @@ void CSlideShowPic::Render(float *x, float *y, CBaseTexture* pTexture, color_t c tex[1][0] = tex[2][0] = u2; tex[2][1] = tex[3][1] = v2; + glUniform4f(uniColLoc,(col[0][0] / 255.0), (col[0][1] / 255.0), (col[0][2] / 255.0), (col[0][3] / 255.0)); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); glDisableVertexAttribArray(posLoc); diff --git a/xbmc/powermanagement/PowerManager.cpp b/xbmc/powermanagement/PowerManager.cpp index a5534c9f22435..72722ddd323cf 100644 --- a/xbmc/powermanagement/PowerManager.cpp +++ b/xbmc/powermanagement/PowerManager.cpp @@ -46,6 +46,7 @@ #include "linux/ConsoleDeviceKitPowerSyscall.h" #include "linux/SystemdUPowerSyscall.h" #include "linux/UPowerSyscall.h" +#include "linux/PivosPowerSyscall.h" #ifdef HAS_HAL #include "linux/HALPowerSyscall.h" #endif @@ -83,6 +84,8 @@ void CPowerManager::Initialize() m_instance = new CSystemdUPowerSyscall(); else if (CUPowerSyscall::HasUPower()) m_instance = new CUPowerSyscall(); + else if (CPivosPowerSyscall::HasPivosPowerSyscall()) + m_instance = new CPivosPowerSyscall(); #ifdef HAS_HAL else m_instance = new CHALPowerSyscall(); diff --git a/xbmc/powermanagement/linux/Makefile b/xbmc/powermanagement/linux/Makefile index 11a91b00001fa..f3814c8e25914 100644 --- a/xbmc/powermanagement/linux/Makefile +++ b/xbmc/powermanagement/linux/Makefile @@ -1,10 +1,11 @@ -SRCS=ConsoleDeviceKitPowerSyscall.cpp \ - ConsoleUPowerSyscall.cpp \ - HALPowerSyscall.cpp \ - UPowerSyscall.cpp \ - SystemdUPowerSyscall.cpp +SRCS = ConsoleDeviceKitPowerSyscall.cpp +SRCS += ConsoleUPowerSyscall.cpp +SRCS += HALPowerSyscall.cpp +SRCS += UPowerSyscall.cpp +SRCS += SystemdUPowerSyscall.cpp +SRCS += PivosPowerSyscall.cpp -LIB=powermanagement_linux.a +LIB = powermanagement_linux.a include ../../../Makefile.include -include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/powermanagement/linux/PivosPowerSyscall.cpp b/xbmc/powermanagement/linux/PivosPowerSyscall.cpp new file mode 100644 index 0000000000000..78642921390ac --- /dev/null +++ b/xbmc/powermanagement/linux/PivosPowerSyscall.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "system.h" +#include "PivosPowerSyscall.h" +#include "utils/AMLUtils.h" +#include "utils/log.h" + +CPivosPowerSyscall::CPivosPowerSyscall() +{ + if (aml_get_cputype() == 1) + { + m_CanPowerdown = true; + m_CanSuspend = false; + } + else + { + m_CanPowerdown = false; + m_CanSuspend = true; + } + m_CanHibernate = false; + m_CanReboot = true; + + m_OnResume = false; + m_OnSuspend = false; +} + +bool CPivosPowerSyscall::Powerdown() +{ + return true; +} + +bool CPivosPowerSyscall::Suspend() +{ + m_OnSuspend = true; + return true; +} + +bool CPivosPowerSyscall::Hibernate() +{ + return false; +} + +bool CPivosPowerSyscall::Reboot() +{ + return true; +} + +bool CPivosPowerSyscall::CanPowerdown() +{ + return m_CanPowerdown; +} + +bool CPivosPowerSyscall::CanSuspend() +{ + return m_CanSuspend; +} + +bool CPivosPowerSyscall::CanHibernate() +{ + return m_CanHibernate; +} + +bool CPivosPowerSyscall::CanReboot() +{ + return m_CanReboot; +} + +int CPivosPowerSyscall::BatteryLevel() +{ + return 0; +} + +bool CPivosPowerSyscall::PumpPowerEvents(IPowerEventsCallback *callback) +{ + if (m_OnSuspend) + { + // do the CPowerManager::OnSleep() callback + callback->OnSleep(); + m_OnResume = true; + m_OnSuspend = false; + // wait for all our threads to do their thing + usleep(1 * 1000 * 1000); + aml_set_sysfs_str("/sys/power/state", "mem"); + usleep(100 * 1000); + } + else if (m_OnResume) + { + // do the CPowerManager::OnWake() callback + callback->OnWake(); + m_OnResume = false; + } + + return true; +} + +bool CPivosPowerSyscall::HasPivosPowerSyscall() +{ + return aml_present(); +} diff --git a/xbmc/powermanagement/linux/PivosPowerSyscall.h b/xbmc/powermanagement/linux/PivosPowerSyscall.h new file mode 100644 index 0000000000000..a6c0ac97f09fe --- /dev/null +++ b/xbmc/powermanagement/linux/PivosPowerSyscall.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "powermanagement/IPowerSyscall.h" + +class CPivosPowerSyscall : public IPowerSyscall +{ +public: + CPivosPowerSyscall(); + virtual ~CPivosPowerSyscall() {} + + virtual bool Powerdown(); + virtual bool Suspend(); + virtual bool Hibernate(); + virtual bool Reboot(); + + virtual bool CanPowerdown(); + virtual bool CanSuspend(); + virtual bool CanHibernate(); + virtual bool CanReboot(); + virtual int BatteryLevel(); + virtual bool PumpPowerEvents(IPowerEventsCallback *callback); + + static bool HasPivosPowerSyscall(); + +private: + bool m_CanPowerdown; + bool m_CanSuspend; + bool m_CanHibernate; + bool m_CanReboot; + + bool m_OnResume; + bool m_OnSuspend; +}; diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp index 5c1207632d4ce..63aec06a64fa7 100644 --- a/xbmc/rendering/gles/RenderSystemGLES.cpp +++ b/xbmc/rendering/gles/RenderSystemGLES.cpp @@ -173,6 +173,15 @@ bool CRenderSystemGLES::DestroyRenderSystem() m_pGUIshader = NULL; } + ResetScissors(); + CDirtyRegionList dirtyRegions; + CDirtyRegion dirtyWindow(g_graphicsContext.GetViewWindow()); + dirtyRegions.push_back(dirtyWindow); + + ClearBuffers(0); + glFinish(); + PresentRenderImpl(dirtyRegions); + m_bRenderCreated = false; return true; @@ -631,4 +640,12 @@ GLint CRenderSystemGLES::GUIShaderGetCoord1() return -1; } +GLint CRenderSystemGLES::GUIShaderGetUniCol() +{ + if (m_pGUIshader[m_method]) + return m_pGUIshader[m_method]->GetUniColLoc(); + + return -1; +} + #endif diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h index 6c3783468fc67..f78b4b45c1b25 100644 --- a/xbmc/rendering/gles/RenderSystemGLES.h +++ b/xbmc/rendering/gles/RenderSystemGLES.h @@ -85,6 +85,7 @@ class CRenderSystemGLES : public CRenderSystemBase GLint GUIShaderGetCol(); GLint GUIShaderGetCoord0(); GLint GUIShaderGetCoord1(); + GLint GUIShaderGetUniCol(); protected: virtual void SetVSyncImpl(bool enable) = 0; diff --git a/xbmc/security/IKeyringManager.h b/xbmc/security/IKeyringManager.h new file mode 100644 index 0000000000000..49764daf1e3c4 --- /dev/null +++ b/xbmc/security/IKeyringManager.h @@ -0,0 +1,61 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "utils/Variant.h" + +#define KEYRING_DEFAULT "" +#define KEYRING_SYSTEM "xbmc-system" + +class IKeyringManager +{ +public: + virtual ~IKeyringManager() { } + + /*! + \brief Find a stored secret + + \param keyring to look for secret within + \param key of secret to look for + \param secret will be filled with secret if found + \return true if found, false if not + */ + virtual bool FindSecret(const std::string &keyring, const std::string &key, CVariant &secret) = 0; + + /*! + \brief Erase a stored secret + + \param keyring to look for secret within + \param key of secret to look for + \return true if secret was erased i.e. not existant in keyring, false if failed to do so + */ + virtual bool EraseSecret(const std::string &keyring, const std::string &key) = 0; + + /*! + \brief Store a secret + + \param keyring to store secret within + \param key of secret to store + \param secret to be stored + \return true if stored, false if not + */ + virtual bool StoreSecret(const std::string &keyring, const std::string &key, const CVariant &secret) = 0; +}; diff --git a/xbmc/security/KeyringManager.cpp b/xbmc/security/KeyringManager.cpp new file mode 100644 index 0000000000000..569c561db3edb --- /dev/null +++ b/xbmc/security/KeyringManager.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "KeyringManager.h" +#include "settings/Settings.h" +#include "XMLKeyringManager.h" + +using namespace std; + +CKeyringManager::CKeyringManager() +{ + m_persistantKeyringManager = NULL; +} + +CKeyringManager::~CKeyringManager() +{ + delete m_persistantKeyringManager; +} + +void CKeyringManager::Initialize() +{ + // TODO Platform specific implementation + + if (m_persistantKeyringManager == NULL) + m_persistantKeyringManager = new CXMLKeyringManager(g_settings.GetUserDataItem("secrets.xml").c_str()); +} + +bool CKeyringManager::FindSecret(const string &keyring, const string &key, CVariant &secret) +{ + if (!m_temporaryKeyringManager.FindSecret(keyring, key, secret)) + { + if (m_persistantKeyringManager && m_persistantKeyringManager->FindSecret(keyring, key, secret)) + { + m_temporaryKeyringManager.StoreSecret(keyring, key, secret); + return true; + } + else + return false; + } + else + return true; +} + +bool CKeyringManager::EraseSecret(const string &keyring, const string &key) +{ + bool success = m_temporaryKeyringManager.EraseSecret(keyring, key); + if (m_persistantKeyringManager) + return m_persistantKeyringManager->EraseSecret(keyring, key); + else + return success; +} + +bool CKeyringManager::StoreSecret(const string &keyring, const string &key, const CVariant &secret, bool persistant) +{ + bool success = m_temporaryKeyringManager.StoreSecret(keyring, key, secret); + if (persistant && m_persistantKeyringManager) + return m_persistantKeyringManager->StoreSecret(keyring, key, secret); + else + return success; +} diff --git a/xbmc/security/KeyringManager.h b/xbmc/security/KeyringManager.h new file mode 100644 index 0000000000000..1766860713f46 --- /dev/null +++ b/xbmc/security/KeyringManager.h @@ -0,0 +1,67 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IKeyringManager.h" +#include "MemoryKeyringManager.h" + +class CKeyringManager +{ +public: + CKeyringManager(); + ~CKeyringManager(); + + void Initialize(); + + /*! + \brief Find a stored secret + + \param keyring to look for secret within + \param key of secret to look for + \param secret will be filled with secret if found + \return true if found, false if not + */ + bool FindSecret(const std::string &keyring, const std::string &key, CVariant &secret); + + /*! + \brief Erase a stored secret + + \param keyring to look for secret within + \param key of secret to look for + \return true if secret was erased i.e. not existant in keyring, false if failed to do so + */ + bool EraseSecret(const std::string &keyring, const std::string &key); + + /*! + \brief Store a secret + + \param keyring to store secret within + \param key of secret to store + \param secret to be stored + \param persistant true if this secret should be stored to persistant storage + \return true if stored, false if not + */ + bool StoreSecret(const std::string &keyring, const std::string &key, const CVariant &secret, bool persistant = true); + +private: + CMemoryKeyringManager m_temporaryKeyringManager; + IKeyringManager *m_persistantKeyringManager; +}; diff --git a/xbmc/security/Makefile b/xbmc/security/Makefile new file mode 100644 index 0000000000000..137afc58caba7 --- /dev/null +++ b/xbmc/security/Makefile @@ -0,0 +1,8 @@ +SRCS = KeyringManager.cpp +SRCS+= MemoryKeyringManager.cpp +SRCS+= XMLKeyringManager.cpp + +LIB = security.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(patsubst %.S,,$(SRCS)))) diff --git a/xbmc/security/MemoryKeyringManager.cpp b/xbmc/security/MemoryKeyringManager.cpp new file mode 100644 index 0000000000000..d1732f91c6b39 --- /dev/null +++ b/xbmc/security/MemoryKeyringManager.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "MemoryKeyringManager.h" + +using namespace std; + +bool CMemoryKeyringManager::FindSecret(const string &keyring, const string &key, CVariant &secret) +{ + map::const_iterator keyring_itr = m_keyrings.find(keyring); + + if (keyring_itr != m_keyrings.end()) + { + Keyring::const_iterator itr = keyring_itr->second.find(key); + if (itr != keyring_itr->second.end()) + { + secret = itr->second; + return true; + } + } + + return false; +} + +bool CMemoryKeyringManager::EraseSecret(const string &keyring, const string &key) +{ + map::iterator keyring_itr = m_keyrings.find(keyring); + + if (keyring_itr != m_keyrings.end()) + { + Keyring::iterator itr = keyring_itr->second.find(key); + if (itr != keyring_itr->second.end()) + keyring_itr->second.erase(itr); + } + + return true; +} + +bool CMemoryKeyringManager::StoreSecret(const string &keyring, const string &key, const CVariant &secret) +{ + m_keyrings[keyring][key] = secret; + return true; +} diff --git a/xbmc/security/MemoryKeyringManager.h b/xbmc/security/MemoryKeyringManager.h new file mode 100644 index 0000000000000..f098c18cb9766 --- /dev/null +++ b/xbmc/security/MemoryKeyringManager.h @@ -0,0 +1,38 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IKeyringManager.h" +#include + +class CMemoryKeyringManager : public IKeyringManager +{ +public: + virtual ~CMemoryKeyringManager() { } + + virtual bool FindSecret(const std::string &keyring, const std::string &key, CVariant &secret); + virtual bool EraseSecret(const std::string &keyring, const std::string &key); + virtual bool StoreSecret(const std::string &keyring, const std::string &key, const CVariant &secret); + +protected: + typedef std::map Keyring; + std::map m_keyrings; +}; diff --git a/xbmc/security/XMLKeyringManager.cpp b/xbmc/security/XMLKeyringManager.cpp new file mode 100644 index 0000000000000..6c50655c74fc4 --- /dev/null +++ b/xbmc/security/XMLKeyringManager.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XMLKeyringManager.h" +#include "filesystem/File.h" +#include "utils/XMLUtils.h" +#include "utils/XMLVariantParser.h" +#include "utils/XMLVariantWriter.h" +#include "utils/log.h" + +using namespace std; + +CXMLKeyringManager::CXMLKeyringManager(const char *store) : CMemoryKeyringManager(), m_store(store) +{ + Load(); +} + +bool CXMLKeyringManager::StoreSecret(const std::string &keyring, const std::string &key, const CVariant &secret) +{ + return CMemoryKeyringManager::StoreSecret(keyring, key, secret) && Save(); +} + +bool CXMLKeyringManager::Load() +{ + if (XFILE::CFile::Exists(m_store)) + { + TiXmlDocument doc; + if (!doc.LoadFile(m_store)) + { + CLog::Log(LOGERROR, "XMLKeyringManager: Unable to load: %s, Line %d\n%s", m_store.c_str(), doc.ErrorRow(), doc.ErrorDesc()); + return false; + } + const TiXmlElement *root = doc.RootElement(); + if (root->ValueStr() != "keyrings") + { + CLog::Log(LOGERROR, "XMLKeyringManager: Failed to find root document"); + return false; + } + + const TiXmlElement *keyringNode = root->FirstChildElement("keyring"); + while (keyringNode) + { + const char *keyring = keyringNode->Attribute("name"); + const TiXmlElement *secretNode = keyringNode->FirstChildElement("secret"); + while (secretNode) + { + const char *key = secretNode->Attribute("key"); + CVariant secret; + CXMLVariantParser::Parse(secretNode->FirstChildElement("value"), secret); + + StoreSecret(keyring, key, secret); + + secretNode = secretNode->NextSiblingElement("secret"); + } + + keyringNode = keyringNode->NextSiblingElement("keyring"); + } + } + + return true; +} + +bool CXMLKeyringManager::Save() +{ + TiXmlDocument doc; + TiXmlElement rootElement("passwords"); + TiXmlNode *root = doc.InsertEndChild(TiXmlElement("keyrings")); + if (!root) + return false; + + bool success = true; + + for (map::const_iterator keyring_itr = m_keyrings.begin(); keyring_itr != m_keyrings.end() && success; keyring_itr++) + { + TiXmlElement keyring("keyring"); + keyring.SetAttribute("name", keyring_itr->first.c_str()); + + for (Keyring::const_iterator secret_itr = keyring_itr->second.begin(); secret_itr != keyring_itr->second.end() && success; secret_itr++) + { + TiXmlElement secret("secret"); + secret.SetAttribute("key", secret_itr->first.c_str()); + + success &= CXMLVariantWriter::Write(&secret, secret_itr->second); + + success &= keyring.InsertEndChild(secret) != NULL; + } + + success &= root->InsertEndChild(keyring) != NULL; + } + + if (success) + doc.SaveFile(m_store); + else + { + CLog::Log(LOGERROR, "XMLKeyringManager: Failed to generate xml for storage"); + return false; + } + + return true; +} diff --git a/xbmc/security/XMLKeyringManager.h b/xbmc/security/XMLKeyringManager.h new file mode 100644 index 0000000000000..92ae31ed01417 --- /dev/null +++ b/xbmc/security/XMLKeyringManager.h @@ -0,0 +1,40 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "MemoryKeyringManager.h" + +class TiXmlElement; + +class CXMLKeyringManager : public CMemoryKeyringManager +{ +public: + CXMLKeyringManager(const char *store); + virtual ~CXMLKeyringManager() { } + + virtual bool StoreSecret(const std::string &keyring, const std::string &key, const CVariant &secret); + +private: + bool Load(); + bool Save(); + + std::string m_store; +}; diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 16800b7596b32..3a23a5d884de1 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -313,10 +313,13 @@ void CAdvancedSettings::Initialize() m_guiVisualizeDirtyRegions = false; m_guiAlgorithmDirtyRegions = 3; m_guiDirtyRegionNoFlipTimeout = 0; + m_enableNetworkManager = false; + m_showNetworkPassPhrase = true; m_logEnableAirtunes = false; m_airTunesPort = 36666; m_airPlayPort = 36667; m_initialized = true; + m_settingsHidden.clear(); m_databaseMusic.Reset(); m_databaseVideo.Reset(); @@ -737,6 +740,10 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) XMLUtils::GetString(pRootElement, "cddbaddress", m_cddbAddress); + //network manager + XMLUtils::GetBoolean(pRootElement, "enablenetworkmanager" , m_enableNetworkManager); + XMLUtils::GetBoolean(pRootElement, "shownetworkpassphrase", m_showNetworkPassPhrase); + //airtunes + airplay XMLUtils::GetBoolean(pRootElement, "enableairtunesdebuglog", m_logEnableAirtunes); XMLUtils::GetInt(pRootElement, "airtunesport", m_airTunesPort); @@ -919,6 +926,26 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) } } + TiXmlElement* pHideSettings = pRootElement->FirstChildElement("hidesettings"); + if (pHideSettings) + { + m_settingsHidden.clear(); + CLog::Log(LOGDEBUG,"Configuring hidden settings"); + TiXmlNode* pSetting = pHideSettings->FirstChildElement("setting"); + CStdString hiddenSetting; + while (pSetting) + { + hiddenSetting = pSetting->FirstChild()->Value(); + if (!hiddenSetting.IsEmpty()) + { + CLog::Log(LOGNOTICE,"Hiding: [%s]", hiddenSetting.c_str()); + m_settingsHidden.push_back(hiddenSetting); + } + // get next one + pSetting = pSetting->NextSiblingElement("setting"); + } + } + XMLUtils::GetInt(pRootElement, "remotedelay", m_remoteDelay, 1, 20); XMLUtils::GetFloat(pRootElement, "controllerdeadzone", m_controllerDeadzone, 0.0f, 1.0f); XMLUtils::GetUInt(pRootElement, "fanartres", m_fanartRes, 0, 1080); diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h index 27887d40615d7..fd6c1a4021e0c 100644 --- a/xbmc/settings/AdvancedSettings.h +++ b/xbmc/settings/AdvancedSettings.h @@ -183,6 +183,10 @@ class CAdvancedSettings int m_logLevelHint; CStdString m_cddbAddress; + // network manager + bool m_enableNetworkManager; + bool m_showNetworkPassPhrase; + //airtunes + airplay bool m_logEnableAirtunes; int m_airTunesPort; @@ -206,6 +210,7 @@ class CAdvancedSettings SETTINGS_TVSHOWLIST m_tvshowEnumRegExps; CStdString m_tvshowMultiPartEnumRegExp; typedef std::vector< std::pair > StringMapping; + CStdStringArray m_settingsHidden; StringMapping m_pathSubstitutions; int m_remoteDelay; ///< \brief number of remote messages to ignore before repeating float m_controllerDeadzone; diff --git a/xbmc/settings/GUIDialogProfileSettings.cpp b/xbmc/settings/GUIDialogProfileSettings.cpp index d34237b3e9cef..01903e6bd51e4 100644 --- a/xbmc/settings/GUIDialogProfileSettings.cpp +++ b/xbmc/settings/GUIDialogProfileSettings.cpp @@ -338,14 +338,7 @@ bool CGUIDialogProfileSettings::ShowForProfile(unsigned int iProfile, bool first } else { - // create some new settings - CGUISettings localSettings; - localSettings.Initialize(); - CStdString path = URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_strDirectory); - path = URIUtils::AddFileToFolder(path, "guisettings.xml"); - CSettings settings; - settings.Initialize(); - settings.SaveSettings(path, &localSettings); + // Do nothing. Settings will be generated at first login. } } diff --git a/xbmc/settings/GUIDialogSettings.cpp b/xbmc/settings/GUIDialogSettings.cpp index 0c8b22f1bb0a2..e4b0caa10c57f 100644 --- a/xbmc/settings/GUIDialogSettings.cpp +++ b/xbmc/settings/GUIDialogSettings.cpp @@ -600,6 +600,8 @@ void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, vecto setting.type = SettingInfo::SPIN; setting.data = current; setting.entry = values; + if (values.size() <= 1) + setting.enabled = false; m_settings.push_back(setting); } @@ -665,6 +667,15 @@ void CGUIDialogSettings::OnInitWindow() SetupPage(); // set the default focus control m_lastControlID = CONTROL_START; + + for (unsigned int i = 0; i < m_settings.size(); i++) + { + if (m_settings.at(i).enabled) + { + m_lastControlID = CONTROL_START + i; + break; + } + } CGUIDialog::OnInitWindow(); } diff --git a/xbmc/settings/GUISettings.cpp b/xbmc/settings/GUISettings.cpp index 76ec0cccda751..e48bfccb3c0ff 100644 --- a/xbmc/settings/GUISettings.cpp +++ b/xbmc/settings/GUISettings.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "GUISettings.h" #include #include @@ -549,36 +548,21 @@ void CGUISettings::Initialize() #endif CSettingsCategory* net = AddCategory(SETTINGS_SYSTEM, "network", 798); - if (g_application.IsStandAlone()) - { -#if !defined(TARGET_DARWIN) - AddString(NULL, "network.interface",775,"", SPIN_CONTROL_TEXT); - - map networkAssignments; - networkAssignments.insert(make_pair(716, NETWORK_DHCP)); - networkAssignments.insert(make_pair(717, NETWORK_STATIC)); - networkAssignments.insert(make_pair(787, NETWORK_DISABLED)); - AddInt(NULL, "network.assignment", 715, NETWORK_DHCP, networkAssignments, SPIN_CONTROL_TEXT); - AddString(NULL, "network.ipaddress", 719, "0.0.0.0", EDIT_CONTROL_IP_INPUT); - AddString(NULL, "network.subnet", 720, "255.255.255.0", EDIT_CONTROL_IP_INPUT); - AddString(NULL, "network.gateway", 721, "0.0.0.0", EDIT_CONTROL_IP_INPUT); - AddString(NULL, "network.dns", 722, "0.0.0.0", EDIT_CONTROL_IP_INPUT); - AddString(NULL, "network.dnssuffix", 22002, "", EDIT_CONTROL_INPUT, true); - AddString(NULL, "network.essid", 776, "0.0.0.0", BUTTON_CONTROL_STANDARD); - - map networkEncapsulations; - networkEncapsulations.insert(make_pair(780, ENC_NONE)); - networkEncapsulations.insert(make_pair(781, ENC_WEP)); - networkEncapsulations.insert(make_pair(782, ENC_WPA)); - networkEncapsulations.insert(make_pair(783, ENC_WPA2)); - AddInt(NULL, "network.enc", 778, ENC_NONE, networkEncapsulations, SPIN_CONTROL_TEXT); - AddString(NULL, "network.key", 777, "0.0.0.0", EDIT_CONTROL_INPUT); -#ifndef _WIN32 - AddString(NULL, "network.save", 779, "", BUTTON_CONTROL_STANDARD); -#endif - AddSeparator(NULL, "network.sep1"); -#endif - } + AddString(net, "network.connection", 714, "", BUTTON_CONTROL_STANDARD); + map networkMethod; + networkMethod.insert(make_pair( 716, IP_CONFIG_DHCP)); + networkMethod.insert(make_pair( 717, IP_CONFIG_STATIC)); + AddInt( net, "network.method", 715, IP_CONFIG_DHCP, networkMethod, SPIN_CONTROL_TEXT); + AddString(net, "network.address", 719, "0.0.0.0", EDIT_CONTROL_IP_INPUT); + AddString(net, "network.netmask", 720, "255.255.255.0", EDIT_CONTROL_IP_INPUT); + AddString(net, "network.gateway", 721, "0.0.0.0", EDIT_CONTROL_IP_INPUT); + AddString(net, "network.nameserver", 722, "0.0.0.0", EDIT_CONTROL_IP_INPUT); + AddString(net, "network.apply", 779, "", BUTTON_CONTROL_STANDARD); + // add hidden essid/passphrase so we can save/restore it + AddString(NULL, "network.essid", 776, "", EDIT_CONTROL_INPUT); + AddString(NULL, "network.passphrase", 777, "", EDIT_CONTROL_INPUT); + // + AddSeparator(net, "network.sep1"); AddBool(net, "network.usehttpproxy", 708, false); AddString(net, "network.httpproxyserver", 706, "", EDIT_CONTROL_INPUT); AddString(net, "network.httpproxyport", 730, "8080", EDIT_CONTROL_NUMBER_INPUT, false, 707); @@ -1324,7 +1308,7 @@ void CGUISettings::GetSettingsGroup(CSettingsCategory* cat, vecSettings &setting vecSettings unorderedSettings; for (unsigned int index = 0; index < cat->m_settings.size(); index++) { - if (!cat->m_settings.at(index)->IsAdvanced()) + if (!cat->m_settings.at(index)->IsAdvanced() && !CheckHiddenOverride(cat->m_settings.at(index))) unorderedSettings.push_back(cat->m_settings.at(index)); } @@ -1351,6 +1335,17 @@ void CGUISettings::GetSettingsGroup(CSettingsCategory* cat, vecSettings &setting } } +bool CGUISettings::CheckHiddenOverride(CSetting *setting) +{ + for (CStdStringArray::iterator i = g_advancedSettings.m_settingsHidden.begin(); + i != g_advancedSettings.m_settingsHidden.end(); i++) + { + if (strncmp(setting->GetSetting(), i->c_str(), i->size()) == 0) + return true; + } + return false; +} + void CGUISettings::LoadXML(TiXmlElement *pRootElement, bool hideSettings /* = false */) { // load our stuff... bool updated = false; diff --git a/xbmc/settings/GUISettings.h b/xbmc/settings/GUISettings.h index a2893c01802b8..585860af4732c 100644 --- a/xbmc/settings/GUISettings.h +++ b/xbmc/settings/GUISettings.h @@ -525,6 +525,7 @@ class CGUISettings : public Observable std::map settingsMap; std::vector settingsGroups; void LoadFromXML(TiXmlElement *pRootElement, mapIter &it, bool advanced = false); + bool CheckHiddenOverride(CSetting *setting); }; XBMC_GLOBAL_REF(CGUISettings, g_guiSettings); diff --git a/xbmc/settings/GUIWindowSettingsCategory.cpp b/xbmc/settings/GUIWindowSettingsCategory.cpp index 4ac26630489c2..fea187946ef77 100644 --- a/xbmc/settings/GUIWindowSettingsCategory.cpp +++ b/xbmc/settings/GUIWindowSettingsCategory.cpp @@ -53,13 +53,12 @@ #include "dialogs/GUIDialogContextMenu.h" #include "dialogs/GUIDialogYesNo.h" #include "dialogs/GUIDialogOK.h" -#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogBusy.h" #include "dialogs/GUIDialogKaiToast.h" #include "addons/Visualisation.h" #include "addons/AddonManager.h" #include "addons/AddonInstaller.h" #include "storage/MediaManager.h" -#include "network/Network.h" #include "guilib/GUIControlGroupList.h" #include "guilib/GUIWindowManager.h" #include "guilib/GUIFontManager.h" @@ -436,6 +435,14 @@ void CGUIWindowSettingsCategory::CreateSettings() continue; #endif } + else if (strSetting.Equals("network.connection")) + { + bool canManage = g_application.getNetworkManager().CanManageConnections(); + CLog::Log(LOGDEBUG, "CGUIWindowSettingsCategory::CreateSettings:network.connection canManage(%d)", canManage); + //((CGUIControl*)GetControl(GetSetting("network.connection")->GetID()))->SetVisible(visible); + if (canManage) + FillInNetworkConnection(); + } else if (strSetting.Equals("network.httpproxyport")) { AddSetting(pSetting, group->GetWidth(), iControlID); @@ -553,11 +560,6 @@ void CGUIWindowSettingsCategory::CreateSettings() FillInRegions(pSetting); continue; } - else if (strSetting.Equals("network.interface")) - { - FillInNetworkInterfaces(pSetting, group->GetWidth(), iControlID); - continue; - } else if (strSetting.Equals("audiooutput.audiodevice")) { AddSetting(pSetting, group->GetWidth(), iControlID); @@ -573,9 +575,6 @@ void CGUIWindowSettingsCategory::CreateSettings() AddSetting(pSetting, group->GetWidth(), iControlID); } - if (m_vecSections[m_iSection]->m_strCategory == "network") - NetworkInterfaceChanged(); - // update our settings (turns controls on/off as appropriate) UpdateSettings(); } @@ -604,6 +603,14 @@ void CGUIWindowSettingsCategory::UpdateSettings() if (pControl) pControl->SetEnabled(g_guiSettings.GetInt("videoscreen.screen") != DM_WINDOWED); } +#if 1 + else if (strSetting.Equals("videoscreen.screen")) + { + CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); + if (pControl) + pControl->SetEnabled(false); + } +#endif else if (strSetting.Equals("videoscreen.screenmode")) { CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); @@ -823,53 +830,54 @@ void CGUIWindowSettingsCategory::UpdateSettings() } } #endif//HAS_AIRPLAY - else if (strSetting.Equals("network.ipaddress") || strSetting.Equals("network.subnet") || strSetting.Equals("network.gateway") || strSetting.Equals("network.dns")) + else if (strSetting.Equals("network.connection")) { -#ifdef _LINUX - bool enabled = (geteuid() == 0); -#else - bool enabled = false; -#endif - CGUISpinControlEx* pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.assignment")->GetID()); - if (pControl1) - enabled = (pControl1->GetValue() == NETWORK_STATIC); + bool canManage = g_application.getNetworkManager().CanManageConnections(); + CGUIButtonControl *pControl = (CGUIButtonControl*)GetControl(GetSetting(strSetting)->GetID()); + pControl->SetEnabled(canManage); - CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); - if (pControl) pControl->SetEnabled(enabled); + CLog::Log(LOGDEBUG, "CGUIWindowSettingsCategory::UpdateSettings:network.connection canManage(%d)", canManage); + if (canManage) + { + CStdString connection_name; + connection_name = g_application.getNetworkManager().GetDefaultConnectionName(); + pControl->SetLabel2(connection_name); + FillInNetworkConnection(); + } + else + { + // Connected + pControl->SetLabel2(g_localizeStrings.Get(13296)); + } } - else if (strSetting.Equals("network.assignment")) + else if (strSetting.Equals("network.method")) { - CGUISpinControlEx* pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.assignment")->GetID()); -#ifdef HAS_LINUX_NETWORK + bool canManage = g_application.getNetworkManager().CanManageConnections(); + CGUISpinControlEx *pControl1 = (CGUISpinControlEx*)GetControl(GetSetting("network.method")->GetID()); if (pControl1) - pControl1->SetEnabled(geteuid() == 0); -#endif + pControl1->SetEnabled(canManage); } - else if (strSetting.Equals("network.essid") || strSetting.Equals("network.enc") || strSetting.Equals("network.key")) + else if (strSetting.Equals("network.address") || + strSetting.Equals("network.netmask") || + strSetting.Equals("network.gateway") || + strSetting.Equals("network.nameserver")) { - // Get network information - CGUISpinControlEx *ifaceControl = (CGUISpinControlEx *)GetControl(GetSetting("network.interface")->GetID()); - CStdString ifaceName = ifaceControl->GetLabel(); - CNetworkInterface* iface = g_application.getNetwork().GetInterfaceByName(ifaceName); - bool bIsWireless = iface->IsWireless(); - -#ifdef HAS_LINUX_NETWORK - bool enabled = bIsWireless && (geteuid() == 0); -#else - bool enabled = bIsWireless; -#endif - CGUISpinControlEx* pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.assignment")->GetID()); + bool enabled = false; + bool canManage = g_application.getNetworkManager().CanManageConnections(); + CGUISpinControlEx *pControl1 = (CGUISpinControlEx*)GetControl(GetSetting("network.method")->GetID()); if (pControl1) - enabled &= (pControl1->GetValue() != NETWORK_DISABLED); - - if (strSetting.Equals("network.key")) - { - pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.enc")->GetID()); - if (pControl1) enabled &= (pControl1->GetValue() != ENC_NONE); - } + enabled = (pControl1->GetValue() == IP_CONFIG_STATIC); - CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); - if (pControl) pControl->SetEnabled(enabled); + CGUIControl *pControl = (CGUIControl*)GetControl(pSettingControl->GetID()); + if (pControl) + pControl->SetEnabled(enabled && canManage); + } + else if (strSetting.Equals("network.apply")) + { + bool canManage = g_application.getNetworkManager().CanManageConnections(); + CGUIButtonControl *pControl = (CGUIButtonControl*)GetControl(GetSetting(strSetting)->GetID()); + if (pControl) + pControl->SetEnabled(canManage); } else if (strSetting.Equals("network.httpproxyserver") || strSetting.Equals("network.httpproxyport") || strSetting.Equals("network.httpproxyusername") || strSetting.Equals("network.httpproxypassword")) @@ -877,20 +885,6 @@ void CGUIWindowSettingsCategory::UpdateSettings() CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("network.usehttpproxy")); } -#ifdef HAS_LINUX_NETWORK - else if (strSetting.Equals("network.key")) - { - CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); - CGUISpinControlEx* pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.enc")->GetID()); - if (pControl && pControl1) - pControl->SetEnabled(!pControl1->IsDisabled() && pControl1->GetValue() > 0); - } - else if (strSetting.Equals("network.save")) - { - CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(pSettingControl->GetID()); - pControl->SetEnabled(geteuid() == 0); - } -#endif else if (strSetting.Equals("scrobbler.lastfmusername") || strSetting.Equals("scrobbler.lastfmpass")) { CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(pSettingControl->GetID()); @@ -1323,19 +1317,46 @@ void CGUIWindowSettingsCategory::OnSettingChanged(BaseSettingControlPtr pSetting g_application.StopAirplayServer(true);//will stop the server before internal #endif//HAS_AIRPLAY } - else if (strSetting.Equals("network.ipaddress")) + else if (strSetting.Equals("network.connection")) { - if (g_guiSettings.GetInt("network.assignment") == NETWORK_STATIC) + CGUIDialogAccessPoints *access_points = (CGUIDialogAccessPoints*)g_windowManager.GetWindow(WINDOW_DIALOG_ACCESS_POINTS); + if (access_points) { - CStdString strDefault = g_guiSettings.GetString("network.ipaddress").Left(g_guiSettings.GetString("network.ipaddress").ReverseFind('.'))+".1"; - if (g_guiSettings.GetString("network.gateway").Equals("0.0.0.0")) - g_guiSettings.SetString("network.gateway",strDefault); - if (g_guiSettings.GetString("network.dns").Equals("0.0.0.0")) - g_guiSettings.SetString("network.dns",strDefault); - + access_points->DoModal(); + FillInNetworkConnection(); + UpdateSettings(); } } + else if (strSetting.Equals("network.apply")) + { + CGUIDialogAccessPoints *access_points = (CGUIDialogAccessPoints*)g_windowManager.GetWindow(WINDOW_DIALOG_ACCESS_POINTS); + if (access_points) + { + // fetch the connection name. + CGUIEditControl* edit_control; + edit_control = (CGUIEditControl*)GetControl(GetSetting("network.connection")->GetID()); + std::string connection_name(edit_control->GetLabel2()); + + CIPConfig ipconfig; + // fetch the current method + CGUISpinControlEx* spin_control = (CGUISpinControlEx*)GetControl(GetSetting("network.method")->GetID()); + ipconfig.m_method = (IPConfigMethod)spin_control->GetValue(); + // fetch the current ip info + edit_control = (CGUIEditControl*)GetControl(GetSetting("network.address")->GetID()); + ipconfig.m_address = edit_control->GetLabel2(); + edit_control = (CGUIEditControl*)GetControl(GetSetting("network.netmask")->GetID()); + ipconfig.m_netmask = edit_control->GetLabel2(); + edit_control = (CGUIEditControl*)GetControl(GetSetting("network.gateway")->GetID()); + ipconfig.m_gateway = edit_control->GetLabel2(); + edit_control = (CGUIEditControl*)GetControl(GetSetting("network.nameserver")->GetID()); + ipconfig.m_nameserver = edit_control->GetLabel2(); + // pass the connection config as an encoded param string + access_points->DoModal(WINDOW_DIALOG_ACCESS_POINTS, EncodeAccessPointParam(connection_name, ipconfig)); + FillInNetworkConnection(); + UpdateSettings(); + } + } else if (strSetting.Equals("network.httpproxyport")) { ValidatePortNumber(pSettingControl, "8080", "8080", false); @@ -1800,85 +1821,6 @@ void CGUIWindowSettingsCategory::OnSettingChanged(BaseSettingControlPtr pSetting // Nothing todo here } } - else if (strSetting.Equals("network.interface")) - { - NetworkInterfaceChanged(); - } -#ifdef HAS_LINUX_NETWORK - else if (strSetting.Equals("network.save")) - { - NetworkAssignment iAssignment; - CStdString sIPAddress; - CStdString sNetworkMask; - CStdString sDefaultGateway; - CStdString sWirelessNetwork; - CStdString sWirelessKey; - CStdString sDns; - EncMode iWirelessEnc; - CStdString ifaceName; - - CGUISpinControlEx *ifaceControl = (CGUISpinControlEx *)GetControl(GetSetting("network.interface")->GetID()); - ifaceName = ifaceControl->GetLabel(); - CNetworkInterface* iface = g_application.getNetwork().GetInterfaceByName(ifaceName); - - // Update controls with information - CGUISpinControlEx* pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.assignment")->GetID()); - if (pControl1) iAssignment = (NetworkAssignment) pControl1->GetValue(); - CGUIButtonControl* pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.ipaddress")->GetID()); - if (pControl2) sIPAddress = pControl2->GetLabel2(); - pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.subnet")->GetID()); - if (pControl2) sNetworkMask = pControl2->GetLabel2(); - pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.gateway")->GetID()); - if (pControl2) sDefaultGateway = pControl2->GetLabel2(); - pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.dns")->GetID()); - if (pControl2) sDns = pControl2->GetLabel2(); - pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.enc")->GetID()); - if (pControl1) iWirelessEnc = (EncMode) pControl1->GetValue(); - pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.essid")->GetID()); - if (pControl2) sWirelessNetwork = pControl2->GetLabel2(); - pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.key")->GetID()); - if (pControl2) sWirelessKey = pControl2->GetLabel2(); - - CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS); - pDlgProgress->SetLine(0, ""); - pDlgProgress->SetLine(1, g_localizeStrings.Get(784)); - pDlgProgress->SetLine(2, ""); - pDlgProgress->StartModal(); - pDlgProgress->Progress(); - - std::vector nameServers; - nameServers.push_back(sDns); - g_application.getNetwork().SetNameServers(nameServers); - iface->SetSettings(iAssignment, sIPAddress, sNetworkMask, sDefaultGateway, sWirelessNetwork, sWirelessKey, iWirelessEnc); - - pDlgProgress->Close(); - - if (iAssignment == NETWORK_DISABLED) - CGUIDialogOK::ShowAndGetInput(0, 788, 0, 0); - else if (iface->IsConnected()) - CGUIDialogOK::ShowAndGetInput(0, 785, 0, 0); - else - CGUIDialogOK::ShowAndGetInput(0, 786, 0, 0); - } - else if (strSetting.Equals("network.essid")) - { - CGUIDialogAccessPoints *dialog = (CGUIDialogAccessPoints *)g_windowManager.GetWindow(WINDOW_DIALOG_ACCESS_POINTS); - if (dialog) - { - CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting("network.interface")->GetID()); - dialog->SetInterfaceName(pControl->GetLabel()); - dialog->DoModal(); - - if (dialog->WasItemSelected()) - { - CGUIButtonControl* pControl2 = (CGUIButtonControl *)GetControl(GetSetting("network.essid")->GetID()); - if (pControl2) pControl2->SetLabel2(dialog->GetSelectedAccessPointEssId()); - pControl = (CGUISpinControlEx *)GetControl(GetSetting("network.enc")->GetID()); - if (pControl) pControl->SetValue(dialog->GetSelectedAccessPointEncMode()); - } - } - } -#endif #ifdef _LINUX else if (strSetting.Equals("locale.timezonecountry")) { @@ -2820,28 +2762,6 @@ void CGUIWindowSettingsCategory::FillInSortMethods(CSetting *pSetting, int windo delete state; } -void CGUIWindowSettingsCategory::FillInNetworkInterfaces(CSetting *pSetting, float groupWidth, int &iControlID) -{ - CGUISpinControlEx *pControl = (CGUISpinControlEx *)AddSetting(pSetting, groupWidth, iControlID); - pControl->Clear(); - - // query list of interfaces - vector vecInterfaces; - std::vector& ifaces = g_application.getNetwork().GetInterfaceList(); - std::vector::const_iterator iter = ifaces.begin(); - while (iter != ifaces.end()) - { - CNetworkInterface* iface = *iter; - vecInterfaces.push_back(iface->GetName()); - ++iter; - } - sort(vecInterfaces.begin(), vecInterfaces.end(), sortstringbyname()); - - int iInterface = 0; - for (unsigned int i = 0; i < vecInterfaces.size(); ++i) - pControl->AddLabel(vecInterfaces[i], iInterface++); -} - void CGUIWindowSettingsCategory::FillInEpgGuideView(CSetting *pSetting) { CSettingInt *pSettingInt = (CSettingInt*)pSetting; @@ -2934,62 +2854,35 @@ void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Pas pControl->SetValue(selectedValue); } -void CGUIWindowSettingsCategory::NetworkInterfaceChanged(void) +void CGUIWindowSettingsCategory::FillInNetworkConnection() { -#if 0 - NetworkAssignment iAssignment; - CStdString sIPAddress; - CStdString sNetworkMask; - CStdString sDefaultGateway; - CStdString sWirelessNetwork; - CStdString sWirelessKey; - EncMode iWirelessEnc; - bool bIsWireless; - CStdString ifaceName; - - // Get network information - CGUISpinControlEx *ifaceControl = (CGUISpinControlEx *)GetControl(GetSetting("network.interface")->GetID()); - ifaceName = ifaceControl->GetLabel(); - CNetworkInterface* iface = g_application.getNetwork().GetInterfaceByName(ifaceName); - iface->GetSettings(iAssignment, sIPAddress, sNetworkMask, sDefaultGateway, sWirelessNetwork, sWirelessKey, iWirelessEnc); - bIsWireless = iface->IsWireless(); - - CStdString dns; - std::vector dnss = g_application.getNetwork().GetNameServers(); - if (dnss.size() >= 1) - dns = dnss[0]; - - // Update controls with information - CGUISpinControlEx* pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.assignment")->GetID()); - if (pControl1) pControl1->SetValue(iAssignment); - GetSetting("network.dns")->GetSetting()->FromString(dns); - if (iAssignment == NETWORK_STATIC || iAssignment == NETWORK_DISABLED) - { - GetSetting("network.ipaddress")->GetSetting()->FromString(sIPAddress); - GetSetting("network.subnet")->GetSetting()->FromString(sNetworkMask); - GetSetting("network.gateway")->GetSetting()->FromString(sDefaultGateway); - } - else - { - GetSetting("network.ipaddress")->GetSetting()->FromString(iface->GetCurrentIPAddress()); - GetSetting("network.subnet")->GetSetting()->FromString(iface->GetCurrentNetmask()); - GetSetting("network.gateway")->GetSetting()->FromString(iface->GetCurrentDefaultGateway()); - } - - pControl1 = (CGUISpinControlEx *)GetControl(GetSetting("network.enc")->GetID()); - if (pControl1) pControl1->SetValue(iWirelessEnc); - - if (bIsWireless) - { - GetSetting("network.essid")->GetSetting()->FromString(sWirelessNetwork); - GetSetting("network.key")->GetSetting()->FromString(sWirelessKey); - } - else - { - GetSetting("network.essid")->GetSetting()->FromString(""); - GetSetting("network.key")->GetSetting()->FromString(""); - } -#endif + CLog::Log(LOGDEBUG, "CGUIWindowSettingsCategory::FillInNetworkConnection1"); + if (!GetSetting("network.connection")) + return; + + CLog::Log(LOGDEBUG, "CGUIWindowSettingsCategory::FillInNetworkConnection2"); + // run the net pump to clear out any stale info, + // the water gets mighty dirty when the pump + // only runs every 500ms. + for (size_t i = 0; i < 20; i++) + g_application.getNetworkManager().PumpNetworkEvents(); + + CStdString name = g_application.getNetworkManager().GetDefaultConnectionName(); + IPConfigMethod method = g_application.getNetworkManager().GetDefaultConnectionMethod(); + CStdString address = g_application.getNetworkManager().GetDefaultConnectionAddress(); + CStdString netmask = g_application.getNetworkManager().GetDefaultConnectionNetmask(); + CStdString gateway = g_application.getNetworkManager().GetDefaultConnectionGateway(); + CStdString nameserver = g_application.getNetworkManager().GetDefaultConnectionNameServer(); + + // set method (dhcp or static) + CGUISpinControlEx* pControl1 = (CGUISpinControlEx*)GetControl(GetSetting("network.method")->GetID()); + if (pControl1) pControl1->SetValue(method); + + // set network ip information + GetSetting("network.address" )->GetSetting()->FromString(address); + GetSetting("network.netmask" )->GetSetting()->FromString(netmask); + GetSetting("network.gateway" )->GetSetting()->FromString(gateway); + GetSetting("network.nameserver")->GetSetting()->FromString(nameserver); } void CGUIWindowSettingsCategory::ValidatePortNumber(BaseSettingControlPtr pSettingControl, const CStdString& userPort, const CStdString& privPort, bool listening/*=true*/) diff --git a/xbmc/settings/GUIWindowSettingsCategory.h b/xbmc/settings/GUIWindowSettingsCategory.h index 5142c6e466ee6..2def566c9a451 100644 --- a/xbmc/settings/GUIWindowSettingsCategory.h +++ b/xbmc/settings/GUIWindowSettingsCategory.h @@ -64,8 +64,7 @@ class CGUIWindowSettingsCategory : void FillInSkinThemes(CSetting *pSetting); void FillInSkinColors(CSetting *pSetting); - void FillInNetworkInterfaces(CSetting *pSetting, float groupWidth, int &iControlID); - void NetworkInterfaceChanged(void); + void FillInNetworkConnection(); void FillInAudioDevices(CSetting* pSetting, bool Passthrough = false); diff --git a/xbmc/settings/GUIWindowSettingsProfile.cpp b/xbmc/settings/GUIWindowSettingsProfile.cpp index 9d4929b1c9a1c..afe0b9f3114e2 100644 --- a/xbmc/settings/GUIWindowSettingsProfile.cpp +++ b/xbmc/settings/GUIWindowSettingsProfile.cpp @@ -24,7 +24,6 @@ #include "Application.h" #include "dialogs/GUIDialogContextMenu.h" #include "GUIDialogProfileSettings.h" -#include "network/Network.h" #include "utils/URIUtils.h" #include "utils/Weather.h" #include "GUIPassword.h" @@ -79,7 +78,7 @@ void CGUIWindowSettingsProfile::OnPopupMenu(int iItem) g_application.StopPlaying(); CGUIMessage msg2(GUI_MSG_ITEM_SELECTED, g_windowManager.GetActiveWindow(), iCtrlID); g_windowManager.SendMessage(msg2); - g_application.getNetwork().NetworkMessage(CNetwork::SERVICES_DOWN,1); + g_application.getNetworkManager().NetworkMessage(CNetworkManager::SERVICES_DOWN,1); g_settings.LoadMasterForLogin(); CGUIWindowLoginScreen::LoadProfile(iItem); return; diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp index 8a430adf98087..c377abeba68dd 100644 --- a/xbmc/settings/Settings.cpp +++ b/xbmc/settings/Settings.cpp @@ -161,6 +161,7 @@ bool CSettings::Reset() { CLog::Log(LOGINFO, "Resetting settings"); CFile::Delete(GetSettingsFile()); + LoadSettings("special://xbmc/system/guisettings.xml"); Save(); return LoadSettings(GetSettingsFile()); } @@ -171,6 +172,47 @@ bool CSettings::Load() CLog::Log(LOGNOTICE, "loading %s", GetSettingsFile().c_str()); if (!LoadSettings(GetSettingsFile())) { + CLog::Log(LOGERROR, "Unable to load %s file, appears to be bad", GetSettingsFile().c_str()); + // Move bad settings file to ".bad" + // Delete old ".bad" file if it exists + if (CFile::Exists(GetSettingsFile() + ".bad")) + CFile::Delete(GetSettingsFile() + ".bad"); + // Rename current file to .bad + CFile::Rename(GetSettingsFile(),GetSettingsFile() + ".bad"); + // Check if lastgood exists so we can restore it + if (CFile::Exists(GetSettingsFile() + ".lastgood")) + { + CLog::Log(LOGERROR, "Trying to restore .lastgood for %s file", GetSettingsFile().c_str()); + // try to restore lastgood if it exists + // we copy instead of rename, so lastgood exist even if this goes bad + CFile source_settings, dest_settings; + char * cpybuffer; + int bytes_read; + cpybuffer = (char*) malloc(sizeof(char) * 32768); + if (cpybuffer != NULL) + { + if (source_settings.Open(GetSettingsFile() + ".lastgood",0)) + { + if (dest_settings.OpenForWrite(GetSettingsFile())) + { + while ( (bytes_read = source_settings.Read(cpybuffer, 32768)) > 0 ) + { + dest_settings.Write(cpybuffer,bytes_read); + } + dest_settings.Close(); + } + source_settings.Close(); + // Try loading the lastgood backup + if ( LoadSettings(GetSettingsFile()) ) + { + CLog::Log(LOGERROR, "System has been restored to last good configuration for %s file", GetSettingsFile().c_str()); + return true; + } + } + free(cpybuffer); + } + } + CLog::Log(LOGERROR, "Unable to load %s, creating new %s with default values", GetSettingsFile().c_str(), GetSettingsFile().c_str()); if (!Reset()) return false; @@ -816,6 +858,34 @@ bool CSettings::LoadSettings(const CStdString& strSettingsFile) CLog::Log(LOGNOTICE, "Disabled debug logging due to GUI setting. Level %d.", g_advancedSettings.m_logLevel); } CLog::SetLogLevel(g_advancedSettings.m_logLevel); + + // + // Settings file looks good, lets back it up to be safe + // back up settings file to what ever its named with a .lastgood extension + + CFile source_settings, dest_settings; + char * cpybuffer; + int bytes_read; + + cpybuffer = (char*) malloc(sizeof(char) * 32768); + if (cpybuffer != NULL) + { + CFile::Delete(strSettingsFile + ".lastgood"); + if (source_settings.Open(strSettingsFile,0)) + { + if (dest_settings.OpenForWrite(strSettingsFile + ".lastgood")) + { + while ( (bytes_read = source_settings.Read(cpybuffer, 32768)) > 0 ) + { + dest_settings.Write(cpybuffer,bytes_read); + } + dest_settings.Close(); + } + source_settings.Close(); + } + free(cpybuffer); + } + return true; } diff --git a/xbmc/storage/linux/LinuxStorageProvider.h b/xbmc/storage/linux/LinuxStorageProvider.h index 1f145486b8eb0..740b2829940ac 100644 --- a/xbmc/storage/linux/LinuxStorageProvider.h +++ b/xbmc/storage/linux/LinuxStorageProvider.h @@ -21,6 +21,7 @@ #include "storage/IStorageProvider.h" #include "HALProvider.h" #include "DeviceKitDisksProvider.h" +#include "UDevProvider.h" #include "UDisksProvider.h" #include "PosixMountProvider.h" @@ -41,6 +42,10 @@ class CLinuxStorageProvider : public IStorageProvider if (m_instance == NULL) m_instance = new CHALProvider(); #endif +#ifdef HAVE_LIBUDEV + if (m_instance == NULL) + m_instance = new CUDevProvider(); +#endif if (m_instance == NULL) m_instance = new CPosixMountProvider(); diff --git a/xbmc/storage/linux/Makefile b/xbmc/storage/linux/Makefile index 9be601de0ad97..325f84d8e2c92 100644 --- a/xbmc/storage/linux/Makefile +++ b/xbmc/storage/linux/Makefile @@ -1,8 +1,9 @@ -SRCS=DeviceKitDisksProvider.cpp \ - HALProvider.cpp \ - UDisksProvider.cpp \ +SRCS = DeviceKitDisksProvider.cpp +SRCS += UDisksProvider.cpp +SRCS += HALProvider.cpp +SRCS += UDevProvider.cpp -LIB=storage_linux.a +LIB = storage_linux.a include ../../../Makefile.include -include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/storage/linux/UDevProvider.cpp b/xbmc/storage/linux/UDevProvider.cpp new file mode 100644 index 0000000000000..d9e6a4f43728f --- /dev/null +++ b/xbmc/storage/linux/UDevProvider.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "UDevProvider.h" + +#ifdef HAVE_LIBUDEV + +#include "linux/PosixMountProvider.h" +#include "utils/log.h" +#include "utils/URIUtils.h" + +extern "C" { +#include +#include +} + +static const char *get_mountpoint(const char *devnode) +{ + static char buf[4096]; + const char *delim = " "; + const char *mountpoint = NULL; + FILE *fp = fopen("/proc/mounts", "r"); + + while (fgets(buf, sizeof (buf), fp)) + { + const char *node = strtok(buf, delim); + if (strcmp(node, devnode) == 0) + { + mountpoint = strtok(NULL, delim); + break; + } + } + + if (mountpoint != NULL) + { + // If mount point contain characters like space, it is converted to + // "\040". This situation should be handled. + char *c1, *c2; + for (c1 = c2 = (char*)mountpoint; *c2; ++c1) + { + if (*c2 == '\\') + { + *c1 = (((c2[1] - '0') << 6) | ((c2[2] - '0') << 3) | (c2[3] - '0')); + c2 += 4; + continue; + } + if (c1 != c2) + *c1 = *c2; + ++c2; + } + *c1 = *c2; + } + + fclose(fp); + return mountpoint; +} + +CUDevProvider::CUDevProvider() +{ + m_udev = NULL; + m_udevMon = NULL; +} + +void CUDevProvider::Initialize() +{ + CLog::Log(LOGDEBUG, "Selected UDev as storage provider"); + + m_udev = udev_new(); + if (!m_udev) + { + CLog::Log(LOGERROR, "%s - failed to allocate udev context", __FUNCTION__); + return; + } + /* set up a devices monitor that listen for any device change */ + m_udevMon = udev_monitor_new_from_netlink(m_udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "block", "disk"); + udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "block", "partition"); + udev_monitor_enable_receiving(m_udevMon); + + PumpDriveChangeEvents(NULL); +} + +void CUDevProvider::Stop() +{ + udev_monitor_unref(m_udevMon); + udev_unref(m_udev); +} + +void CUDevProvider::GetDisks(VECSOURCES& disks, bool removable) +{ + // enumerate existing block devices + struct udev_enumerate *u_enum = udev_enumerate_new(m_udev); + if (u_enum == NULL) + { + fprintf(stderr, "Error: udev_enumerate_new(udev)\n"); + return; + } + + udev_enumerate_add_match_subsystem(u_enum, "block"); + udev_enumerate_add_match_property(u_enum, "DEVTYPE", "disk"); + udev_enumerate_add_match_property(u_enum, "DEVTYPE", "partition"); + udev_enumerate_scan_devices(u_enum); + + struct udev_list_entry *u_list_ent; + struct udev_list_entry *u_first_list_ent; + u_first_list_ent = udev_enumerate_get_list_entry(u_enum); + udev_list_entry_foreach(u_list_ent, u_first_list_ent) + { + const char *name = udev_list_entry_get_name(u_list_ent); + struct udev *context = udev_enumerate_get_udev(u_enum); + struct udev_device *device = udev_device_new_from_syspath(context, name); + if (device == NULL) + continue; + + // filter out devices that are not mounted + const char *mountpoint = get_mountpoint(udev_device_get_devnode(device)); + if (!mountpoint) + { + udev_device_unref(device); + continue; + } + + // filter out things mounted on /tmp + if (strstr(mountpoint, "/tmp")) + { + udev_device_unref(device); + continue; + } + + // look for usb devices on the usb bus or mounted on /media/usbX (sdcards) + const char *bus = udev_device_get_property_value(device, "ID_BUS"); + if (removable && + ((bus && strstr(bus, "usb")) || + (mountpoint && strstr(mountpoint, "usb")))) + { + const char *label = udev_device_get_property_value(device, "ID_FS_LABEL"); + if (!label) + label = URIUtils::GetFileName(mountpoint); + + CMediaSource share; + share.strName = label; + share.strPath = mountpoint; + share.m_ignore = true; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_REMOVABLE; + AddOrReplace(disks, share); + } + udev_device_unref(device); + } + udev_enumerate_unref(u_enum); +} + +void CUDevProvider::GetLocalDrives(VECSOURCES &localDrives) +{ + GetDisks(localDrives, false); +} + +void CUDevProvider::GetRemovableDrives(VECSOURCES &removableDrives) +{ + GetDisks(removableDrives, true); +} + +bool CUDevProvider::Eject(CStdString mountpath) +{ + // just go ahead and try to umount the disk + // if it does umount, life is good, if not, no loss. + std::string cmd = "umount " + mountpath; + system(cmd.c_str()); + + return true; +} + +std::vector CUDevProvider::GetDiskUsage() +{ + CPosixMountProvider legacy; + return legacy.GetDiskUsage(); +} + +bool CUDevProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback) +{ + bool changed = false; + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(udev_monitor_get_fd(m_udevMon), &readfds); + + // non-blocking, check the file descriptor for received data + struct timeval tv = {0}; + int count = select(udev_monitor_get_fd(m_udevMon) + 1, &readfds, NULL, NULL, &tv); + if (count < 0) + return false; + + if (FD_ISSET(udev_monitor_get_fd(m_udevMon), &readfds)) + { + struct udev_device *dev = udev_monitor_receive_device(m_udevMon); + if (!dev) + return false; + + const char *action = udev_device_get_action(dev); + const char *devtype = udev_device_get_devtype(dev); + if (action) + { + const char *label = udev_device_get_property_value(dev, "ID_FS_LABEL"); + const char *mountpoint = get_mountpoint(udev_device_get_devnode(dev)); + if (!label) + label = URIUtils::GetFileName(mountpoint); + + if (!strcmp(action, "add") && !strcmp(devtype, "partition")) + { + CLog::Log(LOGNOTICE, "UDev: Added %s", mountpoint); + if (callback) + callback->OnStorageAdded(label, mountpoint); + changed = true; + } + if (!strcmp(action, "remove") && !strcmp(devtype, "partition")) + { + CLog::Log(LOGNOTICE, "UDev: Removed %s", mountpoint); + if (callback) + callback->OnStorageSafelyRemoved(label); + changed = true; + } + } + udev_device_unref(dev); + } + + return changed; +} + +#endif diff --git a/xbmc/storage/linux/UDevProvider.h b/xbmc/storage/linux/UDevProvider.h new file mode 100644 index 0000000000000..6b51e2a6056bf --- /dev/null +++ b/xbmc/storage/linux/UDevProvider.h @@ -0,0 +1,55 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "storage/IStorageProvider.h" + +#ifdef HAVE_LIBUDEV + +struct udev; +struct udev_monitor; + +class CUDevProvider : public IStorageProvider +{ +public: + CUDevProvider(); + virtual ~CUDevProvider() { } + + virtual void Initialize(); + virtual void Stop(); + + virtual void GetLocalDrives(VECSOURCES &localDrives); + virtual void GetRemovableDrives(VECSOURCES &removableDrives); + + virtual bool Eject(CStdString mountpath); + + virtual std::vector GetDiskUsage(); + + virtual bool PumpDriveChangeEvents(IStorageEventsCallback *callback); + +private: + void GetDisks(VECSOURCES& devices, bool removable); + + struct udev *m_udev; + struct udev_monitor *m_udevMon; +}; + +#endif diff --git a/xbmc/threads/platform/pthreads/ThreadImpl.cpp b/xbmc/threads/platform/pthreads/ThreadImpl.cpp index d10628e9846de..50853bf7f24e3 100644 --- a/xbmc/threads/platform/pthreads/ThreadImpl.cpp +++ b/xbmc/threads/platform/pthreads/ThreadImpl.cpp @@ -71,35 +71,10 @@ void CThread::SetThreadInfo() m_ThreadOpaque.LwpId = syscall(SYS_gettid); #endif -#ifdef TARGET_DARWIN -#if(__MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200) - pthread_setname_np(m_ThreadName.c_str()); -#endif -#endif - -#ifdef RLIMIT_NICE - // get user max prio - struct rlimit limit; - int userMaxPrio; - if (getrlimit(RLIMIT_NICE, &limit) == 0) - { - userMaxPrio = limit.rlim_cur - 20; - if (userMaxPrio < 0) - userMaxPrio = 0; - } - else - userMaxPrio = 0; - - // if the user does not have an entry in limits.conf the following - // call will fail - if (userMaxPrio > 0) - { - // start thread with nice level of appication - int appNice = getpriority(PRIO_PROCESS, getpid()); - if (setpriority(PRIO_PROCESS, m_ThreadOpaque.LwpId, appNice) != 0) - if (logger) logger->Log(LOGERROR, "%s: error %s", __FUNCTION__, strerror(errno)); - } -#endif + // start thread with nice level of appication + int appNice = getpriority(PRIO_PROCESS, getpid()); + if (setpriority(PRIO_PROCESS, m_ThreadOpaque.LwpId, appNice) != 0) + if (logger) logger->Log(LOGERROR, "%s: error %s", __FUNCTION__, strerror(errno)); } ThreadIdentifier CThread::GetCurrentThreadId() @@ -114,24 +89,25 @@ bool CThread::IsCurrentThread(const ThreadIdentifier tid) int CThread::GetMinPriority(void) { - // one level lower than application - return -1; + return -4; + //return THREAD_PRIORITY_IDLE; } int CThread::GetMaxPriority(void) { - // one level higher than application - return 1; + return 4; +// return THREAD_PRIORITY_HIGHEST; } int CThread::GetNormalPriority(void) { - // same level as application return 0; + //return THREAD_PRIORITY_NORMAL; } bool CThread::SetPriority(const int iPriority) { + // iPriority is with respect to that defined in Thread.h bool bReturn = false; // wait until thread is running, it needs to get its lwp id @@ -139,47 +115,25 @@ bool CThread::SetPriority(const int iPriority) CSingleLock lock(m_CriticalSection); - // get min prio for SCHED_RR - int minRR = GetMaxPriority() + 1; - if (!m_ThreadId) - bReturn = false; - else if (iPriority >= minRR) - bReturn = SetPrioritySched_RR(iPriority); -#ifdef RLIMIT_NICE + return false; + + // keep priority in bounds + int prio = iPriority; + if (prio >= GetMaxPriority()) + prio = GetMaxPriority(); + if (prio < GetMinPriority()) + prio = GetMinPriority(); + + // nice level of application + int appNice = getpriority(PRIO_PROCESS, getpid()); + // flip it with respect to the 'nice' levels (-20 to 19) + prio = appNice - prio; + + if (setpriority(PRIO_PROCESS, m_ThreadOpaque.LwpId, prio) == 0) + bReturn = true; else - { - // get user max prio - struct rlimit limit; - int userMaxPrio; - if (getrlimit(RLIMIT_NICE, &limit) == 0) - { - userMaxPrio = limit.rlim_cur - 20; - // is a user has no entry in limits.conf rlim_cur is zero - if (userMaxPrio < 0) - userMaxPrio = 0; - } - else - userMaxPrio = 0; - - // keep priority in bounds - int prio = iPriority; - if (prio >= GetMaxPriority()) - prio = std::min(GetMaxPriority(), userMaxPrio); - if (prio < GetMinPriority()) - prio = GetMinPriority(); - - // nice level of application - int appNice = getpriority(PRIO_PROCESS, getpid()); - if (prio) - prio = prio > 0 ? appNice-1 : appNice+1; - - if (setpriority(PRIO_PROCESS, m_ThreadOpaque.LwpId, prio) == 0) - bReturn = true; - else - if (logger) logger->Log(LOGERROR, "%s: error %s", __FUNCTION__, strerror(errno)); - } -#endif + if (logger) logger->Log(LOGERROR, "%s: error %s", __FUNCTION__, strerror(errno)); return bReturn; } @@ -195,6 +149,8 @@ int CThread::GetPriority() int appNice = getpriority(PRIO_PROCESS, getpid()); int prio = getpriority(PRIO_PROCESS, m_ThreadOpaque.LwpId); + // flip it with respect to the 'nice' levels (-20 to 19), so that + // what is returned is with repect to that defined in Thread.h iReturn = appNice - prio; return iReturn; diff --git a/xbmc/utils/AMLUtils.cpp b/xbmc/utils/AMLUtils.cpp new file mode 100644 index 0000000000000..14a421bcc50b9 --- /dev/null +++ b/xbmc/utils/AMLUtils.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2011-2012 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include +#include +#include +#include + +#include "utils/StringUtils.h" + +int aml_set_sysfs_str(const char *path, const char *val) +{ + int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd >= 0) + { + write(fd, val, strlen(val)); + close(fd); + return 0; + } + return -1; +} + +int aml_get_sysfs_str(const char *path, char *valstr, const int size) +{ + int fd = open(path, O_RDONLY); + if (fd >= 0) + { + read(fd, valstr, size - 1); + valstr[strlen(valstr)] = '\0'; + close(fd); + return 0; + } + + sprintf(valstr, "%s", "fail"); + return -1; +} + +int aml_set_sysfs_int(const char *path, const int val) +{ + int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd >= 0) + { + char bcmd[16]; + sprintf(bcmd, "%d", val); + write(fd, bcmd, strlen(bcmd)); + close(fd); + return 0; + } + return -1; +} + +int aml_get_sysfs_int(const char *path) +{ + int val = 0; + int fd = open(path, O_RDONLY); + if (fd >= 0) + { + char bcmd[16]; + read(fd, bcmd, sizeof(bcmd)); + val = strtol(bcmd, NULL, 16); + close(fd); + } + return val; +} + +bool aml_present() +{ + static int has_aml = -1; + if (has_aml == -1) + { + if (aml_get_sysfs_int("/sys/class/audiodsp/digital_raw") != -1) + has_aml = 1; + else + has_aml = 0; + } + return has_aml; +} + +int aml_get_cputype() +{ + static int aml_cputype = -1; + if (aml_cputype == -1) + { + // defualt to m1 SoC + aml_cputype = 1; + + FILE *cpuinfo_fd = fopen("/proc/cpuinfo", "r"); + if (cpuinfo_fd) + { + char buffer[512]; + while (fgets(buffer, sizeof(buffer), cpuinfo_fd)) + { + std::string stdbuffer(buffer); + if (stdbuffer.find("MESON-M3") != std::string::npos) + { + aml_cputype = 3; + break; + } + else if (stdbuffer.find("MESON3") != std::string::npos) + { + aml_cputype = 3; + break; + } + else if (stdbuffer.find("Meson6") != std::string::npos) + { + aml_cputype = 6; + break; + } + } + fclose(cpuinfo_fd); + } + } + + return aml_cputype; +} + +void aml_cpufreq_limit(bool limit) +{ + int cpufreq = 300000; + if (limit) + cpufreq = 600000; + + aml_set_sysfs_int("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", cpufreq); +} + +void aml_set_audio_passthrough(bool passthrough) +{ + if (aml_present()) + { + int raw = aml_get_cputype() < 3 ? 1:2; + aml_set_sysfs_int("/sys/class/audiodsp/digital_raw", passthrough ? raw:0); + } +} + +void aml_probe_hdmi_audio() +{ + std::vector audio_formats; + // Audio {format, channel, freq, cce} + // {1, 7, 7f, 7} + // {7, 5, 1e, 0} + // {2, 5, 7, 0} + // {11, 7, 7e, 1} + // {10, 7, 6, 0} + // {12, 7, 7e, 0} + + int fd = open("/sys/class/amhdmitx/amhdmitx0/edid", O_RDONLY); + if (fd >= 0) + { + char valstr[1024] = {0}; + + read(fd, valstr, sizeof(valstr) - 1); + valstr[strlen(valstr)] = '\0'; + close(fd); + + std::vector probe_str; + StringUtils::SplitString(valstr, "\n", probe_str); + + for (size_t i = 0; i < probe_str.size(); i++) + { + if (probe_str[i].find("Audio") == std::string::npos) + { + for (size_t j = i+1; j < probe_str.size(); j++) + { + if (probe_str[i].find("{1,") != std::string::npos) + printf(" PCM found {1,\n"); + else if (probe_str[i].find("{2,") != std::string::npos) + printf(" AC3 found {2,\n"); + else if (probe_str[i].find("{3,") != std::string::npos) + printf(" MPEG1 found {3,\n"); + else if (probe_str[i].find("{4,") != std::string::npos) + printf(" MP3 found {4,\n"); + else if (probe_str[i].find("{5,") != std::string::npos) + printf(" MPEG2 found {5,\n"); + else if (probe_str[i].find("{6,") != std::string::npos) + printf(" AAC found {6,\n"); + else if (probe_str[i].find("{7,") != std::string::npos) + printf(" DTS found {7,\n"); + else if (probe_str[i].find("{8,") != std::string::npos) + printf(" ATRAC found {8,\n"); + else if (probe_str[i].find("{9,") != std::string::npos) + printf(" One_Bit_Audio found {9,\n"); + else if (probe_str[i].find("{10,") != std::string::npos) + printf(" Dolby found {10,\n"); + else if (probe_str[i].find("{11,") != std::string::npos) + printf(" DTS_HD found {11,\n"); + else if (probe_str[i].find("{12,") != std::string::npos) + printf(" MAT found {12,\n"); + else if (probe_str[i].find("{13,") != std::string::npos) + printf(" ATRAC found {13,\n"); + else if (probe_str[i].find("{14,") != std::string::npos) + printf(" WMA found {14,\n"); + else + break; + } + break; + } + } + } +} diff --git a/xbmc/cores/amlplayer/AMLUtils.h b/xbmc/utils/AMLUtils.h similarity index 97% rename from xbmc/cores/amlplayer/AMLUtils.h rename to xbmc/utils/AMLUtils.h index b1558afdbba1a..95e5309ce459c 100644 --- a/xbmc/cores/amlplayer/AMLUtils.h +++ b/xbmc/utils/AMLUtils.h @@ -25,5 +25,6 @@ int aml_set_sysfs_int(const char *path, const int val); int aml_get_sysfs_int(const char *path); bool aml_present(); +int aml_get_cputype(); void aml_cpufreq_limit(bool limit); void aml_set_audio_passthrough(bool passthrough); diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile deleted file mode 100644 index d2018846d5824..0000000000000 --- a/xbmc/utils/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -SRCS=AlarmClock.cpp \ - AliasShortcutUtils.cpp \ - Archive.cpp \ - AsyncFileCopy.cpp \ - AutoPtrHandle.cpp \ - Base64.cpp \ - BitstreamConverter.cpp \ - BitstreamStats.cpp \ - CharsetConverter.cpp \ - CPUInfo.cpp \ - Crc32.cpp \ - CryptThreading.cpp \ - DatabaseUtils.cpp \ - DownloadQueue.cpp \ - DownloadQueueManager.cpp \ - EndianSwap.cpp \ - EdenVideoArtUpdater.cpp \ - Fanart.cpp \ - fastmemcpy.c \ - fastmemcpy-arm.S \ - FileOperationJob.cpp \ - FileUtils.cpp \ - fstrcmp.c \ - fft.cpp \ - GLUtils.cpp \ - GroupUtils.cpp \ - HTMLTable.cpp \ - HTMLUtil.cpp \ - HttpHeader.cpp \ - HttpParser.cpp \ - HttpResponse.cpp \ - InfoLoader.cpp \ - JobManager.cpp \ - JSONVariantParser.cpp \ - JSONVariantWriter.cpp \ - LabelFormatter.cpp \ - LangCodeExpander.cpp \ - LCD.cpp \ - LCDFactory.cpp \ - log.cpp \ - md5.cpp \ - Observer.cpp \ - Mime.cpp \ - PerformanceSample.cpp \ - PerformanceStats.cpp \ - POUtils.cpp \ - RecentlyAddedJob.cpp \ - RegExp.cpp \ - RingBuffer.cpp \ - RssReader.cpp \ - ScraperParser.cpp \ - ScraperUrl.cpp \ - Screenshot.cpp \ - SeekHandler.cpp \ - SortUtils.cpp \ - Splash.cpp \ - Stopwatch.cpp \ - StreamDetails.cpp \ - StreamUtils.cpp \ - StringUtils.cpp \ - SystemInfo.cpp \ - TextSearch.cpp \ - TimeSmoother.cpp \ - TimeUtils.cpp \ - TuxBoxUtil.cpp \ - URIUtils.cpp \ - UrlOptions.cpp \ - Variant.cpp \ - Vector.cpp \ - Weather.cpp \ - XBMCTinyXML.cpp \ - XMLUtils.cpp \ - -LIB=utils.a - -include ../../Makefile.include --include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(patsubst %.S,,$(SRCS)))) diff --git a/xbmc/utils/Makefile.in b/xbmc/utils/Makefile.in new file mode 100644 index 0000000000000..736a5ad85b7a1 --- /dev/null +++ b/xbmc/utils/Makefile.in @@ -0,0 +1,84 @@ +SRCS += AlarmClock.cpp +SRCS += AliasShortcutUtils.cpp +SRCS += Archive.cpp +SRCS += AsyncFileCopy.cpp +ifeq (@USE_LIBAMCODEC@,1) +SRCS += AMLUtils.cpp +else ifeq (@USE_AMLPLAYER@,1) +SRCS += AMLUtils.cpp +endif +SRCS += AutoPtrHandle.cpp +SRCS += Base64.cpp +SRCS += BitstreamConverter.cpp +SRCS += BitstreamStats.cpp +SRCS += CharsetConverter.cpp +SRCS += CPUInfo.cpp +SRCS += Crc32.cpp +SRCS += CryptThreading.cpp +SRCS += DatabaseUtils.cpp +SRCS += DownloadQueue.cpp +SRCS += DownloadQueueManager.cpp +SRCS += EndianSwap.cpp +SRCS += EdenVideoArtUpdater.cpp +SRCS += Fanart.cpp +SRCS += fastmemcpy.c +SRCS += fastmemcpy-arm.S +SRCS += FileOperationJob.cpp +SRCS += FileUtils.cpp +SRCS += fstrcmp.c +SRCS += fft.cpp +SRCS += GLUtils.cpp +SRCS += GroupUtils.cpp +SRCS += HTMLTable.cpp +SRCS += HTMLUtil.cpp +SRCS += HttpHeader.cpp +SRCS += HttpParser.cpp +SRCS += HttpResponse.cpp +SRCS += InfoLoader.cpp +SRCS += JobManager.cpp +SRCS += JSONVariantParser.cpp +SRCS += JSONVariantWriter.cpp +SRCS += LabelFormatter.cpp +SRCS += LangCodeExpander.cpp +SRCS += LCD.cpp +SRCS += LCDFactory.cpp +SRCS += log.cpp +SRCS += md5.cpp +SRCS += Observer.cpp +SRCS += Mime.cpp +SRCS += PerformanceSample.cpp +SRCS += PerformanceStats.cpp +SRCS += POUtils.cpp +SRCS += RecentlyAddedJob.cpp +SRCS += RegExp.cpp +SRCS += RingBuffer.cpp +SRCS += RssReader.cpp +SRCS += ScraperParser.cpp +SRCS += ScraperUrl.cpp +SRCS += Screenshot.cpp +SRCS += SeekHandler.cpp +SRCS += SortUtils.cpp +SRCS += Splash.cpp +SRCS += Stopwatch.cpp +SRCS += StreamDetails.cpp +SRCS += StreamUtils.cpp +SRCS += StringUtils.cpp +SRCS += SystemInfo.cpp +SRCS += TextSearch.cpp +SRCS += TimeSmoother.cpp +SRCS += TimeUtils.cpp +SRCS += TuxBoxUtil.cpp +SRCS += URIUtils.cpp +SRCS += UrlOptions.cpp +SRCS += Variant.cpp +SRCS += Vector.cpp +SRCS += Weather.cpp +SRCS += XBMCTinyXML.cpp +SRCS += XMLUtils.cpp +SRCS += XMLVariantParser.cpp +SRCS += XMLVariantWriter.cpp + +LIB = utils.a + +include @abs_top_srcdir@/Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(patsubst %.S,,$(SRCS)))) diff --git a/xbmc/utils/MathUtils.h b/xbmc/utils/MathUtils.h index c95171ff4d012..8c7662810c184 100644 --- a/xbmc/utils/MathUtils.h +++ b/xbmc/utils/MathUtils.h @@ -71,8 +71,8 @@ namespace MathUtils */ inline int round_int(double x) { - assert(x > static_cast(INT_MIN / 2) - 1.0); - assert(x < static_cast(INT_MAX / 2) + 1.0); +// assert(x > static_cast(INT_MIN / 2) - 1.0); +// assert(x < static_cast(INT_MAX / 2) + 1.0); const float round_to_nearest = 0.5f; int i; diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp index 10527b7762be8..2d746155cd041 100644 --- a/xbmc/utils/RssReader.cpp +++ b/xbmc/utils/RssReader.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "threads/SystemClock.h" #include "RssReader.h" #include "utils/HTMLUtil.h" @@ -139,7 +138,7 @@ void CRssReader::Process() CURL url(strUrl); // we wait for the network to come up - if ((url.GetProtocol() == "http" || url.GetProtocol() == "https") && !g_application.getNetwork().IsAvailable(true)) + if ((url.GetProtocol() == "http" || url.GetProtocol() == "https") && !g_application.getNetworkManager().IsAvailable(true)) strXML = ""+g_localizeStrings.Get(15301)+""; else { diff --git a/xbmc/utils/SystemInfo.cpp b/xbmc/utils/SystemInfo.cpp index 745ad13c188cb..434cc8897b65a 100644 --- a/xbmc/utils/SystemInfo.cpp +++ b/xbmc/utils/SystemInfo.cpp @@ -28,7 +28,6 @@ #endif #include "GUIInfoManager.h" #include "filesystem/CurlFile.h" -#include "network/Network.h" #include "Application.h" #include "windowing/WindowingFactory.h" #include "settings/Settings.h" @@ -60,7 +59,6 @@ bool CSysInfoJob::DoWork() m_info.videoEncoder = GetVideoEncoder(); m_info.cpuFrequency = GetCPUFreqInfo(); m_info.kernelVersion = CSysInfo::GetKernelVersion(); - m_info.macAddress = GetMACAddress(); m_info.batteryLevel = GetBatteryLevel(); return true; } @@ -89,16 +87,6 @@ CSysData::INTERNET_STATE CSysInfoJob::GetInternetState() return CSysData::DISCONNECTED; } -CStdString CSysInfoJob::GetMACAddress() -{ -#if defined(HAS_LINUX_NETWORK) || defined(HAS_WIN32_NETWORK) - CNetworkInterface* iface = g_application.getNetwork().GetFirstConnectedInterface(); - if (iface) - return iface->GetMacAddress(); -#endif - return ""; -} - CStdString CSysInfoJob::GetVideoEncoder() { return "GPU: " + g_Windowing.GetRenderRenderer(); diff --git a/xbmc/utils/SystemInfo.h b/xbmc/utils/SystemInfo.h index 75dc609ccc7d9..9e5ec870fc0d6 100644 --- a/xbmc/utils/SystemInfo.h +++ b/xbmc/utils/SystemInfo.h @@ -69,7 +69,6 @@ class CSysInfoJob : public CJob double GetCPUFrequency(); CStdString GetSystemUpTime(bool bTotalUptime); CStdString GetCPUFreqInfo(); - CStdString GetMACAddress(); CStdString GetVideoEncoder(); CStdString GetBatteryLevel(); diff --git a/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp index e54c491184d21..d4fdff06c65f9 100644 --- a/xbmc/utils/URIUtils.cpp +++ b/xbmc/utils/URIUtils.cpp @@ -18,7 +18,6 @@ * */ -#include "network/Network.h" #include "URIUtils.h" #include "Application.h" #include "FileItem.h" @@ -484,6 +483,7 @@ bool URIUtils::IsOnLAN(const CStdString& strPath) address = ntohl(inet_addr(ip.c_str())); } +/* if(address != INADDR_NONE) { // check if we are on the local subnet @@ -493,6 +493,7 @@ bool URIUtils::IsOnLAN(const CStdString& strPath) if (g_application.getNetwork().HasInterfaceForIP(address)) return true; } +*/ return false; } diff --git a/xbmc/utils/Weather.cpp b/xbmc/utils/Weather.cpp index 10c1927c7b6a8..d6de0a285c6a5 100644 --- a/xbmc/utils/Weather.cpp +++ b/xbmc/utils/Weather.cpp @@ -26,7 +26,6 @@ #include "XMLUtils.h" #include "utils/POUtils.h" #include "Temperature.h" -#include "network/Network.h" #include "Application.h" #include "settings/GUISettings.h" #include "settings/Settings.h" @@ -74,7 +73,7 @@ CWeatherJob::CWeatherJob(int location) bool CWeatherJob::DoWork() { // wait for the network - if (!g_application.getNetwork().IsAvailable(true)) + if (!g_application.getNetworkManager().IsAvailable(true)) return false; AddonPtr addon; diff --git a/xbmc/utils/XMLVariantParser.cpp b/xbmc/utils/XMLVariantParser.cpp new file mode 100644 index 0000000000000..d8fc1ab741c57 --- /dev/null +++ b/xbmc/utils/XMLVariantParser.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XMLVariantParser.h" +#include "XMLUtils.h" + +CVariant CXMLVariantParser::Parse(const TiXmlElement *valueNode) +{ + CVariant value; + Parse(valueNode, value); + return value; +} + +void CXMLVariantParser::Parse(const TiXmlElement *valueNode, CVariant &value) +{ + CStdString type = valueNode->Attribute("type"); + if (type.Equals("object")) + { + const TiXmlElement *memberNode = valueNode->FirstChildElement("value"); + while (memberNode) + { + const char *member = memberNode->Attribute("key"); + + if (member) + Parse(memberNode, value[member]); + + memberNode = memberNode->NextSiblingElement("value"); + } + } + else if (type.Equals("array")) + { + const TiXmlElement *memberNode = valueNode->FirstChildElement("value"); + while (memberNode) + { + CVariant member; + Parse(memberNode, member); + value.push_back(member); + memberNode = memberNode->NextSiblingElement("value"); + } + } + else if (type.Equals("integer")) + { + value = (int64_t)atoi(valueNode->FirstChild()->Value()); + } + else if (type.Equals("unsignedinteger")) + { + value = (uint64_t)atol(valueNode->FirstChild()->Value()); + } + else if (type.Equals("boolean")) + { + CStdString boolean = valueNode->FirstChild()->Value(); + value = boolean == "true"; + } + else if (type.Equals("string")) + { + value = valueNode->FirstChild()->Value(); + } + else if (type.Equals("double")) + { + value = (float)atof(valueNode->FirstChild()->Value()); + } +} diff --git a/xbmc/utils/XMLVariantParser.h b/xbmc/utils/XMLVariantParser.h new file mode 100644 index 0000000000000..e0c80933ad503 --- /dev/null +++ b/xbmc/utils/XMLVariantParser.h @@ -0,0 +1,32 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Variant.h" + +class TiXmlElement; + +class CXMLVariantParser +{ +public: + static CVariant Parse(const TiXmlElement *valueNode); + static void Parse(const TiXmlElement *valueNode, CVariant &value); +}; diff --git a/xbmc/utils/XMLVariantWriter.cpp b/xbmc/utils/XMLVariantWriter.cpp new file mode 100644 index 0000000000000..34571580feaf2 --- /dev/null +++ b/xbmc/utils/XMLVariantWriter.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XMLVariantWriter.h" +#include "XMLUtils.h" + +bool CXMLVariantWriter::Write(TiXmlElement *root, const CVariant &value) +{ + TiXmlElement valueNode("value"); + + if (WriteValue(&valueNode, value)) + return root->InsertEndChild(valueNode) != NULL; + else + return false; +} + +bool CXMLVariantWriter::WriteValue(TiXmlElement *valueNode, const CVariant &value) +{ + CStdString strValue; + bool success = true; + switch (value.type()) + { + case CVariant::VariantTypeInteger: + valueNode->SetAttribute("type", "integer"); + strValue.Format("%i", value.asInteger()); + success = valueNode->InsertEndChild(TiXmlText(strValue.c_str())) != NULL; + break; + case CVariant::VariantTypeUnsignedInteger: + valueNode->SetAttribute("type", "unsignedinteger"); + strValue.Format("%i", value.asUnsignedInteger()); + success = valueNode->InsertEndChild(TiXmlText(strValue.c_str())) != NULL; + break; + case CVariant::VariantTypeDouble: + valueNode->SetAttribute("type", "double"); + strValue.Format("%f", value.asFloat()); + success = valueNode->InsertEndChild(TiXmlText(strValue.c_str())) != NULL; + break; + case CVariant::VariantTypeBoolean: + valueNode->SetAttribute("type", "boolean"); + success = valueNode->InsertEndChild(TiXmlText(value.asBoolean() ? "true" : "false")) != NULL; + break; + case CVariant::VariantTypeString: + valueNode->SetAttribute("type", "string"); + success = valueNode->InsertEndChild(TiXmlText(value.asString())) != NULL; + break; + case CVariant::VariantTypeArray: + valueNode->SetAttribute("type", "array"); + for (CVariant::const_iterator_array itr = value.begin_array(); itr != value.end_array() && success; itr++) + { + TiXmlElement memberNode("value"); + if (WriteValue(&memberNode, *itr)) + success &= valueNode->InsertEndChild(memberNode) != NULL; + else + success = false; + } + + break; + case CVariant::VariantTypeObject: + valueNode->SetAttribute("type", "object"); + for (CVariant::const_iterator_map itr = value.begin_map(); itr != value.end_map() && success; itr++) + { + TiXmlElement memberNode("value"); + memberNode.SetAttribute("key", itr->first.c_str()); + if (WriteValue(&memberNode, itr->second)) + success &= valueNode->InsertEndChild(memberNode) != NULL; + else + success = false; + } + break; + case CVariant::VariantTypeConstNull: + case CVariant::VariantTypeNull: + default: + valueNode->SetAttribute("type", "null"); + break; + } + + return success; +} diff --git a/xbmc/utils/XMLVariantWriter.h b/xbmc/utils/XMLVariantWriter.h new file mode 100644 index 0000000000000..0dd66f67eaf17 --- /dev/null +++ b/xbmc/utils/XMLVariantWriter.h @@ -0,0 +1,33 @@ +#pragma once +/* + * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program 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, or (at your option) + * any later version. + * + * This Program 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Variant.h" + +class TiXmlElement; + +class CXMLVariantWriter +{ +public: + static bool Write(TiXmlElement *root, const CVariant &value); +private: + static bool WriteValue(TiXmlElement *valueNode, const CVariant &value); +}; diff --git a/xbmc/windowing/WinEventsLinux.cpp b/xbmc/windowing/WinEventsLinux.cpp index e49ef3a46ffd8..5f166c4a43da1 100644 --- a/xbmc/windowing/WinEventsLinux.cpp +++ b/xbmc/windowing/WinEventsLinux.cpp @@ -34,6 +34,8 @@ PHANDLE_EVENT_FUNC CWinEventsBase::m_pEventFunc = NULL; bool CWinEventsLinux::m_initialized = false; CLinuxInputDevices CWinEventsLinux::m_devices; +std::map CWinEventsLinux::m_altMap; +std::map CWinEventsLinux::m_keyPadMap; CWinEventsLinux::CWinEventsLinux() { @@ -52,9 +54,41 @@ bool CWinEventsLinux::IsRemoteLowBattery() bool CWinEventsLinux::MessagePump() { + static int number; + static bool accumlate; + if (!m_initialized) { m_devices.InitAvailable(); + + // Mapping of ALT-XXX of codepage 437 to unicode + m_altMap[130] = 0x00E9; + m_altMap[225] = 0x00DF; + m_altMap[135] = 0x00E7; + m_altMap[138] = 0x00E8; + m_altMap[149] = 0x00F2; + m_altMap[133] = 0x00E0; + m_altMap[151] = 0x00F9; + m_altMap[129] = 0x00FC; + m_altMap[134] = 0x00E5; + m_altMap[148] = 0x00F6; + m_altMap[162] = 0x00F3; + m_altMap[164] = 0x00F1; + m_altMap[145] = 0x00E6; + m_altMap[132] = 0x00E4; + + // Mapping of keypad scancodes to their numeric values + m_keyPadMap[82] = 0; + m_keyPadMap[79] = 1; + m_keyPadMap[80] = 2; + m_keyPadMap[81] = 3; + m_keyPadMap[75] = 4; + m_keyPadMap[76] = 5; + m_keyPadMap[77] = 6; + m_keyPadMap[71] = 7; + m_keyPadMap[72] = 8; + m_keyPadMap[73] = 9; + m_initialized = true; } @@ -63,7 +97,38 @@ bool CWinEventsLinux::MessagePump() while (1) { event = m_devices.ReadEvent(); - if (event.type != XBMC_NOEVENT) + + // Wait for first left ALT press, then capture numbers pressed, then wait for left ALT release. + // Translate the number into a unicode value. This is to support latin characters. + if (event.key.keysym.sym == XBMCK_LALT && event.type == XBMC_KEYDOWN) + { + accumlate = true; + number = 0; + } + + if (accumlate && (event.type == XBMC_KEYDOWN)) + { + int keyPad = m_keyPadMap.at(event.key.keysym.scancode); + number = number * 10 + keyPad; + } + + if (accumlate && event.key.keysym.sym == XBMCK_LALT && event.type == XBMC_KEYUP) + { + accumlate = false; + event.type = XBMC_KEYDOWN; + event.key.keysym.sym = XBMCK_UNKNOWN;; + + if (m_altMap.find(number) != m_altMap.end()) + { + event.key.keysym.unicode = m_altMap[number]; + } + else + { + event.key.keysym.unicode = number; + } + } + + if ((event.type != XBMC_NOEVENT) && !accumlate) { ret |= g_application.OnEvent(event); } diff --git a/xbmc/windowing/WinEventsLinux.h b/xbmc/windowing/WinEventsLinux.h index f99ae4b09482e..eb71956b92f5f 100644 --- a/xbmc/windowing/WinEventsLinux.h +++ b/xbmc/windowing/WinEventsLinux.h @@ -1,3 +1,4 @@ +#pragma once /* * Copyright (C) 2005-2012 Team XBMC * http://www.xbmc.org @@ -18,13 +19,11 @@ * */ -#ifndef WINDOW_EVENTS_LINUX_H -#define WINDOW_EVENTS_LINUX_H - -#pragma once #include "windowing/WinEvents.h" #include "input/linux/LinuxInputDevices.h" +#include + class CWinEventsLinux : public CWinEventsBase { public: @@ -36,6 +35,6 @@ class CWinEventsLinux : public CWinEventsBase private: static bool m_initialized; static CLinuxInputDevices m_devices; + static std::map m_altMap; + static std::map m_keyPadMap; }; - -#endif diff --git a/xbmc/windowing/egl/EGLNativeTypeAmlogic.cpp b/xbmc/windowing/egl/EGLNativeTypeAmlogic.cpp index d2b580d4befcc..6b200fa08c82f 100644 --- a/xbmc/windowing/egl/EGLNativeTypeAmlogic.cpp +++ b/xbmc/windowing/egl/EGLNativeTypeAmlogic.cpp @@ -17,17 +17,29 @@ * . * */ -#include + #include "EGLNativeTypeAmlogic.h" +#include "guilib/gui3d.h" +#include "utils/AMLUtils.h" +#include "utils/StringUtils.h" + #include #include #include -#include "utils/StringUtils.h" -#include "guilib/gui3d.h" -#define m_framebuffer_name "fb0" +#include CEGLNativeTypeAmlogic::CEGLNativeTypeAmlogic() { + const char *env_framebuffer = getenv("FRAMEBUFFER"); + + // default to framebuffer 0 + m_framebuffer_name = "fb0"; + if (env_framebuffer) + { + std::string framebuffer(env_framebuffer); + std::string::size_type start = framebuffer.find("fb"); + m_framebuffer_name = framebuffer.substr(start); + } m_nativeWindow = NULL; } @@ -38,7 +50,9 @@ CEGLNativeTypeAmlogic::~CEGLNativeTypeAmlogic() bool CEGLNativeTypeAmlogic::CheckCompatibility() { char name[256] = {0}; - get_sysfs_str("/sys/class/graphics/fb0/device/modalias", name, 255); + std::string modalias = "/sys/class/graphics/" + m_framebuffer_name + "/device/modalias"; + + aml_get_sysfs_str(modalias.c_str(), name, 255); CStdString strName = name; strName.Trim(); if (strName == "platform:mesonfb") @@ -48,12 +62,12 @@ bool CEGLNativeTypeAmlogic::CheckCompatibility() void CEGLNativeTypeAmlogic::Initialize() { - SetCpuMinLimit(true); + aml_cpufreq_limit(true); return; } void CEGLNativeTypeAmlogic::Destroy() { - SetCpuMinLimit(false); + aml_cpufreq_limit(false); return; } @@ -111,47 +125,60 @@ bool CEGLNativeTypeAmlogic::DestroyNativeWindow() bool CEGLNativeTypeAmlogic::GetNativeResolution(RESOLUTION_INFO *res) const { char mode[256] = {0}; - get_sysfs_str("/sys/class/display/mode", mode, 255); + aml_get_sysfs_str("/sys/class/display/mode", mode, 255); return ModeToResolution(mode, res); } bool CEGLNativeTypeAmlogic::SetNativeResolution(const RESOLUTION_INFO &res) { - if (res.iScreenWidth == 1920 && res.iScreenHeight == 1080) - { - if (res.dwFlags & D3DPRESENTFLAG_INTERLACED) - { - if ((int)res.fRefreshRate == 60) - SetDisplayResolution("1080i"); - else - SetDisplayResolution("1080i50hz"); - } - else - { - if ((int)res.fRefreshRate == 60) - SetDisplayResolution("1080p"); - else - SetDisplayResolution("1080p50hz"); - } - } - else if (res.iScreenWidth == 1280 && res.iScreenHeight == 720) + switch((int)(0.5 + res.fRefreshRate)) { - if ((int)res.fRefreshRate == 60) - SetDisplayResolution("720p"); - else - SetDisplayResolution("720p50hz"); - } - else if (res.iScreenWidth == 720 && res.iScreenHeight == 480) - { - SetDisplayResolution("480p"); + default: + case 60: + switch(res.iScreenWidth) + { + default: + case 1280: + SetDisplayResolution("720p"); + break; + case 1920: + if (res.dwFlags & D3DPRESENTFLAG_INTERLACED) + SetDisplayResolution("1080i"); + else + SetDisplayResolution("1080p"); + break; + } + break; + case 50: + switch(res.iScreenWidth) + { + default: + case 1280: + SetDisplayResolution("720p50hz"); + break; + case 1920: + if (res.dwFlags & D3DPRESENTFLAG_INTERLACED) + SetDisplayResolution("1080i50hz"); + else + SetDisplayResolution("1080p50hz"); + break; + } + break; + case 30: + SetDisplayResolution("1080p30hz"); + break; + case 24: + SetDisplayResolution("1080p24hz"); + break; } + return true; } bool CEGLNativeTypeAmlogic::ProbeResolutions(std::vector &resolutions) { char valstr[256] = {0}; - get_sysfs_str("/sys/class/amhdmitx/amhdmitx0/disp_cap", valstr, 255); + aml_get_sysfs_str("/sys/class/amhdmitx/amhdmitx0/disp_cap", valstr, 255); std::vector probe_str; StringUtils::SplitString(valstr, "\n", probe_str); @@ -185,75 +212,16 @@ bool CEGLNativeTypeAmlogic::GetPreferredResolution(RESOLUTION_INFO *res) const bool CEGLNativeTypeAmlogic::ShowWindow(bool show) { - std::string blank_framebuffer = "/sys/class/graphics/fb0/blank"; - set_sysfs_int(blank_framebuffer.c_str(), show ? 0 : 1); + std::string blank_framebuffer = "/sys/class/graphics/" + m_framebuffer_name + "/blank"; + aml_set_sysfs_int(blank_framebuffer.c_str(), show ? 0 : 1); return true; } -int CEGLNativeTypeAmlogic::get_sysfs_str(const char *path, char *valstr, const int size) const -{ - int fd = open(path, O_RDONLY); - if (fd >= 0) - { - int len = read(fd, valstr, size - 1); - if (len != -1 ) - valstr[len] = '\0'; - close(fd); - } - else - { - sprintf(valstr, "%s", "fail"); - return -1; - } - return 0; -} - -int CEGLNativeTypeAmlogic::set_sysfs_str(const char *path, const char *val) const -{ - int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); - if (fd >= 0) - { - write(fd, val, strlen(val)); - close(fd); - return 0; - } - return -1; -} - -int CEGLNativeTypeAmlogic::set_sysfs_int(const char *path, const int val) const -{ - char bcmd[16]; - int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); - if (fd >= 0) - { - sprintf(bcmd, "%d", val); - write(fd, bcmd, strlen(bcmd)); - close(fd); - return 0; - } - return -1; -} - -int CEGLNativeTypeAmlogic::get_sysfs_int(const char *path) const -{ - int val = 0; - char bcmd[16]; - int fd = open(path, O_RDONLY); - if (fd >= 0) - { - read(fd, bcmd, sizeof(bcmd)); - val = strtol(bcmd, NULL, 16); - close(fd); - } - return val; -} - bool CEGLNativeTypeAmlogic::SetDisplayResolution(const char *resolution) { CStdString modestr = resolution; // switch display resolution - set_sysfs_str("/sys/class/display/mode", modestr.c_str()); - usleep(250 * 1000); + aml_set_sysfs_str("/sys/class/display/mode", modestr.c_str()); // setup gui freescale depending on display resolution DisableFreeScale(); @@ -279,6 +247,7 @@ bool CEGLNativeTypeAmlogic::ModeToResolution(const char *mode, RESOLUTION_INFO * CStdString fromMode = mode; fromMode.Trim(); // strips, for example, 720p* to 720p + // the * indicate the 'native' mode of the display if (fromMode.Right(1) == "*") fromMode = fromMode.Left(std::max(0, (int)fromMode.size() - 1)); @@ -286,56 +255,79 @@ bool CEGLNativeTypeAmlogic::ModeToResolution(const char *mode, RESOLUTION_INFO * { res->iWidth = 1280; res->iHeight= 720; - res->iScreenWidth = res->iWidth; - res->iScreenHeight = res->iHeight; + res->iScreenWidth = 1280; + res->iScreenHeight= 720; res->fRefreshRate = 60; - res->dwFlags= D3DPRESENTFLAG_PROGRESSIVE; + res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE; } else if (fromMode.Equals("720p50hz")) { res->iWidth = 1280; res->iHeight= 720; - res->iScreenWidth = res->iWidth; - res->iScreenHeight = res->iHeight; + res->iScreenWidth = 1280; + res->iScreenHeight= 720; res->fRefreshRate = 50; - res->dwFlags= D3DPRESENTFLAG_PROGRESSIVE; + res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE; } else if (fromMode.Equals("1080p")) { res->iWidth = 1280; res->iHeight= 720; - res->iScreenWidth = 1920; - res->iScreenHeight = 1080; + res->iScreenWidth = 1920; + res->iScreenHeight= 1080; res->fRefreshRate = 60; - res->dwFlags= D3DPRESENTFLAG_PROGRESSIVE; + res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE; } - else if (fromMode.Equals("1080p50hz")) + else if (fromMode.Equals("1080p24hz")) { + res->iWidth = 1280; + res->iHeight= 720; res->iScreenWidth = 1920; res->iScreenHeight= 1080; + res->fRefreshRate = 24; + res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE; + } + else if (fromMode.Equals("1080p30hz")) + { res->iWidth = 1280; res->iHeight= 720; - res->fRefreshRate = 50; - res->dwFlags= D3DPRESENTFLAG_PROGRESSIVE; + res->iScreenWidth = 1920; + res->iScreenHeight= 1080; + res->fRefreshRate = 30; + res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE; } - else if (fromMode.Equals("1080i")) + else if (fromMode.Equals("1080p50hz")) { + res->iWidth = 1280; + res->iHeight= 720; res->iScreenWidth = 1920; res->iScreenHeight= 1080; + res->fRefreshRate = 50; + res->dwFlags = D3DPRESENTFLAG_PROGRESSIVE; + } + else if (fromMode.Equals("1080i")) + { res->iWidth = 1280; res->iHeight= 720; + res->iScreenWidth = 1920; + res->iScreenHeight= 1080; res->fRefreshRate = 60; - res->dwFlags= D3DPRESENTFLAG_INTERLACED; + res->dwFlags = D3DPRESENTFLAG_INTERLACED; } else if (fromMode.Equals("1080i50hz")) { - res->iWidth = 1920; - res->iHeight= 1080; - res->iScreenWidth = 1280; - res->iScreenHeight= 720; + res->iWidth = 1280; + res->iHeight= 720; + res->iScreenWidth = 1920; + res->iScreenHeight= 1080; res->fRefreshRate = 50; - res->dwFlags= D3DPRESENTFLAG_INTERLACED; + res->dwFlags = D3DPRESENTFLAG_INTERLACED; } + else + { + return false; + } + res->iScreen = 0; res->bFullScreen = true; @@ -350,67 +342,66 @@ bool CEGLNativeTypeAmlogic::ModeToResolution(const char *mode, RESOLUTION_INFO * void CEGLNativeTypeAmlogic::EnableFreeScale() { // remove default OSD and video path (default_osd default) - set_sysfs_str("/sys/class/vfm/map", "rm all"); - usleep(60 * 1000); + aml_set_sysfs_str("/sys/class/vfm/map", "rm all"); // add OSD path - set_sysfs_str("/sys/class/vfm/map", "add osdpath osd amvideo"); + aml_set_sysfs_str("/sys/class/vfm/map", "add osdpath osd amvideo"); // enable OSD free scale using frame buffer size of 720p - set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); - set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); - set_sysfs_int("/sys/class/graphics/fb0/scale_width", 1280); - set_sysfs_int("/sys/class/graphics/fb0/scale_height", 720); - set_sysfs_int("/sys/class/graphics/fb1/scale_width", 1280); - set_sysfs_int("/sys/class/graphics/fb1/scale_height", 720); - set_sysfs_int("/sys/class/graphics/fb0/free_scale", 1); - set_sysfs_int("/sys/class/graphics/fb1/free_scale", 1); - usleep(60 * 1000); + aml_set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb0/scale_width", 1280); + aml_set_sysfs_int("/sys/class/graphics/fb0/scale_height", 720); + aml_set_sysfs_int("/sys/class/graphics/fb1/scale_width", 1280); + aml_set_sysfs_int("/sys/class/graphics/fb1/scale_height", 720); + aml_set_sysfs_int("/sys/class/graphics/fb0/free_scale", 1); + aml_set_sysfs_int("/sys/class/graphics/fb1/free_scale", 1); // remove OSD path - set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); - set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); - set_sysfs_str("/sys/class/vfm/map", "rm osdpath"); - usleep(60 * 1000); + aml_set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); + aml_set_sysfs_str("/sys/class/vfm/map", "rm osdpath"); // add video path - set_sysfs_str("/sys/class/vfm/map", "add videopath decoder ppmgr amvideo"); + aml_set_sysfs_str("/sys/class/vfm/map", "add videopath decoder ppmgr amvideo"); // enable video free scale (scaling to 1920x1080 with frame buffer size 1280x720) - set_sysfs_int("/sys/class/ppmgr/ppscaler", 0); - set_sysfs_int("/sys/class/video/disable_video", 1); - set_sysfs_int("/sys/class/ppmgr/ppscaler", 1); - set_sysfs_str("/sys/class/ppmgr/ppscaler_rect", "0 0 1919 1079 0"); - set_sysfs_str("/sys/class/ppmgr/disp", "1280 720"); - usleep(60 * 1000); + aml_set_sysfs_int("/sys/class/ppmgr/ppscaler", 0); + aml_set_sysfs_int("/sys/class/video/disable_video", 1); + aml_set_sysfs_int("/sys/class/ppmgr/ppscaler", 1); + aml_set_sysfs_str("/sys/class/ppmgr/ppscaler_rect", "0 0 1919 1079 0"); + aml_set_sysfs_str("/sys/class/ppmgr/disp", "1280 720"); // - set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); - set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); - set_sysfs_int("/sys/class/graphics/fb0/scale_width", 1280); - set_sysfs_int("/sys/class/graphics/fb0/scale_height", 720); - set_sysfs_int("/sys/class/graphics/fb1/scale_width", 1280); - set_sysfs_int("/sys/class/graphics/fb1/scale_height", 720); - set_sysfs_int("/sys/class/graphics/fb0/free_scale", 1); - set_sysfs_int("/sys/class/graphics/fb1/free_scale", 1); - usleep(60 * 1000); + aml_set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb0/scale_width", 1280); + aml_set_sysfs_int("/sys/class/graphics/fb0/scale_height", 720); + aml_set_sysfs_int("/sys/class/graphics/fb1/scale_width", 1280); + aml_set_sysfs_int("/sys/class/graphics/fb1/scale_height", 720); // - set_sysfs_int("/sys/class/video/disable_video", 2); - set_sysfs_str("/sys/class/display/axis", "0 0 1279 719 0 0 0 0"); - set_sysfs_str("/sys/class/ppmgr/ppscaler_rect", "0 0 1279 719 1"); + aml_set_sysfs_int("/sys/class/video/disable_video", 2); + aml_set_sysfs_str("/sys/class/display/axis", "0 0 1279 719 0 0 0 0"); + aml_set_sysfs_str("/sys/class/ppmgr/ppscaler_rect", "0 0 1279 719 1"); + // + aml_set_sysfs_int("/sys/class/graphics/fb0/free_scale", 1); + aml_set_sysfs_int("/sys/class/graphics/fb1/free_scale", 1); + aml_set_sysfs_str("/sys/class/graphics/fb0/free_scale_axis", "0 0 1279 719"); } void CEGLNativeTypeAmlogic::DisableFreeScale() { // turn off frame buffer freescale - set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); - set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb0/free_scale", 0); + aml_set_sysfs_int("/sys/class/graphics/fb1/free_scale", 0); + aml_set_sysfs_str("/sys/class/graphics/fb0/free_scale_axis", "0 0 1279 719"); // revert to default video paths - set_sysfs_str("/sys/class/vfm/map", "rm all"); - set_sysfs_str("/sys/class/vfm/map", "add default_osd osd amvideo"); - set_sysfs_str("/sys/class/vfm/map", "add default decoder ppmgr amvideo"); + aml_set_sysfs_str("/sys/class/vfm/map", "rm all"); + aml_set_sysfs_str("/sys/class/vfm/map", "add default_osd osd amvideo"); + aml_set_sysfs_str("/sys/class/vfm/map", "add default decoder ppmgr amvideo"); // disable post processing scaler and disable_video special mode - set_sysfs_int("/sys/class/ppmgr/ppscaler", 0); - set_sysfs_int("/sys/class/video/disable_video", 0); + aml_set_sysfs_int("/sys/class/ppmgr/ppscaler", 0); + aml_set_sysfs_int("/sys/class/video/disable_video", 0); + aml_set_sysfs_int("/sys/class/video/disable_video", 1); // revert display axis int fd0; - std::string framebuffer = "/dev/fb0"; + std::string framebuffer = "/dev/" + m_framebuffer_name; if ((fd0 = open(framebuffer.c_str(), O_RDWR)) >= 0) { @@ -419,29 +410,8 @@ void CEGLNativeTypeAmlogic::DisableFreeScale() { char daxis_str[255] = {0}; sprintf(daxis_str, "%d %d %d %d 0 0 0 0", 0, 0, vinfo.xres, vinfo.yres); - set_sysfs_str("/sys/class/display/axis", daxis_str); + aml_set_sysfs_str("/sys/class/display/axis", daxis_str); } close(fd0); } } - -void CEGLNativeTypeAmlogic::SetCpuMinLimit(bool limit) -{ - // when playing hw decoded audio, we cannot drop below 600MHz - // or risk hw audio issues. AML code does a 2X scaling based off - // /sys/class/audiodsp/codec_mips but tests show that this is - // seems risky so we just clamp to 600Mhz to be safe. - - // only adjust if we are running "ondemand" - char scaling_governor[256] = {0}; - get_sysfs_str("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", scaling_governor, 255); - if (strncmp(scaling_governor, "ondemand", 255)) - return; - - int freq; - if (limit) - freq = 600000; - else - freq = get_sysfs_int("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq"); - set_sysfs_int("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", freq); -} diff --git a/xbmc/windowing/egl/EGLNativeTypeAmlogic.h b/xbmc/windowing/egl/EGLNativeTypeAmlogic.h index 6beb9562a66cc..f8e6a903574df 100644 --- a/xbmc/windowing/egl/EGLNativeTypeAmlogic.h +++ b/xbmc/windowing/egl/EGLNativeTypeAmlogic.h @@ -48,14 +48,11 @@ class CEGLNativeTypeAmlogic : public CEGLNativeType virtual bool ShowWindow(bool show); protected: - int get_sysfs_str(const char *path, char *valstr, const int size) const; - int set_sysfs_str(const char *path, const char *val) const; - int set_sysfs_int(const char *path, const int val) const; - int get_sysfs_int(const char *path) const; - bool SetDisplayResolution(const char *resolution); bool ModeToResolution(const char *mode, RESOLUTION_INFO *res) const; void EnableFreeScale(); void DisableFreeScale(); - void SetCpuMinLimit(bool limit); + +private: + std::string m_framebuffer_name; }; diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp index 4a0f2bb72ddcb..cbc895ea542cb 100644 --- a/xbmc/windowing/egl/WinSystemEGL.cpp +++ b/xbmc/windowing/egl/WinSystemEGL.cpp @@ -43,7 +43,7 @@ CWinSystemEGL::CWinSystemEGL() : CWinSystemBase() m_config = NULL; m_egl = NULL; - m_iVSyncMode = false; + m_iVSyncMode = 0; } CWinSystemEGL::~CWinSystemEGL() @@ -384,9 +384,12 @@ bool CWinSystemEGL::PresentRenderImpl(const CDirtyRegionList &dirty) void CWinSystemEGL::SetVSyncImpl(bool enable) { - m_iVSyncMode = enable; - if (!m_egl->SetVSync(m_display, m_iVSyncMode)) + m_iVSyncMode = enable ? 10:0; + if (!m_egl->SetVSync(m_display, enable)) + { + m_iVSyncMode = 0; CLog::Log(LOGERROR, "%s,Could not set egl vsync", __FUNCTION__); + } } void CWinSystemEGL::ShowOSMouse(bool show) diff --git a/xbmc/windowing/egl/WinSystemEGL.h b/xbmc/windowing/egl/WinSystemEGL.h index fae528c5faaf8..6beb8fcfc91cb 100644 --- a/xbmc/windowing/egl/WinSystemEGL.h +++ b/xbmc/windowing/egl/WinSystemEGL.h @@ -75,7 +75,6 @@ class CWinSystemEGL : public CWinSystemBase, public CRenderSystemGLES EGLConfig m_config; CEGLWrapper *m_egl; - bool m_iVSyncMode; std::string m_extensions; }; diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp index 0a37c528c0797..e020f0fa8ca7f 100644 --- a/xbmc/windows/GUIMediaWindow.cpp +++ b/xbmc/windows/GUIMediaWindow.cpp @@ -30,7 +30,6 @@ #include "GUIPassword.h" #include "Application.h" #include "ApplicationMessenger.h" -#include "network/Network.h" #include "utils/RegExp.h" #include "PartyModeManager.h" #include "dialogs/GUIDialogMediaSource.h" @@ -1150,7 +1149,7 @@ bool CGUIMediaWindow::HaveDiscOrConnection(const CStdString& strPath, int iDrive else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE) { // TODO: Handle not connected to a remote share - if ( !g_application.getNetwork().IsConnected() ) + if ( !g_application.getNetworkManager().IsConnected() ) { CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0); return false; @@ -1646,7 +1645,7 @@ const CFileItemList& CGUIMediaWindow::CurrentDirectory() const bool CGUIMediaWindow::WaitForNetwork() const { - if (g_application.getNetwork().IsAvailable()) + if (g_application.getNetworkManager().IsAvailable()) return true; CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS); @@ -1658,7 +1657,7 @@ bool CGUIMediaWindow::WaitForNetwork() const progress->SetLine(1, url.GetWithoutUserDetails()); progress->ShowProgressBar(false); progress->StartModal(); - while (!g_application.getNetwork().IsAvailable()) + while (!g_application.getNetworkManager().IsAvailable()) { progress->Progress(); if (progress->IsCanceled()) diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp index 1e237891514a0..44865ba312e56 100644 --- a/xbmc/windows/GUIWindowFileManager.cpp +++ b/xbmc/windows/GUIWindowFileManager.cpp @@ -35,7 +35,6 @@ #endif #include "pictures/GUIWindowSlideShow.h" #include "playlists/PlayListFactory.h" -#include "network/Network.h" #include "guilib/GUIWindowManager.h" #include "dialogs/GUIDialogOK.h" #include "dialogs/GUIDialogYesNo.h" @@ -675,7 +674,7 @@ bool CGUIWindowFileManager::HaveDiscOrConnection( CStdString& strPath, int iDriv else if ( iDriveType == CMediaSource::SOURCE_TYPE_REMOTE ) { // TODO: Handle not connected to a remote share - if ( !g_application.getNetwork().IsConnected() ) + if ( !g_application.getNetworkManager().IsConnected() ) { CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0); return false; diff --git a/xbmc/windows/GUIWindowLoginScreen.cpp b/xbmc/windows/GUIWindowLoginScreen.cpp index 494324ffbb5f0..57bff7c3e3004 100644 --- a/xbmc/windows/GUIWindowLoginScreen.cpp +++ b/xbmc/windows/GUIWindowLoginScreen.cpp @@ -34,7 +34,6 @@ #endif #include "interfaces/Builtins.h" #include "utils/Weather.h" -#include "network/Network.h" #include "addons/Skin.h" #include "settings/Profile.h" #include "guilib/GUIMessage.h" @@ -279,7 +278,7 @@ void CGUIWindowLoginScreen::LoadProfile(unsigned int profile) if (profile != 0 || !g_settings.IsMasterUser()) { - g_application.getNetwork().NetworkMessage(CNetwork::SERVICES_DOWN,1); + g_application.getNetworkManager().NetworkMessage(CNetworkManager::SERVICES_DOWN,1); g_settings.LoadProfile(profile); } else @@ -288,7 +287,8 @@ void CGUIWindowLoginScreen::LoadProfile(unsigned int profile) if (pWindow) pWindow->ResetControlStates(); } - g_application.getNetwork().NetworkMessage(CNetwork::SERVICES_UP,1); + + g_application.getNetworkManager().StartServices(); g_settings.UpdateCurrentProfileDate(); g_settings.SaveProfiles(PROFILES_FILE);