diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV/Channel.pm /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV/Channel.pm --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV/Channel.pm 2007-01-22 03:08:41.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV/Channel.pm 2006-04-16 03:05:27.000000000 -0500 @@ -4,7 +4,7 @@ # Object containing info about a particular MythTV channel. # # @url $URL: http://svn.mythtv.org/svn/branches/release-0-20-fixes/mythtv/bindings/perl/MythTV/Channel.pm $ -# @date $Date: 2006-04-16 10:05:27 +0200 (dim, 16 avr 2006) $ +# @date $Date: 2006-04-16 03:05:27 -0500 (Sun, 16 Apr 2006) $ # @version $Revision: 9725 $ # @author $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV/Program.pm /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV/Program.pm --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV/Program.pm 2007-01-22 03:08:41.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV/Program.pm 2006-11-18 23:37:30.000000000 -0600 @@ -4,7 +4,7 @@ # Object containing info about a particular MythTV program. # # @url $URL: http://svn.mythtv.org/svn/branches/release-0-20-fixes/mythtv/bindings/perl/MythTV/Program.pm $ -# @date $Date: 2006-11-19 06:37:30 +0100 (dim, 19 nov 2006) $ +# @date $Date: 2006-11-18 23:37:30 -0600 (Sat, 18 Nov 2006) $ # @version $Revision: 11780 $ # @author $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV/Recording.pm /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV/Recording.pm --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV/Recording.pm 2007-01-22 03:08:41.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV/Recording.pm 2006-10-08 01:48:26.000000000 -0500 @@ -4,7 +4,7 @@ # Object containing info about a particular MythTV recording. # # @url $URL: http://svn.mythtv.org/svn/branches/release-0-20-fixes/mythtv/bindings/perl/MythTV/Recording.pm $ -# @date $Date: 2006-10-08 08:48:26 +0200 (dim, 08 oct 2006) $ +# @date $Date: 2006-10-08 01:48:26 -0500 (Sun, 08 Oct 2006) $ # @version $Revision: 11472 $ # @author $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV.pm /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV.pm --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/bindings/perl/MythTV.pm 2007-01-22 03:08:41.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/bindings/perl/MythTV.pm 2006-10-05 00:46:03.000000000 -0500 @@ -2,7 +2,7 @@ # MythTV bindings for perl. # # @url $URL: http://svn.mythtv.org/svn/branches/release-0-20-fixes/mythtv/bindings/perl/MythTV.pm $ -# @date $Date: 2006-10-05 07:46:03 +0200 (jeu, 05 oct 2006) $ +# @date $Date: 2006-10-05 00:46:03 -0500 (Thu, 05 Oct 2006) $ # @version $Revision: 11444 $ # @author $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/configfiles/lircrc.native.example.mceusb2 /tmp/jxFKjogze3/mythtv-0.20.2/configfiles/lircrc.native.example.mceusb2 --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/configfiles/lircrc.native.example.mceusb2 2007-01-22 03:08:18.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/configfiles/lircrc.native.example.mceusb2 2006-06-12 00:18:38.000000000 -0500 @@ -2,7 +2,7 @@ # lircrc config file for the Microsoft Media Center Edition Remote, model 1039 # # @url $URL$ -# @date $Date: 2006-06-12 07:18:38 +0200 (lun, 12 jun 2006) $ +# @date $Date: 2006-06-12 00:18:38 -0500 (Mon, 12 Jun 2006) $ # @version $Revision: 10187 $ # @author $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/configure /tmp/jxFKjogze3/mythtv-0.20.2/configure --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/configure 2007-01-22 03:08:45.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/configure 2007-08-23 04:27:58.000000000 -0500 @@ -2305,9 +2305,15 @@ if test x"$dts" = x"yes"; then dts="no" - if has_library libdts ; then + if has_library libdca; then dts="yes" - extralibs="$extralibs -ldts" + dtslib="-ldca" + elif has_library libdts_pic ; then + dts="yes" + dtslib="-ldts_pic" + elif has_library libdts ; then + dts="yes" + dtslib="-ldts" fi fi @@ -2941,6 +2947,7 @@ if test "$dts" = "yes" ; then echo "#define CONFIG_DTS 1" >> $TMPH echo "CONFIG_DTS=yes" >> $MYTH_CONFIG_MAK + echo "CONFIG_DTSLIB=$dtslib" >> $MYTH_CONFIG_MAK fi # PP @@ -3497,4 +3504,9 @@ rm -f $TMPO $TMPC $TMPE $TMPS $TMPH -qmake mythtv.pro +if test -x $QTDIR/bin/qmake; then + $QTDIR/bin/qmake QMAKE=${QTDIR}/bin/qmake mythtv.pro +else + qmake mythtv.pro +fi + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/contrib/mythrename.pl /tmp/jxFKjogze3/mythtv-0.20.2/contrib/mythrename.pl --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/contrib/mythrename.pl 2007-01-22 03:08:42.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/contrib/mythrename.pl 2006-02-28 01:40:08.000000000 -0600 @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Date: 2006-02-28 08:40:08 +0100 (mar, 28 fév 2006) $ +# $Date: 2006-02-28 01:40:08 -0600 (Tue, 28 Feb 2006) $ # $Revision: 9198 $ # $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/contrib/optimize_mythdb.pl /tmp/jxFKjogze3/mythtv-0.20.2/contrib/optimize_mythdb.pl --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/contrib/optimize_mythdb.pl 2007-01-22 03:08:42.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/contrib/optimize_mythdb.pl 2006-08-05 16:51:46.000000000 -0500 @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Date: 2006-08-05 23:51:46 +0200 (sam, 05 aoû 2006) $ +# $Date: 2006-08-05 16:51:46 -0500 (Sat, 05 Aug 2006) $ # $Revision: 10685 $ # $Author: xris $ # diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/changelog /tmp/jxFKjogze3/mythtv-0.20.2/debian/changelog --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/changelog 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/changelog 2007-08-25 10:57:26.000000000 -0500 @@ -1,3 +1,23 @@ +mythtv (0.20.2-0ubuntu0.7.04~proposed1) feisty-proposed; urgency=low + + * New upstream version: + - Activates Schedules Direct support to replace Zap2it + for North American users. (LP: #134726) + - Decreases memory usage by up to 75% on mythfrontend. + * debian/rules: + - Add support for --version in mythtv apps for use + when submitting bug reports. + * Drop 03_libdts.dpatch. + * Drop 20_backend_crash_wihout_tuner.dpatch. + * Drop 21_changeset12782_hd_homerun.dpatch + * debian/mythtv-backend.files: + - Put UPNP files in the appropriate locations. + * debian/control: + - Add update notifier to dependencies for + mythtv-database. (LP: #126781) + + -- Mario Limonciello Fri, 24 Aug 2007 15:26:51 -0500 + mythtv (0.20-svn20070122-0.0ubuntu6) feisty; urgency=low [ Mario Limonciello ] diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/control /tmp/jxFKjogze3/mythtv-0.20.2/debian/control --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/control 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/control 2007-08-25 10:57:26.000000000 -0500 @@ -71,7 +71,7 @@ Package: mythtv-database Architecture: all -Depends: mythtv-common (= ${Source-Version}), libdbd-mysql-perl, mysql-client, cron, ${misc:Depends} +Depends: mythtv-common (= ${Source-Version}), libdbd-mysql-perl, mysql-client, cron, ${misc:Depends}, update-notifier Conflicts: mythtv (<< 0.8-1), mythtv-common (<< 0.8-2) Replaces: mythtv (<< 0.8-1), mythtv-common (<< 0.8-2) Description: A personal video recorder application (database) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/mythtv-backend.files /tmp/jxFKjogze3/mythtv-0.20.2/debian/mythtv-backend.files --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/mythtv-backend.files 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/mythtv-backend.files 2007-08-25 10:57:26.000000000 -0500 @@ -4,6 +4,9 @@ usr/bin/mythjobqueue usr/bin/mythtranscode usr/bin/mythtv-setup.real +usr/share/mythtv/MSRR_scpd.xml usr/share/mythtv/CDS_scpd.xml usr/share/mythtv/CMGR_scpd.xml -usr/share/mythtv/upnpavcd.xml +usr/share/mythtv/MXML_scpd.xml +usr/share/mythtv/devicemaster.xml +usr/share/mythtv/deviceslave.xml diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/00list /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/00list --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/00list 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/00list 2007-08-25 10:57:26.000000000 -0500 @@ -1,8 +1,5 @@ 01_debian 02_settings.pro -03_libdts 05_11372 06_gnomescreensaver 07_alsa_default.dpatch -20_backend_crash_without_tuner -21_changeset12782_hd_homerun diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/03_libdts.dpatch /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/03_libdts.dpatch --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/03_libdts.dpatch 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/03_libdts.dpatch 1969-12-31 18:00:00.000000000 -0600 @@ -1,19 +0,0 @@ -#! /bin/sh /usr/share/dpatch/dpatch-run -## 01_debian-patch.dpatch by Christian Marillat -## -## All lines beginning with `## DP:' are a description of the patch. -## DP: libdts patch for libavcodec. - -@DPATCH@ - ---- mythtv-0.18.1.orig/libs/libavcodec/libavcodec.pro.orig 2006-02-15 05:36:11.000000000 -0800 -+++ mythtv-0.18.1/libs/libavcodec/libavcodec.pro 2006-02-15 05:36:23.000000000 -0800 -@@ -317,7 +317,7 @@ - - contains( CONFIG_DTS, yes ) { - SOURCES += dtsdec.c -- LIBS += -ldts -+ LIBS += -ldts_pic - } - - using_xvmc { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/20_backend_crash_without_tuner.dpatch /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/20_backend_crash_without_tuner.dpatch --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/20_backend_crash_without_tuner.dpatch 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/20_backend_crash_without_tuner.dpatch 1969-12-31 18:00:00.000000000 -0600 @@ -1,38 +0,0 @@ -#! /bin/sh /usr/share/dpatch/dpatch-run -## 20_backend_crash_without_tuner.dpatch by Kees Cook -## -## All lines beginning with `## DP:' are a description of the patch. -## DP: Closes LP: #93250. -## DP: http://www.archivesat.com/MythTV_development/thread2049262.htm - -@DPATCH@ -diff -urNad mythtv-0.20-svn20070122~/programs/mythbackend/mainserver.cpp mythtv-0.20-svn20070122/programs/mythbackend/mainserver.cpp ---- mythtv-0.20-svn20070122~/programs/mythbackend/mainserver.cpp 2007-01-22 01:08:40.000000000 -0800 -+++ mythtv-0.20-svn20070122/programs/mythbackend/mainserver.cpp 2007-03-23 22:47:40.616622082 -0700 -@@ -2951,10 +2951,22 @@ - QMap::Iterator iter = encoderList->find(recnum); - if (iter == encoderList->end()) - { -- VERBOSE(VB_IMPORTANT, "MainServer: " + -- QString("HandleRemoteEncoder(cmd %1) ").arg(slist[1]) + -- QString("Unknown encoder: %1, exiting").arg(recnum)); -- exit(BACKEND_BUGGY_EXIT_UNKNOWN_ENC); -+ // Encoder not found -+ QString command = slist[1]; -+ if (command == "GET_STATE") -+ { -+ QStringList retlist; -+ retlist << QString::number((int)kState_Error); -+ SendResponse(pbssock, retlist); -+ return; -+ } -+ else -+ { -+ VERBOSE(VB_IMPORTANT, "MainServer: " + -+ QString("HandleRemoteEncoder(cmd %1) ").arg(slist[1]) + -+ QString("Unknown encoder: %1, exiting").arg(recnum)); -+ exit(BACKEND_BUGGY_EXIT_UNKNOWN_ENC); -+ } - } - - EncoderLink *enc = iter.data(); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/21_changeset12782_hd_homerun.dpatch /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/21_changeset12782_hd_homerun.dpatch --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/patches/21_changeset12782_hd_homerun.dpatch 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/patches/21_changeset12782_hd_homerun.dpatch 1969-12-31 18:00:00.000000000 -0600 @@ -1,111 +0,0 @@ -#! /bin/sh /usr/share/dpatch/dpatch-run -## changeset12782_hd_homerun.dpatch by Mario Limonciello -## -## All lines beginning with `## DP:' are a description of the patch. -## DP: No description. - -@DPATCH@ -diff -urNad mythtv-0.20-svn20070122~/libs/libmythtv/mpeg/mpegstreamdata.cpp mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegstreamdata.cpp ---- mythtv-0.20-svn20070122~/libs/libmythtv/mpeg/mpegstreamdata.cpp 2007-01-22 03:08:23.000000000 -0600 -+++ mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegstreamdata.cpp 2007-04-10 12:58:17.000000000 -0500 -@@ -406,11 +406,11 @@ - pmt.Parse(); - - vector videoPIDs, audioPIDs; -- vector videoTypes, audioTypes; -+ vector videoTypes; - vector pids, types; - - // Video -- uint video_cnt = pmt.FindPIDs(StreamID::AnyVideo, videoPIDs, videoTypes); -+ uint video_cnt = pmt.FindPIDs(StreamID::AnyVideo, videoPIDs, videoTypes, true); - if (video_cnt < _pmt_single_program_num_video) - { - VERBOSE(VB_RECORD, "Only "<& pids) const - { -+ uint pids_start = pids.size(); -+ - if ((StreamID::AnyMask & type) != StreamID::AnyMask) - { - for (uint i=0; i < StreamCount(); i++) -@@ -337,11 +339,14 @@ - * \param type StreamType to match - * \param pids vector pids will be added to - * \param types vector types will be added to -+ * \param normalize if set, types will be normalized - * \return number of items in pids and types lists. - */ - uint ProgramMapTable::FindPIDs(uint type, vector& pids, -- vector& types) const -+ vector& types, bool normalize) const - { -+ uint pids_start = pids.size(); -+ - if ((StreamID::AnyMask & type) != StreamID::AnyMask) - { - for (uint i=0; i < StreamCount(); i++) -@@ -351,6 +356,7 @@ - types.push_back(StreamType(i)); - } - } -+ - else if (StreamID::AnyVideo == type) - { - for (uint i=0; i < StreamCount(); i++) -@@ -370,6 +376,20 @@ - } - } - -+ if (!normalize) -+ return pids.size(); -+ -+ for (uint i = pids_start; i < pids.size(); i++) -+ { -+ int index = FindPID(pids[i]); -+ if (index >= 0) -+ { -+ desc_list_t desc = MPEGDescriptor::Parse( -+ StreamInfo(i), StreamInfoLength(i)); -+ types[i] = StreamID::Normalize(types[i], desc); -+ } -+ } -+ - return pids.size(); - } - -diff -urNad mythtv-0.20-svn20070122~/libs/libmythtv/mpeg/mpegtables.h mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegtables.h ---- mythtv-0.20-svn20070122~/libs/libmythtv/mpeg/mpegtables.h 2007-01-22 03:08:23.000000000 -0600 -+++ mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegtables.h 2007-04-10 12:58:17.000000000 -0500 -@@ -541,7 +541,7 @@ - QString GetLanguage(uint i) const; - - uint FindPIDs(uint type, vector& pids) const; -- uint FindPIDs(uint type, vector& pids, vector& types) const; -+ uint FindPIDs(uint type, vector& pids, vector& types, bool normalize) const; - - /// \brief Locates stream index of pid. - /// \return stream index if successful, -1 otherwise diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/rules /tmp/jxFKjogze3/mythtv-0.20.2/debian/rules --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/debian/rules 2007-08-25 10:57:25.000000000 -0500 +++ /tmp/jxFKjogze3/mythtv-0.20.2/debian/rules 2007-08-25 10:57:26.000000000 -0500 @@ -42,6 +42,8 @@ configure-stamp: patch-stamp dh_testdir + sed -i -e "s/\`(svnversion \$$\$${SVNTREEDIR} 2>\/dev\/null) || echo Unknown\`/$(SVN_REVISION)/" version.pro + CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-lirc --enable-audio-alsa \ --enable-audio-oss --enable-audio-jack --enable-audio-arts --enable-dvb \ --enable-ivtv --enable-firewire --enable-joystick-menu $(CONFIGURE_OPTS) \ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/docs/doxygen-create-developer-docs.cfg /tmp/jxFKjogze3/mythtv-0.20.2/docs/doxygen-create-developer-docs.cfg --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/docs/doxygen-create-developer-docs.cfg 2007-01-22 03:08:18.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/docs/doxygen-create-developer-docs.cfg 2007-08-23 22:11:45.000000000 -0500 @@ -23,7 +23,7 @@ # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.20 +PROJECT_NUMBER = 0.20.2 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libavcodec/libavcodec.pro /tmp/jxFKjogze3/mythtv-0.20.2/libs/libavcodec/libavcodec.pro --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libavcodec/libavcodec.pro 2007-01-22 03:08:32.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libavcodec/libavcodec.pro 2007-08-20 19:59:15.000000000 -0500 @@ -359,7 +359,7 @@ contains( CONFIG_DTS, yes ) { SOURCES += dtsdec.c - LIBS += -ldts + LIBS += $$CONFIG_DTSLIB } using_xvmc { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/dbsettings.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/dbsettings.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/dbsettings.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/dbsettings.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -4,40 +4,6 @@ #include #include -class TransientSetting: public TransientStorage, virtual public Configurable { -public: - TransientSetting() { }; -}; - -class TransientSpinBox: public SpinBoxSetting, public TransientSetting { - public: - TransientSpinBox(int min, int max, int step, - bool allow_single_step = false) : - SpinBoxSetting(min, max, step, allow_single_step) { }; -}; - -class TransientCheckBox: public CheckBoxSetting, public TransientSetting { - public: - TransientCheckBox() { }; -}; - -class TransientLineEdit: public LineEditSetting, public TransientSetting { - public: - TransientLineEdit(bool rw = true) : - LineEditSetting(rw) { }; -}; - -class TransientComboBox: public ComboBoxSetting, public TransientSetting { - public: - TransientComboBox(bool rw = true) : - ComboBoxSetting(rw) { }; -}; - -class TransientLabel: public LabelSetting, public TransientSetting { - public: - TransientLabel() { }; -}; - class MythDbSettings1: public VerticalConfigurationGroup { public: MythDbSettings1(); @@ -46,12 +12,12 @@ void save(); protected: - TransientLabel *info; - TransientLineEdit *dbHostName; - TransientLineEdit *dbName; - TransientLineEdit *dbUserName; - TransientLineEdit *dbPassword; - TransientComboBox *dbType; + TransLabelSetting *info; + TransLineEditSetting *dbHostName; + TransLineEditSetting *dbName; + TransLineEditSetting *dbUserName; + TransLineEditSetting *dbPassword; + TransComboBoxSetting *dbType; }; class MythDbSettings2: public VerticalConfigurationGroup { @@ -62,12 +28,12 @@ void save(); protected: - TransientCheckBox *localEnabled; - TransientLineEdit *localHostName; - TransientCheckBox *wolEnabled; - TransientSpinBox *wolReconnect; - TransientSpinBox *wolRetry; - TransientLineEdit *wolCommand; + TransCheckBoxSetting *localEnabled; + TransLineEditSetting *localHostName; + TransCheckBoxSetting *wolEnabled; + TransSpinBoxSetting *wolReconnect; + TransSpinBoxSetting *wolRetry; + TransLineEditSetting *wolCommand; }; @@ -115,7 +81,7 @@ setLabel(QObject::tr("Database Configuration") + " 1/2"); setUseLabel(false); - info = new TransientLabel(); + info = new TransLabelSetting(); MSqlQuery query(MSqlQuery::InitCon()); if (query.isConnected()) @@ -127,34 +93,33 @@ "below.")); addChild(info); - dbHostName = new TransientLineEdit(true); + dbHostName = new TransLineEditSetting(true); dbHostName->setLabel(QObject::tr("Host name")); dbHostName->setHelpText(QObject::tr("The host name or IP address of " "the machine hosting the database. " "This information is required.")); addChild(dbHostName); - - dbName = new TransientLineEdit(true); + dbName = new TransLineEditSetting(true); dbName->setLabel(QObject::tr("Database")); dbName->setHelpText(QObject::tr("The name of the database. " "This information is required.")); addChild(dbName); - dbUserName = new TransientLineEdit(true); + dbUserName = new TransLineEditSetting(true); dbUserName->setLabel(QObject::tr("User")); dbUserName->setHelpText(QObject::tr("The user name to use while " "connecting to the database. " "This information is required.")); addChild(dbUserName); - dbPassword = new TransientLineEdit(true); + dbPassword = new TransLineEditSetting(true); dbPassword->setLabel(QObject::tr("Password")); dbPassword->setHelpText(QObject::tr("The password to use while " "connecting to the database. " "This information is required.")); addChild(dbPassword); - dbType = new TransientComboBox(false); + dbType = new TransComboBoxSetting(false); dbType->setLabel(QObject::tr("Database type")); dbType->addSelection(QObject::tr("MySQL"), "QMYSQL3"); //dbType->addSelection(QObject::tr("PostgreSQL"), "QPSQL7"); @@ -171,7 +136,7 @@ setLabel(QObject::tr("Database Configuration") + " 2/2"); setUseLabel(false); - localEnabled = new TransientCheckBox(); + localEnabled = new TransCheckBoxSetting(); localEnabled->setLabel(QObject::tr("Use custom identifier for frontend " "preferences")); localEnabled->setHelpText(QObject::tr("If this frontend's host name " @@ -183,7 +148,7 @@ "be used to save preferences in " "the database.")); - localHostName = new TransientLineEdit(true); + localHostName = new TransLineEditSetting(true); localHostName->setLabel(QObject::tr("Custom identifier")); localHostName->setHelpText(QObject::tr("An identifier to use while " "saving the settings for this " @@ -197,24 +162,24 @@ new LocalHostNameSettings(localEnabled, group1); addChild(sub3); - wolEnabled = new TransientCheckBox(); + wolEnabled = new TransCheckBoxSetting(); wolEnabled->setLabel(QObject::tr("Use Wake-On-LAN to wake database")); wolEnabled->setHelpText(QObject::tr("If checked, the frontend will use " "Wake-On-LAN parameters to " "reconnect to the database server.")); - wolReconnect = new TransientSpinBox(0, 60, 1, true); + wolReconnect = new TransSpinBoxSetting(0, 60, 1, true); wolReconnect->setLabel(QObject::tr("Reconnect time")); wolReconnect->setHelpText(QObject::tr("The time in seconds to wait for " "the server to wake up.")); - wolRetry = new TransientSpinBox(1, 10, 1, true); + wolRetry = new TransSpinBoxSetting(1, 10, 1, true); wolRetry->setLabel(QObject::tr("Retry attempts")); wolRetry->setHelpText(QObject::tr("The number of retries to wake the " "server before the frontend gives " "up.")); - wolCommand = new TransientLineEdit(true); + wolCommand = new TransLineEditSetting(true); wolCommand->setLabel(QObject::tr("Wake command")); wolCommand->setHelpText(QObject::tr("The command executed on this " "frontend to wake up the database " diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/DisplayResX.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/DisplayResX.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/DisplayResX.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/DisplayResX.cpp 2007-08-18 17:50:42.000000000 -0500 @@ -120,14 +120,24 @@ } X11L; + Window root = RootWindow(display, DefaultScreen(display)); - XRRScreenConfiguration *cfg = XRRGetScreenInfo(display, root); + + XRRScreenConfiguration *cfg = NULL; + int event_basep = 0, error_basep = 0; + if (XRRQueryExtension(display, &event_basep, &error_basep)) + cfg = XRRGetScreenInfo(display, root); + if (!cfg) { if (display) + { XCloseDisplay(display); + display = NULL; + } cerr<<"DisplaResX: Unable to XRRgetScreenInfo"<request(header); + http->request(header, pData); } void HttpComms::stop() @@ -469,7 +472,119 @@ return res; } +/** + * \brief Static function for performing an http post request to a url. + * + * This is a synchronous function, it will block according to the vars. + */ +QString HttpComms::postHttp(QUrl &url , + QHttpRequestHeader *pAddlHdr , QIODevice *pData, + int timeoutMS , int maxRetries, + int maxRedirects, bool allowGzip, + Credentials *webCred , bool isInQtEventThread) +{ + int redirectCount = 0; + int timeoutCount = 0; + QString res = ""; + HttpComms *httpGrabber = NULL; + QString hostname = ""; + + QHttpRequestHeader header( "POST", url.encodedPathAndQuery()); + QString userAgent = "Mozilla/9.876 (X11; U; Linux 2.2.12-20 i686, en) " + "Gecko/25250101 Netscape/5.432b1"; + + header.setValue("Host", url.host()); + header.setValue("User-Agent", userAgent); + + if (allowGzip) + header.setValue( "Accept-Encoding", "gzip"); + + // Merge any Additional Headers passed by caller. + + if (pAddlHdr) + { + QStringList keys = pAddlHdr->keys(); + + for ( QStringList::Iterator it = keys.begin(); it != keys.end(); ++it ) + header.setValue( *it, pAddlHdr->value( *it )); + } + + while (1) + { + if (hostname == "") + hostname = url.host(); // hold onto original host + if (!url.hasHost()) // can occur on redirects to partial paths + url.setHost(hostname); + + VERBOSE(VB_NETWORK, QString("postHttp: grabbing: %1").arg(url.toString())); + if (httpGrabber != NULL) + delete httpGrabber; + + httpGrabber = new HttpComms; + + if (webCred) + httpGrabber->setCredentials(*webCred, CRED_WEB); + + httpGrabber->request(url, header, timeoutMS, pData ); + + while (!httpGrabber->isDone()) + { + if (isInQtEventThread) + qApp->processEvents(); + usleep(10000); + } + + // Handle timeout + if (httpGrabber->isTimedout()) + { + VERBOSE(VB_NETWORK, QString("timeout for url: %1").arg(url.toString())); + + // Increment the counter and check were not over the limit + if (timeoutCount++ >= maxRetries) + { + VERBOSE(VB_IMPORTANT, QString("Failed to contact server for url: %1").arg(url.toString())); + break; + } + + // Try again + VERBOSE(VB_NETWORK, QString("Attempt # %1/%2 for url: %3") + .arg(timeoutCount + 1) + .arg(maxRetries) + .arg(url.toString())); + + continue; + } + + // Check for redirection + if (!httpGrabber->getRedirectedURL().isEmpty()) + { + VERBOSE(VB_NETWORK, QString("Redirection: %1, count: %2, max: %3") + .arg(httpGrabber->getRedirectedURL().latin1()) + .arg(redirectCount) + .arg(maxRedirects)); + if (redirectCount++ < maxRedirects) + url = QUrl( httpGrabber->getRedirectedURL() ); + + // Try again + timeoutCount = 0; + continue; + } + + res = httpGrabber->getData(); + break; + } + + delete httpGrabber; + + + VERBOSE(VB_NETWORK, QString("Got %1 bytes from url: '%2'") + .arg(res.length()) + .arg(url.toString())); + VERBOSE(VB_NETWORK, res); + + return res; +} bool HttpComms::createDigestAuth ( bool isForProxy, const QString& authStr, QHttpRequestHeader* request) { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/httpcomms.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/httpcomms.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/httpcomms.h 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/httpcomms.h 2007-08-23 11:09:51.000000000 -0500 @@ -59,9 +59,18 @@ int maxRetries = 3, int maxRedirects = 3, bool allowGzip = false, Credentials* webCred = NULL); + static QString postHttp(QUrl &url, + QHttpRequestHeader *pAddlHdr = NULL, + QIODevice *pData = NULL, + int timeoutMS = 10000, + int maxRetries = 3, + int maxRedirects = 3, + bool allowGzip = false, + Credentials *webCred = NULL, + bool isInQtEventThread = true ); void request(QUrl &url, int timeoutms = -1, bool allowGzip = false); - void request(QUrl &url, QHttpRequestHeader &header, int timeoutms = -1); + void request(QUrl &url, QHttpRequestHeader &header, int timeoutms = -1, QIODevice *pData = NULL); void setCookie( const QString& cookie ) { m_cookie = cookie; } const QString& getCookie() const { return m_cookie; } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/jsmenu.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/jsmenu.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/jsmenu.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/jsmenu.cpp 2007-06-03 20:07:01.000000000 -0500 @@ -73,13 +73,13 @@ if (axes) { - free(axes); - buttons = NULL; + delete [] axes; + axes = NULL; } if (buttons) { - free(buttons); + delete [] buttons; buttons = NULL; } } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcdrom.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcdrom.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcdrom.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcdrom.cpp 2007-04-03 01:26:40.000000000 -0500 @@ -70,12 +70,12 @@ m_Status = MEDIASTAT_MOUNTED; DetectPath.sprintf("%s%s", (const char*)m_MountPath, PATHTO_DVD_DETECT); - VERBOSE(VB_IMPORTANT, QString("Looking for: '%1'").arg(DetectPath)); + VERBOSE(VB_MEDIA, QString("Looking for: '%1'").arg(DetectPath)); struct stat sbuf; if (stat(DetectPath, &sbuf) == 0) { - VERBOSE(VB_GENERAL, "Probable DVD detected."); + VERBOSE(VB_MEDIA, "Probable DVD detected."); m_MediaType = MEDIATYPE_DVD; // HACK make it possible to eject a DVD by unmounting it performMountCmd(false); @@ -83,14 +83,14 @@ } DetectPath.sprintf("%s%s", (const char*)m_MountPath, PATHTO_VCD_DETECT); - VERBOSE(VB_IMPORTANT, QString("Looking for: '%1'").arg(DetectPath)); + VERBOSE(VB_MEDIA, QString("Looking for: '%1'").arg(DetectPath)); DetectPath2.sprintf("%s%s", (const char*)m_MountPath, PATHTO_SVCD_DETECT); - VERBOSE(VB_IMPORTANT, QString("Looking for: '%1'").arg(DetectPath2)); + VERBOSE(VB_MEDIA, QString("Looking for: '%1'").arg(DetectPath2)); if (stat(DetectPath, &sbuf) == 0 || stat(DetectPath2, &sbuf) == 0) { - VERBOSE(VB_GENERAL, "Probable VCD/SVCD detected."); + VERBOSE(VB_MEDIA, "Probable VCD/SVCD detected."); m_MediaType = MEDIATYPE_VCD; // HACK make it possible to eject a VCD/SVCD by unmounting it performMountCmd(false); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcdrom-linux.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcdrom-linux.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcdrom-linux.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcdrom-linux.cpp 2007-04-03 01:26:40.000000000 -0500 @@ -10,12 +10,24 @@ MediaError MythCDROMLinux::eject(bool open_close) { + if (!isDeviceOpen()) + openDevice(); + if (open_close) return (ioctl(m_DeviceHandle, CDROMEJECT) == 0) ? MEDIAERR_OK : MEDIAERR_FAILED; else - return (ioctl(m_DeviceHandle, CDROMCLOSETRAY) == 0) ? MEDIAERR_OK - : MEDIAERR_FAILED; + { + // If the tray is empty, this will fail (Input/Output error) + ioctl(m_DeviceHandle, CDROMCLOSETRAY); + + // This allows us to catch any drives that the OS has problems + // detecting the status of (some always report OPEN when empty) + if (ioctl(m_DeviceHandle, CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN) + return MEDIAERR_FAILED; + else + return MEDIAERR_OK; + } } bool MythCDROMLinux::mediaChanged() @@ -39,18 +51,14 @@ //cout << "Device is not open - "; if (!openDevice()) { - //cout << "failed to open device - "; + //VERBOSE(VB_MEDIA, "MythCDROMLinux::testMedia - failed to open " + // + getDevicePath() + ENO); if (errno == EBUSY) - { - //cout << "errno == EBUSY" << endl; return isMounted(true) ? MEDIAERR_OK : MEDIAERR_FAILED; - } else - { return MEDIAERR_FAILED; - } } - //cout << "Opened it - "; + //VERBOSE(VB_MEDIA, "MythCDROMLinux::testMedia - Opened device"); OpenedHere = true; } @@ -60,8 +68,16 @@ // Be nice and close the device if we opened it, otherwise it might be locked when the user doesn't want it to be. if (OpenedHere) closeDevice(); - //cout << "Stat == " << Stat << endl; - return (Stat >= 0) ? MEDIAERR_OK : MEDIAERR_FAILED; + + if (Stat == -1) + { + VERBOSE(VB_MEDIA, + "MythCDROMLinux::testMedia - Failed to get drive status of " + + getDevicePath() + ENO); + return MEDIAERR_FAILED; + } + + return MEDIAERR_OK; } MediaStatus MythCDROMLinux::checkMedia() @@ -70,25 +86,22 @@ if (testMedia() != MEDIAERR_OK) { - //cout << "MythCDROMLinux::checkMedia - "; - //cout << "Test Media result != MEDIAERR_OK" << endl; m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_UNKNOWN, OpenedHere); } - //cout << "MythCDROMLinux::checkMedia - "; // If it's not already open we need to at least TRY to open it for most of these checks to work. if (!isDeviceOpen()) OpenedHere = openDevice(); if (isDeviceOpen()) { - //cout << "device is open - "; + //VERBOSE(VB_MEDIA, "MythCDROMLinux::checkMedia - Device is open..."); int ret = ioctl(m_DeviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT); switch (ret) { case CDS_DISC_OK: - //cout << "disk ok - "; + VERBOSE(VB_MEDIA, getDevicePath() + " Disk OK..."); // If the disc is ok and we already know it's mediatype // returns MOUNTED. if (isMounted(true) && m_MediaType != MEDIATYPE_UNKNOWN) @@ -96,43 +109,51 @@ // If the disk is ok but not yet mounted we'll test it further down after this switch exits. break; case CDS_TRAY_OPEN: - case CDS_NO_DISC: - //cout << "Tray open or no disc" << endl; + VERBOSE(VB_MEDIA, getDevicePath() + " Tray open"); m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_OPEN, OpenedHere); break; + case CDS_NO_DISC: + VERBOSE(VB_MEDIA, getDevicePath() + " No disc"); + m_MediaType = MEDIATYPE_UNKNOWN; + return setStatus(MEDIASTAT_NODISK, OpenedHere); + break; case CDS_NO_INFO: case CDS_DRIVE_NOT_READY: - //cout << "No info or drive not ready" << endl; + VERBOSE(VB_MEDIA, getDevicePath() + + " No info or drive not ready"); m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_UNKNOWN, OpenedHere); default: - //cout << "unknown result from ioctl (" << ret << ")" << endl; + VERBOSE(VB_IMPORTANT, "Failed to get drive status of " + + getDevicePath() + " : " + ENO); m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_UNKNOWN, OpenedHere); } if (mediaChanged()) { - //cout << "media changed - "; + VERBOSE(VB_MEDIA, getDevicePath() + " Media changed"); // Regardless of the actual status lie here and say // it's open for now, so we can cover the case of a missed open. return setStatus(MEDIASTAT_OPEN, OpenedHere); } else { - //cout << "media unchanged - "; + //VERBOSE(VB_MEDIA, getDevicePath() + " Media unchanged..."); if ((m_Status == MEDIASTAT_OPEN) || (m_Status == MEDIASTAT_UNKNOWN)) { - //cout << "Current status == " << MythMediaDevice::MediaStatusStrings[m_Status] << endl; - int type = ioctl(m_DeviceHandle, CDROM_DISC_STATUS, CDSL_CURRENT); + VERBOSE(VB_MEDIA, getDevicePath() + " Current status " + + MythMediaDevice::MediaStatusStrings[m_Status]); + int type = ioctl(m_DeviceHandle, + CDROM_DISC_STATUS, CDSL_CURRENT); switch (type) { case CDS_DATA_1: case CDS_DATA_2: m_MediaType = MEDIATYPE_DATA; - //cout << "found a data disk" << endl; + VERBOSE(VB_MEDIA, "Found a data disk"); //grab information from iso9660 (& udf) struct iso_primary_descriptor buf; lseek(this->m_DeviceHandle,(off_t) 2048*16,SEEK_SET); @@ -143,10 +164,12 @@ .arg(this->m_VolumeID) .arg(QString(buf.creation_date).left(16)); - // attempt to mount the disc // the base class's onDeviceMounted will do fine // grained detection of the type of data on this disc - mount(); + if (isMounted(true)) + onDeviceMounted(); + else + mount(); // onDeviceMounted() called as side-effect if (isMounted(true)) { @@ -166,13 +189,13 @@ return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere); break; case CDS_AUDIO: - //cout << "found an audio disk" << endl; + VERBOSE(VB_MEDIA, "found an audio disk"); m_MediaType = MEDIATYPE_AUDIO; return setStatus(MEDIASTAT_USEABLE, OpenedHere); break; case CDS_MIXED: m_MediaType = MEDIATYPE_MIXED; - //cout << "found a mixed CD" << endl; + VERBOSE(VB_MEDIA, "found a mixed CD"); // Note: Mixed mode CDs require an explixit mount call since we'll usually want the audio portion. // undefine ASSUME_WANT_AUDIO to change this behavior. #ifdef ASSUME_WANT_AUDIO @@ -196,13 +219,13 @@ break; case CDS_NO_INFO: case CDS_NO_DISC: - //cout << "found no disk" << endl; + VERBOSE(VB_MEDIA, "found no disk"); m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_UNKNOWN, OpenedHere); break; default: - //cout << "found unknown disk type" << endl; - fprintf(stderr, "Unknown data type: %d\n", type); + VERBOSE(VB_MEDIA, "found unknown disk type: " + + QString().setNum(type)); m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_UNKNOWN, OpenedHere); } @@ -210,7 +233,9 @@ else if (m_Status == MEDIASTAT_MOUNTED || m_Status == MEDIASTAT_NOTMOUNTED) { - //cout << "current status == " << MythMediaDevice::MediaStatusStrings[m_Status] << " setting status to not mounted - "; + VERBOSE(VB_MEDIA, QString("Current status == ") + + MythMediaDevice::MediaStatusStrings[m_Status]); + VERBOSE(VB_MEDIA, "Setting status to not mounted?"); if (isMounted(true)) setStatus(MEDIASTAT_MOUNTED, OpenedHere); else @@ -223,7 +248,7 @@ } // isDeviceOpen(); else { - //cout << "device not open returning unknown" << endl; + VERBOSE(VB_MEDIA, "Device not open - returning UNKNOWN"); m_MediaType = MEDIATYPE_UNKNOWN; return setStatus(MEDIASTAT_UNKNOWN, OpenedHere); } @@ -231,7 +256,8 @@ if (OpenedHere) closeDevice(); - //cout << "returning " << MythMediaDevice::MediaStatusStrings[m_Status] << endl; + //VERBOSE(VB_MEDIA, QString("Returning ") + // + MythMediaDevice::MediaStatusStrings[m_Status]); return m_Status; } @@ -248,7 +274,7 @@ { if (isDeviceOpen() || openDevice()) { - //cout << "Unlocking CDROM door" << endl; + //VERBOSE(VB_MEDIA, "MythCDROMLinux::unlock - Unlocking CDROM door"); ioctl(m_DeviceHandle, CDROM_LOCKDOOR, 0); } else diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcontext.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcontext.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcontext.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcontext.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -153,6 +153,38 @@ return QString("%1 (%2)").arg(strerror(errnum)).arg(errnum); } +// Global function to retrieve InstallPrefix path + +void GetInstallPrefixPath( QString &sInstallPrefix, QString &sInstallLibDir ) +{ + char *tmp_installprefix = getenv("MYTHTVDIR"); + if (tmp_installprefix) + sInstallPrefix = tmp_installprefix; + +#if QT_VERSION >= 0x030200 + QDir prefixDir = qApp->applicationDirPath(); +#else + QString appPath = QFileInfo(qApp->argv()[0]).absFilePath(); + QDir prefixDir(appPath.left(appPath.findRev("/"))); +#endif + + if (QDir( sInstallPrefix ).isRelative()) + { + // If the PREFIX is relative, evaluate it relative to our + // executable directory. This can be fragile on Unix, so + // use relative PREFIX values with care. + prefixDir.cd( sInstallPrefix ); + sInstallPrefix = prefixDir.canonicalPath(); + } + else if (prefixDir.path().contains(".app/Contents/MacOS")) + { + prefixDir.cd("../Resources"); + if (QDir(prefixDir.canonicalPath() + "/share").exists()) + sInstallPrefix = prefixDir.canonicalPath(); + if (QDir(prefixDir.canonicalPath() + "/lib").exists()) + sInstallLibDir = prefixDir.canonicalPath() + "/lib"; + } +} class MythContextPrivate { @@ -294,33 +326,9 @@ m_priv_mutex(new QMutex(true)), useSettingsCache(false) { - char *tmp_installprefix = getenv("MYTHTVDIR"); - if (tmp_installprefix) - m_installprefix = tmp_installprefix; -#if QT_VERSION >= 0x030200 - QDir prefixDir = qApp->applicationDirPath(); -#else - QString appPath = QFileInfo(qApp->argv()[0]).absFilePath(); - QDir prefixDir(appPath.left(appPath.findRev("/"))); -#endif + GetInstallPrefixPath( m_installprefix, m_installlibdir ); - if (QDir(m_installprefix).isRelative()) - { - // If the PREFIX is relative, evaluate it relative to our - // executable directory. This can be fragile on Unix, so - // use relative PREFIX values with care. - prefixDir.cd(m_installprefix); - m_installprefix = prefixDir.canonicalPath(); - } - else if (prefixDir.path().contains(".app/Contents/MacOS")) - { - prefixDir.cd("../Resources"); - if (QDir(prefixDir.canonicalPath() + "/share").exists()) - m_installprefix = prefixDir.canonicalPath(); - if (QDir(prefixDir.canonicalPath() + "/lib").exists()) - m_installlibdir = prefixDir.canonicalPath() + "/lib"; - } VERBOSE(VB_IMPORTANT, QString("Using runtime prefix = %1") .arg(m_installprefix)); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcontext.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcontext.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythcontext.h 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythcontext.h 2007-08-23 11:09:51.000000000 -0500 @@ -99,6 +99,8 @@ "upnp debugging messages") \ F(VB_SOCKET, 0x00100000, "socket", 1, \ "socket debugging messages") \ + F(VB_MEDIA, 0x00800000, "media", 1, \ + "Media Manager debugging messages") \ F(VB_TIMESTAMP, 0x80000000, "timestamp", 1, \ "Conditional data driven messages") \ F(VB_NONE, 0x00000000, "none", 0, \ @@ -198,6 +200,8 @@ /// Verbose helper function for ENO macro QString safe_eno_to_string(int errnum); +void GetInstallPrefixPath( QString &sInstallPrefix, QString &sInstallLibDir ); + /** \class MythPrivRequest * \brief Container for requests that require privledge escalation. * @@ -222,7 +226,7 @@ /// Update this whenever the plug-in API changes. /// Including changes in the libmythtv class methods used by plug-ins. -#define MYTH_BINARY_VERSION "0.20.20060828-3" +#define MYTH_BINARY_VERSION "0.20.20070821-1" /** \brief Increment this whenever the MythTV network protocol changes. * diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythhdd.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythhdd.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythhdd.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythhdd.cpp 2007-02-14 18:10:44.000000000 -0600 @@ -33,6 +33,10 @@ { if (isMounted(true)) { + // A lazy way to present volume name for the user to eject. + // Hotplug devices are usually something like /media/VOLUME + m_VolumeID = m_MountPath; + // device is mounted, trigger event return setStatus(MEDIASTAT_MOUNTED); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythmedia.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythmedia.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythmedia.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythmedia.cpp 2007-04-03 01:26:40.000000000 -0500 @@ -31,6 +31,7 @@ "MEDIASTAT_UNKNOWN", "MEDIASTAT_UNPLUGGED", "MEDIASTAT_OPEN", + "MEDIASTAT_NODISK", "MEDIASTAT_USEABLE", "MEDIASTAT_NOTMOUNTED", "MEDIASTAT_MOUNTED" @@ -117,7 +118,7 @@ .arg((DoMount) ? PATHTO_MOUNT : PATHTO_UNMOUNT) .arg(m_DevicePath); - VERBOSE(VB_IMPORTANT, QString("Executing '%1'").arg(MountCommand)); + VERBOSE(VB_MEDIA, QString("Executing '%1'").arg(MountCommand)); if (0 == myth_system(MountCommand)) { if (DoMount) @@ -127,7 +128,8 @@ isMounted(true); m_Status = MEDIASTAT_MOUNTED; onDeviceMounted(); - VERBOSE(VB_IMPORTANT, "m_MediaType: "<> DeviceName >> MountPoint; - //cout << "Found Device: " << DeviceName - // << " Mountpoint: " << MountPoint << endl; + //VERBOSE(VB_MEDIA, "Found Device: " << DeviceName + // << " Mountpoint: " << MountPoint); // Skip the rest of the line line = stream.readLine(); @@ -357,6 +360,7 @@ // the disk is not / should not be mounted. case MEDIASTAT_ERROR: case MEDIASTAT_OPEN: + case MEDIASTAT_NODISK: case MEDIASTAT_NOTMOUNTED: if (isMounted(true)) unmount(); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythmedia.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythmedia.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythmedia.h 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythmedia.h 2007-03-20 17:48:33.000000000 -0500 @@ -9,7 +9,8 @@ MEDIASTAT_ERROR, MEDIASTAT_UNKNOWN, MEDIASTAT_UNPLUGGED, - MEDIASTAT_OPEN, + MEDIASTAT_OPEN, /**< CD/DVD tray open. Meaningless for other types */ + MEDIASTAT_NODISK, /**< CD/DVD tray closed, SCSI drive unformatted? */ MEDIASTAT_USEABLE, MEDIASTAT_NOTMOUNTED, MEDIASTAT_MOUNTED diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythmediamonitor.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythmediamonitor.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/mythmediamonitor.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/mythmediamonitor.cpp 2007-04-13 02:03:31.000000000 -0500 @@ -157,6 +157,7 @@ if (!selected) return; + bool doEject = false; int status = selected->getStatus(); QString dev = selected->getVolumeID(); @@ -165,10 +166,21 @@ if (MEDIASTAT_OPEN == status) { - selected->eject(false); + VERBOSE(VB_MEDIA, + QString("Disk %1's tray is OPEN. Closing tray").arg(dev)); + + if (selected->eject(false)) + { + QString msg = "Unable to open or close the empty drive %1.\n\n"; + msg += "You may have to use the eject button under its tray."; + MythPopupBox::showOkPopup(gContext->GetMainWindow(), + "eject close-tray fail", + tr(msg).arg(dev)); + } } else if (MEDIASTAT_MOUNTED == status) { + VERBOSE(VB_MEDIA, QString("Disk %1 is mounted? Unmounting").arg(dev)); selected->unmount(); if (selected->isMounted(true)) @@ -177,9 +189,16 @@ "eject unmount fail", tr("Failed to unmount %1").arg(dev)); } + else + doEject = true; } else + doEject = true; + + if (doEject) { + VERBOSE(VB_MEDIA, + QString("Unlocking disk %1, then eject()ing").arg(dev)); selected->unlock(); if (selected->eject() == MEDIAERR_UNSUPPORTED) @@ -340,14 +359,18 @@ line = stream.readLine(); if (line.startsWith("drive name:")) { - QString s = line.section('\t', 2, 2); - l.append(s); + QStringList devs = QStringList::split('\t', line); + + devs.pop_front(); // Remove 'drive name:' field + l += devs; } } file.close(); } #endif // linux + VERBOSE(VB_MEDIA, "MediaMonitorUnix::GetCDROMBlockDevices() returning " + + l.join(", ")); return l; } @@ -585,6 +608,11 @@ if (*pit == "." || *pit == "..") continue; + // skip some sysfs dirs that are _not_ sub-partitions + if (*pit == "device" || *pit == "holders" || *pit == "queue" + || *pit == "slaves" || *pit == "subsystem") + continue; + found_partitions |= FindPartitions(sysfs.absFilePath(*pit), false); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/settings.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/settings.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/settings.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/settings.cpp 2007-08-20 16:10:34.000000000 -0500 @@ -494,7 +494,8 @@ connect(edit, SIGNAL(changeHelpText(QString)), cg, SIGNAL(changeHelpText(QString))); - edit->setRW(rw); + setRW(rw); + SetPasswordEcho(password_echo); return widget; } @@ -519,6 +520,13 @@ } } +void LineEditSetting::SetPasswordEcho(bool b) +{ + password_echo = b; + if (edit) + edit->setEchoMode(b ? QLineEdit::Password : QLineEdit::Normal); +} + QWidget* SliderSetting::configWidget(ConfigurationGroup *cg, QWidget* parent, const char* widgetName) { QHBox* widget; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/settings.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/settings.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/settings.h 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/settings.h 2007-08-20 16:10:34.000000000 -0500 @@ -276,7 +276,8 @@ class LineEditSetting: virtual public Setting { protected: - LineEditSetting(bool readwrite = true) : edit(NULL) { rw = readwrite; }; + LineEditSetting(bool readwrite = true) : + edit(NULL), rw(readwrite), password_echo(false) { } public: virtual QWidget* configWidget(ConfigurationGroup *cg, QWidget* parent, const char* widgetName = 0); @@ -292,10 +293,12 @@ virtual void setEnabled(bool b); virtual void setVisible(bool b); + virtual void SetPasswordEcho(bool b); private: MythLineEdit* edit; bool rw; + bool password_echo; }; // TODO: set things up so that setting the value as a string emits @@ -719,11 +722,6 @@ MythPushButton *button; }; -class TransButtonSetting: public ButtonSetting, public TransientStorage { -public: - TransButtonSetting(QString name = "button") : ButtonSetting(name) {} -}; - class ConfigPopupDialogWidget: public MythPopupBox { Q_OBJECT public: @@ -767,16 +765,6 @@ int totalSteps; }; -class TransLabelSetting: public LabelSetting, public TransientStorage { -public: - TransLabelSetting() {}; -}; - -class TransCheckBoxSetting: public CheckBoxSetting, public TransientStorage { -public: - TransCheckBoxSetting() {}; -}; - class HostSetting: public SimpleDBStorage, virtual public Configurable { public: HostSetting(QString name): @@ -802,6 +790,57 @@ virtual QString setClause(MSqlBindings& bindings); }; +/////////////////////////////////////////////////////////////////////////////// + +class TransButtonSetting : + public ButtonSetting, public TransientStorage +{ + public: + TransButtonSetting(QString name = "button") : ButtonSetting(name) { } +}; + +class TransLabelSetting : + public LabelSetting, public TransientStorage +{ + public: + TransLabelSetting() { } +}; + +class TransLineEditSetting : + public LineEditSetting, public TransientStorage +{ + public: + TransLineEditSetting(bool rw = true) : LineEditSetting(rw) { } +}; + +class TransCheckBoxSetting : + public CheckBoxSetting, public TransientStorage +{ + public: + TransCheckBoxSetting() { } +}; + +class TransComboBoxSetting : + public ComboBoxSetting, public TransientStorage +{ + public: + TransComboBoxSetting(bool rw = true, int _step = 1) : + ComboBoxSetting(rw, _step) { } +}; + +class TransSpinBoxSetting : + public SpinBoxSetting, public TransientStorage +{ + public: + TransSpinBoxSetting(int min, int max, int step, + bool allow_single_step = false, + QString special_value_text = "") : + SpinBoxSetting(min, max, step, allow_single_step, + special_value_text) { } +}; + +/////////////////////////////////////////////////////////////////////////////// + class HostSlider: public SliderSetting, public HostSetting { public: HostSlider(const QString &name, int min, int max, int step) : diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/util.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/util.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/util.cpp 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/util.cpp 2007-08-21 13:30:38.000000000 -0500 @@ -28,12 +28,14 @@ #include #include #include +#include // Myth headers #include "mythconfig.h" #include "exitcodes.h" #include "util.h" #include "mythcontext.h" +#include "mythmediamonitor.h" #ifdef CONFIG_DARWIN #include @@ -190,6 +192,8 @@ (void)flags; /* Kill warning */ bool ready_to_lock = gContext && gContext->GetMainWindow(); + + (void) ready_to_lock; // stop warning #ifdef USE_LIRC bool lirc_lock_flag = !(flags & MYTH_SYSTEM_DONT_BLOCK_LIRC); LircEventLock lirc_lock(lirc_lock_flag && ready_to_lock); @@ -401,9 +405,15 @@ (statbuf.f_blocks > 0) && (statbuf.f_bsize > 0)) { - freespace = statbuf.f_bsize * (statbuf.f_bavail >> 10); - total = statbuf.f_bsize * (statbuf.f_blocks >> 10); - used = total - freespace; + total = statbuf.f_blocks; + total *= statbuf.f_bsize; + total = total >> 10; + + freespace = statbuf.f_bavail; + freespace *= statbuf.f_bsize; + freespace = freespace >> 10; + + used = total - freespace; } return freespace; @@ -417,8 +427,8 @@ */ bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM) { - size_t MB = (1024*1024); #ifdef __linux__ + size_t MB = (1024*1024); struct sysinfo sinfo; if (sysinfo(&sinfo) == -1) { @@ -472,3 +482,135 @@ return true; } + +/** + * \brief Eject a disk, unmount a drive, open a tray + * + * If the Media Monitor is enabled, we use its fully-featured routine. + * Otherwise, we guess a drive and use a primitive OS-specific command + */ +void myth_eject() +{ + MediaMonitor *mon = MediaMonitor::GetMediaMonitor(); + if (mon) + mon->ChooseAndEjectMedia(); + else + { + VERBOSE(VB_MEDIA, "CD/DVD Monitor isn't enabled."); +#ifdef __linux__ + VERBOSE(VB_MEDIA, "Trying Linux 'eject -T' command"); + myth_system("eject -T"); +#elif defined(CONFIG_DARWIN) + VERBOSE(VB_MEDIA, "Trying 'disktool -e disk1"); + myth_system("disktool -e disk1"); +#endif + } +} + +/** \fn copy(QFile&,QFile&,uint) + * \brief Copies src file to dst file. + * + * If the dst file is open, it must be open for writing. + * If the src file is open, if must be open for reading. + * + * The files will be in the same open or close state after + * this function runs as they were prior to this function being called. + * + * This function does not care if the files are actual files. + * For compatibility with pipes and socket streams the file location + * will not be reset to 0 at the end of this function. If the function + * is succesful the file pointers will be at the end of the copied + * data. + * + * \param dst Destination QFile + * \param src Source QFile + * \param block_size Optional block size in bytes, must be at least 1024, + * otherwise the default of 16 KB will be used. + * \return bytes copied on success, -1 on failure. + */ +long long copy(QFile &dst, QFile &src, uint block_size) +{ + uint buflen = (block_size < 1024) ? (16 * 1024) : block_size; + char *buf = new char[buflen]; + bool odst = false, osrc = false; + + if (!buf) + return -1LL; + + if (!dst.isWritable() && !dst.isOpen()) + odst = dst.open(IO_Raw|IO_WriteOnly|IO_Truncate); + + if (!src.isReadable() && !src.isOpen()) + osrc = src.open(IO_Raw|IO_ReadOnly); + + bool ok = dst.isWritable() && src.isReadable(); + long long total_bytes = 0LL; + while (ok) + { + long long rlen, wlen, off = 0; + rlen = src.readBlock(buf, buflen); + if (rlen<0) + { + VERBOSE(VB_IMPORTANT, "util.cpp:copy: read error"); + ok = false; + break; + } + if (rlen==0) + break; + + total_bytes += (long long) rlen; + + while ((rlen-off>0) && ok) + { + wlen = dst.writeBlock(buf + off, rlen - off); + if (wlen>=0) + off+= wlen; + if (wlen<0) + { + VERBOSE(VB_IMPORTANT, "util.cpp:copy: write error"); + ok = false; + } + } + } + delete[] buf; + + if (odst) + dst.close(); + + if (osrc) + src.close(); + + return (ok) ? total_bytes : -1LL; +} + +QString createTempFile(QString name_template, bool dir) +{ + const char *tmp = name_template.ascii(); + char *ctemplate = strdup(tmp); + int ret = -1; + + if (dir) + { + ret = (mkdtemp(ctemplate)) ? 0 : -1; + } + else + { + ret = mkstemp(ctemplate); + } + + QString tmpFileName(ctemplate); + free(ctemplate); + + if (ret == -1) + { + VERBOSE(VB_IMPORTANT, QString("createTempFile(%1), Error ") + .arg(name_template) + ENO); + return name_template; + } + + if (!dir && (ret >= 0)) + close(ret); + + return tmpFileName; +} + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/util.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/util.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmyth/util.h 2007-01-22 03:08:26.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmyth/util.h 2007-08-21 12:47:53.000000000 -0500 @@ -14,6 +14,7 @@ class QImage; class QPainter; class QFont; +class QFile; class MythTimer { @@ -62,4 +63,11 @@ bool getUptime(time_t &uptime); bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM); +void myth_eject(void); + +long long copy(QFile &dst, QFile &src, uint block_size = 0); +QString createTempFile(QString name_template = "/tmp/mythtv_XXXXXX", + bool dir = false); + + #endif // UTIL_H_ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/AAFilter.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/AAFilter.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/AAFilter.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/AAFilter.cpp 2006-05-23 12:40:36.000000000 -0500 @@ -12,7 +12,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 19:40:36 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 12:40:36 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9996 $ // // $Id: AAFilter.cpp 9996 2006-05-23 17:40:36Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/AAFilter.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/AAFilter.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/AAFilter.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/AAFilter.h 2004-11-13 16:29:45.000000000 -0600 @@ -13,7 +13,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2004-11-13 23:29:45 +0100 (sam, 13 nov 2004) $ +// Last changed : $Date: 2004-11-13 16:29:45 -0600 (Sat, 13 Nov 2004) $ // File revision : $Revision: 4714 $ // // $Id: AAFilter.h 4714 2004-11-13 22:29:45Z ijr $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/BPMDetect.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/BPMDetect.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/BPMDetect.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/BPMDetect.h 2006-05-23 12:40:36.000000000 -0500 @@ -26,7 +26,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 19:40:36 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 12:40:36 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9996 $ // // $Id: BPMDetect.h 9996 2006-05-23 17:40:36Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/cpu_detect.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/cpu_detect.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/cpu_detect.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/cpu_detect.h 2006-03-16 04:00:28.000000000 -0600 @@ -12,7 +12,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-03-16 11:00:28 +0100 (jeu, 16 mar 2006) $ +// Last changed : $Date: 2006-03-16 04:00:28 -0600 (Thu, 16 Mar 2006) $ // File revision : $Revision: 9376 $ // // $Id: cpu_detect.h 9376 2006-03-16 10:00:28Z ijr $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/cpu_detect_x86_gcc.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/cpu_detect_x86_gcc.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/cpu_detect_x86_gcc.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/cpu_detect_x86_gcc.cpp 2006-05-23 12:40:36.000000000 -0500 @@ -12,7 +12,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 19:40:36 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 12:40:36 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9996 $ // // $Id: cpu_detect_x86_gcc.cpp 9996 2006-05-23 17:40:36Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIFOSampleBuffer.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIFOSampleBuffer.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIFOSampleBuffer.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIFOSampleBuffer.cpp 2006-05-23 12:40:36.000000000 -0500 @@ -15,7 +15,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 19:40:36 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 12:40:36 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9996 $ // // $Id: FIFOSampleBuffer.cpp 9996 2006-05-23 17:40:36Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIFOSampleBuffer.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIFOSampleBuffer.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIFOSampleBuffer.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIFOSampleBuffer.h 2006-05-23 12:40:36.000000000 -0500 @@ -15,7 +15,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 19:40:36 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 12:40:36 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9996 $ // // $Id: FIFOSampleBuffer.h 9996 2006-05-23 17:40:36Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIFOSamplePipe.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIFOSamplePipe.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIFOSamplePipe.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIFOSamplePipe.h 2005-05-23 15:39:12.000000000 -0500 @@ -17,7 +17,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2005-05-23 22:39:12 +0200 (lun, 23 mai 2005) $ +// Last changed : $Date: 2005-05-23 15:39:12 -0500 (Mon, 23 May 2005) $ // File revision : $Revision: 6468 $ // // $Id: FIFOSamplePipe.h 6468 2005-05-23 20:39:12Z ijr $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIRFilter.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIRFilter.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIRFilter.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIRFilter.cpp 2006-04-01 00:19:12.000000000 -0600 @@ -11,7 +11,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-04-01 08:19:12 +0200 (sam, 01 avr 2006) $ +// Last changed : $Date: 2006-04-01 00:19:12 -0600 (Sat, 01 Apr 2006) $ // File revision : $Revision: 9588 $ // // $Id: FIRFilter.cpp 9588 2006-04-01 06:19:12Z ijr $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIRFilter.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIRFilter.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/FIRFilter.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/FIRFilter.h 2004-11-13 16:29:45.000000000 -0600 @@ -11,7 +11,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2004-11-13 23:29:45 +0100 (sam, 13 nov 2004) $ +// Last changed : $Date: 2004-11-13 16:29:45 -0600 (Sat, 13 Nov 2004) $ // File revision : $Revision: 4714 $ // // $Id: FIRFilter.h 4714 2004-11-13 22:29:45Z ijr $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/mmx_gcc.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/mmx_gcc.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/mmx_gcc.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/mmx_gcc.cpp 2006-05-23 14:14:24.000000000 -0500 @@ -15,7 +15,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 21:14:24 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 14:14:24 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9998 $ // // $Id: mmx_gcc.cpp 9998 2006-05-23 19:14:24Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/RateTransposer.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/RateTransposer.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/RateTransposer.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/RateTransposer.cpp 2006-05-23 14:14:24.000000000 -0500 @@ -10,7 +10,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 21:14:24 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 14:14:24 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9998 $ // // $Id: RateTransposer.cpp 9998 2006-05-23 19:14:24Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/RateTransposer.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/RateTransposer.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/RateTransposer.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/RateTransposer.h 2004-11-13 16:29:45.000000000 -0600 @@ -14,7 +14,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2004-11-13 23:29:45 +0100 (sam, 13 nov 2004) $ +// Last changed : $Date: 2004-11-13 16:29:45 -0600 (Sat, 13 Nov 2004) $ // File revision : $Revision: 4714 $ // // $Id: RateTransposer.h 4714 2004-11-13 22:29:45Z ijr $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/SoundTouch.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/SoundTouch.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/SoundTouch.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/SoundTouch.cpp 2006-05-24 05:48:15.000000000 -0500 @@ -41,7 +41,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-24 12:48:15 +0200 (mer, 24 mai 2006) $ +// Last changed : $Date: 2006-05-24 05:48:15 -0500 (Wed, 24 May 2006) $ // File revision : $Revision: 10003 $ // // $Id: SoundTouch.cpp 10003 2006-05-24 10:48:15Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/SoundTouch.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/SoundTouch.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/SoundTouch.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/SoundTouch.h 2006-05-23 12:40:36.000000000 -0500 @@ -41,7 +41,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 19:40:36 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 12:40:36 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9996 $ // // $Id: SoundTouch.h 9996 2006-05-23 17:40:36Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/STTypes.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/STTypes.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/STTypes.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/STTypes.h 2006-05-24 09:56:37.000000000 -0500 @@ -8,7 +8,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-24 16:56:37 +0200 (mer, 24 mai 2006) $ +// Last changed : $Date: 2006-05-24 09:56:37 -0500 (Wed, 24 May 2006) $ // File revision : $Revision: 10005 $ // // $Id: STTypes.h 10005 2006-05-24 14:56:37Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/TDStretch.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/TDStretch.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/TDStretch.cpp 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/TDStretch.cpp 2006-05-23 14:14:24.000000000 -0500 @@ -13,7 +13,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 21:14:24 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 14:14:24 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9998 $ // // $Id: TDStretch.cpp 9998 2006-05-23 19:14:24Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/TDStretch.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/TDStretch.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythsoundtouch/TDStretch.h 2007-01-22 03:08:24.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythsoundtouch/TDStretch.h 2006-05-23 14:14:24.000000000 -0500 @@ -13,7 +13,7 @@ /// //////////////////////////////////////////////////////////////////////////////// // -// Last changed : $Date: 2006-05-23 21:14:24 +0200 (mar, 23 mai 2006) $ +// Last changed : $Date: 2006-05-23 14:14:24 -0500 (Tue, 23 May 2006) $ // File revision : $Revision: 9998 $ // // $Id: TDStretch.h 9998 2006-05-23 19:14:24Z danielk $ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/avformatdecoder.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/avformatdecoder.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/avformatdecoder.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/avformatdecoder.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -28,12 +28,12 @@ #ifdef USING_XVMC #include "videoout_xv.h" extern "C" { -#include "libavcodec/xvmc_render.h" +#include "../libavcodec/xvmc_render.h" } #endif // USING_XVMC extern "C" { -#include "libavcodec/liba52/a52.h" +#include "../libavcodec/liba52/a52.h" #include "../libmythmpeg2/mpeg2.h" #include "ivtv_myth.h" } @@ -1277,7 +1277,14 @@ { case CODEC_TYPE_VIDEO: { - assert(enc->codec_id); + //assert(enc->codec_id); + if (!enc->codec_id) + { + VERBOSE(VB_IMPORTANT, + LOC + QString("Stream #%1 has an unknown video " + "codec id, skipping.").arg(i)); + continue; + } // HACK -- begin // ffmpeg is unable to compute H.264 bitrates in mpegts? @@ -1506,7 +1513,6 @@ { if (tracks[kTrackTypeAudio].size() > 1) { - qBubbleSort(tracks[kTrackTypeAudio]); int trackNo = ringBuffer->DVD()->GetTrack(kTrackTypeAudio); if (trackNo >= (int)GetTrackCount(kTrackTypeAudio)) trackNo = GetTrackCount(kTrackTypeAudio) - 1; @@ -2252,7 +2258,7 @@ if (ringBuffer->isDVD()) lang_key = ringBuffer->DVD()->GetSubtitleLanguage(trackNo); - return QObject::tr("Subtitle") + QString("%1: %2") + return QObject::tr("Subtitle") + QString(" %1: %2") .arg(trackNo + 1).arg(iso639_key_toName(lang_key)); } else diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/cardutil.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/cardutil.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/cardutil.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/cardutil.cpp 2007-04-19 16:20:17.000000000 -0500 @@ -51,7 +51,7 @@ #ifdef USING_DVB QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device); - int fd_frontend = open(dvbdev.ascii(), O_RDWR | O_NONBLOCK); + int fd_frontend = open(dvbdev.ascii(), O_RDONLY | O_NONBLOCK); if (fd_frontend < 0) return "ERROR_OPEN"; @@ -735,6 +735,7 @@ { int rcardid = (parentid) ? parentid : cardid; QStringList inputs; + bool is_dtv = !IsEncoder(cardtype) && !IsUnscanable(cardtype); if (("FIREWIRE" == cardtype) || ("FREEBOX" == cardtype) || @@ -753,7 +754,7 @@ QStringList::iterator it = inputs.begin(); for (; it != inputs.end(); ++it) { - CardInput* cardinput = new CardInput(false, cardid); + CardInput* cardinput = new CardInput(is_dtv, false, cardid); cardinput->loadByInput(rcardid, (*it)); cardinput->SetChildCardID((parentid) ? cardid : 0); inputLabels.push_back( @@ -774,7 +775,7 @@ InputNames::const_iterator it; for (it = list.begin(); it != list.end(); ++it) { - CardInput *cardinput = new CardInput(true, rcardid); + CardInput *cardinput = new CardInput(is_dtv, true, rcardid); cardinput->loadByInput(rcardid, *it); cardinput->SetChildCardID(parentid ? cardid : 0); inputLabels.push_back( @@ -786,7 +787,7 @@ // plus add one "new" input if (needs_conf) { - CardInput *newcard = new CardInput(true, rcardid); + CardInput *newcard = new CardInput(is_dtv, true, rcardid); QString newname = QString("DVBInput #%1").arg(list.size() + 1); newcard->loadByInput(rcardid, newname); newcard->SetChildCardID((parentid) ? cardid : 0); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/datadirect.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/datadirect.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/datadirect.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/datadirect.cpp 2007-08-23 17:39:10.000000000 -0500 @@ -6,6 +6,7 @@ // Qt headers #include +#include #include #include #include @@ -25,6 +26,7 @@ #define SHOW_WGET_OUTPUT 0 #define LOC QString("DataDirect: ") +#define LOC_WARN QString("DataDirect, Warning: ") #define LOC_ERR QString("DataDirect, Error: ") static QMutex lineup_type_lock; @@ -41,7 +43,7 @@ static QString process_dd_station(uint sourceid, QString chan_major, QString chan_minor, QString &tvformat, uint &freqid); -static void update_channel_basic(uint sourceid, bool insert, +static uint update_channel_basic(uint sourceid, bool insert, QString xmltvid, QString callsign, QString name, uint freqid, QString chan_major, QString chan_minor); @@ -66,9 +68,10 @@ DataDirectSchedule::DataDirectSchedule() : programid(""), stationid(""), time(QDateTime()), duration(QTime()), - repeat(false), stereo(false), - subtitled(false), hdtv(false), - closecaptioned(false), tvrating(""), + repeat(false), isnew(false), + stereo(false), subtitled(false), + hdtv(false), closecaptioned(false), + tvrating(""), partnumber(0), parttotal(0) { } @@ -165,7 +168,14 @@ curr_schedule.duration = QTime(durstr.mid(2, 2).toInt(), durstr.mid(5, 2).toInt(), 0, 0); - curr_schedule.repeat = (pxmlatts.value("repeat") == "true"); + QString isrepeat = pxmlatts.value("repeat"); + curr_schedule.repeat = (isrepeat == "true"); + saw_repeat |= !isrepeat.isEmpty(); + + QString isnew = pxmlatts.value("new"); + curr_schedule.isnew = (isnew == "true"); + saw_new |= !isnew.isEmpty(); + curr_schedule.stereo = (pxmlatts.value("stereo") == "true"); curr_schedule.subtitled = (pxmlatts.value("subtitled") == "true"); curr_schedule.hdtv = (pxmlatts.value("hdtv") == "true"); @@ -276,13 +286,13 @@ " duration, isrepeat, stereo, " " subtitled, hdtv, closecaptioned, " " tvrating, partnumber, parttotal, " - " endtime) " + " endtime, isnew) " "VALUES " " (:PROGRAMID, :STATIONID, :TIME, " " :DURATION, :ISREPEAT, :STEREO, " " :SUBTITLED, :HDTV, :CAPTIONED, " " :TVRATING, :PARTNUMBER, :PARTTOTAL, " - " :ENDTIME)"); + " :ENDTIME, :ISNEW)"); query.bindValue(":PROGRAMID", curr_schedule.programid); query.bindValue(":STATIONID", curr_schedule.stationid); @@ -297,6 +307,7 @@ query.bindValue(":PARTNUMBER", curr_schedule.partnumber); query.bindValue(":PARTTOTAL", curr_schedule.parttotal); query.bindValue(":ENDTIME", endtime); + query.bindValue(":ISNEW", curr_schedule.isnew); if (!query.exec()) MythContext::DBError("Inserting into dd_schedule", query); @@ -409,6 +420,16 @@ bool DDStructureParser::endDocument() { + MSqlQuery query(MSqlQuery::DDCon()); + query.prepare( + "INSERT INTO dd_state (sawrepeat, sawnew) " + "VALUES (:SAWREPEAT, :SAWNEW)"); + query.bindValue(":SAWREPEAT", saw_repeat); + query.bindValue(":SAWNEW", saw_new); + + if (!query.exec()) + MythContext::DBError("Inserting into dd_state", query); + return true; } @@ -511,30 +532,12 @@ return true; } -static QString makeTempFile(QString name_template) -{ - const char *tmp = name_template.ascii(); - char *ctemplate = strdup(tmp); - int ret = mkstemp(ctemplate); - QString tmpFileName(ctemplate); - free(ctemplate); - - if (ret == -1) - { - VERBOSE(VB_IMPORTANT, LOC_ERR + "Creating temp file from " + - QString("template '%1'").arg(name_template) + ENO); - return name_template; - } - close(ret); - - return tmpFileName; -} - DataDirectProcessor::DataDirectProcessor(uint lp, QString user, QString pass) : listings_provider(lp % DD_PROVIDER_COUNT), - userid(user), password(pass), - inputfilename(""), tmpPostFile(""), - tmpResultFile(""), cookieFile(""), + userid(user), password(pass), + tmpDir("/tmp"), cachedata(false), + inputfilename(""), tmpPostFile(QString::null), + tmpResultFile(QString::null), cookieFile(QString::null), cookieFileDT() { DataDirectURLs urls0( @@ -542,19 +545,47 @@ "http://datadirect.webservices.zap2it.com/tvlistings/xtvdService", "http://labs.zap2it.com", "/ztvws/ztvws_login/1,1059,TMS01-1,00.html"); + DataDirectURLs urls1( + "Schedules Direct", + "http://webservices.schedulesdirect.tmsdatadirect.com" + "/schedulesdirect/tvlistings/xtvdService", + "http://schedulesdirect.org", + "/login/index.php"); providers.push_back(urls0); - - QString tmpDir = "/tmp"; - tmpPostFile = makeTempFile(tmpDir + "/mythtv_post_XXXXXX"); - tmpResultFile = makeTempFile(tmpDir + "/mythtv_result_XXXXXX"); - cookieFile = makeTempFile(tmpDir + "/mythtv_cookies_XXXXXX"); + providers.push_back(urls1); } DataDirectProcessor::~DataDirectProcessor() { - unlink(tmpPostFile.ascii()); - unlink(tmpResultFile.ascii()); - unlink(cookieFile.ascii()); + VERBOSE(VB_GENERAL, LOC + "Deleting temporary files"); + + if (!tmpPostFile.isEmpty()) + unlink(tmpPostFile.ascii()); + + if (!tmpResultFile.isEmpty()) + unlink(tmpResultFile.ascii()); + + if (!cookieFile.isEmpty()) + unlink(cookieFile.ascii()); + + QDir d(tmpDir, "mythtv_dd_cache_*", QDir::Name, + QDir::Files | QDir::NoSymLinks); + + for (uint i = 0; i < d.count(); i++) + { + //cout<<"deleting '"<(tmpDir); } void DataDirectProcessor::UpdateStationViewTable(QString lineupid) @@ -585,11 +616,33 @@ void DataDirectProcessor::UpdateProgramViewTable(uint sourceid) { MSqlQuery query(MSqlQuery::DDCon()); - + + query.prepare( + "SELECT sawrepeat, sawnew " + "FROM dd_state"); + + if (!query.exec()) + { + MythContext::DBError("Querying into dd_state", query); + return; + } + + if (!query.next()) + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "UpdateProgramViewTable no dd_state!"); + return; + } + + bool saw_repeat = query.value(0).toUInt(); (void) saw_repeat; + bool saw_new = query.value(1).toUInt(); (void) saw_new; + +// VERBOSE(VB_GENERAL, LOC + QString("Saw new: %1, saw repeat: %2") +// .arg(saw_new).arg(saw_repeat)); + if (!query.exec("TRUNCATE TABLE dd_v_program;")) MythContext::DBError("Truncating temporary table dd_v_program", query); - query.prepare( + QString qstr = "INSERT INTO dd_v_program " " ( chanid, starttime, endtime, " " title, subtitle, description, " @@ -601,7 +654,7 @@ " tvrating, mpaarating, programid ) " "SELECT chanid, scheduletime, endtime, " " title, subtitle, description, " - " year, stars, isrepeat, " + " year, stars, %1, " " stereo, subtitled, hdtv, " " closecaptioned, partnumber, parttotal, " " seriesid, originalairdate, showtype, " @@ -610,7 +663,9 @@ "FROM channel, dd_schedule, dd_program " "WHERE ((dd_schedule.programid = dd_program.programid) AND " " (channel.xmltvid = dd_schedule.stationid) AND " - " (channel.sourceid = :SOURCEID))"); + " (channel.sourceid = :SOURCEID))"; + + query.prepare(qstr.arg((saw_new) ? "not isnew" : "isrepeat")); query.bindValue(":SOURCEID", sourceid); @@ -624,8 +679,10 @@ MythContext::DBError("Analyzing table dd_productioncrew", query); } -int DataDirectProcessor::UpdateChannelsSafe(uint sourceid, - bool insert_channels) +int DataDirectProcessor::UpdateChannelsSafe( + uint sourceid, + bool insert_channels, + bool filter_new_channels) { int new_channels = 0; @@ -649,6 +706,8 @@ return -1; } + bool is_encoder = SourceUtil::IsEncoder(sourceid, true); + while (query.next()) { QString xmltvid = query.value(0).toString(); @@ -658,13 +717,24 @@ QString chan_major = query.value(4).toString(); QString chan_minor = query.value(5).toString(); - update_channel_basic(sourceid, insert_channels, - xmltvid, callsign, name, freqid, - chan_major, chan_minor); + if (filter_new_channels && is_encoder && + (query.value(5).toUInt() > 0)) + { + VERBOSE(VB_GENERAL, LOC + QString( + "Not adding channel %1-%2 '%3' (%4),\n\t\t\t" + "looks like a digital channel on an analog source.") + .arg(chan_major).arg(chan_minor).arg(name).arg(callsign)); + continue; + } + + uint mods = + update_channel_basic(sourceid, insert_channels && is_encoder, + xmltvid, callsign, name, freqid, + chan_major, chan_minor); - if (!insert_channels) + if (!insert_channels && !mods) { - VERBOSE(VB_GENERAL, LOC + QString("Not adding channel %1 (%2).") + VERBOSE(VB_GENERAL, LOC + QString("Not adding channel '%1' (%2).") .arg(name).arg(callsign)); } new_channels++; @@ -673,7 +743,8 @@ return new_channels; } -bool DataDirectProcessor::UpdateChannelsUnsafe(uint sourceid) +bool DataDirectProcessor::UpdateChannelsUnsafe( + uint sourceid, bool filter_new_channels) { MSqlQuery dd_station_info(MSqlQuery::DDCon()); dd_station_info.prepare( @@ -695,6 +766,8 @@ " atsc_minor_chan = :MINORCHAN " "WHERE xmltvid = :STATIONID AND sourceid = :SOURCEID"); + bool is_encoder = SourceUtil::IsEncoder(sourceid, true); + while (dd_station_info.next()) { uint freqid = dd_station_info.value(3).toUInt(); @@ -704,6 +777,18 @@ QString channum = process_dd_station( sourceid, chan_major, chan_minor, tvformat, freqid); + if (filter_new_channels && is_encoder && + (dd_station_info.value(5).toUInt() > 0)) + { + VERBOSE(VB_GENERAL, LOC + QString( + "Not adding channel %1-%2 '%3' (%4),\n\t\t\t" + "looks like a digital channel on an analog source.") + .arg(chan_major).arg(chan_minor) + .arg(dd_station_info.value(1).toString()) + .arg(dd_station_info.value(0).toString())); + continue; + } + chan_update_q.bindValue(":CALLSIGN", dd_station_info.value(0)); chan_update_q.bindValue(":NAME", dd_station_info.value(1)); chan_update_q.bindValue(":STATIONID", dd_station_info.value(2)); @@ -723,16 +808,63 @@ return true; } +void DataDirectProcessor::FixProgramIDs(void) +{ + VERBOSE(VB_GENERAL, "DataDirectProcessor::FixProgramIDs() -- begin"); + + MSqlQuery query(MSqlQuery::DDCon()); + query.prepare( + "UPDATE recorded " + "SET programid=CONCAT(SUBSTRING(programid, 1, 2), " + " '00', SUBSTRING(programid, 3)) " + "WHERE length(programid) = 12"); + + if (!query.exec()) + { + MythContext::DBError("Fixing program ids in recorded", query); + return; + } + + query.prepare( + "UPDATE oldrecorded " + "SET programid=CONCAT(SUBSTRING(programid, 1, 2), " + " '00', SUBSTRING(programid, 3)) " + "WHERE length(programid) = 12"); + + if (!query.exec()) + { + MythContext::DBError("Fixing program ids in oldrecorded", query); + return; + } + + query.prepare( + "UPDATE program " + "SET programid=CONCAT(SUBSTRING(programid, 1, 2), " + " '00', SUBSTRING(programid, 3)) " + "WHERE length(programid) = 12"); + + if (!query.exec()) + { + MythContext::DBError("Fixing program ids in program", query); + return; + } + + gContext->SaveSetting("MythFillFixProgramIDsHasRunOnce", "1"); + + VERBOSE(VB_GENERAL, "DataDirectProcessor::FixProgramIDs() -- end"); +} + FILE *DataDirectProcessor::DDPost( QString ddurl, QString postFilename, QString inputFile, QString userid, QString password, QDateTime pstartDate, QDateTime pendDate, - QString &err_txt) + QString &err_txt, bool &is_pipe) { if (!inputFile.isEmpty()) { err_txt = QString("Unable to open '%1'").arg(inputFile); + is_pipe = false; return fopen(inputFile.ascii(), "r"); } @@ -780,6 +912,7 @@ err_txt = command; + is_pipe = true; return popen(command.ascii(), "r"); } @@ -788,12 +921,12 @@ VERBOSE(VB_GENERAL, "Grabbing next suggested grabbing time"); QString ddurl = providers[listings_provider].webServiceURL; - - QFile postfile(tmpPostFile); + + QFile postfile(GetPostFilename()); if (!postfile.open(IO_WriteOnly)) { VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Opening '%1'") - .arg(tmpPostFile) + ENO); + .arg(GetPostFilename()) + ENO); return false; } @@ -814,8 +947,8 @@ QString command = QString("wget --http-user='%1' --http-passwd='%2' " "--post-file='%3' %4 --output-document='%5'") - .arg(GetUserID()).arg(GetPassword()).arg(tmpPostFile) - .arg(ddurl).arg(tmpResultFile); + .arg(GetUserID()).arg(GetPassword()).arg(GetPostFilename()) + .arg(ddurl).arg(GetResultFilename()); if (SHOW_WGET_OUTPUT) VERBOSE(VB_GENERAL, "command: "<=0; +} + bool DataDirectProcessor::GrabData(const QDateTime pstartDate, const QDateTime pendDate) { @@ -929,10 +1079,28 @@ QString err = ""; QString ddurl = providers[listings_provider].webServiceURL; + QString inputfile = inputfilename; + QString cache_dd_data = QString::null; + + if (cachedata) + { + cache_dd_data = tmpDir + QString("/mythtv_dd_cache_%1_%2_%3_%4") + .arg(GetListingsProvider()) + .arg(GetUserID().ascii()) + .arg(pstartDate.toString()) + .arg(pendDate.toString()); - FILE *fp = DDPost(ddurl, tmpPostFile, inputfilename, + if (QFile(cache_dd_data).exists() && inputfilename.isEmpty()) + { + VERBOSE(VB_GENERAL, LOC + "Copying from DD cache"); + inputfile = cache_dd_data; + } + } + + bool fp_is_pipe; + FILE *fp = DDPost(ddurl, GetPostFilename(), inputfile, GetUserID(), GetPassword(), - pstartDate, pendDate, err); + pstartDate, pendDate, err, fp_is_pipe); if (!fp) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to get data " + @@ -940,6 +1108,56 @@ return false; } + if (cachedata && (inputfile != cache_dd_data)) + { + QFile in, out(cache_dd_data); + bool ok = out.open(IO_WriteOnly); + if (!ok) + { + VERBOSE(VB_IMPORTANT, LOC_WARN + + "Can not open DD cache file in '" + + tmpDir + "' for writing!"); + } + else + { + VERBOSE(VB_GENERAL, LOC + "Saving listings to DD cache"); + ok = in.open(IO_ReadOnly, fp); + out.close(); // let copy routine handle dst file + } + + if (ok) + { + ok = copy(out, in); + in.close(); + + close_fp(fp, fp_is_pipe); + + if (ok) + { + fp = fopen(cache_dd_data.ascii(), "r"); + fp_is_pipe = false; + } + else + { + VERBOSE(VB_IMPORTANT, + LOC_ERR + "Failed to save DD cache! " + "redownloading data..."); + cachedata = false; + fp = DDPost(ddurl, GetPostFilename(), inputfile, + GetUserID(), GetPassword(), + pstartDate, pendDate, err, fp_is_pipe); + } + } + } + + if (!fp) + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to get data 2 " + + QString("(%1) -- ").arg(err) + ENO); + return false; + } + + bool ok = true; QFile f; if (f.open(IO_ReadOnly, fp)) { @@ -953,11 +1171,13 @@ else { VERBOSE(VB_GENERAL, LOC_ERR + "Error opening DataDirect file"); - pclose(fp); - fp = NULL; + ok = false; } - return fp; + // f.close() only flushes pipe/file, we need to actually close it. + close_fp(fp, fp_is_pipe); + + return ok; } bool DataDirectProcessor::GrabLineupsOnly(void) @@ -996,6 +1216,9 @@ { QMap dd_tables; + dd_tables["dd_state"] = + "( sawrepeat bool, sawnew bool )"; + dd_tables["dd_station"] = "( stationid char(12), callsign char(10), " " stationname varchar(40), affiliate varchar(25), " @@ -1018,17 +1241,17 @@ " channelMinor char(3) )"; dd_tables["dd_schedule"] = - "( programid char(12), stationid char(12), " + "( programid char(40), stationid char(12), " " scheduletime datetime, duration time, " " isrepeat bool, stereo bool, " " subtitled bool, hdtv bool, " " closecaptioned bool, tvrating char(5), " " partnumber int, parttotal int, " - " endtime datetime, " + " endtime datetime, isnew bool, " "INDEX progidx (programid) )"; dd_tables["dd_program"] = - "( programid char(12) NOT NULL, seriesid char(12), " + "( programid char(40) NOT NULL, seriesid char(12), " " title varchar(120), subtitle varchar(150), " " description text, mpaarating char(5), " " starrating char(5), runtime time, " @@ -1050,19 +1273,19 @@ " partnumber int, parttotal int, " " seriesid char(12), originalairdate date, " " showtype varchar(30), colorcode varchar(20), " - " syndicatedepisodenumber varchar(20), programid char(12), " + " syndicatedepisodenumber varchar(20), programid char(40), " " tvrating char(5), mpaarating char(5), " "INDEX progidx (programid))"; dd_tables["dd_productioncrew"] = - "( programid char(12), role char(30), " + "( programid char(40), role char(30), " " givenname char(20), surname char(20), " " fullname char(41), " "INDEX progidx (programid), " "INDEX nameidx (fullname))"; dd_tables["dd_genre"] = - "( programid char(12) NOT NULL, class char(30), " + "( programid char(40) NOT NULL, class char(30), " " relevance char(1), " "INDEX progidx (programid))"; @@ -1083,11 +1306,12 @@ QString labsURL = providers[listings_provider].webURL; QString loginPage = providers[listings_provider].loginPage; - bool ok = Post(labsURL + loginPage, list, tmpResultFile, "", cookieFile); + bool ok = Post(labsURL + loginPage, list, GetResultFilename(), "", + GetCookieFilename()); - bool got_cookie = QFileInfo(cookieFile).size() > 100; + bool got_cookie = QFileInfo(GetCookieFilename()).size() > 100; - ok &= got_cookie && (!parse_lineups || ParseLineups(tmpResultFile)); + ok &= got_cookie && (!parse_lineups || ParseLineups(GetResultFilename())); if (ok) cookieFileDT = QDateTime::currentDateTime(); @@ -1110,10 +1334,10 @@ list.push_back(PostItem("submit", "Modify")); QString labsURL = providers[listings_provider].webURL; - bool ok = Post(labsURL + (*it).get_action, list, tmpResultFile, - cookieFile, ""); + bool ok = Post(labsURL + (*it).get_action, list, GetResultFilename(), + GetCookieFilename(), ""); - return ok && ParseLineup(lineupid, tmpResultFile); + return ok && ParseLineup(lineupid, GetResultFilename()); } void DataDirectProcessor::SetAll(const QString &lineupid, bool val) @@ -1392,7 +1616,8 @@ .arg(lineupid).arg(list.size() - 1)); QString labsURL = providers[listings_provider].webURL; - return Post(labsURL + lineup.set_action, list, "", cookieFile, ""); + return Post(labsURL + lineup.set_action, list, "", + GetCookieFilename(), ""); } bool DataDirectProcessor::UpdateListings(uint sourceid) @@ -1469,6 +1694,42 @@ return (*it); } +QString DataDirectProcessor::GetPostFilename(void) const +{ + if (tmpPostFile.isEmpty()) + tmpPostFile = createTempFile(tmpDir + "/mythtv_post_XXXXXX"); + return QDeepCopy(tmpPostFile); +} + +QString DataDirectProcessor::GetResultFilename(void) const +{ + if (tmpResultFile.isEmpty()) + tmpResultFile = createTempFile(tmpDir + "/mythtv_result_XXXXXX"); + return QDeepCopy(tmpResultFile); +} + +QString DataDirectProcessor::GetCookieFilename(void) const +{ + if (cookieFile.isEmpty()) + cookieFile = createTempFile(tmpDir + "/mythtv_cookies_XXXXXX"); + return QDeepCopy(cookieFile); +} + +void DataDirectProcessor::SetUserID(const QString &uid) +{ + userid = QDeepCopy(uid); +} + +void DataDirectProcessor::SetPassword(const QString &pwd) +{ + password = QDeepCopy(pwd); +} + +void DataDirectProcessor::SetInputFile(const QString &file) +{ + inputfilename = QDeepCopy(file); +} + bool DataDirectProcessor::Post(QString url, const PostList &list, QString documentFile, QString inCookieFile, QString outCookieFile) @@ -1795,7 +2056,7 @@ return channum; } -static void update_channel_basic(uint sourceid, bool insert, +static uint update_channel_basic(uint sourceid, bool insert, QString xmltvid, QString callsign, QString name, uint freqid, QString chan_major, QString chan_minor) @@ -1822,10 +2083,10 @@ { MythContext::DBError( "Getting chanid of existing channel", query); - return; // go on to next channel without xmltv + return 0; // go on to next channel without xmltv } - if (query.size() > 0) + if (query.next()) { // The channel already exists in DB, at least once, // so set the xmltvid.. @@ -1835,24 +2096,33 @@ "SET xmltvid = :XMLTVID " "WHERE chanid = :CHANID AND sourceid = :SOURCEID"); - while (query.next()) + uint i = 0; + do { uint chanid = query.value(0).toInt(); chan_update_q.bindValue(":CHANID", chanid); chan_update_q.bindValue(":XMLTVID", xmltvid); chan_update_q.bindValue(":SOURCEID", sourceid); + + VERBOSE(VB_GENERAL, LOC + + QString("Updating channel %1: '%2' (%3).") + .arg(chanid).arg(name).arg(callsign)); + if (!chan_update_q.exec() || !chan_update_q.isActive()) { MythContext::DBError( "Updating XMLTVID of existing channel", chan_update_q); continue; // go on to next instance of this channel } + i++; } - return; // go on to next channel without xmltv + while (query.next()); + + return i; // go on to next channel without xmltv } if (!insert) - return; // go on to next channel without xmltv + return 0; // go on to next channel without xmltv // The channel doesn't exist in the DB, insert it... int mplexid = -1, majorC, minorC, chanid = 0; @@ -1866,6 +2136,9 @@ if ((mplexid > 0) || (minorC == 0)) chanid = ChannelUtil::CreateChanID(sourceid, channum); + VERBOSE(VB_GENERAL, LOC + QString("Adding channel %1 '%2' (%3).") + .arg(channum).arg(name).arg(callsign)); + if (chanid > 0) { QString icon = ""; @@ -1882,6 +2155,8 @@ freqid, icon, tvformat, xmltvid); } + + return 1; } static void set_lineup_type(const QString &lineupid, const QString &type) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/datadirect.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/datadirect.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/datadirect.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/datadirect.h 2007-08-23 17:39:10.000000000 -0500 @@ -11,8 +11,9 @@ enum DD_PROVIDERS { - DD_ZAP2IT = 0, - DD_PROVIDER_COUNT, + DD_ZAP2IT = 0, + DD_SCHEDULES_DIRECT = 1, + DD_PROVIDER_COUNT = 2, }; class DataDirectURLs @@ -95,6 +96,7 @@ QDateTime time; QTime duration; bool repeat; + bool isnew; bool stereo; bool subtitled; bool hdtv; @@ -212,7 +214,8 @@ class DDStructureParser: public QXmlDefaultHandler { public: - DDStructureParser(DataDirectProcessor& _ddparent): parent(_ddparent) {} + DDStructureParser(DataDirectProcessor& _ddparent) : + parent(_ddparent), saw_repeat(false), saw_new(false) {} bool startElement(const QString &pnamespaceuri, const QString &plocalname, const QString &pqname, const QXmlAttributes &pxmlatts); @@ -237,8 +240,11 @@ DataDirectProductionCrew curr_productioncrew; DataDirectGenre curr_genre; QString lastprogramid; + bool saw_repeat; + bool saw_new; }; + typedef QMap DDStationList; // stationid -> typedef vector DDLineupList; typedef vector DDLineupChannels; @@ -252,6 +258,8 @@ QString userid = "", QString password = ""); ~DataDirectProcessor(); + QString CreateTempDirectory(void); + // web service commands bool GrabData(const QDateTime pstartdate, const QDateTime penddate); bool GrabNextSuggestedTime(void); @@ -301,19 +309,26 @@ RawLineup GetRawLineup( const QString &lineupid) const; // sets - void SetUserID(QString uid) { userid = uid; } - void SetPassword(QString pwd) { password = pwd; } + void SetUserID(const QString &uid); + void SetPassword(const QString &pwd); void SetListingsProvider(uint i) { listings_provider = i % DD_PROVIDER_COUNT; } - void SetInputFile(const QString &file) { inputfilename = file; } + + void SetInputFile(const QString &file); + void SetCacheData(bool cd) { cachedata = cd; } // static commands (these update temp DB tables) static void UpdateStationViewTable(QString lineupid); static void UpdateProgramViewTable(uint sourceid); // static commands (these update regular DB tables from temp DB tables) - static int UpdateChannelsSafe(uint sourceid, bool insert_channels); - static bool UpdateChannelsUnsafe(uint sourceid); + static int UpdateChannelsSafe( + uint sourceid, bool insert_channels, bool filter_new_channels); + static bool UpdateChannelsUnsafe( + uint sourceid, bool filter_new_channels); + + // static command, makes Labs and Schedules Direct ProgramIDs compatible. + static void FixProgramIDs(void); private: void CreateTempTables(void); @@ -323,6 +338,10 @@ bool ParseLineups(const QString &documentFile); bool ParseLineup(const QString &lineupid, const QString &documentFile); + QString GetPostFilename(void) const; + QString GetResultFilename(void) const; + QString GetCookieFilename(void) const; + void SetAll(const QString &lineupid, bool val); void SetDDProgramsStartAt(QDateTime begts) { actuallistingsfrom = begts; } void SetDDProgramsEndAt(QDateTime endts) { actuallistingsto = endts; } @@ -334,7 +353,7 @@ QString postFilename, QString inputFilename, QString userid, QString password, QDateTime pstartDate, QDateTime pendDate, - QString &err_txt); + QString &err_txt, bool &is_pipe); private: @@ -343,6 +362,8 @@ QString userid; QString password; + QString tmpDir; + bool cachedata; QDateTime actuallistingsfrom; QDateTime actuallistingsto; @@ -353,11 +374,11 @@ DDLineupList lineups; DDLineupMap lineupmaps; - RawLineupMap rawlineups; - QString tmpPostFile; - QString tmpResultFile; - QString cookieFile; - QDateTime cookieFileDT; + RawLineupMap rawlineups; + mutable QString tmpPostFile; + mutable QString tmpResultFile; + mutable QString cookieFile; + QDateTime cookieFileDT; }; #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dbcheck.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dbcheck.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dbcheck.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dbcheck.cpp 2007-08-20 16:10:34.000000000 -0500 @@ -8,6 +8,7 @@ #include "mythcontext.h" #include "mythdbcon.h" +#include "datadirect.h" // for DataDirectProcessor::FixProgramIDs /// This is the DB schema version expected by the running MythTV instance. const QString currentDatabaseVersion = "1160"; @@ -424,6 +425,9 @@ VERBOSE(VB_IMPORTANT, QString("Current Schema Version: %1").arg(dbver)); + if (!gContext->GetNumSetting("MythFillFixProgramIDsHasRunOnce", 0)) + DataDirectProcessor::FixProgramIDs(); + if (dbver == currentDatabaseVersion) return true; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/decoderbase.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/decoderbase.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/decoderbase.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/decoderbase.cpp 2007-07-30 12:45:45.000000000 -0500 @@ -792,7 +792,7 @@ hnum = tracks[type][trackNo].stream_id; if (!lang) - return type_msg + QString("-%1").arg(hnum); + return type_msg + QString(" %1").arg(hnum); else { QString lang_msg = iso639_key_toName(lang); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/diseqc.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/diseqc.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/diseqc.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/diseqc.cpp 2007-04-12 10:16:40.000000000 -0500 @@ -1103,7 +1103,7 @@ // populate switch parameters from db MSqlQuery query(MSqlQuery::InitCon()); query.prepare( - "SELECT subtype, switch_ports " + "SELECT subtype, switch_ports, cmd_repeat " "FROM diseqc_tree " "WHERE diseqcid = :DEVID"); query.bindValue(":DEVID", GetDeviceID()); @@ -1117,6 +1117,7 @@ { m_type = SwitchTypeFromString(query.value(0).toString()); m_num_ports = query.value(1).toUInt(); + m_repeat = query.value(2).toUInt(); m_children.resize(m_num_ports); for (uint i = 0; i < m_num_ports; i++) m_children[i] = NULL; @@ -1166,7 +1167,8 @@ " type = 'switch', " " description = :DESC, " " subtype = :TYPE, " - " switch_ports = :PORTS " + " switch_ports = :PORTS, " + " cmd_repeat = :REPEAT " "WHERE diseqcid = :DEVID"); } else @@ -1174,10 +1176,12 @@ query.prepare( "INSERT INTO diseqc_tree" " ( parentid, ordinal, type, " - " description, subtype, switch_ports) " + " description, subtype, switch_ports, " + " cmd_repeat )" "VALUES " " (:PARENT, :ORDINAL, 'switch', " - " :DESC, :TYPE, :PORTS)"); + " :DESC, :TYPE, :PORTS, " + " :REPEAT )"); } if (m_parent) @@ -1187,6 +1191,7 @@ query.bindValue(":DESC", GetDescription()); query.bindValue(":TYPE", type); query.bindValue(":PORTS", m_num_ports); + query.bindValue(":REPEAT", m_repeat); query.bindValue(":DEVID", GetDeviceID()); if (!query.exec()) @@ -1604,7 +1609,8 @@ MSqlQuery query(MSqlQuery::InitCon()); query.prepare( "SELECT subtype, rotor_positions, " - " rotor_hi_speed, rotor_lo_speed " + " rotor_hi_speed, rotor_lo_speed, " + " cmd_repeat " "FROM diseqc_tree " "WHERE diseqcid = :DEVID"); query.bindValue(":DEVID", GetDeviceID()); @@ -1619,6 +1625,7 @@ m_type = RotorTypeFromString(query.value(0).toString()); m_speed_hi = query.value(2).toDouble(); m_speed_lo = query.value(3).toDouble(); + m_repeat = query.value(4).toUInt(); // form of "angle1=index1:angle2=index2:..." QString positions = query.value(1).toString(); @@ -1688,7 +1695,8 @@ " subtype = :TYPE, " " rotor_hi_speed = :HISPEED, " " rotor_lo_speed = :LOSPEED, " - " rotor_positions = :POSMAP " + " rotor_positions = :POSMAP, " + " cmd_repeat = :REPEAT " "WHERE diseqcid = :DEVID"); } else @@ -1697,11 +1705,11 @@ "INSERT INTO diseqc_tree " " ( parentid, ordinal, type, " " description, subtype, rotor_hi_speed, " - " rotor_lo_speed, rotor_positions ) " + " rotor_lo_speed, rotor_positions, cmd_repeat ) " "VALUES " " (:PARENT, :ORDINAL, 'rotor', " " :DESC, :TYPE, :HISPEED, " - " :LOSPEED, :POSMAP )"); + " :LOSPEED, :POSMAP, :REPEAT )"); } if (m_parent) @@ -1713,6 +1721,7 @@ query.bindValue(":HISPEED", m_speed_hi); query.bindValue(":LOSPEED", m_speed_lo); query.bindValue(":POSMAP", posmap); + query.bindValue(":REPEAT", m_repeat); query.bindValue(":DEVID", GetDeviceID()); if (!query.exec()) @@ -1932,7 +1941,8 @@ MSqlQuery query(MSqlQuery::InitCon()); query.prepare( "SELECT subtype, lnb_lof_switch, " - " lnb_lof_hi, lnb_lof_lo " + " lnb_lof_hi, lnb_lof_lo, " + " cmd_repeat " "FROM diseqc_tree " "WHERE diseqcid = :DEVID"); query.bindValue(":DEVID", GetDeviceID()); @@ -1948,6 +1958,7 @@ m_lof_switch = query.value(1).toInt(); m_lof_hi = query.value(2).toInt(); m_lof_lo = query.value(3).toInt(); + m_repeat = query.value(4).toUInt(); } return true; @@ -1970,7 +1981,8 @@ " subtype = :TYPE, " " lnb_lof_switch = :LOFSW, " " lnb_lof_lo = :LOFLO, " - " lnb_lof_hi = :LOFHI " + " lnb_lof_hi = :LOFHI, " + " cmd_repeat = :REPEAT " "WHERE diseqcid = :DEVID"); } else @@ -1979,11 +1991,11 @@ "INSERT INTO diseqc_tree" " ( parentid, ordinal, type, " " description, subtype, lnb_lof_switch, " - " lnb_lof_lo, lnb_lof_hi ) " + " lnb_lof_lo, lnb_lof_hi, cmd_repeat ) " "VALUES " " (:PARENT, :ORDINAL, 'lnb', " " :DESC, :TYPE, :LOFSW, " - " :LOFLO, :LOFHI ) "); + " :LOFLO, :LOFHI, :REPEAT ) "); } if (m_parent) @@ -1995,6 +2007,7 @@ query.bindValue(":LOFSW", m_lof_switch); query.bindValue(":LOFLO", m_lof_lo); query.bindValue(":LOFHI", m_lof_hi); + query.bindValue(":REPEAT", m_repeat); query.bindValue(":DEVID", GetDeviceID()); // update dev_id diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/diseqc.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/diseqc.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/diseqc.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/diseqc.h 2007-04-12 10:16:40.000000000 -0500 @@ -147,6 +147,7 @@ void SetParent(DiSEqCDevDevice* parent) { m_parent = parent; } void SetOrdinal(uint ordinal) { m_ordinal = ordinal; } void SetDescription(const QString &desc) { m_desc = desc; } + void SetRepeatCount(uint repeat) { m_repeat = repeat; } virtual bool SetChild(uint, DiSEqCDevDevice*){return false; } // Gets @@ -157,6 +158,7 @@ DiSEqCDevDevice *GetParent(void) const { return m_parent; } uint GetOrdinal(void) const { return m_ordinal; } QString GetDescription(void) const { return m_desc; } + uint GetRepeatCount(void) const { return m_repeat; } virtual uint GetChildCount(void) const { return 0; } virtual bool IsCommandNeeded( const DiSEqCDevSettings&, const DVBTuning&) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/diseqcsettings.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/diseqcsettings.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/diseqcsettings.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/diseqcsettings.cpp 2007-04-12 10:16:40.000000000 -0500 @@ -96,6 +96,36 @@ DiSEqCDevDevice &m_device; }; + +//////////////////////////////////////// DeviceRepeatSetting + +class DeviceRepeatSetting : public SpinBoxSetting +{ + public: + DeviceRepeatSetting(DiSEqCDevDevice &device) : + SpinBoxSetting(1, 5, 1), m_device(device) + { + setLabel(DeviceTree::tr("Repeat Count")); + QString help = DeviceTree::tr( + "Number of times to repeat DiSEqC commands sent to this device. " + "Larger values may help with less reliable devices."); + setHelpText(help); + } + + virtual void load(void) + { + setValue(m_device.GetRepeatCount()); + } + + virtual void save(void) + { + m_device.SetRepeatCount(getValue().toUInt()); + } + + private: + DiSEqCDevDevice &m_device; +}; + //////////////////////////////////////// SwitchTypeSetting class SwitchTypeSetting : public ComboBoxSetting @@ -171,6 +201,7 @@ group->setLabel(DeviceTree::tr("Switch Configuration")); group->addChild(new DeviceDescrSetting(switch_dev)); + group->addChild(new DeviceRepeatSetting(switch_dev)); m_type = new SwitchTypeSetting(switch_dev); group->addChild(m_type); m_ports = new SwitchPortsSetting(switch_dev); @@ -408,6 +439,7 @@ group->setLabel(DeviceTree::tr("Rotor Configuration")); group->addChild(new DeviceDescrSetting(rotor)); + group->addChild(new DeviceRepeatSetting(rotor)); ConfigurationGroup *tgroup = new HorizontalConfigurationGroup(false, false, true, true); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvconfparserhelpers.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvconfparserhelpers.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvconfparserhelpers.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvconfparserhelpers.cpp 2007-04-14 23:22:12.000000000 -0500 @@ -0,0 +1,351 @@ +#include "mythcontext.h" +#include "dtvconfparserhelpers.h" + +bool DTVParamHelper::ParseParam(const QString &symbol, int &value, + const DTVParamHelperStruct *table) +{ + const DTVParamHelperStruct *p = table; + + while (p->symbol != NULL) + { + if (p->symbol == symbol.left(p->symbol.length())) + { + //symbol = symbol.mid(p->symbol.length()); + value = p->value; + return true; + } + p++; + } + + return false; +} + +QString DTVParamHelper::toString(const char *strings[], int index, + uint strings_size) +{ + if ((index < 0) || ((uint)index >= strings_size)) + { + VERBOSE(VB_IMPORTANT, + "DTVParamHelper::toString() index out of bounds"); + + return QString::null; + } + + return strings[index]; +} + +const DTVParamHelperStruct DTVInversion::confTable[] = +{ + { "INVERSION_AUTO", kInversionAuto }, + { "INVERSION_OFF", kInversionOff }, + { "INVERSION_ON", kInversionOn }, + { NULL, kInversionAuto }, +}; + +const DTVParamHelperStruct DTVInversion::vdrTable[] = +{ + { "999", kInversionAuto }, + { "0", kInversionOff }, + { "1", kInversionOn }, + { NULL, kInversionAuto }, +}; + +const DTVParamHelperStruct DTVInversion::parseTable[] = +{ + { "a", kInversionAuto }, + { "0", kInversionOff }, + { "1", kInversionOn }, + { NULL, kInversionAuto }, +}; + +const char *DTVInversion::dbStr[DTVInversion::kDBStrCnt] = +{ + "0", ///< kInversionOff + "1", ///< kInversionOn + "a" ///< kInversionAuto +}; + +const DTVParamHelperStruct DTVBandwidth::confTable[] = +{ + { "BANDWIDTH_AUTO", kBandwidthAuto }, + { "BANDWIDTH_8_MHZ", kBandwidth8Mhz }, + { "BANDWIDTH_7_MHZ", kBandwidth7Mhz }, + { "BANDWIDTH_6_MHZ", kBandwidth6Mhz }, + { NULL, kBandwidthAuto }, +}; + +const DTVParamHelperStruct DTVBandwidth::vdrTable[] = +{ + { "999", kBandwidthAuto }, + { "8", kBandwidth8Mhz }, + { "7", kBandwidth7Mhz }, + { "6", kBandwidth6Mhz }, + { NULL, kBandwidthAuto }, +}; + +const DTVParamHelperStruct DTVBandwidth::parseTable[] = +{ + { "auto", kBandwidthAuto }, + { "8", kBandwidth8Mhz }, + { "7", kBandwidth7Mhz }, + { "6", kBandwidth6Mhz }, + { NULL, kBandwidthAuto }, +}; + +const char *DTVBandwidth::dbStr[DTVBandwidth::kDBStrCnt] = +{ + "8", ///< kBandwidth8Mhz + "7", ///< kBandwidth7Mhz + "6", ///< kBandwidth6Mhz + "auto" ///< kBandwidthAUTO +}; + +const DTVParamHelperStruct DTVCodeRate::confTable[] = +{ + { "FEC_AUTO", kFECAuto }, + { "FEC_1_2", kFEC_1_2 }, + { "FEC_2_3", kFEC_2_3 }, + { "FEC_3_4", kFEC_3_4 }, + { "FEC_4_5", kFEC_4_5 }, + { "FEC_5_6", kFEC_5_6 }, + { "FEC_6_7", kFEC_6_7 }, + { "FEC_7_8", kFEC_7_8 }, + { "FEC_8_9", kFEC_8_9 }, + { "FEC_NONE", kFECNone }, + { NULL, kFECAuto }, +}; + +const DTVParamHelperStruct DTVCodeRate::vdrTable[] = +{ + { "999", kFECAuto }, + { "12", kFEC_1_2 }, + { "23", kFEC_2_3 }, + { "34", kFEC_3_4 }, + { "45", kFEC_4_5 }, + { "56", kFEC_5_6 }, + { "67", kFEC_6_7 }, + { "78", kFEC_7_8 }, + { "89", kFEC_8_9 }, + { "0", kFECNone }, + { NULL, kFECAuto } +}; + +const DTVParamHelperStruct DTVCodeRate::parseTable[] = +{ + { "auto", kFECAuto }, + { "1/2", kFEC_1_2 }, + { "2/3", kFEC_2_3 }, + { "3/4", kFEC_3_4 }, + { "4/5", kFEC_4_5 }, + { "5/6", kFEC_5_6 }, + { "6/7", kFEC_6_7 }, + { "7/8", kFEC_7_8 }, + { "8/9", kFEC_8_9 }, + { "none", kFECNone }, + { NULL, kFECAuto } +}; + +const char *DTVCodeRate::dbStr[DTVCodeRate::kDBStrCnt] = +{ + "none", ///< kFECNone + "1/2", ///< kFEC_1_2 + "2/3", ///< kFEC_2_3 + "3/4", ///< kFEC_3_4 + "4/5", ///< kFEC_4_5 + "5/6", ///< kFEC_5_6 + "6/7", ///< kFEC_6_7 + "7/8", ///< kFEC_7_8 + "8/9", ///< kFEC_8_9 + "auto" ///< kFECAuto +}; + +const DTVParamHelperStruct DTVModulation::confTable[] = +{ + { "QAM_AUTO", kModulationQAMAuto }, + { "QAM_16", kModulationQAM16 }, + { "QAM_32", kModulationQAM32 }, + { "QAM_64", kModulationQAM64 }, + { "QAM_128", kModulationQAM128 }, + { "QAM_256", kModulationQAM256 }, + { "QPSK", kModulationQPSK }, + { "8PSK", kModulation8PSK }, + { NULL, kModulationQAMAuto }, +}; + +const DTVParamHelperStruct DTVModulation::vdrTable[] = +{ + { "999", kModulationQAMAuto }, + { "16", kModulationQAM16 }, + { "32", kModulationQAM32 }, + { "64", kModulationQAM64 }, + { "128", kModulationQAM128 }, + { "256", kModulationQAM256 }, + { "0", kModulationQPSK }, + { NULL, kModulationQAMAuto }, +}; + +const DTVParamHelperStruct DTVModulation::parseTable[] = +{ + { "auto", kModulationQAMAuto }, + { "qam_16", kModulationQAM16 }, + { "qam_32", kModulationQAM32 }, + { "qam_64", kModulationQAM64 }, + { "qam_128", kModulationQAM128 }, + { "qam_256", kModulationQAM256 }, + { "qpsk", kModulationQPSK }, + { "8vsb", kModulation8VSB }, + { "16vsb", kModulation16VSB }, + { "8psk", kModulation8PSK }, + // alternates from dvbtypes + { "a", kModulationQAMAuto }, + { "qam_auto", kModulationQAMAuto }, + { "qam-16", kModulationQAM16 }, + { "qam-32", kModulationQAM32 }, + { "qam-64", kModulationQAM64 }, + { "qam-128", kModulationQAM128 }, + { "qam-256", kModulationQAM256 }, + { "8-vsb", kModulation8VSB }, + { "16-vsb", kModulation16VSB }, + { "8-psk", kModulation8PSK }, + { NULL, kModulationQAMAuto }, +}; + +const char *DTVModulation::dbStr[DTVModulation::kDBStrCnt] = +{ + "qpsk", ///< kModulationQPSK, + "qam_16", ///< kModulationQAM16 + "qam_32", ///< kModulationQAM32 + "qam_64", ///< kModulationQAM64 + "qam_128", ///< kModulationQAM128 + "qam_256", ///< kModulationQAM256 + "auto", ///< kModulationQAMAuto + "8vsb", ///< kModulation8VSB + "16vsb", ///< kModulation16VSB + "8psk", ///< kModulation8PSK +}; + +const DTVParamHelperStruct DTVTransmitMode::confTable[] = +{ + { "TRANSMISSION_MODE_AUTO", kTransmissionModeAuto }, + { "TRANSMISSION_MODE_2K", kTransmissionMode2K }, + { "TRANSMISSION_MODE_8K", kTransmissionMode8K }, + { NULL, kTransmissionModeAuto }, +}; + +const DTVParamHelperStruct DTVTransmitMode::vdrTable[] = +{ + { "999", kTransmissionModeAuto }, + { "2", kTransmissionMode2K }, + { "8", kTransmissionMode8K }, + { NULL, kTransmissionModeAuto }, +}; + +const DTVParamHelperStruct DTVTransmitMode::parseTable[] = +{ + { "auto", kTransmissionModeAuto }, + { "2", kTransmissionMode2K }, + { "8", kTransmissionMode8K }, + { NULL, kTransmissionModeAuto }, +}; + +const char *DTVTransmitMode::dbStr[DTVTransmitMode::kDBStrCnt] = +{ + "2", ///< kTransmissionMode2K + "8", ///< kTransmissionMode8K + "auto" ///< kTransmissionModeAuto +}; + +const DTVParamHelperStruct DTVGuardInterval::confTable[] = +{ + { "GUARD_INTERVAL_AUTO", kGuardIntervalAuto }, + { "GUARD_INTERVAL_1_32", kGuardInterval_1_32 }, + { "GUARD_INTERVAL_1_16", kGuardInterval_1_16 }, + { "GUARD_INTERVAL_1_8", kGuardInterval_1_8 }, + { "GUARD_INTERVAL_1_4", kGuardInterval_1_4 }, + { NULL, kGuardIntervalAuto }, +}; + +const DTVParamHelperStruct DTVGuardInterval::vdrTable[] = +{ + { "999", kGuardIntervalAuto }, + { "32", kGuardInterval_1_32 }, + { "16", kGuardInterval_1_16 }, + { "8", kGuardInterval_1_8 }, + { "4", kGuardInterval_1_4 }, + { NULL, kGuardIntervalAuto }, +}; + +const DTVParamHelperStruct DTVGuardInterval::parseTable[] = +{ + { "auto", kGuardIntervalAuto }, + { "1/32", kGuardInterval_1_32 }, + { "1/16", kGuardInterval_1_16 }, + { "1/8", kGuardInterval_1_8 }, + { "1/4", kGuardInterval_1_4 }, + { NULL, kGuardIntervalAuto }, +}; + +const char *DTVGuardInterval::dbStr[DTVGuardInterval::kDBStrCnt] = +{ + "1/32", ///< kGuardInterval_1_32 + "1/16", ///< kGuardInterval_1_16 + "1/8", ///< kGuardInterval_1_8 + "1/4", ///< kGuardInterval_1_4 + "auto" ///< kGuardIntervalAuto +}; + +const DTVParamHelperStruct DTVHierarchy::confTable[] = +{ + { "HIERARCHY_NONE", kHierarchyNone }, + { "HIERARCHY_1", kHierarchy1 }, + { "HIERARCHY_2", kHierarchy2 }, + { "HIERARCHY_4", kHierarchy4 }, + { "HIERARCHY_AUTO", kHierarchyAuto }, + { NULL, kHierarchyAuto }, +}; + +const DTVParamHelperStruct DTVHierarchy::vdrTable[] = +{ + { "0", kHierarchyNone }, + { "1", kHierarchy1 }, + { "2", kHierarchy2 }, + { "4", kHierarchy4 }, + { "999", kHierarchyAuto }, + { NULL, kHierarchyAuto }, +}; + +const DTVParamHelperStruct DTVHierarchy::parseTable[] = +{ + { "n", kHierarchyNone }, + { "1", kHierarchy1 }, + { "2", kHierarchy2 }, + { "4", kHierarchy4 }, + { "a", kHierarchyAuto }, + { NULL, kHierarchyAuto }, +}; + +const char *DTVHierarchy::dbStr[DTVHierarchy::kDBStrCnt] = +{ + "n", ///< kHierarchyNone + "1", ///< kHierarchy1 + "2", ///< kHierarchy2 + "4", ///< kHierarchy4 + "a" ///< kHierarchyAuto +}; + +const DTVParamHelperStruct DTVPolarity::parseTable[] = +{ + { "v", kPolarityVertical }, + { "h", kPolarityHorizontal }, + { "r", kPolarityRight }, + { "l", kPolarityLeft }, + { NULL, kPolarityVertical }, +}; + +const char *DTVPolarity::dbStr[DTVPolarity::kDBStrCnt] = +{ + "v", ///< kPolarityVertical + "h", ///< kPolarityHorizontal + "r", ///< kPolarityRight + "l" ///< kPolarityLeft +}; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvconfparserhelpers.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvconfparserhelpers.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvconfparserhelpers.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvconfparserhelpers.h 2007-04-14 23:22:12.000000000 -0500 @@ -0,0 +1,348 @@ +/* -*- Mode: c++ -*- + * vim: set expandtab tabstop=4 shiftwidth=4: + * + * Original Project + * MythTV http://www.mythtv.org + * + * Author(s): + * John Pullan (john@pullan.org) + * + * Description: + * Collection of classes to provide dvb channel scanning + * functionallity + * + * + * 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 + * of the License, 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef _DTVCONFPARSERHELPERS_H_ +#define _DTVCONFPARSERHELPERS_H_ + +#include + +// The following are a set of helper classes to allow easy translation +// between the different string representations of various tuning params. + +struct DTVParamHelperStruct +{ + QString symbol; + int value; +}; + +/** \class DTVParamHelper + * \brief Helper abstract template to do some of the mundane portions + * of translating and comparing the paramater strings. + */ +class DTVParamHelper +{ + public: + DTVParamHelper(int _value) : value(_value) { } + + operator int() const { return value; } + int operator=(int _value) { return value = _value; } + bool operator==(const int& v) const { return value == v; } + + protected: + static bool ParseParam(const QString &symbol, int &value, + const DTVParamHelperStruct *table); + + static QString toString(const char *strings[], int index, + uint strings_size); + + protected: + int value; +}; + +class DTVInversion : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 3; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kInversionOff, + kInversionOn, + kInversionAuto, + }; + + DTVInversion(int _default = kInversionAuto) + : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVBandwidth : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 4; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kBandwidth8Mhz, + kBandwidth7Mhz, + kBandwidth6Mhz, + kBandwidthAuto, + }; + + DTVBandwidth(int _default = kBandwidthAuto) : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVCodeRate : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 10; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kFECNone, + kFEC_1_2, + kFEC_2_3, + kFEC_3_4, + kFEC_4_5, + kFEC_5_6, + kFEC_6_7, + kFEC_7_8, + kFEC_8_9, + kFECAuto, + }; + + DTVCodeRate(int _default = kFECAuto) : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVModulation : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 10; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kModulationQPSK, + kModulationQAM16, + kModulationQAM32, + kModulationQAM64, + kModulationQAM128, + kModulationQAM256, + kModulationQAMAuto, + kModulation8VSB, + kModulation16VSB, + kModulation8PSK, + }; + + DTVModulation(int _default = kModulationQAMAuto) + : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVTransmitMode : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 3; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kTransmissionMode2K, + kTransmissionMode8K, + kTransmissionModeAuto, + }; + + DTVTransmitMode(int _default = kTransmissionModeAuto) + : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVGuardInterval : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 5; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kGuardInterval_1_32, + kGuardInterval_1_16, + kGuardInterval_1_8, + kGuardInterval_1_4, + kGuardIntervalAuto, + }; + + DTVGuardInterval(int _default = kGuardIntervalAuto) + : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVHierarchy : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct confTable[]; + static const DTVParamHelperStruct vdrTable[]; + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 5; + static const char *dbStr[kDBStrCnt]; + + public: + enum + { + kHierarchyNone, + kHierarchy1, + kHierarchy2, + kHierarchy4, + kHierarchyAuto, + }; + + DTVHierarchy(int _default = kHierarchyAuto) : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, confTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, vdrTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +class DTVPolarity : public DTVParamHelper +{ + protected: + static const DTVParamHelperStruct parseTable[]; + static const uint kDBStrCnt = 4; + static const char *dbStr[kDBStrCnt]; + + public: + enum PolarityValues + { + kPolarityVertical, + kPolarityHorizontal, + kPolarityRight, + kPolarityLeft + }; + + DTVPolarity(int _default = kPolarityVertical) + : DTVParamHelper(_default) { } + + bool ParseConf(const QString &_value) + { return ParseParam(_value, value, parseTable); } + bool ParseVDR(const QString &_value) + { return ParseParam(_value, value, parseTable); } + bool Parse(const QString &_value) + { return ParseParam(_value, value, parseTable); } + + QString toString() const { return toString(value); } + + static QString toString(int _value) + { return DTVParamHelper::toString(dbStr, _value, kDBStrCnt); } +}; + +#endif // _DTVCONFPARSERHELPERS_H_ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvrecorder.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvrecorder.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvrecorder.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvrecorder.cpp 2007-03-03 00:25:06.000000000 -0600 @@ -366,8 +366,10 @@ MARK_GOP_BYFRAME); _position_map_delta.clear(); - if (ringBuffer) - curRecording->SetFilesize(ringBuffer->GetWritePosition()); + // Stop setting the filesize here until we get the contention issue + // between with this thread and the scheduler worked out. + //if (ringBuffer) + // curRecording->SetFilesize(ringBuffer->GetWritePosition()); } } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvsignalmonitor.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvsignalmonitor.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dtvsignalmonitor.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dtvsignalmonitor.cpp 2007-08-22 13:59:21.000000000 -0500 @@ -214,7 +214,7 @@ if (GetDVBStreamData()) { - GetDVBStreamData()->SetDesiredService(tsid, netid, programNumber); + GetDVBStreamData()->SetDesiredService(netid, tsid, programNumber); AddFlags(kDTVSigMon_WaitForPMT | kDTVSigMon_WaitForSDT); GetDVBStreamData()->AddListeningPID(DVB_SDT_PID); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbconfparser.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbconfparser.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbconfparser.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbconfparser.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -1,5 +1,5 @@ /* - * $Id: dvbconfparser.cpp 9334 2006-03-12 04:31:03Z danielk $ + * $Id: dvbconfparser.cpp 13237 2007-04-14 04:59:17Z jarod $ * vim: set expandtab tabstop=4 shiftwidth=4: * * Original Project @@ -29,558 +29,308 @@ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html * */ -#include + +// Qt headers +#include +#include #include -#include + +// MythTV headers #include "mythcontext.h" -#include "dvbtypes.h" -#include "dvbconfparser.h" #include "mythdbcon.h" +#include "dvbconfparser.h" #include "channelutil.h" -void DVBConfParser::Multiplex::dump() +#define PARSE_SKIP(VAR) do { \ + if (it == tokens.end()) return false; else it++; } while(0) + +#define PARSE_CONF(VAR) do { \ + if (it == tokens.end() || !VAR.ParseConf(*it++)) \ + return false; } while(0) + +#define PARSE_STR(VAR) do { \ + if (it != tokens.end()) VAR = *it++; else return false; } while(0) + +#define PARSE_UINT(VAR) do { \ + if (it != tokens.end()) \ + VAR = (*it++).toUInt(); else return false; } while(0) + +#define PARSE_UINT_1000(VAR) do { \ + if (it != tokens.end()) \ + VAR = (*it++).toUInt() * 1000ULL; else return false; } while(0) + + +QString DTVMultiplex::toString() const { - cerr << frequency<<" "<(_file)) { - type=_type; - filename = _file; - sourceid=_sourceid; } -int DVBConfParser::parse() +DTVConfParser::return_t DTVConfParser::Parse(void) { - QFile file( filename ); - if (file.open( IO_ReadOnly ) ) + channels.clear(); + + QFile file(filename); + if (!file.open(IO_ReadOnly)) + return ERROR_OPEN; + + bool ok = true; + QTextStream stream(&file); + QString line; + while (!stream.atEnd()) { - QTextStream stream( &file ); - QString line; - while ( !stream.atEnd() ) + line = stream.readLine(); // line of text excluding '\n' + line.stripWhiteSpace(); + if (line.startsWith("#")) + continue; + + QStringList list = QStringList::split(":", line); + QString str = *list.at(0); + int channelNo = -1; + + if (str.at(0) == '@') { - line = stream.readLine(); // line of text excluding '\n' - line.stripWhiteSpace(); - if (line.startsWith("#")) - continue; - QStringList list=QStringList::split(":",line); - QString str = *list.at(0); - int channelNo = -1; - if (str.at(0)=='@') - { - channelNo=str.mid(1).toInt(); - line = stream.readLine(); - list=QStringList::split(":",line); - } - str = *list.at(3); - if ((str == "T") || (str == "C") || (str=="S")) - { - if ((type == OFDM) && (str=="T")) - parseVDR(list,channelNo); - else if ((type == QPSK) && (str=="S")) - parseVDR(list,channelNo); - else if ((type == QAM) && (str=="C")) - parseVDR(list,channelNo); - } - else if (type==OFDM) - parseConfOFDM(list); - else if (type==ATSC) - parseConfATSC(list); - else if (type==QPSK) - parseConfQPSK(list); - else if (type==QAM) - parseConfQAM(list); + channelNo = str.mid(1).toInt(); + line = stream.readLine(); + list = QStringList::split(":", line); } - file.close(); - processChannels(); - return OK; + str = *list.at(3); + + if ((str == "T") || (str == "C") || (str == "S")) + { + if ((type == OFDM) && (str == "T")) + ok &= ParseVDR(list, channelNo); + else if ((type == QPSK) && (str == "S")) + ok &= ParseVDR(list, channelNo); + else if ((type == QAM) && (str == "C")) + ok &= ParseVDR(list, channelNo); + } + else if (type == OFDM) + ok &= ParseConfOFDM(list); + else if (type == ATSC) + ok &= ParseConfATSC(list); + else if (type == QPSK) + ok &= ParseConfQPSK(list); + else if (type == QAM) + ok &= ParseConfQAM(list); } - return ERROR_OPEN; + file.close(); + + return (ok) ? OK : ERROR_PARSE; } -bool DVBConfParser::parseConfOFDM(QStringList& tokens) +bool DTVConfParser::ParseConfOFDM(const QStringList &tokens) { - Channel c; - QStringList::Iterator i = tokens.begin(); - QStringList::Iterator end = tokens.end(); - if (i != end) c.name = *i++; else return false; - if (i != end) c.frequency = (*i++).toInt(); else return false; - if (i == end || !c.inversion.parseConf(*i++)) return false; - if (i == end || !c.bandwidth.parseConf(*i++)) return false; - if (i == end || !c.coderate_hp.parseConf(*i++)) return false; - if (i == end || !c.coderate_lp.parseConf(*i++)) return false; - if (i == end || !c.constellation.parseConf(*i++)) return false; - if (i == end || !c.transmit_mode.parseConf(*i++)) return false; - if (i == end || !c.guard_interval.parseConf(*i++)) return false; - if (i == end || !c.hierarchy.parseConf(*i++)) return false; - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i != end) c.serviceid = (*i++).toInt(); else return false; + DTVChannelInfo chan; + DTVMultiplex mux; - channels.append(c); + QStringList::const_iterator it = tokens.begin(); - return true; -} - -bool DVBConfParser::parseConfATSC(QStringList& tokens) -{ - Channel c; - QStringList::Iterator i = tokens.begin(); - QStringList::Iterator end = tokens.end(); - if (i != end) c.name = *i++; else return false; - if (i != end) c.frequency = (*i++).toInt(); else return false; - if (i == end || !c.modulation.parseConf(*i++)) return false; - // We need the program number in the transport stream, - // otherwise we cannot "tune" to the program. - if (i == end ) return false; else i++; // Ignore video pid - if (i == end ) return false; else i++; // Ignore audio pid - if (i != end) c.serviceid = (*i++).toInt(); else return false; + PARSE_SKIP(unknown); + PARSE_UINT(mux.frequency); + PARSE_CONF(mux.inversion); + PARSE_CONF(mux.bandwidth); + PARSE_CONF(mux.hp_code_rate); + PARSE_CONF(mux.lp_code_rate); + PARSE_CONF(mux.constellation); + PARSE_CONF(mux.trans_mode); + PARSE_CONF(mux.guard_interval); + PARSE_CONF(mux.hierarchy); + PARSE_SKIP(unknown); + PARSE_SKIP(unknown); + PARSE_UINT(chan.serviceid); - channels.append(c); + AddChannel(mux, chan); return true; } -bool DVBConfParser::parseConfQAM(QStringList& tokens) +bool DTVConfParser::ParseConfATSC(const QStringList &tokens) { - Channel c; - QStringList::Iterator i = tokens.begin(); - QStringList::Iterator end = tokens.end(); - - if (i != end) c.name = *i++; else return false; - if (i != end) c.frequency = (*i++).toInt(); else return false; - if (i == end || !c.inversion.parseConf(*i++)) return false; - if (i != end) c.symbolrate = (*i++).toInt(); else return false; - if (i == end || !c.fec.parseConf(*i++)) return false; - if (i == end || !c.modulation.parseConf(*i++)) return false; - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i != end) c.serviceid = (*i++).toInt(); else return false; - - channels.append(c); - return true; -} + DTVChannelInfo chan; + DTVMultiplex mux; -bool DVBConfParser::parseConfQPSK(QStringList& tokens) -{ - Channel c; - QStringList::iterator it = tokens.begin(); + QStringList::const_iterator it = tokens.begin(); - if (it != tokens.end()) - c.name = *it++; - else - return false; + PARSE_STR(chan.name); + PARSE_UINT(mux.frequency); + PARSE_CONF(mux.modulation); + PARSE_SKIP(Ignore_Video_PID); + PARSE_SKIP(Ignore_Audio_PID); + PARSE_UINT(chan.serviceid); - if (it != tokens.end()) - c.frequency = (*it++).toUInt() * 1000; - else - return false; + AddChannel(mux, chan); - if (it == tokens.end() || !c.polarity.parseConf(*it++)) - return false; + return true; +} - if (it == tokens.end()) - return false; - else - it++; //Sat num +bool DTVConfParser::ParseConfQAM(const QStringList &tokens) +{ + DTVChannelInfo chan; + DTVMultiplex mux; - if (it != tokens.end()) - c.symbolrate = (*it++).toUInt() * 1000; - else - return false; + QStringList::const_iterator it = tokens.begin(); - if (it == tokens.end()) - return false; - else - it++; + PARSE_SKIP(unknown); + PARSE_UINT(mux.frequency); + PARSE_CONF(mux.inversion); + PARSE_UINT(mux.symbolrate); + PARSE_CONF(mux.fec); + PARSE_CONF(mux.modulation); + PARSE_SKIP(unknown); + PARSE_SKIP(unknown); + PARSE_UINT(chan.serviceid); - if (it == tokens.end()) - return false; - else - it++; + AddChannel(mux, chan); - if (it != tokens.end()) - c.serviceid = (*it++).toInt(); - else - return false; - - channels.append(c); return true; } -bool DVBConfParser::parseVDR(QStringList& tokens, int channelNo) +bool DTVConfParser::ParseConfQPSK(const QStringList &tokens) { - Channel c; - QStringList::Iterator i = tokens.begin(); - QStringList::Iterator end = tokens.end(); - c.lcn = channelNo; - -//BBC ONE:754166:I999B8C34D34M16T2G32Y0:T:27500:600:601,602:0:0:4168:0:0:0 - if (i != end) c.name = *i++; else return false; - if (i != end) c.frequency = (*i++).toInt()*1000; else return false; - if (i == end) return false; - QString params = (*i++); - while (!params.isEmpty()) - { - QString ori = params; - int s = *(const char*)params; - params=params.mid(1); - switch(s) - { - case 'I': - c.inversion.parseVDR(params); - break; - case 'B': - c.bandwidth.parseVDR(params); - break; - case 'C': - c.coderate_hp.parseVDR(params); - break; - case 'D': - c.coderate_lp.parseVDR(params); - break; - case 'M': - c.constellation.parseVDR(params); - break; - case 'T': - c.transmit_mode.parseVDR(params); - break; - case 'G': - c.guard_interval.parseVDR(params); - break; - case 'Y': - c.hierarchy.parseVDR(params); - break; - case 'V': - case 'H': - case 'R': - case 'L': - c.polarity.parseVDR(ori); - break; - default: - return false; - } - } - - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i == end ) return false; else i++; - if (i != end) c.serviceid = (*i++).toInt(); else return false; + DTVChannelInfo chan; + DTVMultiplex mux; + + QStringList::const_iterator it = tokens.begin(); + + PARSE_STR(chan.name); + PARSE_UINT_1000(mux.frequency); + PARSE_CONF(mux.polarity); + PARSE_SKIP(Satelite_Number); + PARSE_UINT_1000(mux.symbolrate); + PARSE_SKIP(unknown); + PARSE_SKIP(unknown); + PARSE_UINT(chan.serviceid); + + AddChannel(mux, chan); - channels.append(c); return true; } -int DVBConfParser::generateNewChanID(int sourceID) +bool DTVConfParser::ParseVDR(const QStringList &tokens, int channelNo) { - MSqlQuery query(MSqlQuery::InitCon()); + DTVChannelInfo chan; + DTVMultiplex mux; - QString theQuery = - QString("SELECT max(chanid) as maxchan " - "FROM channel WHERE sourceid=%1").arg(sourceID); - query.prepare(theQuery); + QStringList::const_iterator it = tokens.begin(); - if (!query.exec()) - MythContext::DBError("Calculating new ChanID", query); + chan.lcn = channelNo; - if (!query.isActive()) - MythContext::DBError("Calculating new ChanID for Analog Channel",query); +// BBC ONE:754166:I999B8C34D34M16T2G32Y0:T:27500:600:601, 602:0:0:4168:0:0:0 - query.next(); + PARSE_SKIP(unknown); - // If transport not present add it, and move on to the next - if (query.size() <= 0) - return sourceID * 1000; + PARSE_UINT_1000(mux.frequency); - int MaxChanID = query.value(0).toInt(); - - if (MaxChanID == 0) - return sourceID * 1000; - else - return MaxChanID + 1; -} - -int DVBConfParser::findMultiplex(const DVBConfParser::Multiplex& m) -{ - MSqlQuery query(MSqlQuery::InitCon()); - QString queryStr=QString("SELECT mplexid FROM dtv_multiplex WHERE " - "sourceid= %1 AND frequency=%2 AND inversion=\"%3\" AND ") - .arg(sourceid).arg(m.frequency).arg(m.inversion.toString()); + if (it == tokens.end()) + return false; - switch (type) - { - case OFDM: - queryStr+=QString("sistandard=\"dvb\" AND bandwidth=\"%1\" AND " - "hp_code_rate=\"%2\" AND " - "lp_code_rate=\"%3\" AND constellation=\"%4\" AND " - "transmission_mode=\"%5\" AND guard_interval=\"%6\" AND " - "hierarchy=\"%7\";") - .arg(m.bandwidth.toString()) - .arg(m.coderate_hp.toString()) - .arg(m.coderate_lp.toString()) - .arg(m.constellation.toString()) - .arg(m.transmit_mode.toString()) - .arg(m.guard_interval.toString()) - .arg(m.hierarchy.toString()); - break; - case QPSK: - queryStr+=QString("sistandard=\"dvb\" AND symbolrate=%1 AND " - "polarity=\"%2\";").arg(m.symbolrate) - .arg(m.polarity.toString()); - break; - case QAM: - queryStr+=QString("symbolrate=%1 AND modulation=\"%2\" AND fec=\"%3\";") - .arg(m.symbolrate) - .arg(m.modulation.toString()) - .arg(m.fec.toString()); - break; - case ATSC: - queryStr+=QString("modulation=\"%1\";") - .arg(m.modulation.toString()); - break; - } - query.prepare(queryStr); - if (!query.exec()) - MythContext::DBError("searching for transport", query); - if (!query.isActive()) - MythContext::DBError("searching for transport.", query); - if (query.size() > 0) + QString params = (*it++); + while (!params.isEmpty()) { - query.next(); - return query.value(0).toInt(); + QString ori = params; + int s = *((const char*)params); + params = params.mid(1); + switch (s) + { + case 'I': + mux.inversion.ParseVDR(params); + break; + case 'B': + mux.bandwidth.ParseVDR(params); + break; + case 'C': + mux.hp_code_rate.ParseVDR(params); + break; + case 'D': + mux.lp_code_rate.ParseVDR(params); + break; + case 'M': + mux.constellation.ParseVDR(params); + break; + case 'T': + mux.trans_mode.ParseVDR(params); + break; + case 'G': + mux.guard_interval.ParseVDR(params); + break; + case 'Y': + mux.hierarchy.ParseVDR(params); + break; + case 'V': + case 'H': + case 'R': + case 'L': + mux.polarity.ParseVDR(ori); + break; + default: + return false; + } } - return -1; -} -int DVBConfParser::findChannel(const DVBConfParser::Channel &c, int &mplexid) -{ - MSqlQuery query(MSqlQuery::InitCon()); + for (uint i = 0; i < 6; i++) + PARSE_SKIP(unknown); - // try to find exact match first - query.prepare("SELECT chanid " - "FROM channel " - "WHERE callsign = :CALLSIGN AND " - " mplexid = :MPLEXID AND " - " sourceid = :SOURCEID"); - query.bindValue(":MPLEXID", multiplexes[c.mplexnumber].mplexid); - query.bindValue(":SOURCEID", sourceid); - query.bindValue(":CALLSIGN", c.name.utf8()); - - if (!query.exec() || !query.isActive()) - MythContext::DBError("searching for channel", query); - else if (query.next()) - { - mplexid = multiplexes[c.mplexnumber].mplexid; - return query.value(0).toInt(); - } + PARSE_UINT(chan.serviceid); - // if we didn't find exact match, try to match just the source & callsign - query.prepare("SELECT chanid, mplexid " - "FROM channel " - "WHERE callsign = :CALLSIGN AND " - " sourceid = :SOURCEID"); - query.bindValue(":SOURCEID", sourceid); - query.bindValue(":CALLSIGN", c.name.utf8()); - - if (!query.exec() || !query.isActive()) - MythContext::DBError("searching for channel", query); - else if (query.next()) - { - mplexid = query.value(1).toInt(); - return query.value(0).toInt(); - } + AddChannel(mux, chan); - return -1; -} + return true; +} -void DVBConfParser::processChannels() +void DTVConfParser::AddChannel(const DTVMultiplex &mux, DTVChannelInfo &chan) { - ListChannels::iterator iter; - for (iter=channels.begin();iter!=channels.end();iter++) + for (uint i = 0; i < channels.size(); i++) { - bool fFound = false; - for (unsigned i=0;i 0) - { - query.next(); - multiplexes[i].mplexid = query.value(0).toInt(); - } - } - else - multiplexes[i].mplexid = mplexid; } - // If the channel number cannot be determined from the config - // file, assign temporary unique numbers. First determine the - // highest channel number already assigned. This will likely - // fail if there are any ATSC channels, since channum is not - // really an integer. But in that case 501 is a generally safe - // offset for the first unknown channel. - int maxchannum = 500; - query.prepare("SELECT MAX(channum) FROM channel"); - if (!query.exec() || !query.isActive()) - MythContext::DBError("Getting highest channel number.", query); - else if (query.next()) - maxchannum = max(maxchannum, query.value(0).toInt()); - for (iter = channels.begin(); iter != channels.end(); ++iter) - maxchannum = max(maxchannum, (*iter).lcn); + channels.push_back(mux); + channels.back().channels.push_back(chan); - // Now insert the channels - for (iter=channels.begin();iter!=channels.end();iter++) - { - int mplexid = multiplexes[(*iter).mplexnumber].mplexid; - int db_mplexid = 0; - int chanid = findChannel(*iter, db_mplexid); - if (chanid < 0) - { - // The channel does not exist in the DB at all, insert it. - query.prepare("INSERT INTO channel (chanid, channum, " - "sourceid, callsign, name, mplexid, " - "serviceid) " - "VALUES (:CHANID,:CHANNUM,:SOURCEID,:CALLSIGN," - ":NAME,:MPLEXID,:SERVICEID);"); - - // If the channel number is unknown, get next unique number - int channum = (*iter).lcn; - if (-1 == channum) - channum = ++maxchannum; - - int chanid = ChannelUtil::CreateChanID( - sourceid, QString::number(channum)); - - query.bindValue(":CHANID", chanid); - query.bindValue(":CHANNUM", channum); - query.bindValue(":SOURCEID", sourceid); - query.bindValue(":CALLSIGN", (*iter).name.utf8()); - query.bindValue(":NAME", (*iter).name.utf8()); - query.bindValue(":MPLEXID", mplexid); - query.bindValue(":SERVICEID", (*iter).serviceid); - if (!query.exec() || !query.isActive()) - { - MythContext::DBError("Adding new DVB Channel", query); - emit updateText(QObject::tr("Failed to add %1: DB error") - .arg((*iter).name)); - } - else - { - emit updateText(QObject::tr("Adding %1").arg((*iter).name)); - } - } - else if (db_mplexid == 32767) - { - // The channel in the database if from the listings provider amd - // does not have tuning information. Just fill in the tuning info. - query.prepare("UPDATE channel " - "SET mplexid = :MPLEXID, " - " serviceid = :SERVICEID " - "WHERE chanid = :CHANID AND " - " sourceid = :SOURCEID "); - - query.bindValue(":MPLEXID", mplexid); - query.bindValue(":SERVICEID", (*iter).serviceid); - query.bindValue(":CHANID", chanid); - query.bindValue(":SOURCEID", sourceid); - - if (!query.exec() || !query.isActive()) - { - MythContext::DBError("Updating DVB Channel", query); - emit updateText(QObject::tr("Failed to add %1: DB error") - .arg((*iter).name)); - } - else - { - emit updateText(QObject::tr("Updating %1").arg((*iter).name)); - } - } - else - emit updateText(QObject::tr("Skipping %1").arg((*iter).name)); - } + VERBOSE(VB_IMPORTANT, "Imported channel: "< -#include +// POSIX headers +#include +#include + +// C++ headers +#include +using namespace std; + +// Qt headers #include -#include -/** - * class DVBConfParser - * @brief parses channels.conf files into the mythtv structure +// MythTV headers +#include "dtvconfparserhelpers.h" + +class QStringList; + +class DTVMultiplex +{ + public: + DTVMultiplex() : frequency(0), symbolrate(0) { } + + bool operator==(const DTVMultiplex &m) const; + + QString toString() const; + + public: + uint64_t frequency; + uint symbolrate; + DTVInversion inversion; + DTVBandwidth bandwidth; + DTVCodeRate hp_code_rate; + DTVCodeRate lp_code_rate; + DTVModulation constellation; + DTVModulation modulation; + DTVTransmitMode trans_mode; + DTVGuardInterval guard_interval; + DTVHierarchy hierarchy; + DTVPolarity polarity; + DTVCodeRate fec; +}; + +class DTVChannelInfo +{ + public: + DTVChannelInfo() : + name(QString::null), serviceid(0), lcn(-1) {} + + QString toString() const; + + public: + QString name; + uint serviceid; + int lcn; +}; +typedef vector DTVChannelInfoList; + +class DTVTransport : public DTVMultiplex +{ + public: + DTVTransport(const DTVMultiplex &other) : DTVMultiplex(other) { } + + public: + DTVChannelInfoList channels; +}; +typedef vector DTVChannelList; + +/** \class DTVConfParser + * \brief Parses dvb-utils channel scanner output files. */ -class DVBConfParser : public QObject +class DTVConfParser { - Q_OBJECT -protected: - class Multiplex - { - public: - Multiplex() : frequency(0),symbolrate(0),mplexid(0) {} - bool operator==(const Multiplex& m) const; - - unsigned frequency; - unsigned symbolrate; - DVBInversion inversion; - DVBBandwidth bandwidth; - DVBCodeRate coderate_hp; - DVBCodeRate coderate_lp; - DVBModulation constellation; - DVBModulation modulation; - DVBTransmitMode transmit_mode; - DVBGuardInterval guard_interval; - DVBHierarchy hierarchy; - DVBPolarity polarity; - DVBCodeRate fec; - unsigned mplexid; - - void dump(); - }; - - class Channel : public Multiplex - { - public: - Channel() : serviceid(0),mplexnumber(0), lcn(-1) {} - - QString name; - unsigned serviceid; - unsigned mplexnumber; - int lcn; - - void dump(); - }; - - typedef QValueList ListChannels; - - ListChannels channels; - QValueVector multiplexes; - -public: - enum RETURN {ERROR_OPEN,ERROR_PARSE,OK}; - enum TYPE {ATSC,OFDM,QPSK,QAM}; - - DVBConfParser(enum TYPE _type,unsigned sourceid, const QString& _file); - virtual ~DVBConfParser() {}; - int parse(); - -signals: - /** @brief Status message from the scan engine - @param status the message - */ - void updateText(const QString& status); -protected: - QString filename; - TYPE type; - unsigned sourceid; - bool parseVDR(QStringList& tokens, int channelNo = -1); - bool parseConf(QStringList& tokens); - bool parseConfOFDM(QStringList& tokens); - bool parseConfQPSK(QStringList& tokens); - bool parseConfQAM(QStringList& tokens); - bool parseConfATSC(QStringList& tokens); - void processChannels(); - int findMultiplex(const Multiplex& m); - int findChannel(const Channel& c, int& mplexid); - int generateNewChanID(int sourceID); + public: + enum return_t { ERROR_OPEN, ERROR_PARSE, OK }; + enum cardtype_t { ATSC, OFDM, QPSK, QAM, UNKNOWN }; + + DTVConfParser(enum cardtype_t _type, uint sourceid, const QString &_file); + virtual ~DTVConfParser() { } + + return_t Parse(void); + + DTVChannelList GetChannels(void) const { return channels; } + + private: + bool ParseVDR( const QStringList &tokens, int channelNo = -1); + bool ParseConf( const QStringList &tokens); + bool ParseConfOFDM(const QStringList &tokens); + bool ParseConfQPSK(const QStringList &tokens); + bool ParseConfQAM( const QStringList &tokens); + bool ParseConfATSC(const QStringList &tokens); + + private: + cardtype_t type; + uint sourceid; + QString filename; + + void AddChannel(const DTVMultiplex &mux, DTVChannelInfo &chan); + + DTVChannelList channels; }; -#endif +#endif // _DTVCONFPARSER_H_ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbdev/dvbci.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbdev/dvbci.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbdev/dvbci.cpp 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbdev/dvbci.cpp 2007-08-22 13:51:58.000000000 -0500 @@ -978,6 +978,13 @@ d += 2; l -= 2; if (numCaSystemIds < MAXCASYSTEMIDS) { + int i; + // Make sure the id is not already present + for (i = 0; i < numCaSystemIds; i++) + if (caSystemIds[i] == id) + break; + if (i < numCaSystemIds) + continue; caSystemIds[numCaSystemIds++] = id; caSystemIds[numCaSystemIds] = 0; } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbrecorder.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbrecorder.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbrecorder.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbrecorder.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -66,9 +66,11 @@ #include "dvbrecorder.h" // AVLib/FFMPEG includes +extern "C" { #include "../libavcodec/avcodec.h" #include "../libavformat/avformat.h" #include "../libavformat/mpegts.h" +} const int DVBRecorder::TSPACKETS_BETWEEN_PSIP_SYNC = 2000; const int DVBRecorder::POLL_INTERVAL = 50; // msec @@ -349,6 +351,7 @@ if (fd_tmp < 0) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Could not open demux device." + ENO); + _max_pid_filters = _open_pid_filters; return -1; } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbtypes.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbtypes.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbtypes.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbtypes.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -8,317 +8,6 @@ static QString mod2dbstr(fe_modulation mod); static QString coderate(fe_code_rate_t coderate); -DVBParamHelper::Table DVBPolarity::parseTable[] = -{ - {"v",Vertical}, - {"h",Horizontal}, - {"r",Right}, - {"l",Left}, - {NULL,Vertical} -}; - -char *DVBPolarity::stringLookup[] = -{ - "v", // Vertical - "h", // Horizontal - "r", // Right - "l" // Left -}; - -DVBParamHelper::Table DVBInversion::confTable[] = -{ - {"INVERSION_AUTO",INVERSION_AUTO}, - {"INVERSION_OFF",INVERSION_OFF}, - {"INVERSION_ON",INVERSION_ON}, - {NULL,INVERSION_AUTO} -}; - -DVBParamHelper::Table DVBInversion::vdrTable[] = -{ - {"999",INVERSION_AUTO}, - {"0",INVERSION_OFF}, - {"1",INVERSION_ON}, - {NULL,INVERSION_AUTO} -}; - -DVBParamHelper::Table DVBInversion::parseTable[] = -{ - {"a",INVERSION_AUTO}, - {"0",INVERSION_OFF}, - {"1",INVERSION_ON}, - {NULL,INVERSION_AUTO} -}; - -char* DVBInversion::stringLookup[] = -{ - "0", // INVERSION_OFF, - "1", // INVERSION_ON, - "a" //INVERSION_AUTO -}; - -DVBParamHelper::Table DVBBandwidth::confTable[] = -{ - {"BANDWIDTH_AUTO",BANDWIDTH_AUTO}, - {"BANDWIDTH_8_MHZ",BANDWIDTH_8_MHZ}, - {"BANDWIDTH_7_MHZ",BANDWIDTH_7_MHZ}, - {"BANDWIDTH_6_MHZ",BANDWIDTH_6_MHZ}, - {NULL,BANDWIDTH_AUTO} -}; - -DVBParamHelper::Table DVBBandwidth::vdrTable[] = -{ - {"999",BANDWIDTH_AUTO}, - {"8",BANDWIDTH_8_MHZ}, - {"7",BANDWIDTH_7_MHZ}, - {"6",BANDWIDTH_6_MHZ}, - {NULL,BANDWIDTH_AUTO}, -}; - -DVBParamHelper::Table DVBBandwidth::parseTable[] = -{ - {"auto",BANDWIDTH_AUTO}, - {"8",BANDWIDTH_8_MHZ}, - {"7",BANDWIDTH_7_MHZ}, - {"6",BANDWIDTH_6_MHZ}, - {NULL,BANDWIDTH_AUTO} -}; - -char *DVBBandwidth::stringLookup[]= -{ - "8", //BANDWIDTH_8_MHZ, - "7", //BANDWIDTH_7_MHZ, - "6", //BANDWIDTH_6_MHZ, - "auto" //BANDWIDTH_AUTO -}; - -DVBParamHelper::Table DVBCodeRate::confTable[] = -{ - {"FEC_AUTO",FEC_AUTO}, - {"FEC_1_2",FEC_1_2}, - {"FEC_2_3",FEC_2_3}, - {"FEC_3_4",FEC_3_4}, - {"FEC_4_5",FEC_4_5}, - {"FEC_5_6",FEC_5_6}, - {"FEC_6_7",FEC_6_7}, - {"FEC_7_8",FEC_7_8}, - {"FEC_8_9",FEC_8_9}, - {"FEC_NONE",FEC_NONE}, - {NULL,FEC_AUTO} -}; - -DVBParamHelper::Table DVBCodeRate::vdrTable[] = -{ - {"999",FEC_AUTO}, - {"12",FEC_1_2}, - {"23",FEC_2_3}, - {"34",FEC_3_4}, - {"45",FEC_4_5}, - {"56",FEC_5_6}, - {"67",FEC_6_7}, - {"78",FEC_7_8}, - {"89",FEC_8_9}, - {"0",FEC_NONE}, - {NULL,FEC_AUTO} -}; - -DVBParamHelper::Table DVBCodeRate::parseTable[] = -{ - {"auto",FEC_AUTO}, - {"1/2",FEC_1_2}, - {"2/3",FEC_2_3}, - {"3/4",FEC_3_4}, - {"4/5",FEC_4_5}, - {"5/6",FEC_5_6}, - {"6/7",FEC_6_7}, - {"7/8",FEC_7_8}, - {"8/9",FEC_8_9}, - {"none",FEC_NONE}, - {NULL,FEC_AUTO} -}; - -char *DVBCodeRate::stringLookup[] = -{ - "none", //FEC_NONE, - "1/2", //FEC_1_2, - "2/3", //FEC_2_3, - "3/4", //FEC_3_4, - "4/5", //FEC_4_5, - "5/6", //FEC_5_6, - "6/7", //FEC_6_7, - "7/8", //FEC_7_8, - "8/9", //FEC_8_9, - "auto" //FEC_AUTO -}; - -DVBParamHelper::Table DVBModulation::confTable[] = -{ - {"QAM_AUTO",QAM_AUTO}, - {"QAM_16",QAM_16}, - {"QAM_32",QAM_32}, - {"QAM_64",QAM_64}, - {"QAM_128",QAM_128}, - {"QAM_256",QAM_256}, - {"QPSK",QPSK}, -#ifdef FE_GET_EXTENDED_INFO - {"8PSK", MOD_8PSK}, -#endif - {NULL,QAM_AUTO}, -}; - -DVBParamHelper::Table DVBModulation::vdrTable[] = -{ - {"999",QAM_AUTO}, - {"16",QAM_16}, - {"32",QAM_32}, - {"64",QAM_64}, - {"128",QAM_128}, - {"256",QAM_256}, - {"0",QPSK}, - {NULL,QAM_AUTO}, -}; - -DVBParamHelper::Table DVBModulation::parseTable[] = -{ - {"auto",QAM_AUTO}, - {"qam_16",QAM_16}, - {"qam_32",QAM_32}, - {"qam_64",QAM_64}, - {"qam_128",QAM_128}, - {"qam_256",QAM_256}, - {"qpsk",QPSK}, - {"8vsb",VSB_8}, - {"16vsb",VSB_16}, -#ifdef FE_GET_EXTENDED_INFO - {"8psk", MOD_8PSK}, -#endif - {NULL,QAM_AUTO}, -}; - -char *DVBModulation::stringLookup[] = -{ - "qpsk", //QPSK, - "qam_16", //QAM_16, - "qam_32", //QAM_32, - "qam_64", //QAM_64, - "qam_128", //QAM_128, - "qam_256", //QAM_256, - "auto", //QAM_AUTO, - "8vsb", //VSB_8, - "16vsb", //VSB_16 -#ifdef FE_GET_EXTENDED_INFO - "8psk", //MOD_8PSK -#endif -}; - -DVBParamHelper::Table DVBTransmitMode::confTable[] = -{ - {"TRANSMISSION_MODE_AUTO",TRANSMISSION_MODE_AUTO}, - {"TRANSMISSION_MODE_2K",TRANSMISSION_MODE_2K}, - {"TRANSMISSION_MODE_8K",TRANSMISSION_MODE_8K}, - {NULL,TRANSMISSION_MODE_AUTO}, -}; - -DVBParamHelper::Table DVBTransmitMode::vdrTable[] = -{ - {"999",TRANSMISSION_MODE_AUTO}, - {"2",TRANSMISSION_MODE_2K}, - {"8",TRANSMISSION_MODE_8K}, - {NULL,TRANSMISSION_MODE_AUTO}, -}; - -DVBParamHelper::Table DVBTransmitMode::parseTable[] = -{ - {"auto",TRANSMISSION_MODE_AUTO}, - {"2",TRANSMISSION_MODE_2K}, - {"8",TRANSMISSION_MODE_8K}, - {NULL,TRANSMISSION_MODE_AUTO}, -}; - -char *DVBTransmitMode::stringLookup[] = -{ - "2", //TRANSMISSION_MODE_2K, - "8", //TRANSMISSION_MODE_8K, - "auto" //TRANSMISSION_MODE_AUTO -}; - -DVBParamHelper::Table DVBGuardInterval::confTable[] = -{ - {"GUARD_INTERVAL_AUTO",GUARD_INTERVAL_AUTO}, - {"GUARD_INTERVAL_1_32",GUARD_INTERVAL_1_32}, - {"GUARD_INTERVAL_1_16",GUARD_INTERVAL_1_16}, - {"GUARD_INTERVAL_1_8",GUARD_INTERVAL_1_8}, - {"GUARD_INTERVAL_1_4",GUARD_INTERVAL_1_4}, - {NULL,GUARD_INTERVAL_AUTO}, -}; - -DVBParamHelper::Table DVBGuardInterval::vdrTable[] = -{ - {"999",GUARD_INTERVAL_AUTO}, - {"32",GUARD_INTERVAL_1_32}, - {"16",GUARD_INTERVAL_1_16}, - {"8",GUARD_INTERVAL_1_8}, - {"4",GUARD_INTERVAL_1_4}, - {NULL,GUARD_INTERVAL_AUTO}, -}; - -DVBParamHelper::Table DVBGuardInterval::parseTable[] = -{ - {"auto",GUARD_INTERVAL_AUTO}, - {"1/32",GUARD_INTERVAL_1_32}, - {"1/16",GUARD_INTERVAL_1_16}, - {"1/8",GUARD_INTERVAL_1_8}, - {"1/4",GUARD_INTERVAL_1_4}, - {NULL,GUARD_INTERVAL_AUTO}, -}; - -char *DVBGuardInterval::stringLookup[] = -{ - "1/32", // GUARD_INTERVAL_1_32, - "1/16", // GUARD_INTERVAL_1_16, - "1/8", // GUARD_INTERVAL_1_8, - "1/4", // GUARD_INTERVAL_1_4, - "auto" // GUARD_INTERVAL_AUTO -}; - -DVBParamHelper::Table DVBHierarchy::confTable[] = -{ - {"HIERARCHY_NONE",HIERARCHY_NONE}, - {"HIERARCHY_1",HIERARCHY_1}, - {"HIERARCHY_2",HIERARCHY_2}, - {"HIERARCHY_4",HIERARCHY_4}, - {"HIERARCHY_AUTO",HIERARCHY_AUTO}, - {NULL,HIERARCHY_AUTO}, -}; - -DVBParamHelper::Table DVBHierarchy::vdrTable[] = -{ - {"0",HIERARCHY_NONE}, - {"1",HIERARCHY_1}, - {"2",HIERARCHY_2}, - {"4",HIERARCHY_4}, - {"999",HIERARCHY_AUTO}, - {NULL,HIERARCHY_AUTO}, -}; - -DVBParamHelper::Table DVBHierarchy::parseTable[] = -{ - {"n",HIERARCHY_NONE}, - {"1",HIERARCHY_1}, - {"2",HIERARCHY_2}, - {"4",HIERARCHY_4}, - {"a",HIERARCHY_AUTO}, - {NULL,HIERARCHY_AUTO}, -}; - -char *DVBHierarchy::stringLookup[] = -{ - "n", //HIERARCHY_NONE, - "1", //HIERARCHY_1, - "2", //HIERARCHY_2, - "4", //HIERARCHY_4, - "a" //HIERARCHY_AUTO -}; - bool equal_qpsk(const struct dvb_fe_params &p, const struct dvb_fe_params &op, uint range) { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbtypes.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbtypes.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/dvbtypes.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/dvbtypes.h 2007-04-13 23:59:17.000000000 -0500 @@ -67,225 +67,6 @@ QString toString(fe_status); QString toString(const struct dvb_frontend_event&, const fe_type_t); -//The following are a set of helper classes to allow easy translation -//between the actual dvb enums and db strings. - -//Helper abstract template to do some of the mundain bits -//of translating the DVBParamHelpers -template class DVBParamHelper -{ -protected: - V value; - - struct Table - { - QString symbol; - V value; - }; - - static bool parseParam(QString& symbol, V& value, Table *table) - { - Table *p = table; - while (p->symbol!=NULL) - { - if (p->symbol==symbol.left(p->symbol.length())) - { - symbol=symbol.mid(p->symbol.length()); - value = p->value; - return true; - } - p++; - } - return false; - } - -public: - DVBParamHelper(V _value) : value(_value) {} - - operator V() const { return value; } - V operator=(V _value) {return value = _value;} - bool operator==(const V& v) const {return value == v;} -}; - -class DVBInversion : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBInversion() : DVBParamHelper(INVERSION_AUTO) {} - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_spectral_inversion_t _value) - {return stringLookup[_value];} -}; - -class DVBBandwidth : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBBandwidth() : DVBParamHelper(BANDWIDTH_AUTO) {} - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_bandwidth_t _value) - {return stringLookup[_value];} -}; - -class DVBCodeRate : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBCodeRate() : DVBParamHelper(FEC_AUTO) {} - - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_code_rate_t _value) - {return stringLookup[_value];} -}; - -class DVBModulation : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBModulation() : DVBParamHelper(QAM_AUTO) {} - - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_modulation_t _value) - {return stringLookup[_value];} -}; - -class DVBTransmitMode : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBTransmitMode() : DVBParamHelper(TRANSMISSION_MODE_AUTO) {} - - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_transmit_mode_t _value) - {return stringLookup[_value];} -}; - -class DVBGuardInterval : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBGuardInterval() : DVBParamHelper(GUARD_INTERVAL_AUTO) {} - - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_guard_interval_t _value) - {return stringLookup[_value];} -}; - -class DVBHierarchy : public DVBParamHelper -{ -protected: - static Table confTable[]; - static Table vdrTable[]; - static Table parseTable[]; - static char* stringLookup[]; - -public: - DVBHierarchy() : DVBParamHelper(HIERARCHY_AUTO) {} - - bool parseConf(QString& _value) - {return parseParam(_value,value,confTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,vdrTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(fe_hierarchy_t _value) - {return stringLookup[_value];} -}; - -enum PolarityValues {Vertical,Horizontal,Right,Left}; -class DVBPolarity : public DVBParamHelper -{ -protected: - static Table parseTable[]; - static char* stringLookup[]; -public: - DVBPolarity() : DVBParamHelper(Vertical) { } - - bool parseConf(QString& _value) - {return parseParam(_value,value,parseTable);} - bool parseVDR(QString& _value) - {return parseParam(_value,value,parseTable);} - bool parse(QString& _value) - {return parseParam(_value,value,parseTable);} - - QString toString() const {return toString(value);} - static QString toString(PolarityValues _value) - {return stringLookup[_value];} -}; typedef vector dvb_pid_t; // needs to add provider id so dvbcam doesnt require parsing @@ -398,7 +179,6 @@ const QString& pol, const QString& modulation); #endif - private: bool ParseTuningParams( fe_type_t type, QString frequency, QString inversion, QString symbolrate, diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/DVDRingBuffer.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/DVDRingBuffer.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/DVDRingBuffer.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/DVDRingBuffer.cpp 2007-04-17 10:15:28.000000000 -0500 @@ -802,6 +802,34 @@ decode_rle(bitmap + w, w * 2, w, h / 2, spu_pkt, offset2 * 2, buf_size); guess_palette(sub->rects[0].rgba_palette, palette, alpha); + if (!IsInMenu() && y1 < 5) + { + uint8_t *tmp_bitmap; + int sy; + bool found = false; + for (sy = 0; sy < h && !found; ++sy) + { + for (int tmpx = 0; tmpx < w; ++tmpx) + { + const uint8_t color = bitmap[sy * w + tmpx]; + if (color > 0) + { + found = true; + break; + } + } + } + + int newh = h - sy; + tmp_bitmap = (uint8_t*) av_malloc(w * newh); + memcpy(tmp_bitmap, bitmap + (w * sy), (w * newh)); + av_free(bitmap); + y1 = sy + y1; + h = newh; + bitmap = (uint8_t*) av_malloc(w * h); + memcpy(bitmap, tmp_bitmap, (w * h)); + av_free(tmp_bitmap); + } sub->rects[0].bitmap = bitmap; sub->rects[0].x = x1; sub->rects[0].y = y1; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/DVDRingBuffer.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/DVDRingBuffer.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/DVDRingBuffer.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/DVDRingBuffer.h 2007-08-22 13:46:08.000000000 -0500 @@ -9,7 +9,9 @@ #include #include #include "util.h" +extern "C" { #include "../libavcodec/avcodec.h" +} #define DVDNAV_COMPILE #include "../libmythdvdnav/dvdnav.h" diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/eit.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/eit.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/eit.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/eit.cpp 2007-08-22 13:49:45.000000000 -0500 @@ -297,7 +297,7 @@ if (lcategory.isEmpty() && !match.category.isEmpty()) lcategory = match.category; - if (lairdate.isEmpty() && !match.airdate.isEmpty() && !match.airdate == '0') + if (lairdate.isEmpty() && !match.airdate.isEmpty() && match.airdate != '0') lairdate = match.airdate; if (!loriginalairdate.isValid() && match.originalairdate.isValid()) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/eithelper.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/eithelper.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/eithelper.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/eithelper.cpp 2007-07-24 14:27:09.000000000 -0500 @@ -535,30 +535,50 @@ fix[ 4096 << 16] = EITFixUp::kFixAUStar; fix[ 4096 << 16] = EITFixUp::kFixAUStar; - fix[ 769LL << 32 | 8468 << 16] = EITFixUp::kEFixPro7Sat; // DVB-T Berlin - fix[ 3074LL << 32 | 8468 << 16] = EITFixUp::kEFixPro7Sat; // DVB-T Hamburg - fix[ 3075LL << 32 | 8468 << 16] = EITFixUp::kEFixPro7Sat; // DVB-T Bremen - fix[ 8705LL << 32 | 8468 << 16] = EITFixUp::kEFixPro7Sat; // DVB-T Hessen - fix[13057LL << 32 | 8468 << 16] = EITFixUp::kEFixPro7Sat; // DVB-T Munich - // DVB-C germany: Kabel Deutschland encoding fixes fix[ 112LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10000LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10001LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10002LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10003LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; - fix[ 10004LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10005LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10006LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; - fix[ 10008LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; fix[ 10009LL << 32 | 61441U << 16] = EITFixUp::kEFixPro7Sat; - // on the multiplex with RTL only following channels must be fixed - fix[ 10007LL << 32 | 61441U << 16 | 53605] = EITFixUp::kEFixPro7Sat; //terranova - fix[ 10007LL << 32 | 61441U << 16 | 53607] = EITFixUp::kEFixPro7Sat; //Eurosport - fix[ 10007LL << 32 | 61441U << 16 | 53608] = EITFixUp::kEFixPro7Sat; //Das Vierte - fix[ 10007LL << 32 | 61441U << 16 | 53609] = EITFixUp::kEFixPro7Sat; //Viva + // On transport 10004 only DMAX needs no fixing: + fix[ 10004LL<<32 | 61441U << 16 | 50403] = // BBC World Service + fix[10004LL<<32 | 61441U << 16 | 53101] = // BBC Prime (engl) + fix[10004LL<<32 | 61441U << 16 | 53108] = // Toon Disney (engl) + fix[10004LL<<32 | 61441U << 16 | 53109] = // Sky News (engl) + fix[10004LL<<32 | 61441U << 16 | 53406] = // BBC Prime + fix[10004LL<<32 | 61441U << 16 | 53407] = // Boomerang (engl) + fix[10004LL<<32 | 61441U << 16 | 53404] = // Boomerang + fix[10004LL<<32 | 61441U << 16 | 53408] = // TCM Classic Movies (engl) + fix[10004LL<<32 | 61441U << 16 | 53409] = // Extreme Sports + fix[10004LL<<32 | 61441U << 16 | 53410] = // CNBC Europe (engl) + fix[10004LL<<32 | 61441U << 16 | 53503] = // Detski Mir + fix[10004LL<<32 | 61441U << 16 | 53411] = // Sat.1 Comedy + fix[10004LL<<32 | 61441U << 16 | 53412] = // kabel eins classics + fix[10004LL<<32 | 61441U << 16 | 53112] = // Extreme Sports (engl) + fix[10004LL<<32 | 61441U << 16 | 53513] = // Playhouse Disney (engl) + fix[10004LL<<32 | 61441U << 16 | 53618] = // K1010 + fix[10004LL<<32 | 61441U << 16 | 53619] = // GemsTV + EITFixUp::kEFixPro7Sat; + // On transport 10007 only following channels need fixing: + fix[ 10007LL<<32| 61441U << 16 | 53605] = // terranova + fix[10007LL<<32| 61441U << 16 | 53607] = // Eurosport + fix[10007LL<<32| 61441U << 16 | 53608] = // Das Vierte + fix[10007LL<<32| 61441U << 16 | 53609] = // Viva + fix[10007LL<<32| 61441U << 16 | 53628] = // COMEDY CENTRAL + EITFixUp::kEFixPro7Sat; + // On transport 10008 only following channels need fixing: + fix[ 10008LL<<32 | 61441U << 16 | 53002] = // Tele 5 + fix[10008LL<<32 | 61441U << 16 | 53624] = // DSF + fix[10008LL<<32 | 61441U << 16 | 53630] = // HSE24 + EITFixUp::kEFixPro7Sat; fix[ 774LL << 32 | 8468 << 16 | 16392] = EITFixUp::kEFixPro7Sat; //DVB-T Berlin dsf + fix[ 772LL << 32 | 8468 << 16 | 16387] = EITFixUp::kEFixPro7Sat; //DVB-T Berlin HSE/MonA TV + fix[8707LL << 32 | 8468 << 16 | 16413] = EITFixUp::kEFixPro7Sat; //DVB-T Ruhrgebiet Tele 5 fix[1082LL << 32 | 1 << 16 | 20001] = EITFixUp::kEFixPro7Sat; //DVB-S Pro7 Swiss fix[1082LL << 32 | 1 << 16 | 20002] = EITFixUp::kEFixPro7Sat; //DVB-S Pro7 Austria fix[1082LL << 32 | 1 << 16 | 20003] = EITFixUp::kEFixPro7Sat; //DVB-S Kabel1 Swiss diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/frequencies.c /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/frequencies.c --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/frequencies.c 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/frequencies.c 2007-04-12 10:23:34.000000000 -0500 @@ -238,268 +238,268 @@ /* US HRC */ static struct CHANLIST ntsc_hrc[] = { - { "1", 72000 }, + { "1", 72004 }, - { "2", 54000 }, - { "3", 60000 }, - { "4", 66000 }, - - { "5", 78000 }, - { "6", 84000 }, - - { "7", 174000 }, - { "8", 180000 }, - { "9", 186000 }, - { "10", 192000 }, - { "11", 198000 }, - { "12", 204000 }, - { "13", 210000 }, - { "14", 120000 }, - { "15", 126000 }, - { "16", 132000 }, - { "17", 138000 }, - { "18", 144000 }, - { "19", 150000 }, - { "20", 156000 }, - { "21", 162000 }, - { "22", 168000 }, - { "23", 216000 }, - { "24", 222000 }, - { "25", 228000 }, - { "26", 234000 }, - { "27", 240000 }, - { "28", 246000 }, - { "29", 252000 }, - { "30", 258000 }, - { "31", 264000 }, - { "32", 270000 }, - { "33", 276000 }, - { "34", 282000 }, - { "35", 288000 }, - { "36", 294000 }, - { "37", 300000 }, - { "38", 306000 }, - { "39", 312000 }, - { "40", 318000 }, - { "41", 324000 }, - { "42", 330000 }, - { "43", 336000 }, - { "44", 342000 }, - { "45", 348000 }, - { "46", 354000 }, - { "47", 360000 }, - { "48", 366000 }, - { "49", 372000 }, - { "50", 378000 }, - { "51", 384000 }, - { "52", 390000 }, - { "53", 396000 }, - { "54", 402000 }, - { "55", 408000 }, - { "56", 414000 }, - { "57", 420000 }, - { "58", 426000 }, - { "59", 432000 }, - { "60", 438000 }, - { "61", 444000 }, - { "62", 450000 }, - { "63", 456000 }, - { "64", 462000 }, - { "65", 468000 }, - { "66", 474000 }, - { "67", 480000 }, - { "68", 486000 }, - { "69", 492000 }, - { "70", 498000 }, - { "71", 504000 }, - { "72", 510000 }, - { "73", 516000 }, - { "74", 522000 }, - { "75", 528000 }, - { "76", 534000 }, - { "77", 540000 }, - { "78", 546000 }, - { "79", 552000 }, - { "80", 558000 }, - { "81", 564000 }, - { "82", 570000 }, - { "83", 576000 }, - { "84", 582000 }, - { "85", 588000 }, - { "86", 594000 }, - { "87", 600000 }, - { "88", 606000 }, - { "89", 612000 }, - { "90", 618000 }, - { "91", 624000 }, - { "92", 630000 }, - { "93", 636000 }, - { "94", 642000 }, - - { "95", 90000 }, - { "96", 96000 }, - { "97", 102000 }, - { "98", 108000 }, - { "99", 114000 }, - - { "100", 648000 }, - { "101", 654000 }, - { "102", 660000 }, - { "103", 666000 }, - { "104", 672000 }, - { "105", 678000 }, - { "106", 684000 }, - { "107", 690000 }, - { "108", 696000 }, - { "109", 702000 }, - { "110", 708000 }, - { "111", 714000 }, - { "112", 720000 }, - { "113", 726000 }, - { "114", 732000 }, - { "115", 738000 }, - { "116", 744000 }, - { "117", 750000 }, - { "118", 756000 }, - { "119", 762000 }, - { "120", 768000 }, - { "121", 774000 }, - { "122", 780000 }, - { "123", 786000 }, - { "124", 792000 }, - { "125", 798000 }, + { "2", 54003 }, + { "3", 60003 }, + { "4", 66003 }, + + { "5", 78004 }, + { "6", 84004 }, + + { "7", 174009 }, + { "8", 180009 }, + { "9", 186009 }, + { "10", 192010 }, + { "11", 198010 }, + { "12", 204010 }, + { "13", 210011 }, + { "14", 120006 }, + { "15", 126006 }, + { "16", 132007 }, + { "17", 138007 }, + { "18", 144007 }, + { "19", 150008 }, + { "20", 156008 }, + { "21", 162008 }, + { "22", 168008 }, + { "23", 216011 }, + { "24", 222011 }, + { "25", 228011 }, + { "26", 234012 }, + { "27", 240012 }, + { "28", 246012 }, + { "29", 252013 }, + { "30", 258013 }, + { "31", 264013 }, + { "32", 270014 }, + { "33", 276014 }, + { "34", 282014 }, + { "35", 288014 }, + { "36", 294015 }, + { "37", 300015 }, + { "38", 306015 }, + { "39", 312016 }, + { "40", 318016 }, + { "41", 324016 }, + { "42", 330017 }, + { "43", 336017 }, + { "44", 342017 }, + { "45", 348017 }, + { "46", 354018 }, + { "47", 360018 }, + { "48", 366018 }, + { "49", 372019 }, + { "50", 378019 }, + { "51", 384019 }, + { "52", 390020 }, + { "53", 396020 }, + { "54", 402020 }, + { "55", 408020 }, + { "56", 414021 }, + { "57", 420021 }, + { "58", 426021 }, + { "59", 432022 }, + { "60", 438022 }, + { "61", 444022 }, + { "62", 450023 }, + { "63", 456023 }, + { "64", 462023 }, + { "65", 468023 }, + { "66", 474024 }, + { "67", 480024 }, + { "68", 486024 }, + { "69", 492025 }, + { "70", 498025 }, + { "71", 504025 }, + { "72", 510026 }, + { "73", 516026 }, + { "74", 522026 }, + { "75", 528026 }, + { "76", 534027 }, + { "77", 540027 }, + { "78", 546027 }, + { "79", 552028 }, + { "80", 558028 }, + { "81", 564028 }, + { "82", 570029 }, + { "83", 576029 }, + { "84", 582029 }, + { "85", 588029 }, + { "86", 594030 }, + { "87", 600030 }, + { "88", 606030 }, + { "89", 612031 }, + { "90", 618031 }, + { "91", 624031 }, + { "92", 630032 }, + { "93", 636032 }, + { "94", 642032 }, + + { "95", 90005 }, + { "96", 96005 }, + { "97", 102005 }, + { "98", 108005 }, + { "99", 114006 }, + + { "100", 648032 }, + { "101", 654033 }, + { "102", 660033 }, + { "103", 666033 }, + { "104", 672034 }, + { "105", 678034 }, + { "106", 684034 }, + { "107", 690035 }, + { "108", 696035 }, + { "109", 702035 }, + { "110", 708035 }, + { "111", 714036 }, + { "112", 720036 }, + { "113", 726036 }, + { "114", 732037 }, + { "115", 738037 }, + { "116", 744037 }, + { "117", 750038 }, + { "118", 756038 }, + { "119", 762038 }, + { "120", 768038 }, + { "121", 774039 }, + { "122", 780039 }, + { "123", 786039 }, + { "124", 792040 }, + { "125", 798040 }, }; /** US IRC http://www.jneuhaus.com/fccindex/cablech.html */ static struct CHANLIST ntsc_irc[] = { - { "1", 73250 }, - { "2", 55250 }, - { "3", 61250 }, - { "4", 67250 }, - { "5", 79250 }, - { "6", 85250 }, - { "7", 175250 }, - { "8", 181250 }, - { "9", 187250 }, - { "10", 193250 }, - { "11", 199250 }, - { "12", 205250 }, - - { "13", 211250 }, - { "14", 121250 }, - { "15", 127250 }, - { "16", 133250 }, - { "17", 139250 }, - { "18", 145250 }, - { "19", 151250 }, - { "20", 157250 }, + { "1", 73263 }, + { "2", 55263 }, + { "3", 61263 }, + { "4", 67263 }, + { "5", 79263 }, + { "6", 85263 }, + { "7", 175263 }, + { "8", 181263 }, + { "9", 187263 }, + { "10", 193263 }, + { "11", 199263 }, + { "12", 205263 }, + + { "13", 211263 }, + { "14", 121263 }, + { "15", 127263 }, + { "16", 133263 }, + { "17", 139263 }, + { "18", 145263 }, + { "19", 151263 }, + { "20", 157263 }, - { "21", 163250 }, - { "22", 169250 }, - { "23", 217250 }, - { "24", 223250 }, - { "25", 229250 }, - { "26", 235250 }, - { "27", 241250 }, - { "28", 247250 }, - { "29", 253250 }, - { "30", 259250 }, - { "31", 265250 }, - { "32", 271250 }, - { "33", 277250 }, - { "34", 283250 }, - { "35", 289250 }, - { "36", 295250 }, - { "37", 301250 }, - { "38", 307250 }, - { "39", 313250 }, - { "40", 319250 }, - { "41", 325250 }, - { "42", 331250 }, - { "43", 337250 }, - { "44", 343250 }, - { "45", 349250 }, - { "46", 355250 }, - { "47", 361250 }, - { "48", 367250 }, - { "49", 373250 }, - { "50", 379250 }, - { "51", 385250 }, - { "52", 391250 }, - { "53", 397250 }, - { "54", 403250 }, - { "55", 409250 }, - { "56", 415250 }, - { "57", 421250 }, - { "58", 427250 }, - { "59", 433250 }, - { "60", 439250 }, - { "61", 445250 }, - { "62", 451250 }, - { "63", 457250 }, - { "64", 463250 }, - { "65", 469250 }, - { "66", 475250 }, - { "67", 481250 }, - { "68", 487250 }, - { "69", 493250 }, + { "21", 163263 }, + { "22", 169263 }, + { "23", 217263 }, + { "24", 223263 }, + { "25", 229263 }, + { "26", 235263 }, + { "27", 241263 }, + { "28", 247263 }, + { "29", 253263 }, + { "30", 259263 }, + { "31", 265263 }, + { "32", 271263 }, + { "33", 277263 }, + { "34", 283263 }, + { "35", 289263 }, + { "36", 295263 }, + { "37", 301263 }, + { "38", 307263 }, + { "39", 313263 }, + { "40", 319263 }, + { "41", 325263 }, + { "42", 331275 }, + { "43", 337263 }, + { "44", 343263 }, + { "45", 349263 }, + { "46", 355263 }, + { "47", 361263 }, + { "48", 367263 }, + { "49", 373263 }, + { "50", 379263 }, + { "51", 385263 }, + { "52", 391263 }, + { "53", 397263 }, + { "54", 403263 }, + { "55", 409263 }, + { "56", 415263 }, + { "57", 421263 }, + { "58", 427263 }, + { "59", 433263 }, + { "60", 439263 }, + { "61", 445263 }, + { "62", 451263 }, + { "63", 457263 }, + { "64", 463263 }, + { "65", 469263 }, + { "66", 475263 }, + { "67", 481263 }, + { "68", 487263 }, + { "69", 493263 }, - { "70", 499250 }, - { "71", 505250 }, - { "72", 511250 }, - { "73", 517250 }, - { "74", 523250 }, - { "75", 529250 }, - { "76", 535250 }, - { "77", 541250 }, - { "78", 547250 }, - { "79", 553250 }, - { "80", 559250 }, - { "81", 565250 }, - { "82", 571250 }, - { "83", 577250 }, - { "84", 583250 }, - { "85", 589250 }, - { "86", 595250 }, - { "87", 601250 }, - { "88", 607250 }, - { "89", 613250 }, - { "90", 619250 }, - { "91", 625250 }, - { "92", 631250 }, - { "93", 637250 }, - { "94", 643250 }, - { "95", 91250 }, - { "96", 97250 }, - { "97", 103250 }, - { "98", 109250 }, - { "99", 115250 }, - { "100", 649250 }, - { "101", 655250 }, - { "102", 661250 }, - { "103", 667250 }, - { "104", 673250 }, - { "105", 679250 }, - { "106", 685250 }, - { "107", 691250 }, - { "108", 697250 }, - { "109", 703250 }, - { "110", 709250 }, - { "111", 715250 }, - { "112", 721250 }, - { "113", 727250 }, - { "114", 733250 }, - { "115", 739250 }, - { "116", 745250 }, - { "117", 751250 }, - { "118", 757250 }, - { "119", 763250 }, - { "120", 769250 }, - { "121", 775250 }, - { "122", 781250 }, - { "123", 787250 }, - { "124", 793250 }, - { "125", 799250 }, + { "70", 499263 }, + { "71", 505263 }, + { "72", 511263 }, + { "73", 517263 }, + { "74", 523263 }, + { "75", 529263 }, + { "76", 535263 }, + { "77", 541263 }, + { "78", 547263 }, + { "79", 553263 }, + { "80", 559263 }, + { "81", 565263 }, + { "82", 571263 }, + { "83", 577263 }, + { "84", 583263 }, + { "85", 589263 }, + { "86", 595263 }, + { "87", 601263 }, + { "88", 607263 }, + { "89", 613263 }, + { "90", 619263 }, + { "91", 625263 }, + { "92", 631263 }, + { "93", 637263 }, + { "94", 643263 }, + { "95", 91263 }, + { "96", 97263 }, + { "97", 103263 }, + { "98", 109275 }, + { "99", 115275 }, + { "100", 649263 }, + { "101", 655263 }, + { "102", 661263 }, + { "103", 667263 }, + { "104", 673263 }, + { "105", 679263 }, + { "106", 685263 }, + { "107", 691263 }, + { "108", 697263 }, + { "109", 703263 }, + { "110", 709263 }, + { "111", 715263 }, + { "112", 721263 }, + { "113", 727263 }, + { "114", 733263 }, + { "115", 739263 }, + { "116", 745263 }, + { "117", 751263 }, + { "118", 757263 }, + { "119", 763263 }, + { "120", 769263 }, + { "121", 775263 }, + { "122", 781263 }, + { "123", 787263 }, + { "124", 793263 }, + { "125", 799263 }, }; /* --------------------------------------------------------------------- */ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/frequencytables.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/frequencytables.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/frequencytables.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/frequencytables.cpp 2007-08-22 18:41:24.000000000 -0500 @@ -55,6 +55,51 @@ } #endif // USING_DVB + +TransportScanItem::TransportScanItem(int _sourceid, + const QString &_std, + const QString &_name, + const QString &_cardtype, + const DTVTransport &_tuning, + uint _timeoutTune) + : mplexid(-1), standard(_std), + FriendlyName(_name), friendlyNum(0), + SourceID(_sourceid), UseTimer(false), + scanning(false), timeoutTune(_timeoutTune) +{ + (void) _cardtype; + + bzero(freq_offsets, sizeof(int) * 3); + expectedChannels = _tuning.channels; + +#ifdef USING_DVB + bzero(&tuning, sizeof(DVBTuning)); + + fe_type type = FE_QPSK; + + type = (_cardtype.upper() == "QAM") ? FE_QAM : type; + type = (_cardtype.upper() == "OFDM") ? FE_OFDM : type; + type = (_cardtype.upper() == "ATSC") ? FE_ATSC : type; +#ifdef FE_GET_EXTENDED_INFO + type = (_cardtype.upper() == "DVB_S2") ? FE_DVB_S2 : type; +#endif + + tuning.ParseTuningParams( + type, + QString::number(_tuning.frequency), _tuning.inversion.toString(), + QString::number(_tuning.symbolrate), _tuning.fec.toString(), + _tuning.polarity.toString(), _tuning.hp_code_rate.toString(), + _tuning.lp_code_rate.toString(), _tuning.constellation.toString(), + _tuning.trans_mode.toString(), _tuning.guard_interval.toString(), + _tuning.hierarchy.toString(), _tuning.modulation.toString(), + _tuning.bandwidth.toString()); + +#else + frequency = _tuning.frequency; + modulation = _tuning.modulation; +#endif +} + TransportScanItem::TransportScanItem(int sourceid, const QString &std, const QString &fn, @@ -89,8 +134,12 @@ } else if (standard == "atsc") { -#if (DVB_API_VERSION_MINOR == 1) +#if (DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR >= 1) + // HACK ATSC could either be VSB (terrestrial) or QAM (cable) + // structs are compatible in this regard, qam tuning does only work + // this way amd I'm too tired to find the bug tuning.params.u.vsb.modulation = (fe_modulation) ft.modulation; + tuning.params.u.qam.modulation = (fe_modulation) ft.modulation; #endif if (dvbft) { @@ -161,7 +210,7 @@ .arg(UseTimer).arg(scanning); str += QString("\ttimeoutTune(%3 msec)\n").arg(timeoutTune); #ifdef USING_DVB -#if (DVB_API_VERSION_MINOR == 1) +#if (DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR >= 1) if (standard == "atsc") { str += QString("\tfrequency(%1) modulation(%2)\n") @@ -169,7 +218,7 @@ .arg(tuning.params.u.vsb.modulation); } else -#endif // (DVB_API_VERSION_MINOR == 1) +#endif // (DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR >= 1) { str += QString("\tfrequency(%1) constellation(%2)\n") .arg(tuning.params.frequency) @@ -356,67 +405,96 @@ uint mod[] = { VSB_8, QAM_256, QAM_128, QAM_64, }; QString desc[] = { "ATSC ", "QAM-256 ", "QAM-128 ", "QAM-64 ", }; -#define FREQ(A,B, C,D, E,F,G, H) \ +#define FREQ(A,B, C,D, E,F,G, H, I) \ fmap[QString("atsc_%1_us%2").arg(A).arg(B)] = \ - new FrequencyTable(C+D, E, F, G, 6000000, H); + new FrequencyTable(C+D, E, F, G, H, I); for (uint i = 0; i < 4; i++) { // USA Cable, ch 2 to 159 and T.7 to T.14 FREQ(modStr[i], "cable0", desc[i], "Channel %1", - 2, 57000000, 69000000, mod[i]); // 2-4 + 2, 57000000, 69000000, 6000000, mod[i]); // 2-4 FREQ(modStr[i], "cable1", desc[i], "Channel %1", - 5, 79000000, 85000000, mod[i]); // 5-6 + 5, 79000000, 85000000, 6000000, mod[i]); // 5-6 FREQ(modStr[i], "cable2", desc[i], "Channel %1", - 7, 177000000, 213000000, mod[i]); // 7-13 + 7, 177000000, 213000000, 6000000, mod[i]); // 7-13 FREQ(modStr[i], "cable3", desc[i], "Channel %1", - 14, 123000000, 171000000, mod[i]); // 14-22 + 14, 123000000, 171000000, 6000000, mod[i]); // 14-22 FREQ(modStr[i], "cable4", desc[i], "Channel %1", - 23, 219000000, 645000000, mod[i]); // 23-94 + 23, 219000000, 645000000, 6000000, mod[i]); // 23-94 FREQ(modStr[i], "cable5", desc[i], "Channel %1", - 95, 93000000, 117000000, mod[i]); // 95-99 + 95, 93000000, 117000000, 6000000, mod[i]); // 95-99 + // The center frequency of any EIA-542 std cable channel over 99 is + // Frequency_MHz = ( 6 * ( 8 + channel_designation ) ) + 3 FREQ(modStr[i], "cable6", desc[i], "Channel %1", - 100, 651000000, 1005000000, mod[i]); // 100-159 + 100, 651000000, 1005000000, 6000000, mod[i]); // 100-159 FREQ(modStr[i], "cable7", desc[i], "Channel T-%1", - 7, 8750000, 50750000, mod[i]); // T7-14 + 7, 8750000, 50750000, 6000000, mod[i]); // T7-14 // USA Cable, QAM 256 ch 78 to 159 FREQ(modStr[i], "cablehigh0", desc[i], "Channel %1", - 78, 549000000, 645000000, mod[i]); // 78-94 + 78, 549000000, 645000000, 6000000, mod[i]); // 78-94 FREQ(modStr[i], "cablehigh1", desc[i], "Channel %1", - 100, 651000000, 1005000000, mod[i]); // 100-159 - - QString std[] = { "hrc", "irc" }; - QString sdesc[] = { "HRC ", "IRC " }; - int off[] = { 0, 1250000 }; + 100, 651000000, 1005000000, 6000000, mod[i]); // 100-159 - for (uint j = 0; j < 2; j++) - { - // USA Cable HRC/IRC, ch 1 to 125 - FREQ(modStr[i], std[j] + "0", desc[i], sdesc[j] + "%1", - 1, 73750000 + off[j], 73750001 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "1", desc[i], sdesc[j] + "%1", - 2, 55750000 + off[j], 67750000 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "2", desc[i], sdesc[j] + "%1", - 5, 79750000 + off[j], 85750000 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "3", desc[i], sdesc[j] + "%1", - 7, 175750000 + off[j], 211750000 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "4", desc[i], sdesc[j] + "%1", - 14, - 121750000 + off[j] - (j ? 100000 : 0), - 169750000 + off[j] - (j ? 100000 : 0), mod[i]); - FREQ(modStr[i], std[j] + "5", desc[i], sdesc[j] + "%1", - 23, 217750000 + off[j], 643750000 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "6", desc[i], sdesc[j] + "%1", - 95, 91750000 + off[j], 115750000 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "7", desc[i], sdesc[j] + "%1", - 100, 649750000 + off[j], 799750000 + off[j], mod[i]); - - // USA Cable HRC/IRC, ch 76-125 - FREQ(modStr[i], std[j] + "high0", desc[i], sdesc[j] + "%1", - 76, 535750000 + off[j], 643750000 + off[j], mod[i]); - FREQ(modStr[i], std[j] + "high1", desc[i], sdesc[j] + "%1", - 100, 649750000 + off[j], 799750000 + off[j], mod[i]); - } + // USA Cable HRC, ch 1 to 125 + FREQ(modStr[i], "hrc0", desc[i], "HRC %1", + 1, 73753600, 73753601, 6000300, mod[i]); // 1 + FREQ(modStr[i], "hrc1", desc[i], "HRC %1", + 2, 55752700, 67753300, 6000300, mod[i]); // 2-4 + FREQ(modStr[i], "hrc2", desc[i], "HRC %1", + 5, 79753900, 85754200, 6000300, mod[i]); // 5-6 + FREQ(modStr[i], "hrc3", desc[i], "HRC %1", + 7, 175758700, 211760500, 6000300, mod[i]); // 7-13 + FREQ(modStr[i], "hrc4", desc[i], "HRC %1", + 14, 121756000, 169758400, 6000300, mod[i]); // 14-22 + FREQ(modStr[i], "hrc5", desc[i], "HRC %1", + 23, 217760800, 643782100, 6000300, mod[i]); // 23-94 + FREQ(modStr[i], "hrc6", desc[i], "HRC %1", + 95, 91754500, 115755700, 6000300, mod[i]); // 95-99 + // The center frequency of any EIA-542 HRC cable channel over 99 is + // Frequency_MHz = ( 6.0003 * ( 8 + channel_designation ) ) + 1.75 + FREQ(modStr[i], "hrc7", desc[i], "HRC %1", + 100, 649782400, 799789900, 6000300, mod[i]); // 100-125 + + // USA Cable HRC, ch 76-94 and 100-125 + // Channels 95-99 are low frequency despite high channel numbers + FREQ(modStr[i], "hrchigh0", desc[i], "HRC %1", + 76, 535776700, 643782100, 6000300, mod[i]); // 76-94 + FREQ(modStr[i], "hrchigh1", desc[i], "HRC %1", + 100, 649782400, 799789900, 6000300, mod[i]); // 100-125 + + // USA Cable IRC, ch 1 to 125 + FREQ(modStr[i], "irc0", desc[i], "IRC %1", + 1, 75012500, 75012501, 6000000, mod[i]); // 1 + FREQ(modStr[i], "irc1", desc[i], "IRC %1", + 2, 57012500, 69012500, 6000000, mod[i]); // 2-4 + FREQ(modStr[i], "irc2", desc[i], "IRC %1", + 5, 81012500, 87012500, 6000000, mod[i]); // 5-6 + FREQ(modStr[i], "irc3", desc[i], "IRC %1", + 7, 177012500, 213012500, 6000000, mod[i]); // 7-13 + FREQ(modStr[i], "irc4", desc[i], "IRC %1", + 14, 123012500, 171012500, 6000000, mod[i]); // 14-22 + FREQ(modStr[i], "irc5", desc[i], "IRC %1", + 23, 219012500, 327012500, 6000000, mod[i]); // 23-41 + FREQ(modStr[i], "irc6", desc[i], "IRC %1", + 42, 333025000, 333025001, 6000000, mod[i]); // 42 + FREQ(modStr[i], "irc7", desc[i], "IRC %1", + 43, 339012500, 645012500, 6000000, mod[i]); // 43-94 + FREQ(modStr[i], "irc8", desc[i], "IRC %1", + 95, 93012500, 105012500, 6000000, mod[i]); // 95-97 + FREQ(modStr[i], "irc9", desc[i], "IRC %1", + 98, 111025000, 117025000, 6000000, mod[i]); // 98-99 + // The center frequency of any EIA-542 IRC cable channel over 99 is + // Frequency_MHz = ( 6 * ( 8 + channel_designation ) ) + 3.0125 + FREQ(modStr[i], "irc10", desc[i], "IRC %1", + 100, 651012500, 801012500, 6000000, mod[i]); // 100-125 + + // USA Cable IRC, ch 76-94 and 100-125 + // Channels 95-99 are low frequency despite high channel numbers + FREQ(modStr[i], "irchigh0", desc[i], "IRC %1", + 76, 537012500, 645012500, 6000000, mod[i]); // 76-94 + FREQ(modStr[i], "irchigh1", desc[i], "IRC %1", + 100, 651012500, 801012500, 6000000, mod[i]); // 100-125 } } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/frequencytables.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/frequencytables.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/frequencytables.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/frequencytables.h 2007-04-13 23:59:17.000000000 -0500 @@ -29,6 +29,7 @@ #endif // USING_DVB class FrequencyTable; +#include "dvbconfparser.h" class TransportScanItem; typedef QMap freq_table_map_t; @@ -130,6 +131,13 @@ uint _timeoutTune); #endif // USING_DVB + TransportScanItem(int _sourceid, + const QString &_std, + const QString &_name, + const QString &_cardtype, + const DTVTransport &_tuning, + uint _timeoutTune); + TransportScanItem(int sourceid, /* source id in DB */ const QString &std, /* atsc/dvb */ const QString &strFmt, /* fmt for info shown to user */ @@ -144,6 +152,7 @@ uint freq_offset(uint i) const; QString ModulationDB(void) const; + QString toString() const; private: int GetMultiplexIdFromDB() const; @@ -168,7 +177,8 @@ uint frequency; ///< Tuning frequency if mplexid == -1 uint modulation; ///< Tuning frequency if mplexid == -1 #endif - QString toString() const; + + DTVChannelInfoList expectedChannels; }; class transport_scan_items_it_t diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/guidegrid.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/guidegrid.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/guidegrid.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/guidegrid.cpp 2007-03-07 01:53:30.000000000 -0600 @@ -710,6 +710,9 @@ m_programInfos[row][x] = NULL; } + if (m_channelInfos.size() == 0) + return; + int chanNum = row + m_currentStartChannel; if (chanNum >= (int) m_channelInfos.size()) chanNum -= (int) m_channelInfos.size(); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_config.c /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_config.c --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_config.c 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_config.c 2007-02-12 21:47:27.000000000 -0600 @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include "hdhomerun_os.h" #include "hdhomerun_pkt.h" #include "hdhomerun_discover.h" @@ -33,6 +32,7 @@ printf("\t%s get help\n", appname); printf("\t%s get \n", appname); printf("\t%s set \n", appname); + printf("\t%s scan \n", appname); printf("\t%s upgrade \n", appname); return 1; } @@ -234,6 +234,132 @@ return 0; } +int cmd_scan(struct hdhomerun_control_sock_t *control_sock, const char *tuner_str, const char *start_value) +{ + int tuner = atoi(tuner_str); + + /* Test starting channel. */ + char item[64]; + sprintf(item, "/tuner%d/channel", tuner); + int ret = cmd_set(control_sock, item, start_value); + if (ret != 0) { + return ret; + } + + char channel_value[64]; + strncpy(channel_value, start_value, sizeof(channel_value)); + channel_value[sizeof(channel_value) - 8] = 0; + + char *ptr = strrchr(channel_value, ':'); + if (!ptr) { + ptr = channel_value; + } else { + ptr++; + } + + int channel = atol(ptr); + if (channel == 0) { + fprintf(stderr, "invalid starting channel\n"); + return 1; + } + + while (1) { + /* Update channel value */ + sprintf(ptr, "%d", channel); + + /* Set channel. */ + sprintf(item, "/tuner%d/channel", tuner); + if (hdhomerun_control_send_set_request(control_sock, item, channel_value) < 0) { + fprintf(stderr, "communication error sending request to hdhomerun device\n"); + return 1; + } + + /* Verify set succeeded. */ + struct hdhomerun_control_data_t result; + if (hdhomerun_control_recv(control_sock, &result, 1000) <= 0) { + fprintf(stderr, "communication error receiving response from hdhomerun device\n"); + return 1; + } + if (result.type != HDHOMERUN_TYPE_GETSET_RPY) { + fprintf(stderr, "unexpected reply type from hdhomerun device\n"); + return 1; + } + while (result.ptr < result.end) { + unsigned char tag; + int length; + unsigned char *value; + if (hdhomerun_read_tlv(&result.ptr, result.end, &tag, &length, &value) < 0) { + break; + } + if (tag == HDHOMERUN_TAG_ERROR_MESSAGE) { + return 0; + } + } + + /* Wait for 1s. */ + sleep(1); + + /* Get status. */ + sprintf(item, "/tuner%d/status", tuner); + if (hdhomerun_control_send_get_request(control_sock, item) < 0) { + fprintf(stderr, "communication error sending request to hdhomerun device\n"); + return 1; + } + + /* Status result. */ + if (hdhomerun_control_recv(control_sock, &result, 1000) <= 0) { + fprintf(stderr, "communication error receiving response from hdhomerun device\n"); + return 1; + } + if (result.type != HDHOMERUN_TYPE_GETSET_RPY) { + fprintf(stderr, "unexpected reply type from hdhomerun device\n"); + return 1; + } + char *status = NULL; + while (result.ptr < result.end) { + unsigned char tag; + int length; + unsigned char *value; + if (hdhomerun_read_tlv(&result.ptr, result.end, &tag, &length, &value) < 0) { + break; + } + if (tag == HDHOMERUN_TAG_ERROR_MESSAGE) { + return 0; + } + if (tag == HDHOMERUN_TAG_GETSET_VALUE) { + status = (char *)value; + } + } + if (!status) { + fprintf(stderr, "unexpected reply type from hdhomerun device\n"); + return 1; + } + + /* If no signal then advance to next channel. */ + char *ss_str = strstr(status, "ss="); + if (!ss_str) { + printf("%s\n", status); + channel++; + continue; + } + int ss = atoi(ss_str + strlen("ss=")); + if (ss == 0) { + printf("%s\n", status); + channel++; + continue; + } + + /* Wait for 2s. */ + sleep(2); + + /* Display channel status. */ + cmd_get(control_sock, item); + + /* Advance to next channel. */ + channel++; + } +} + int cmd_upgrade(struct hdhomerun_control_sock_t *control_sock, const char *filename) { FILE *fp = fopen(filename, "rb"); @@ -272,6 +398,7 @@ return 1; } + printf("upgrade complete\n"); return 0; } @@ -297,6 +424,13 @@ return cmd_set(control_sock, argv[0], argv[1]); } + if (contains(cmd, "scan")) { + if (argc < 2) { + return help(); + } + return cmd_scan(control_sock, argv[0], argv[1]); + } + if (contains(cmd, "upgrade")) { if (argc < 1) { return help(); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_discover.c /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_discover.c --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_discover.c 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_discover.c 2007-02-12 21:47:27.000000000 -0600 @@ -22,6 +22,12 @@ #include "hdhomerun_pkt.h" #include "hdhomerun_discover.h" +#if defined(__CYGWIN__) +#include +#include +#include +#endif + struct hdhomerun_discover_sock_t { int sock; }; @@ -71,7 +77,7 @@ free(ds); } -int hdhomerun_discover_send(struct hdhomerun_discover_sock_t *ds, unsigned long device_type, unsigned long device_id) +static int hdhomerun_discover_send_packet(struct hdhomerun_discover_sock_t *ds, unsigned long ip_addr, unsigned long device_type, unsigned long device_id) { unsigned char buffer[1024]; unsigned char *ptr = buffer; @@ -80,7 +86,7 @@ struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + sock_addr.sin_addr.s_addr = htonl(ip_addr); sock_addr.sin_port = htons(HDHOMERUN_DISCOVER_UDP_PORT); int length = ptr - buffer; @@ -91,6 +97,115 @@ return 0; } +#if defined(__CYGWIN__) +static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, unsigned long device_type, unsigned long device_id) +{ + PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO)); + unsigned long ulOutBufLen = sizeof(IP_ADAPTER_INFO); + + DWORD Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); + if (Ret != NO_ERROR) { + free(pAdapterInfo); + if (Ret != ERROR_BUFFER_OVERFLOW) { + return -1; + } + pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen); + Ret = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen); + if (Ret != NO_ERROR) { + free(pAdapterInfo); + return -1; + } + } + + int send_count = 0; + PIP_ADAPTER_INFO pAdapter = pAdapterInfo; + while (pAdapter) { + IP_ADDR_STRING *pIPAddr = &pAdapter->IpAddressList; + while (pIPAddr) { + unsigned long addr = ntohl(inet_addr(pIPAddr->IpAddress.String)); + unsigned long mask = ntohl(inet_addr(pIPAddr->IpMask.String)); + + unsigned long broadcast = addr | ~mask; + if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) { + pIPAddr = pIPAddr->Next; + continue; + } + + hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id); + send_count++; + + pIPAddr = pIPAddr->Next; + } + + pAdapter = pAdapter->Next; + } + + free(pAdapterInfo); + + if (send_count == 0) { + return -1; + } + return 0; +} +#endif + +#if defined(__linux__) +static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, unsigned long device_type, unsigned long device_id) +{ + FILE *fp = fopen("/proc/net/route", "r"); + if (!fp) { + return -1; + } + + int send_count = 0; + while (1) { + char line[256]; + if (!fgets(line, sizeof(line), fp)) { + break; + } + line[255] = 0; + + unsigned long dest; + unsigned long mask; + if (sscanf(line, "%*s %lx %*x %*x %*d %*d %*d %lx", &dest, &mask) != 2) { + continue; + } + dest = ntohl(dest); + mask = ntohl(mask); + + unsigned long broadcast = dest | ~mask; + + if ((broadcast == 0x00000000) || (broadcast == 0xFFFFFFFF)) { + continue; + } + + hdhomerun_discover_send_packet(ds, broadcast, device_type, device_id); + send_count++; + } + + fclose(fp); + if (send_count == 0) { + return -1; + } + return 0; +} +#endif + +#if !defined(__CYGWIN__) && !defined(__linux__) +static int hdhomerun_discover_send_internal(struct hdhomerun_discover_sock_t *ds, unsigned long device_type, unsigned long device_id) +{ + return -1; +} +#endif + +int hdhomerun_discover_send(struct hdhomerun_discover_sock_t *ds, unsigned long device_type, unsigned long device_id) +{ + if (hdhomerun_discover_send_internal(ds, device_type, device_id) < 0) { + return hdhomerun_discover_send_packet(ds, 0xFFFFFFFF, device_type, device_id); + } + return 0; +} + int hdhomerun_discover_recv(struct hdhomerun_discover_sock_t *ds, struct hdhomerun_discover_device_t *result, unsigned long timeout) { struct timeval t; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_os.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_os.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_os.h 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_os.h 2007-02-12 21:47:27.000000000 -0600 @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -29,3 +30,10 @@ #include #include #include + +#if !defined(TRUE) +#define TRUE 1 +#endif +#if !defined(FALSE) +#define FALSE 0 +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_pkt.c /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_pkt.c --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_pkt.c 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_pkt.c 2007-02-12 21:47:27.000000000 -0600 @@ -182,7 +182,7 @@ hdhomerun_write_u16(&ptr, length); } -static void hdhomerun_write_crc(unsigned char **pptr, unsigned char *start) +void hdhomerun_write_crc(unsigned char **pptr, unsigned char *start) { unsigned char *ptr = *pptr; unsigned long crc = hdhomerun_calc_crc(start, ptr); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_pkt.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_pkt.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_pkt.h 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_pkt.h 2007-02-12 21:47:27.000000000 -0600 @@ -49,6 +49,7 @@ extern void hdhomerun_write_u8(unsigned char **pptr, unsigned char v); extern void hdhomerun_write_u16(unsigned char **pptr, unsigned short v); extern void hdhomerun_write_u32(unsigned char **pptr, unsigned long v); +extern void hdhomerun_write_crc(unsigned char **pptr, unsigned char *start); extern int hdhomerun_peek_packet_length(unsigned char *ptr); extern int hdhomerun_process_packet(unsigned char **pptr, unsigned char **pend); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_video.c /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_video.c --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_video.c 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_video.c 2007-02-12 21:47:27.000000000 -0600 @@ -129,6 +129,11 @@ return 1; } +int hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs) +{ + return vs->sock; +} + static void *hdhomerun_video_thread(void *arg) { struct hdhomerun_video_sock_t *vs = (struct hdhomerun_video_sock_t *)arg; @@ -183,6 +188,18 @@ vs->tail = tail; } +unsigned long hdhomerun_video_available_length(struct hdhomerun_video_sock_t *vs) +{ + unsigned long head = vs->head; + unsigned long tail = vs->tail; + + if (head >= tail) { + return head - tail - vs->advance; + } else { + return head + vs->buffer_size - tail - vs->advance; + } +} + unsigned long hdhomerun_video_recv_memcpy(struct hdhomerun_video_sock_t *vs, unsigned char *buffer, unsigned long size) { unsigned long head = vs->head; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_video.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_video.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/hdhomerun_video.h 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/hdhomerun_video.h 2007-02-12 21:47:27.000000000 -0600 @@ -30,6 +30,8 @@ extern void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs); extern unsigned short hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs); extern int hdhomerun_video_get_state(struct hdhomerun_video_sock_t *vs); +extern int hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs); +extern unsigned long hdhomerun_video_available_length(struct hdhomerun_video_sock_t *vs); extern unsigned long hdhomerun_video_recv_memcpy(struct hdhomerun_video_sock_t *vs, unsigned char *buffer, unsigned long size); extern unsigned char *hdhomerun_video_recv_inplace(struct hdhomerun_video_sock_t *vs, unsigned long max_size, unsigned long *pactual_size); extern void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/Makefile /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/Makefile --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdhomerun/Makefile 2007-01-22 03:08:22.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdhomerun/Makefile 2007-02-12 21:47:27.000000000 -0600 @@ -1,14 +1,19 @@ -HDHOMERUN_CONFIG_SRCS += hdhomerun_pkt.c -HDHOMERUN_CONFIG_SRCS += hdhomerun_discover.c -HDHOMERUN_CONFIG_SRCS += hdhomerun_control.c -HDHOMERUN_CONFIG_SRCS += hdhomerun_config.c +SRCS += hdhomerun_pkt.c +SRCS += hdhomerun_discover.c +SRCS += hdhomerun_control.c +SRCS += hdhomerun_config.c CFLAGS += -Wall -O2 -hdhomerun_config : $(HDHOMERUN_CONFIG_SRCS) - gcc $(CFLAGS) $(HDHOMERUN_CONFIG_SRCS) -o hdhomerun_config - strip hdhomerun_config +hdhomerun_config : $(SRCS) + gcc $(CFLAGS) $(SRCS) -o $@ + strip $@ + +hdhomerun_config.exe : $(SRCS) + gcc $(CFLAGS) $(SRCS) -liphlpapi -o $@ + strip $@ clean : rm -f hdhomerun_config + rm -f hdhomerun_config.exe diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdtvrecorder.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdtvrecorder.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/hdtvrecorder.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/hdtvrecorder.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -86,9 +86,11 @@ #include "tv_rec.h" // AVLib/FFMPEG includes +extern "C" { #include "../libavcodec/avcodec.h" #include "../libavformat/avformat.h" #include "../libavformat/mpegts.h" +} #define REPORT_RING_STATS 1 diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/libmythtv.pro /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/libmythtv.pro --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/libmythtv.pro 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/libmythtv.pro 2007-04-13 23:59:17.000000000 -0500 @@ -322,10 +322,12 @@ # Channel scanner stuff HEADERS += scanwizard.h scanwizardhelpers.h + HEADERS += dvbconfparser.h dtvconfparserhelpers.h HEADERS += siscan.h HEADERS += scanwizardscanner.h SOURCES += scanwizard.cpp scanwizardhelpers.cpp SOURCES += siscan.cpp + SOURCES += dvbconfparser.cpp dtvconfparserhelpers.cpp SOURCES += scanwizardscanner.cpp # EIT stuff @@ -449,8 +451,8 @@ SOURCES += dvbrecorder.cpp # Misc - HEADERS += dvbconfparser.h dvbdev/dvbci.h - SOURCES += dvbconfparser.cpp dvbdev/dvbci.cpp + HEADERS += dvbdev/dvbci.h + SOURCES += dvbdev/dvbci.cpp DEFINES += USING_DVB } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mhi.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mhi.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mhi.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mhi.h 2007-08-22 13:46:08.000000000 -0500 @@ -27,7 +27,9 @@ #include "mythdbcon.h" #include "NuppelVideoPlayer.h" +extern "C" { #include "../libavcodec/avcodec.h" // to decode single MPEG I-frames +} class OSDSet; class DSMCCPacket; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpeg/dvbdescriptors.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpeg/dvbdescriptors.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpeg/dvbdescriptors.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpeg/dvbdescriptors.cpp 2007-03-26 11:54:22.000000000 -0500 @@ -80,7 +80,7 @@ // Strip formatting characters // Also, if a override encoding is specified copy it in front of the text - char dst[raw_length + encoding_override_length]; + unsigned char dst[raw_length + encoding_override_length]; uint length = encoding_override_length; if (encoding_override) memcpy(dst, encoding_override, encoding_override_length); @@ -93,7 +93,7 @@ length++; } } - const char *buf = dst; + const unsigned char *buf = dst; // Exit on empty string, sans formatting. if (!length) @@ -102,11 +102,11 @@ // Decode using the correct text codec if (buf[0] >= 0x20) { - return decode_iso6937((unsigned char*)buf, length); + return decode_iso6937(buf, length); } else if ((buf[0] >= 0x01) && (buf[0] <= 0x0B)) { - return iso8859_codecs[4 + buf[0]]->toUnicode(buf + 1, length - 1); + return iso8859_codecs[4 + buf[0]]->toUnicode((char*)(buf + 1), length - 1); } else if (buf[0] == 0x10) { @@ -118,14 +118,14 @@ uint code = buf[1] << 8 | buf[2]; if (code <= 15) - return iso8859_codecs[code]->toUnicode(buf + 3, length - 3); + return iso8859_codecs[code]->toUnicode((char*)(buf + 3), length - 3); else - return QString::fromLocal8Bit(buf + 3, length - 3); + return QString::fromLocal8Bit((char*)(buf + 3), length - 3); } else { // Unknown/invalid encoding - assume local8Bit - return QString::fromLocal8Bit(buf + 1, length - 1); + return QString::fromLocal8Bit((char*)(buf + 1), length - 1); } } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegstreamdata.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpeg/mpegstreamdata.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegstreamdata.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpeg/mpegstreamdata.cpp 2007-02-12 15:01:15.000000000 -0600 @@ -406,11 +406,11 @@ pmt.Parse(); vector videoPIDs, audioPIDs; - vector videoTypes, audioTypes; + vector videoTypes; vector pids, types; // Video - uint video_cnt = pmt.FindPIDs(StreamID::AnyVideo, videoPIDs, videoTypes); + uint video_cnt = pmt.FindPIDs(StreamID::AnyVideo, videoPIDs, videoTypes, true); if (video_cnt < _pmt_single_program_num_video) { VERBOSE(VB_RECORD, "Only "< encryption_system; + for (uint i = 0; i < descs.size(); i++) { - ConditionalAccessDescriptor ca(data); - return 0x0 != ca.SystemID(); // System ID of 0 == no encrytion + ConditionalAccessDescriptor cad(descs[i]); + encryption_system[cad.PID()] = cad.SystemID(); + encrypted |= cad.SystemID(); + + //VERBOSE(VB_IMPORTANT, "DTVsm: "< encryption_system; - if (data) + for (uint i = 0; i < StreamCount(); i++) { - for (uint i = 0; i < descs.size(); ++i) + desc_list_t descs = MPEGDescriptor::ParseOnlyInclude( + StreamInfo(i), StreamInfoLength(i), + DescriptorID::conditional_access); + + for (uint j = 0; j < descs.size(); j++) { - MPEGDescriptor mpegdesc(descs[i]); - VERBOSE(VB_IMPORTANT, "DTVsm: "<& pids) const { + uint pids_start = pids.size(); + if ((StreamID::AnyMask & type) != StreamID::AnyMask) { for (uint i=0; i < StreamCount(); i++) @@ -337,11 +342,14 @@ * \param type StreamType to match * \param pids vector pids will be added to * \param types vector types will be added to + * \param normalize if set, types will be normalized * \return number of items in pids and types lists. */ uint ProgramMapTable::FindPIDs(uint type, vector& pids, - vector& types) const + vector& types, bool normalize) const { + uint pids_start = pids.size(); + if ((StreamID::AnyMask & type) != StreamID::AnyMask) { for (uint i=0; i < StreamCount(); i++) @@ -351,6 +359,7 @@ types.push_back(StreamType(i)); } } + else if (StreamID::AnyVideo == type) { for (uint i=0; i < StreamCount(); i++) @@ -370,6 +379,20 @@ } } + if (!normalize) + return pids.size(); + + for (uint i = pids_start; i < pids.size(); i++) + { + int index = FindPID(pids[i]); + if (index >= 0) + { + desc_list_t desc = MPEGDescriptor::Parse( + StreamInfo(i), StreamInfoLength(i)); + types[i] = StreamID::Normalize(types[i], desc); + } + } + return pids.size(); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegtables.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpeg/mpegtables.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpeg/mpegtables.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpeg/mpegtables.h 2007-02-12 15:01:15.000000000 -0600 @@ -541,7 +541,7 @@ QString GetLanguage(uint i) const; uint FindPIDs(uint type, vector& pids) const; - uint FindPIDs(uint type, vector& pids, vector& types) const; + uint FindPIDs(uint type, vector& pids, vector& types, bool normalize) const; /// \brief Locates stream index of pid. /// \return stream index if successful, -1 otherwise diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpegrecorder.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpegrecorder.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/mpegrecorder.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/mpegrecorder.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -22,7 +22,9 @@ #include // avlib headers +extern "C" { #include "../libavcodec/avcodec.h" +} // MythTV headers #include "mpegrecorder.h" @@ -456,11 +458,11 @@ ext_ctrl[3].id = V4L2_CID_MPEG_AUDIO_L2_BITRATE; ext_ctrl[3].value = audbitrate - 1; - ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE; - ext_ctrl[4].value = (bitrate = min(bitrate, maxbitrate)) * 1000; + ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK; + ext_ctrl[4].value = maxbitrate * 1000; - ext_ctrl[5].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK; - ext_ctrl[5].value = maxbitrate * 1000; + ext_ctrl[5].id = V4L2_CID_MPEG_VIDEO_BITRATE; + ext_ctrl[5].value = (bitrate = min(bitrate, maxbitrate)) * 1000; ext_ctrl[6].id = V4L2_CID_MPEG_STREAM_TYPE; ext_ctrl[6].value = V4L2_MPEG_STREAM_TYPE_MPEG2_PS; @@ -875,7 +877,9 @@ { curRecording->SetPositionMapDelta(positionMapDelta, MARK_GOP_START); - curRecording->SetFilesize(lastpackheaderpos); + // Stop setting the filesize here until we get the contention issue + // between with this thread and the scheduler worked out. + //curRecording->SetFilesize(lastpackheaderpos); positionMapDelta.clear(); } } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/NuppelVideoPlayer.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/NuppelVideoPlayer.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/NuppelVideoPlayer.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/NuppelVideoPlayer.cpp 2007-07-30 12:45:45.000000000 -0500 @@ -1455,7 +1455,7 @@ if (!osd || !osd_msg) return; - QString msg = " "; + QString msg = ""; if (kDisplayNUVTeletextCaptions & mode) msg += QObject::tr("TXT CAP"); if (kDisplayTeletextCaptions & mode) @@ -1485,7 +1485,7 @@ GetTrack(kTrackTypeCC708)); } - if (msg != " ") + if (! msg.isEmpty()) { msg += " " + QObject::tr("Off"); osd->SetSettingsText(msg, 3 /* seconds until message timeout */); @@ -5480,30 +5480,26 @@ ++commBreakIter; if (commBreakIter == commBreakMap.end()) { - commBreakMapLock.unlock(); VERBOSE(VB_COMMFLAG, LOC + "AutoCommercialSkip(), at " "end of commercial break list, will not skip."); - return; } - - if (commBreakIter.data() == MARK_COMM_START) + else if (commBreakIter.data() == MARK_COMM_START) { - commBreakMapLock.unlock(); VERBOSE(VB_COMMFLAG, LOC + "AutoCommercialSkip(), new " "commBreakIter mark is another start, will not skip."); - return; } - - VERBOSE(VB_COMMFLAG, LOC + QString("AutoCommercialSkip(), new " - "commBreakIter frame %1").arg(commBreakIter.key())); - - if (commBreakIter.key() == totalFrames) + else if ((totalFrames) && + ((commBreakIter.key() + (10 * video_frame_rate)) > + totalFrames)) { - VERBOSE(VB_IMPORTANT, LOC + "Skipping commercial to end of file"); - eof = true; + VERBOSE(VB_COMMFLAG, LOC + "AutoCommercialSkip(), skipping " + "would take us to the end of the file, will not skip."); } else { + VERBOSE(VB_COMMFLAG, LOC + QString("AutoCommercialSkip(), new " + "commBreakIter frame %1").arg(commBreakIter.key())); + if (osd) { QString comm_msg; @@ -5668,7 +5664,10 @@ { commBreakIter++; - if (commBreakIter == commBreakMap.end()) + if ((commBreakIter == commBreakMap.end()) || + ((totalFrames) && + ((commBreakIter.key() + (10 * video_frame_rate)) > + totalFrames))) { if (osd) { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/NuppelVideoRecorder.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/NuppelVideoRecorder.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/NuppelVideoRecorder.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/NuppelVideoRecorder.cpp 2007-03-03 00:25:06.000000000 -0600 @@ -2063,7 +2063,9 @@ if (curRecording && force) { curRecording->SetPositionMapDelta(positionMapDelta, MARK_KEYFRAME); - curRecording->SetFilesize(lastPositionMapPos); + // Stop setting the filesize here until we get the contention issue + // between with this thread and the scheduler worked out. + //curRecording->SetFilesize(lastPositionMapPos); positionMapDelta.clear(); } } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/NuppelVideoRecorder.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/NuppelVideoRecorder.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/NuppelVideoRecorder.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/NuppelVideoRecorder.h 2007-08-22 13:46:08.000000000 -0500 @@ -17,7 +17,9 @@ #include "filter.h" #include "minilzo.h" #undef HAVE_AV_CONFIG_H +extern "C" { #include "../libavcodec/avcodec.h" +} // C++ std headers #include diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osd.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osd.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osd.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osd.cpp 2007-02-19 15:16:11.000000000 -0600 @@ -2474,6 +2474,13 @@ totalfadetime = 0; } +int OSD::GetRevision(void) const +{ + if (drawSurface) + return drawSurface->GetRevision(); + return 0; +} + OSDSurface *OSD::Display(void) { bool anytodisplay = false; @@ -2545,7 +2552,7 @@ m_setsvisible = anytodisplay; - if (m_setsvisible) + if (m_setsvisible && !drawSurface->IsClear()) return drawSurface; return NULL; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osd.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osd.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osd.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osd.h 2007-02-19 15:16:11.000000000 -0600 @@ -60,6 +60,8 @@ OSDSurface *Display(void); + int GetRevision(void) const; + void ClearAll(const QString &name); void ClearAllText(const QString &name); void SetText(const QString &name, QMap &infoMap, diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osdsurface.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osdsurface.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osdsurface.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osdsurface.cpp 2007-02-19 15:16:11.000000000 -0600 @@ -135,6 +135,11 @@ usedRegions = QRegion(); } +bool OSDSurface::IsClear(void) const +{ + return (usedRegions == QRegion()); +} + bool OSDSurface::IntersectsDrawn(QRect &newrect) { QMutexLocker lock(&usedRegionsLock); @@ -799,7 +804,7 @@ if (startline < 0) startline = 0; if (endline >= height) endline = height - 1; - if (startcol < 0) endcol = 0; + if (startcol < 0) startcol = 0; if (endcol >= width) endcol = width - 1; unsigned char *src; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osdsurface.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osdsurface.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/osdsurface.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/osdsurface.h 2007-02-19 15:16:11.000000000 -0600 @@ -24,6 +24,7 @@ void Clear(void); void ClearUsed(void); + bool IsClear(void) const; bool IntersectsDrawn(QRect &newrect); void AddRect(QRect &newrect); @@ -35,7 +36,7 @@ if (change) ++revision; } - int GetRevision() { return revision; } + int GetRevision(void) const { return revision; } void BlendToYV12(unsigned char *yptr, unsigned char *uptr, diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/profilegroup.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/profilegroup.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/profilegroup.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/profilegroup.cpp 2007-03-07 02:05:50.000000000 -0600 @@ -94,10 +94,13 @@ it != cardtypes.end(); it++) if (result.value(4).toString() == *it) match = true; - else if (result.value(4).toString() == "TRANSCODE") - transcodeID = result.value(1).toString(); + if (! match) + { + if (result.value(4).toString() == "TRANSCODE") + transcodeID = result.value(1).toString(); continue; + } } QString value = QString::fromUtf8(result.value(0).toString()); if (result.value(2).toString() != NULL && diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/progfind.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/progfind.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/progfind.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/progfind.cpp 2007-03-05 22:08:07.000000000 -0600 @@ -516,11 +516,9 @@ void ProgFinder::cursorLeft() { - inSearch--; - if (inSearch == -1 && arrowAccel) - escape(); - else + if (inSearch > 0) { + inSearch--; if (inSearch == 0) showSearchList(); else if (inSearch == 1) @@ -529,6 +527,9 @@ clearShowData(); } } + else if (inSearch == 0 && arrowAccel) + escape(); + update(infoRect); update(listRect); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/programinfo.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/programinfo.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/programinfo.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/programinfo.cpp 2007-06-29 09:39:26.000000000 -0500 @@ -1576,7 +1576,7 @@ " AND title = :TITLE;"); query.bindValue(":CHANID", chanid); query.bindValue(":START", startts); - query.bindValue(":TITLE", title); + query.bindValue(":TITLE", title.utf8()); if (!query.exec() || !query.isActive()) MythContext::DBError("Copy program data on record", query); @@ -3561,7 +3561,7 @@ "editing, bookmark, stereo, closecaptioned, hdtv " "FROM recorded LEFT JOIN recordedprogram ON " "(recorded.chanid = recordedprogram.chanid AND " - "recorded.starttime = recordedprogram.starttime) " + "recorded.progstart = recordedprogram.starttime) " "WHERE recorded.chanid = :CHANID AND recorded.starttime = :STARTTIME ;"); query.bindValue(":CHANID", chanid); query.bindValue(":STARTTIME", recstartts); @@ -4232,7 +4232,7 @@ "LEFT JOIN channel ON recorded.chanid = channel.chanid " "LEFT JOIN recordedprogram ON " " ( recorded.chanid = recordedprogram.chanid AND " - " recorded.starttime = recordedprogram.starttime ) " + " recorded.progstart = recordedprogram.starttime ) " "WHERE ( recorded.deletepending = 0 OR " " DATE_ADD(recorded.lastmodified, INTERVAL 5 MINUTE) <= NOW() " " ) " diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/recordingprofile.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/recordingprofile.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/recordingprofile.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/recordingprofile.cpp 2007-08-22 13:41:40.000000000 -0500 @@ -669,7 +669,7 @@ setValue(false); setHelpText(QObject::tr("Automatically transcode when a recording is " "made using this profile and the recording's " - "schedule is configurd to allow transcoding.")); + "schedule is configured to allow transcoding.")); }; }; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizard.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizard.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizard.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizard.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -39,6 +39,7 @@ : paneOFDM(new OFDMPane()), paneQPSK(new QPSKPane()), paneATSC(new ATSCPane()), paneQAM(new QAMPane()), paneSingle(new STPane()), + paneDVBUtilsImport(new DVBUtilsImportPane()), #ifdef FE_GET_EXTENDED_INFO paneDVBS2(new DVBS2Pane()), #endif @@ -111,7 +112,10 @@ bool vl1 = (configPane->scanConfig-> ignoreSignalTimeoutAll->getValue().toInt()); - return (ts0) ? vl0 : ((ts1) ? vl1 : false); + bool ts2 = (ScanTypeSetting::DVBUtilsImport == scanType()); + bool vl2 = paneDVBUtilsImport->DoIgnoreSignalTimeout(); + + return (ts0) ? vl0 : ((ts1) ? vl1 : (ts2) ? vl2 : false); } QString ScanWizard::country(void) const @@ -121,5 +125,5 @@ QString ScanWizard::filename(void) const { - return configPane->scanConfig->filename->getValue(); + return paneDVBUtilsImport->GetFilename(); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizard.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizard.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizard.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizard.h 2007-04-13 23:59:17.000000000 -0500 @@ -42,6 +42,7 @@ class ATSCPane; class QAMPane; class STPane; +class DVBUtilsImportPane; class ScanWizardScanType; class ScanWizardScanner; @@ -77,6 +78,8 @@ ATSCPane *paneATSC; QAMPane *paneQAM; STPane *paneSingle; + DVBUtilsImportPane *paneDVBUtilsImport; + int nVideoDev; unsigned nCardType; int nCaptureCard; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizardhelpers.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizardhelpers.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizardhelpers.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizardhelpers.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -1,5 +1,5 @@ /* -*- Mode: c++ -*- - * $Id: scanwizardhelpers.cpp 10791 2006-08-16 21:47:43Z danielk $ + * $Id: scanwizardhelpers.cpp 13237 2007-04-14 04:59:17Z jarod $ * vim: set expandtab tabstop=4 shiftwidth=4: * * Original Project @@ -315,29 +315,27 @@ addSelection(tr("Full Scan (Tuned)"), QString::number(NITAddScan_OFDM)); addSelection(tr("Import channels.conf"), - QString::number(Import)); + QString::number(DVBUtilsImport)); break; case CardUtil::QPSK: addSelection(tr("Full Scan (Tuned)"), QString::number(NITAddScan_QPSK)); addSelection(tr("Import channels.conf"), - QString::number(Import)); + QString::number(DVBUtilsImport)); break; case CardUtil::QAM: addSelection(tr("Full Scan (Tuned)"), QString::number(NITAddScan_QAM)); addSelection(tr("Import channels.conf"), - QString::number(Import)); + QString::number(DVBUtilsImport)); break; case CardUtil::ATSC: case CardUtil::HDTV: case CardUtil::HDHOMERUN: addSelection(tr("Full Scan"), QString::number(FullScan_ATSC), true); -#ifdef USING_DVB addSelection(tr("Import channels.conf"), - QString::number(Import)); -#endif + QString::number(DVBUtilsImport)); break; case CardUtil::FREEBOX: addSelection(tr("M3U Import"), @@ -393,8 +391,7 @@ ConfigurationGroup(false, false, true, true), VerticalConfigurationGroup(false, false, true, true), country(new ScanCountry()), - ignoreSignalTimeoutAll(new IgnoreSignalTimeout()), - filename(new ScanFileImport()) + ignoreSignalTimeoutAll(new IgnoreSignalTimeout()) { setTrigger(scanType); @@ -432,8 +429,8 @@ scanAllTransports); addTarget(QString::number(ScanTypeSetting::FreeBoxImport), new BlankSetting()); - addTarget(QString::number(ScanTypeSetting::Import), - filename); + addTarget(QString::number(ScanTypeSetting::DVBUtilsImport), + wizard->paneDVBUtilsImport); } void ScanOptionalConfig::triggerChanged(const QString& value) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizardhelpers.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizardhelpers.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizardhelpers.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizardhelpers.h 2007-04-13 23:59:17.000000000 -0500 @@ -1,5 +1,5 @@ /* -*- Mode: c++ -*- - * $Id: scanwizardhelpers.h 10645 2006-07-26 13:10:21Z danielk $ + * $Id: scanwizardhelpers.h 13237 2007-04-14 04:59:17Z jarod $ * vim: set expandtab tabstop=4 shiftwidth=4: * * Original Project @@ -188,16 +188,6 @@ ScanCountry(); }; -class ScanFileImport : public LineEditSetting, public TransientStorage -{ -public: - ScanFileImport() : LineEditSetting() - { - setLabel(QObject::tr("File location")); - setHelpText(QObject::tr("Location of the channels.conf file.")); - } -}; - class ScanTypeSetting : public ComboBoxSetting, public TransientStorage { Q_OBJECT @@ -222,7 +212,7 @@ // Freebox import of channels from M3U URL FreeBoxImport, // Imports lists from dvb-utils scanners - Import + DVBUtilsImport, }; ScanTypeSetting() : nCaptureCard(-1) { @@ -244,7 +234,7 @@ ScanCountry *country; IgnoreSignalTimeout *ignoreSignalTimeoutAll; - ScanFileImport *filename; + protected slots: void triggerChanged(const QString&); }; @@ -781,6 +771,43 @@ IgnoreSignalTimeout *ignore_signal_timeout; }; +class DVBUtilsImportPane : public VerticalConfigurationGroup +{ + public: + DVBUtilsImportPane() : + ConfigurationGroup(false,false,true,false), + VerticalConfigurationGroup(false,false,true,false), + filename(new TransLineEditSetting()), + atsc_format(new ScanATSCChannelFormat()), + old_channel_treatment(new ScanOldChannelTreatment()), + ignore_signal_timeout(new IgnoreSignalTimeout()) + { + filename->setLabel(tr("File location")); + filename->setHelpText(tr("Location of the channels.conf file.")); + addChild(filename); + + addChild(atsc_format); + addChild(old_channel_treatment); + addChild(ignore_signal_timeout); + } + + QString GetFilename(void) const { return filename->getValue(); } + QString GetATSCFormat(void) const { return atsc_format->getValue(); } + + bool DoDeleteChannels(void) const + { return old_channel_treatment->getValue() == "delete"; } + bool DoRenameChannels(void) const + { return old_channel_treatment->getValue() == "rename"; } + bool DoIgnoreSignalTimeout(void) const + { return ignore_signal_timeout->getValue().toInt(); } + + private: + TransLineEditSetting *filename; + ScanATSCChannelFormat *atsc_format; + ScanOldChannelTreatment *old_channel_treatment; + IgnoreSignalTimeout *ignore_signal_timeout; +}; + class ErrorPane : public HorizontalConfigurationGroup { public: diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizardscanner.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizardscanner.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/scanwizardscanner.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/scanwizardscanner.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -43,6 +43,7 @@ #include "channelbase.h" #include "dtvsignalmonitor.h" #include "siscan.h" +#include "dvbconfparser.h" #ifdef USING_V4L #include "channel.h" @@ -53,7 +54,6 @@ #ifdef USING_DVB #include "dvbchannel.h" #include "dvbsignalmonitor.h" -#include "dvbconfparser.h" #endif #ifdef USING_HDHOMERUN @@ -302,10 +302,9 @@ do_scan = false; ScanAnalog(cardid, nVideoSource); } - else if (nScanType == ScanTypeSetting::Import) + else if (nScanType == ScanTypeSetting::DVBUtilsImport) { - do_scan = false; - Import(nVideoSource, parent->nCardType, parent->filename()); + ImportDVBUtils(nVideoSource, parent->nCardType, parent->filename()); } else if ((nScanType == ScanTypeSetting::FullScan_ATSC) || (nScanType == ScanTypeSetting::FullTransportScan) || @@ -416,49 +415,36 @@ } } -void ScanWizardScanner::Import(uint sourceid, int cardtype, - const QString &file) +void ScanWizardScanner::ImportDVBUtils(uint sourceid, int cardtype, + const QString &file) { - (void) sourceid; - (void) cardtype; - (void) file; + channels.clear(); -#ifdef USING_DVB - DVBConfParser *parser = NULL; - - if (CardUtil::OFDM == cardtype) - parser = new DVBConfParser(DVBConfParser::OFDM, sourceid, file); - else if (CardUtil::QPSK == cardtype) - parser = new DVBConfParser(DVBConfParser::QPSK, sourceid, file); - else if (CardUtil::QAM == cardtype) - parser = new DVBConfParser(DVBConfParser::QAM, sourceid, file); - else if ((CardUtil::ATSC == cardtype) || - (CardUtil::HDTV == cardtype)) - parser = new DVBConfParser(DVBConfParser::ATSC, sourceid, file); + DTVConfParser::cardtype_t type = DTVConfParser::UNKNOWN; + type = (CardUtil::OFDM == cardtype) ? DTVConfParser::OFDM : type; + type = (CardUtil::QPSK == cardtype) ? DTVConfParser::QPSK : type; + type = (CardUtil::QAM == cardtype) ? DTVConfParser::QAM : type; + type = ((CardUtil::ATSC == cardtype) || (CardUtil::HDTV == cardtype) || + (CardUtil::HDHOMERUN == cardtype)) ? DTVConfParser::ATSC : type; - if (!parser) + if (type == DTVConfParser::UNKNOWN) return; - connect(parser, SIGNAL(updateText(const QString&)), - this, SLOT( updateText(const QString&))); - - int ret = parser->parse(); - parser->deleteLater(); + DTVConfParser parser(type, sourceid, file); - if (DVBConfParser::ERROR_OPEN == ret) + DTVConfParser::return_t ret = parser.Parse(); + if (DTVConfParser::OK != ret) { - MythPopupBox::showOkPopup( - gContext->GetMainWindow(), tr("ScanWizard"), - tr("Failed to open '%1'").arg(file)); - } + QString msg = (DTVConfParser::ERROR_PARSE == ret) ? + tr("Failed to parse '%1'") : tr("Failed to open '%1'"); - if (DVBConfParser::ERROR_PARSE == ret) + MythPopupBox::showOkPopup(gContext->GetMainWindow(), + tr("ScanWizard"), msg.arg(file)); + } + else { - MythPopupBox::showOkPopup( - gContext->GetMainWindow(), tr("ScanWizard"), - tr("Failed to parse '%1'").arg(file)); + channels = parser.GetChannels(); } -#endif // USING_DVB } void ScanWizardScanner::PreScanCommon(uint cardid, uint sourceid) @@ -734,6 +720,55 @@ ScannerEvent::ERROR_TUNE); } } + else if (nScanType == ScanTypeSetting::DVBUtilsImport && channels.size()) + { + ok = true; + + VERBOSE(VB_SIPARSER, LOC + "ScanForChannels("<SetChannelFormat(parent->paneDVBUtilsImport->GetATSCFormat()); + + if (parent->paneDVBUtilsImport->DoDeleteChannels()) + { + MSqlQuery query(MSqlQuery::InitCon()); + query.prepare("DELETE FROM channel " + "WHERE sourceid = :SOURCEID"); + query.bindValue(":SOURCEID", nVideoSource); + query.exec(); + } + + scanner->SetRenameChannels( + parent->paneDVBUtilsImport->DoRenameChannels()); + + int ccardid = parent->captureCard(); + int pcardid = CardUtil::GetParentCardID(ccardid); + int cardid = (pcardid) ? pcardid : ccardid; + QString card_type = CardUtil::GetRawCardType(cardid, nVideoSource); + QString sub_type = card_type; + if (card_type == "DVB") + { + QString device = CardUtil::GetVideoDevice(cardid, nVideoSource); + ok = !device.isEmpty(); + if (ok) + sub_type = CardUtil::ProbeDVBType(device.toUInt()).upper(); + } + + if (ok) + { + ok = scanner->ScanForChannels(nVideoSource, std, + sub_type, channels); + } + if (ok) + { + post_event(this, ScannerEvent::ServicePct, + TRANSPORT_PCT); + } + else + { + post_event(this, ScannerEvent::TuneComplete, + ScannerEvent::ERROR_TUNE); + } + } else if (nScanType == ScanTypeSetting::TransportScan) { VERBOSE(VB_SIPARSER, LOC + "ScanTransport("< startChan; + + // dvb-utils imported channels + DTVChannelList channels; }; #endif // _SCANWIZARDSCANNER_H_ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/signalmonitor.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/signalmonitor.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/signalmonitor.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/signalmonitor.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -10,8 +10,10 @@ #include "mythcontext.h" #include "signalmonitor.h" -#include "libavcodec/avcodec.h" -#include "libmyth/util.h" +extern "C" { +#include "../libavcodec/avcodec.h" +} +#include "../libmyth/util.h" #ifdef USING_DVB # include "dvbsignalmonitor.h" diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/siscan.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/siscan.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/siscan.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/siscan.cpp 2007-04-13 23:59:17.000000000 -0500 @@ -322,7 +322,10 @@ pat_vec_t pats = sd->GetCachedPATs(); pmt_map_t pmt_map = sd->GetCachedPMTMap(); for (uint i = 0; i < pats.size(); i++) - UpdatePATinDB(mplexid, fn, freqid, pats[i], pmt_map, true); + { + UpdatePATinDB(mplexid, fn, freqid, pats[i], pmt_map, + (*current).expectedChannels, true); + } sd->ReturnCachedPMTTables(pmt_map); sd->ReturnCachedPATTables(pats); @@ -354,13 +357,19 @@ // Insert Terrestrial VCTs tvct_vec_t tvcts = sd->GetAllCachedTVCTs(); for (uint i = 0; i < tvcts.size(); i++) - UpdateVCTinDB(mplexid, fn, freqid, tvcts[i], true); + { + UpdateVCTinDB(mplexid, fn, freqid, tvcts[i], + (*current).expectedChannels, true); + } sd->ReturnCachedTVCTTables(tvcts); // Insert Cable VCTs cvct_vec_t cvcts = sd->GetAllCachedCVCTs(); for (uint i = 0; i < cvcts.size(); i++) - UpdateVCTinDB(mplexid, fn, freqid, cvcts[i], true); + { + UpdateVCTinDB(mplexid, fn, freqid, cvcts[i], + (*current).expectedChannels, true); + } sd->ReturnCachedCVCTTables(cvcts); // tell UI we are done with these channels @@ -386,7 +395,10 @@ vector sdts = sd->GetAllCachedSDTs(); for (uint i = 0; i < sdts.size(); i++) - UpdateSDTinDB((*current).mplexid, sdts[i], forceUpdate); + { + UpdateSDTinDB((*current).mplexid, sdts[i], + (*current).expectedChannels, forceUpdate); + } sd->ReturnCachedSDTTables(sdts); emit ServiceScanUpdateText(tr("Finished processing Services")); @@ -535,20 +547,19 @@ if (!waitingForTables) return true; - QString offset_str = current.offset() ? - QObject::tr(" offset %2").arg(current.offset()) : ""; - QString cur_chan = QString("%1%2") - .arg((*current).FriendlyName).arg(offset_str); - QString time_out_table_str = - QObject::tr("Timeout Scanning %1 -- no tables").arg(cur_chan); - QString time_out_sig_str = - QObject::tr("Timeout Scanning %1 -- no signal").arg(cur_chan); - // have the tables have timed out? if (timer.elapsed() > (int)channelTimeout) { + QString offset_str = current.offset() ? + QObject::tr(" offset %2").arg(current.offset()) : ""; + QString cur_chan = QString("%1%2") + .arg((*current).FriendlyName).arg(offset_str); + QString time_out_table_str = + QObject::tr("Timeout Scanning %1 -- no tables").arg(cur_chan); + emit ServiceScanUpdateText(time_out_table_str); VERBOSE(VB_SIPARSER, LOC + time_out_table_str); + return true; } @@ -560,6 +571,13 @@ if (NULL == sm || sm->HasSignalLock()) return false; + QString offset_str = current.offset() ? + QObject::tr(" offset %2").arg(current.offset()) : ""; + QString cur_chan = QString("%1%2") + .arg((*current).FriendlyName).arg(offset_str); + QString time_out_sig_str = + QObject::tr("Timeout Scanning %1 -- no signal").arg(cur_chan); + emit ServiceScanUpdateText(time_out_sig_str); VERBOSE(VB_SIPARSER, LOC + time_out_sig_str); @@ -787,6 +805,41 @@ return true; } +bool SIScan::ScanForChannels(uint sourceid, + const QString &std, + const QString &cardtype, + const DTVChannelList &channels) +{ + scanTransports.clear(); + nextIt = scanTransports.end(); + + DTVChannelList::const_iterator it = channels.begin(); + for (uint i = 0; it != channels.end(); ++it, i++) + { + TransportScanItem item(sourceid, std, QString::number(i), + cardtype, *it, signalTimeout); + + scanTransports += item; + + VERBOSE(VB_SIPARSER, LOC + item.toString()); + } + + if (scanTransports.empty()) + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "ScanForChannels() no transports"); + return false; + } + + timer.start(); + waitingForTables = false; + + nextIt = scanTransports.begin(); + transportsScanned = 0; + scanMode = TRANSPORT_LIST; + + return true; +} + /** \fn SIScan::ScanTransportsStartingOn(int,const QMap&) * \brief Generates a list of frequencies to scan and adds it to the * scanTransport list, and then sets the scanMode to TRANSPORT_LIST. @@ -953,6 +1006,52 @@ #endif // USING_DVB } +/** \fn SIScan::CheckImportedList(const DTVChannelInfoList&,uint,QString&,QString&,QString&) + * \brief If we as scanning a dvb-utils import verify channel is in list.. + */ +bool SIScan::CheckImportedList(const DTVChannelInfoList &channels, + uint mpeg_program_num, + QString &service_name, + QString &callsign, + QString &common_status_info) +{ + if (channels.empty()) + return true; + + bool found = false; + for (uint i = 0; i < channels.size(); i++) + { + VERBOSE(VB_IMPORTANT, + QString("comparing %1 %2 against %3 %4") + .arg(channels[i].serviceid).arg(channels[i].name) + .arg(mpeg_program_num).arg(common_status_info)); + + if (channels[i].serviceid == mpeg_program_num) + { + found = true; + if (!channels[i].name.isEmpty()) + { + service_name = QDeepCopy(channels[i].name); + callsign = QDeepCopy(channels[i].name); + } + } + } + + if (found) + { + common_status_info += QString(" %1 %2") + .arg(tr("as")).arg(service_name); + } + else + { + emit ServiceScanUpdateText( + tr("Skipping %1, not in imported channel map") + .arg(common_status_info)); + } + + return found; +} + // ///////////////////// DB STUFF ///////////////////// // ///////////////////// DB STUFF ///////////////////// // ///////////////////// DB STUFF ///////////////////// @@ -962,7 +1061,8 @@ void SIScan::UpdatePMTinDB( int db_source_id, int db_mplexid, const QString &friendlyName, int freqid, - int pmt_indx, const ProgramMapTable *pmt, bool /*force_update*/) + int pmt_indx, const ProgramMapTable *pmt, + const DTVChannelInfoList &channels, bool /*force_update*/) { // See if service already in database based on program number int chanid = ChannelUtil::GetChanID( @@ -988,10 +1088,16 @@ QString common_status_info = tr("%1%2%3 on %4 (%5)") .arg(service_name) - .arg(service_name.isEmpty() ? "" : " as ") + .arg(service_name.isEmpty() ? "" : QString(" %1 ").arg(tr("as"))) .arg(chan_num) .arg(friendlyName).arg(freqid); + if (!CheckImportedList(channels, pmt->ProgramNumber(), + service_name, chan_num, common_status_info)) + { + return; + } + if (chanid < 0) { // The service is not in database, add it emit ServiceScanUpdateText( @@ -1028,7 +1134,7 @@ void SIScan::UpdatePATinDB( int db_mplexid, const QString &friendlyName, int freqid, const ProgramAssociationTable *pat, const pmt_map_t &pmt_map, - bool force_update) + const DTVChannelInfoList &channels, bool force_update) { VERBOSE(VB_SIPARSER, LOC + QString("UpdatePATinDB(): tsid: 0x%1 mplex: %2") @@ -1066,7 +1172,7 @@ continue; UpdatePMTinDB(db_source_id, db_mplexid, friendlyName, freqid, - i, *vit, force_update); + i, *vit, channels, force_update); } } } @@ -1076,6 +1182,7 @@ void SIScan::UpdateVCTinDB(int db_mplexid, const QString &friendlyName, int freqid, const VirtualChannelTable *vct, + const DTVChannelInfoList &channels, bool force_update) { (void) force_update; @@ -1142,6 +1249,13 @@ .arg(vct->MajorChannel(i)).arg(vct->MinorChannel(i)) .arg(chan_num).arg(friendlyName).arg(freqid); + QString callsign = vct->ShortChannelName(i); + + if (!CheckImportedList(channels, vct->ProgramNumber(i), + longName, callsign, common_status_info)) + { + continue; + } QString msg = ""; if (chanid < 0) { // The service is not in database, add it @@ -1153,7 +1267,7 @@ db_mplexid, db_source_id, chanid, - vct->ShortChannelName(i), + callsign, longName, chan_num, vct->ProgramNumber(i), @@ -1170,7 +1284,7 @@ db_mplexid, db_source_id, chanid, - vct->ShortChannelName(i), + callsign, longName, chan_num, vct->ProgramNumber(i), @@ -1186,6 +1300,7 @@ * \brief Inserts channels from service description table. */ void SIScan::UpdateSDTinDB(int /*mplexid*/, const ServiceDescriptionTable *sdt, + const DTVChannelInfoList &channels, bool force_update) { if (!sdt->ServiceCount()) @@ -1261,6 +1376,14 @@ continue; } + QString common_status_info = service_name; + + if (!CheckImportedList(channels, sdt->ServiceID(i), + service_name, service_name, common_status_info)) + { + continue; + } + // See if service already in database based on service ID int chanid = ChannelUtil::GetChanID(db_mplexid, -1, -1, -1, sdt->ServiceID(i)); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/siscan.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/siscan.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/siscan.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/siscan.h 2007-04-13 23:59:17.000000000 -0500 @@ -14,6 +14,7 @@ // MythTV includes #include "frequencytables.h" #include "streamlisteners.h" +#include "dvbconfparser.h" class MSqlQuery; @@ -54,6 +55,9 @@ bool ScanTransportsStartingOn( int sourceid, const QMap &valueMap); bool ScanTransport(int mplexid); + bool ScanForChannels( + uint sourceid, const QString &std, const QString &cardtype, + const DTVChannelList&); bool ScanServicesSourceID(int SourceID); @@ -119,24 +123,34 @@ /// \brief Updates Transport Scan progress bar inline void UpdateScanPercentCompleted(void); + bool CheckImportedList(const DTVChannelInfoList&, + uint mpeg_program_num, + QString &service_name, + QString &callsign, + QString &common_status_info); + void HandleMPEGDBInsertion(const ScanStreamData *sd, bool wait); void UpdatePATinDB(int mplexid, const QString &friendlyName, int freqid, const ProgramAssociationTable*, const pmt_map_t&, + const DTVChannelInfoList&, bool force_update); void UpdatePMTinDB(int sourceid, int mplexid, const QString &friendlyName, int freqid, int pmt_indx, const ProgramMapTable*, + const DTVChannelInfoList&, bool force_update); void HandleATSCDBInsertion(const ScanStreamData *sd, bool wait); void UpdateVCTinDB(int mplexid, const QString &friendlyName, int freqid, const VirtualChannelTable*, + const DTVChannelInfoList&, bool force_update); void HandleDVBDBInsertion(const ScanStreamData *sd, bool wait); void UpdateSDTinDB(int mplexid, const ServiceDescriptionTable*, + const DTVChannelInfoList&, bool force_update); bool HandlePostInsertion(void); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/sourceutil.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/sourceutil.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/sourceutil.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/sourceutil.cpp 2007-08-23 17:39:10.000000000 -0500 @@ -114,7 +114,7 @@ return list; } -bool SourceUtil::IsEncoder(uint sourceid) +bool SourceUtil::IsEncoder(uint sourceid, bool strict) { bool encoder = true; @@ -135,15 +135,19 @@ "WHERE sourceid = :SOURCEID"); query.bindValue(":SOURCEID", sourceid); + bool has_any_chan = false; if (!query.exec() || !query.isActive()) MythContext::DBError("SourceUtil::IsEncoder", query); else { while (query.next()) + { encoder &= !query.value(0).toInt() && !query.value(1).toInt(); + has_any_chan = true; + } } - return encoder; + return (strict && !has_any_chan) ? false: encoder; } bool SourceUtil::IsUnscanable(uint sourceid) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/sourceutil.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/sourceutil.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/sourceutil.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/sourceutil.h 2007-08-23 17:39:10.000000000 -0500 @@ -14,7 +14,7 @@ static bool GetListingsLoginData(uint sourceid, QString &grabber, QString &userid, QString &passwd, QString &lineupid); - static bool IsEncoder(uint sourceid); + static bool IsEncoder(uint sourceid, bool strict = false); static bool IsUnscanable(uint sourceid); static bool UpdateChannelsFromListings( uint sourceid, QString cardtype = QString::null); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/tv_rec.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/tv_rec.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/tv_rec.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/tv_rec.cpp 2007-04-14 00:04:11.000000000 -0500 @@ -1685,6 +1685,9 @@ sd->SetCaching(true); } + bool fta = CardUtil::IgnoreEncrypted( + GetCaptureCardNum(), channel->GetCurrentInput()); + // Check if this is an ATSC Channel int major = channel->GetMajorChannel(); int minor = channel->GetMinorChannel(); @@ -1706,7 +1709,7 @@ sm->SetStreamData(sd); sm->SetChannel(major, minor); sd->SetVideoStreamsRequired(1); - sm->SetFTAOnly(true); + sm->SetFTAOnly(fta); // Try to get pid of VCT from cache and // require MGT if we don't have VCT pid. @@ -1756,9 +1759,6 @@ if (GetDVBChannel()) sd->SetIgnoreCRC(GetDVBChannel()->HasCRCBug()); - bool fta = CardUtil::IgnoreEncrypted( - GetCaptureCardNum(), channel->GetCurrentInput()); - dsd->Reset(netid, tsid, progNum); sm->SetStreamData(sd); sm->SetDVBService(netid, tsid, progNum); @@ -1797,9 +1797,6 @@ sd->SetIgnoreCRC(GetDVBChannel()->HasCRCBug()); #endif // USING_DVB - bool fta = CardUtil::IgnoreEncrypted( - GetCaptureCardNum(), channel->GetCurrentInput()); - sd->Reset(progNum); sm->SetStreamData(sd); sm->SetProgramNumber(progNum); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videobuffers.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videobuffers.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videobuffers.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videobuffers.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -4,7 +4,9 @@ #include #include "mythcontext.h" #include "videobuffers.h" +extern "C" { #include "../libavcodec/avcodec.h" +} #include "fourcc.h" #ifdef USING_XVMC diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videooutbase.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videooutbase.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videooutbase.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videooutbase.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -31,7 +31,9 @@ #include "dithertable.h" +extern "C" { #include "../libavcodec/avcodec.h" +} #include "filtermanager.h" diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_ivtv.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_ivtv.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_ivtv.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_ivtv.cpp 2007-08-22 13:46:08.000000000 -0500 @@ -39,7 +39,9 @@ #include "libmyth/mythcontext.h" #include "NuppelVideoPlayer.h" +extern "C" { #include "../libavcodec/avcodec.h" +} #include "yuv2rgb.h" #include "osd.h" #include "osdsurface.h" diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_quartz.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_quartz.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_quartz.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_quartz.cpp 2007-03-15 17:34:28.000000000 -0500 @@ -17,7 +17,7 @@ * a second time, may cause a crash (backtraces appreciated) * * = REVISION - * $Id: videoout_quartz.cpp 11013 2006-09-01 03:25:59Z nigel $ + * $Id: videoout_quartz.cpp 13053 2007-03-15 22:34:28Z nigel $ * * = AUTHORS * Nigel Pearson, Jeremiah Morris @@ -1638,10 +1638,8 @@ accel->DecodeFrame(buffer); #endif // CONFIG_MAC_ACCEL - if (!buffer) - buffer = vbuffers.GetScratchFrame(); - - framesPlayed = buffer->frameNumber + 1; + if (buffer) + framesPlayed = buffer->frameNumber + 1; } void VideoOutputQuartz::Show(FrameScanType t) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_xv.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_xv.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_xv.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_xv.cpp 2007-07-23 01:36:34.000000000 -0500 @@ -273,7 +273,7 @@ * \bug This works for all user specified mode lines, but sometimes * fails for autogenerated mode lines. * - * \return integer approximation of monitor refresh rate. + * \return integer approximation of monitor refresh time (microseconds) */ int VideoOutputXv::GetRefreshRate(void) @@ -294,13 +294,19 @@ return -1; } - double rate = (double)((double)(dot_clock * 1000.0) / - (double)(mode_line.htotal * mode_line.vtotal)); - - // Assume 60Hz if we can't otherwise determine it. - if (rate == 0) - rate = 60; + double rate = mode_line.htotal * mode_line.vtotal; + + // Catch bad data from video drivers (divide by zero causes return of NaN) + if (rate == 0.0 || dot_clock == 0) + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "GetRefreshRate(): " + "X11 ModeLine query returned zeroes"); + return -1; + } + + rate = (dot_clock * 1000.0) / rate; + // Assume 60Hz if rate isn't good: if (rate < 20 || rate > 200) { VERBOSE(VB_PLAYBACK, LOC + QString("Unreasonable refresh rate %1Hz " @@ -2859,9 +2865,7 @@ VideoFrame *osdframe = NULL; int ret = DisplayOSD(xvmc_osd->OSDFrame(), osd, -1, xvmc_osd->GetRevision()); - OSDSurface *osdsurf = osd->Display(); - if (osdsurf) - xvmc_osd->SetRevision(osdsurf->GetRevision()); + xvmc_osd->SetRevision(osd->GetRevision()); if (ret >= 0 && xvmc_osd->NeedFrame()) { // If there are no available buffer, try to toss old diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_xv.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_xv.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videoout_xv.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videoout_xv.h 2007-08-22 13:46:08.000000000 -0500 @@ -14,7 +14,9 @@ #include #undef HAVE_AV_CONFIG_H +extern "C" { #include "../libavcodec/avcodec.h" +} class NuppelVideoPlayer; class ChromaKeyOSD; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videosource.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videosource.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videosource.cpp 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videosource.cpp 2007-08-20 16:10:34.000000000 -0500 @@ -18,6 +18,8 @@ #include #include #include +#include +#include // MythTV headers #include "mythconfig.h" @@ -103,7 +105,7 @@ XMLTVGrabber(const VideoSource& parent) : VSSetting(parent, "xmltvgrabber") { - setLabel(QObject::tr("XMLTV listings grabber")); + setLabel(QObject::tr("Listings grabber")); }; }; @@ -147,7 +149,9 @@ class DataDirectPassword: public LineEditSetting, public VSSetting { public: DataDirectPassword(const VideoSource& parent): - VSSetting(parent, "password") { + VSSetting(parent, "password") + { + SetPasswordEcho(true); setLabel(QObject::tr("Password")); }; }; @@ -158,7 +162,6 @@ { (void) uid; (void) pwd; - (void) _source; #ifdef USING_BACKEND if (uid.isEmpty() || pwd.isEmpty()) return; @@ -199,8 +202,11 @@ void DataDirect_config::load() { VerticalConfigurationGroup::load(); - if ((userid->getValue() != lastloadeduserid) || - (password->getValue() != lastloadedpassword)) + bool is_sd_userid = userid->getValue().contains("@") > 0; + bool match = ((is_sd_userid && (source == DD_SCHEDULES_DIRECT)) || + (!is_sd_userid && (source == DD_ZAP2IT))); + if (((userid->getValue() != lastloadeduserid) || + (password->getValue() != lastloadedpassword)) && match) { lineupselector->fillSelections(userid->getValue(), password->getValue(), @@ -295,15 +301,7 @@ "instead of just 'mythfilldatabase'.\nYour grabber does not provide " "channel numbers, so you have to set them manually."); - if (grabber == "tv_grab_de_tvtoday" || grabber == "tv_grab_se_swedb" || - grabber == "tv_grab_fi" || grabber == "tv_grab_es" || - grabber == "tv_grab_es_laguiatv" || - grabber == "tv_grab_nl" || grabber == "tv_grab_jp" || - grabber == "tv_grab_no" || grabber == "tv_grab_pt" || - grabber == "tv_grab_ee" || grabber == "tv_grab_be_tvb" || - grabber == "tv_grab_be_tlm" || grabber == "tv_grab_is" || - grabber == "tv_grab_br" || grabber == "tv_grab_cz" || - grabber == "tv_grab_il" || grabber == "tv_grab_ru") + if (is_grabber_external(grabber)) { VERBOSE(VB_IMPORTANT, "\n" << err_msg); MythPopupBox::showOkPopup( @@ -370,75 +368,61 @@ // only save settings for the selected grabber setSaveAll(false); - - addTarget("datadirect", new DataDirect_config(parent)); - grabber->addSelection("North America (DataDirect)", "datadirect"); - - addTarget("eitonly", new EITOnly_config(parent)); - grabber->addSelection("Transmitted guide only (EIT)", "eitonly"); - - addTarget("tv_grab_de_tvtoday", new XMLTV_generic_config(parent, "tv_grab_de_tvtoday")); - grabber->addSelection("Germany (tvtoday)", "tv_grab_de_tvtoday"); - - addTarget("tv_grab_se_swedb", new XMLTV_generic_config(parent, "tv_grab_se_swedb")); - grabber->addSelection("Sweden (tv.swedb.se)","tv_grab_se_swedb"); - - addTarget("tv_grab_no", new XMLTV_generic_config(parent, "tv_grab_no")); - grabber->addSelection("Norway","tv_grab_no"); - - addTarget("tv_grab_uk_rt", new XMLTV_generic_config(parent, "tv_grab_uk_rt")); - grabber->addSelection("United Kingdom (alternative)","tv_grab_uk_rt"); - - addTarget("tv_grab_au", new XMLTV_generic_config(parent, "tv_grab_au")); - grabber->addSelection("Australia", "tv_grab_au"); - - addTarget("tv_grab_fi", new XMLTV_generic_config(parent, "tv_grab_fi")); - grabber->addSelection("Finland", "tv_grab_fi"); - - addTarget("tv_grab_es", new XMLTV_generic_config(parent, "tv_grab_es")); - grabber->addSelection("Spain", "tv_grab_es"); - - addTarget("tv_grab_es_laguiatv", new XMLTV_generic_config(parent, "tv_grab_es_laguiatv")); - grabber->addSelection("Spain (Alt)", "tv_grab_es_laguiatv"); - - addTarget("tv_grab_nl", new XMLTV_generic_config(parent, "tv_grab_nl")); - grabber->addSelection("Holland", "tv_grab_nl"); - - addTarget("tv_grab_dk", new XMLTV_generic_config(parent, "tv_grab_dk")); - grabber->addSelection("Denmark", "tv_grab_dk"); - addTarget("tv_grab_fr", new XMLTV_generic_config(parent, "tv_grab_fr")); - grabber->addSelection("France", "tv_grab_fr"); - - addTarget("tv_grab_jp", new XMLTV_generic_config(parent, "tv_grab_jp")); - grabber->addSelection("Japan", "tv_grab_jp"); - - addTarget("tv_grab_pt", new XMLTV_generic_config(parent, "tv_grab_pt")); - grabber->addSelection("Portugal", "tv_grab_pt"); - - addTarget("tv_grab_ee", new XMLTV_generic_config(parent, "tv_grab_ee")); - grabber->addSelection("Estonia", "tv_grab_ee"); - - addTarget("tv_grab_be_tvb", new XMLTV_generic_config(parent, "tv_grab_be_tvb")); - grabber->addSelection("Belgium (Dutch)", "tv_grab_be_tvb"); - - addTarget("tv_grab_be_tlm", new XMLTV_generic_config(parent, "tv_grab_be_tlm")); - grabber->addSelection("Belgium (French)", "tv_grab_be_tlm"); - - addTarget("tv_grab_is", new XMLTV_generic_config(parent, "tv_grab_is")); - grabber->addSelection("Iceland", "tv_grab_is"); + addTarget("schedulesdirect1", + new DataDirect_config(parent, DD_SCHEDULES_DIRECT)); + grabber->addSelection("North America (SchedulesDirect.org) " + "(Internal)", "schedulesdirect1"); + +#if 1 + addTarget("datadirect", new DataDirect_config(parent, DD_ZAP2IT)); + grabber->addSelection( + "North America (TMS Labs) (Internal)", "datadirect"); +#endif - addTarget("tv_grab_br", new XMLTV_generic_config(parent, "tv_grab_br")); - grabber->addSelection("Brazil", "tv_grab_br"); + addTarget("eitonly", new EITOnly_config(parent)); + grabber->addSelection("Transmitted guide only (EIT)", "eitonly"); - addTarget("tv_grab_cz", new XMLTV_generic_config(parent, "tv_grab_cz")); - grabber->addSelection("Czech Republic", "tv_grab_cz"); + QProcess find_grabber_proc( QString("tv_find_grabbers"), this ); + find_grabber_proc.addArgument("baseline"); + find_grabber_proc.addArgument("manualconfig"); + if ( find_grabber_proc.start() ) + { - addTarget("tv_grab_il", new XMLTV_generic_config(parent, "tv_grab_il")); - grabber->addSelection("Israel", "tv_grab_il"); + int i=0; + // Assume it shouldn't take more than 25 seconds + // Broken versions of QT cause QProcess::start + // and QProcess::isRunning to return true even + // when the executable doesn't exist + while (find_grabber_proc.isRunning() && i < 250) + { + usleep(100000); + ++i; + } - addTarget("tv_grab_ru", new XMLTV_generic_config(parent, "tv_grab_ru")); - grabber->addSelection("Russia", "tv_grab_ru"); + if (find_grabber_proc.normalExit()) + { + while (find_grabber_proc.canReadLineStdout()) + { + QString grabber_list = find_grabber_proc.readLineStdout(); + QStringList grabber_split = QStringList::split("|", + grabber_list); + QString grabber_name = grabber_split[1] + " (xmltv)"; + QFileInfo grabber_file(grabber_split[0]); + addTarget(grabber_file.fileName(), + new XMLTV_generic_config(parent, grabber_file.fileName())); + grabber->addSelection(grabber_name, grabber_file.fileName()); + } + } + else + { + VERBOSE(VB_IMPORTANT, "tv_find_grabbers exited early or we timed out waiting"); + } + } + else + { + VERBOSE(VB_IMPORTANT, "Failed to run tv_find_grabbers"); + } addTarget("/bin/true", new NoGrabber_config(parent)); grabber->addSelection("No grabber", "/bin/true"); @@ -1477,26 +1461,32 @@ }; }; -class FreeToAir: public CheckBoxSetting, public CISetting { +class FreeToAir: public CheckBoxSetting, public CISetting +{ public: FreeToAir(const CardInput& parent): CISetting(parent, "freetoaironly") { setValue(true); - setLabel(QObject::tr("Free to air channels only.")); - setHelpText(QObject::tr("If set, only free to air channels will be " - "used.")); + setLabel(QObject::tr("Unencrypted channels only")); + setHelpText(QObject::tr( + "If set, only unencrypted channels will be tuned to " + "by MythTV or not be ignored by the MythTV channel " + "scanner.")); }; }; -class RadioServices: public CheckBoxSetting, public CISetting { -public: +class RadioServices: public CheckBoxSetting, public CISetting +{ + public: RadioServices(const CardInput& parent): CISetting(parent, "radioservices") { setValue(true); - setLabel(QObject::tr("Radio channels.")); - setHelpText(QObject::tr("If set, radio channels will also be included.")); + setLabel(QObject::tr("Allow audio only channels")); + setHelpText(QObject::tr( + "If set, audio only channels will not be ignored " + "by the MythTV channel scanner.")); }; }; @@ -1597,7 +1587,7 @@ }; }; -CardInput::CardInput(bool isDVBcard, int _cardid) +CardInput::CardInput(bool isDTVcard, bool isDVBcard, int _cardid) { (void) _cardid; @@ -1621,28 +1611,30 @@ group->addChild(new PresetTuner(*this)); } -#ifdef USING_DVB - if (isDVBcard) + if (isDTVcard) { - ConfigurationGroup *dvbgroup = - new HorizontalConfigurationGroup(); - dvbgroup->setLabel(QObject::tr("DVB options")); - + // we place this in a group just so the margins match the DVB ones. ConfigurationGroup *chgroup = - new VerticalConfigurationGroup(false, false, true, true); + new HorizontalConfigurationGroup(false, false, true, true); + chgroup->addChild(new FreeToAir(*this)); + group->addChild(chgroup); + } +#ifdef USING_DVB + if (isDVBcard) + { TransButtonSetting *diseqc = new TransButtonSetting(); diseqc->setLabel(tr("DVB-S")); diseqc->setHelpText(tr("Input and satellite settings.")); diseqc->setVisible(DTVDeviceNeedsConfiguration(_cardid)); - dvbgroup->addChild(diseqc); + group->addChild(diseqc); connect(diseqc, SIGNAL(pressed()), SLOT(diseqcConfig())); - chgroup->addChild(new FreeToAir(*this)); + ConfigurationGroup *chgroup = + new HorizontalConfigurationGroup(false, false, true, true); chgroup->addChild(new RadioServices(*this)); chgroup->addChild(new DishNetEIT(*this)); - dvbgroup->addChild(chgroup); - group->addChild(dvbgroup); + group->addChild(chgroup); } #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videosource.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videosource.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythtv/videosource.h 2007-01-22 03:08:23.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythtv/videosource.h 2007-08-20 16:10:34.000000000 -0500 @@ -11,8 +11,26 @@ class SignalTimeout; class ChannelTimeout; class UseEIT; - class VideoSource; + +static inline bool is_grabber_external(const QString &grabber) +{ + return !(grabber == "datadirect" || + grabber == "eitonly" || + grabber == "schedulesdirect1" || + grabber == "/bin/true"); +} + +static inline bool is_grabber_datadirect(const QString &grabber) +{ + return (grabber == "datadirect") || (grabber == "schedulesdirect1"); +} + +static inline bool is_grabber_labs(const QString &grabber) +{ + return grabber == "datadirect"; +} + class VSSetting: public SimpleDBStorage { protected: VSSetting(const VideoSource& _parent, QString name): @@ -62,7 +80,7 @@ { Q_OBJECT public: - DataDirect_config(const VideoSource& _parent, int _source = DD_ZAP2IT); + DataDirect_config(const VideoSource& _parent, int _ddsource); virtual void load(void); @@ -531,7 +549,7 @@ { Q_OBJECT public: - CardInput(bool is_dvb_card, int cardid); + CardInput(bool is_dtv_card, bool is_dvb_card, int cardid); int getInputID(void) const { return id->intValue(); }; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythui/mythmainwindow.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythui/mythmainwindow.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythui/mythmainwindow.cpp 2007-01-22 03:08:25.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythui/mythmainwindow.cpp 2007-06-08 04:04:19.000000000 -0500 @@ -142,6 +142,7 @@ int screenwidth, screenheight; QRect screenRect; + QRect uiScreenRect; int xbase, ybase; bool does_fill_screen; @@ -428,7 +429,7 @@ } if (!d->painter->SupportsClipping()) - d->repaintRegion = d->repaintRegion.unite(d->screenRect); + d->repaintRegion = d->repaintRegion.unite(d->uiScreenRect); d->painter->Begin(this); @@ -439,7 +440,7 @@ if (rects[i].width() == 0 || rects[i].height() == 0) continue; - if (rects[i] != d->screenRect) + if (rects[i] != d->uiScreenRect) d->painter->SetClipRect(rects[i]); for (it = d->stackList.begin(); it != d->stackList.end(); ++it) @@ -503,6 +504,7 @@ d->ybase, d->screenheight, d->hmult); d->screenRect = QRect(d->xbase, d->ybase, d->screenwidth, d->screenheight); + d->uiScreenRect = QRect(0, 0, d->screenwidth, d->screenheight); setGeometry(d->xbase, d->ybase, d->screenwidth, d->screenheight); setFixedSize(QSize(d->screenwidth, d->screenheight)); @@ -814,8 +816,6 @@ else { QString inskey = keybind; - inskey.replace('\\', "\\\\"); - inskey.replace('\"', "\\\""); query.prepare("INSERT INTO keybindings (context, action, " "description, keylist, hostname) VALUES " @@ -936,8 +936,6 @@ else { QString inskey = keybind; - inskey.replace('\\', "\\\\"); - inskey.replace('\"', "\\\""); query.prepare("INSERT INTO jumppoints (destination, description, " "keylist, hostname) VALUES ( :DEST, :DESC, :KEYLIST, " @@ -1474,6 +1472,6 @@ QRect MythMainWindow::GetUIScreenRect(void) { - return QRect(0, 0, d->screenwidth, d->screenheight); + return d->uiScreenRect; } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythui/myththemedmenu.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythui/myththemedmenu.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythui/myththemedmenu.cpp 2007-01-22 03:08:25.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythui/myththemedmenu.cpp 2007-06-02 09:35:10.000000000 -0500 @@ -14,7 +14,6 @@ #include "mythfontproperties.h" #include "mythimage.h" #include "mythdialogbox.h" -#include "mythmediamonitor.h" #include "lcddevice.h" #include "mythplugin.h" @@ -136,6 +135,7 @@ bool buttoncenter; QMap titleIcons; + QMap m_loadedImages; QString titleText; QPoint titlePos; @@ -301,6 +301,7 @@ jt.data()->DownRef(); } titleIcons.clear(); + m_loadedImages.clear(); normalAttributes = activeAttributes = TextAttributes(); setDefaults(); @@ -766,16 +767,32 @@ { if (info.tagName() == "image") { - QString titlepath = dir + getFirstText(info); - QImage *tmppix = gContext->LoadScaleImage(titlepath); - if (!tmppix) - continue; + QString titlepath = dir + getFirstText(info); QString name = info.attribute("mode", ""); if (name != "") { - titleIcons[name] = MythImage::FromQImage(&tmppix); + MythImage *icon; + QImage *tmppix; + + if (m_loadedImages[titlepath]) + { + icon = m_loadedImages[titlepath]; + icon->UpRef(); + } + else + { + tmppix = gContext->LoadScaleImage(titlepath); + + if (!tmppix) + continue; + + icon = MythImage::FromQImage(&tmppix); + m_loadedImages.insert(titlepath, icon); + } + + titleIcons[name] = icon; } else { @@ -884,9 +901,10 @@ bool hasicon = false; QString name = ""; - QImage *image = NULL; - QImage *activeimage = NULL; - QImage *watermark = NULL; + QImage *tmpimg = NULL; + MythImage *image = NULL; + MythImage *activeimage = NULL; + MythImage *watermark = NULL; QPoint offset; name = element.attribute("name", ""); @@ -895,21 +913,44 @@ for (QDomNode child = element.firstChild(); !child.isNull(); child = child.nextSibling()) - { + { QDomElement info = child.toElement(); if (!info.isNull()) { if (info.tagName() == "image") { - QString imagepath = dir + getFirstText(info); - image = gContext->LoadScaleImage(imagepath); + QString imagepath = dir + getFirstText(info); + + if (m_loadedImages[imagepath]) + { + image = m_loadedImages[imagepath]; + image->UpRef(); + } + else + { + tmpimg = gContext->LoadScaleImage(imagepath); + image = MythImage::FromQImage(&tmpimg); + m_loadedImages.insert(imagepath, image); + } + if (image) hasicon = true; } else if (info.tagName() == "activeimage") { QString imagepath = dir + getFirstText(info); - activeimage = gContext->LoadScaleImage(imagepath); + + if (m_loadedImages[imagepath]) + { + activeimage = m_loadedImages[imagepath]; + activeimage->UpRef(); + } + else + { + tmpimg = gContext->LoadScaleImage(imagepath); + activeimage = MythImage::FromQImage(&tmpimg); + m_loadedImages.insert(imagepath, activeimage); + } } else if (info.tagName() == "offset") { @@ -919,8 +960,19 @@ else if (info.tagName() == "watermarkimage") { QString imagepath = dir + getFirstText(info); - watermark = gContext->LoadScaleImage(imagepath); - } + + if (m_loadedImages[imagepath]) + { + watermark = m_loadedImages[imagepath]; + watermark->UpRef(); + } + else + { + tmpimg = gContext->LoadScaleImage(imagepath); + watermark = MythImage::FromQImage(&tmpimg); + m_loadedImages.insert(imagepath, watermark); + } + } else { VERBOSE(VB_GENERAL, QString("MythThemedMenuPrivate: Unknown tag %1 " @@ -953,9 +1005,9 @@ ButtonIcon newbutton; newbutton.name = name; - newbutton.icon = MythImage::FromQImage(&image); + newbutton.icon = image; newbutton.offset = offset; - newbutton.activeicon = MythImage::FromQImage(&activeimage); + newbutton.activeicon = activeimage; if (watermark) { @@ -966,9 +1018,12 @@ watermarkRect.setHeight(watermark->height()); } - newbutton.watermark = MythImage::FromQImage(&watermark); + newbutton.watermark = watermark; allButtonIcons[name] = newbutton; + + if (tmpimg) + delete tmpimg; } void MythThemedMenuState::setDefaults(void) @@ -1870,9 +1925,7 @@ } else if (action == "EJECT") { - MediaMonitor *mon = MediaMonitor::GetMediaMonitor(); - if (mon) - mon->ChooseAndEjectMedia(); + myth_eject(); } else handled = false; @@ -2078,9 +2131,7 @@ } else if (action.left(5) == "EJECT") { - MediaMonitor *mon = MediaMonitor::GetMediaMonitor(); - if (mon) - mon->ChooseAndEjectMedia(); + myth_eject(); } else if (action.left(5) == "JUMP ") { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/broadcast.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/broadcast.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/broadcast.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/broadcast.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,56 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: broadcast.h +// +// Purpose - Broadcast QSocketDevice Implmenetation +// +// Created By : David Blain Created On : Oct. 1, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __BROADCAST_H__ +#define __BROADCAST_H__ + +#include + +///////////////////////////////////////////////////////////////////////////// +// Broadcast Socket is used for XBox (original) since Multicast is not supported +///////////////////////////////////////////////////////////////////////////// + +class QBroadcastSocket : public QSocketDevice +{ + public: + + QHostAddress m_address; + Q_UINT16 m_port; + struct ip_mreq m_imr; + + public: + + QBroadcastSocket( QString sAddress, Q_UINT16 nPort ) + : QSocketDevice( QSocketDevice::Datagram ) + { + m_address.setAddress( sAddress ); + m_port = nPort; + + int one = 1; + + if ( setsockopt( socket(), SOL_SOCKET, SO_BROADCAST, &one, sizeof( one )) < 0) + { + VERBOSE(VB_IMPORTANT, QString( "QBroadcastSocket: setsockopt - SO_BROADCAST Error" )); + } + + setAddressReusable( true ); + + bind( m_address, m_port ); + } + + virtual ~QBroadcastSocket() + { + int zero = 0; + + setsockopt( socket(), SOL_SOCKET, SO_BROADCAST, &zero, sizeof( zero )); + } +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/bufferedsocketdevice.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/bufferedsocketdevice.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/bufferedsocketdevice.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/bufferedsocketdevice.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -8,8 +8,9 @@ // ////////////////////////////////////////////////////////////////////////////// +#include "util.h" #include "bufferedsocketdevice.h" -#include "upnpglobal.h" +#include "upnputil.h" ///////////////////////////////////////////////////////////////////////////// // @@ -19,8 +20,14 @@ { m_pSocket = new QSocketDevice(); - m_pSocket->setSocket( nSocket, QSocketDevice::Stream ); - m_pSocket->setBlocking( true ); + m_pSocket->setSocket ( nSocket, QSocketDevice::Stream ); + m_pSocket->setBlocking ( false ); + m_pSocket->setAddressReusable( true ); + + struct linger ling = {1, 1}; + + if ( setsockopt( socket(), SOL_SOCKET, SO_LINGER, &ling, sizeof( ling )) < 0) + VERBOSE(VB_IMPORTANT, QString( "BufferedSocketDevice: setsockopt - SO_LINGER Error" )); m_bufWrite.setAutoDelete( TRUE ); @@ -30,14 +37,14 @@ m_nWriteSize = 0; m_nWriteIndex = 0; m_bHandleSocketDelete= true; - } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -BufferedSocketDevice::BufferedSocketDevice( QSocketDevice *pSocket /*= NULL*/ ) +BufferedSocketDevice::BufferedSocketDevice( QSocketDevice *pSocket /*= NULL*/, + bool bTakeOwnership /* = false */ ) { m_bufWrite.setAutoDelete( TRUE ); @@ -49,7 +56,7 @@ m_nMaxReadBufferSize = 0; m_nWriteSize = 0; m_nWriteIndex = 0; - m_bHandleSocketDelete= false; + m_bHandleSocketDelete= bTakeOwnership; } @@ -91,6 +98,18 @@ // ///////////////////////////////////////////////////////////////////////////// +bool BufferedSocketDevice::Connect( const QHostAddress &addr, Q_UINT16 port ) +{ + if (m_pSocket == NULL) + return false; + + return m_pSocket->connect( addr, port ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + QSocketDevice *BufferedSocketDevice::SocketDevice() { return( m_pSocket ); @@ -142,10 +161,10 @@ // ///////////////////////////////////////////////////////////////////////////// -void BufferedSocketDevice::ReadBytes() +int BufferedSocketDevice::ReadBytes() { if (m_pSocket == NULL) - return; + return m_bufRead.size(); Q_LONG maxToRead = 0; @@ -154,7 +173,7 @@ maxToRead = m_nMaxReadBufferSize - m_bufRead.size(); if ( maxToRead <= 0 ) - return; + return m_bufRead.size(); } Q_LONG nbytes = m_pSocket->bytesAvailable(); @@ -176,8 +195,14 @@ } if (a) + { + // for( long n = 0; n < a->count(); n++ ) + // cerr << a->at( n ); + m_bufRead.append( a ); + } + return m_bufRead.size(); } ///////////////////////////////////////////////////////////////////////////// @@ -228,11 +253,11 @@ bool osBufferFull = FALSE; int consumed = 0; - while ( !osBufferFull && ( m_nWriteSize > 0 )) + while ( !osBufferFull && ( m_nWriteSize > 0 ) && m_pSocket->isValid()) { QByteArray *a = m_bufWrite.first(); - int nwritten; + int nwritten = 0; int i = 0; if ( (int)a->size() - m_nWriteIndex < 1460 ) @@ -338,9 +363,7 @@ if ( !m_pSocket->isValid() ) return 0; - ReadBytes(); - - return m_bufRead.size(); + return ReadBytes(); } ///////////////////////////////////////////////////////////////////////////// @@ -349,6 +372,8 @@ Q_ULONG BufferedSocketDevice::WaitForMore( int msecs, bool *pTimeout /* = NULL*/ ) { + bool bTimeout = false; + if ( !m_pSocket->isValid() ) return 0; @@ -356,16 +381,39 @@ if (nBytes == 0) { - if ( pTimeout == NULL ) - nBytes = m_pSocket->waitForMore( msecs ); - else - nBytes = m_pSocket->waitForMore( msecs, pTimeout ); +/* + The following code is a possible workaround to the lost request problem + I just hate looping too much to put it in. I believe there is something + I'm missing that is causing the lost packets... Just need to find it. + + bTimeout = true; + int nCount = 0; + int msWait = msecs / 100; + + while (((nBytes = ReadBytes()) == 0 ) && + (nCount++ < 100 ) && + bTimeout && + m_pSocket->isValid() ) + { + // give up control + + usleep( 1000 ); // should be some multiple of msWait. - if ( nBytes > 0 ) - ReadBytes(); + } } +*/ + // -=>TODO: Override the timeout to 1 second... Closes connection sooner + // to help recover from lost requests. (hack until better fix found) - return BytesAvailable(); //m_bufRead.size(); + msecs = 1000; + + nBytes = m_pSocket->waitForMore( msecs, &bTimeout ); + + if (pTimeout != NULL) + *pTimeout = bTimeout; + } + + return nBytes; // nBytes //m_bufRead.size(); } ///////////////////////////////////////////////////////////////////////////// @@ -565,6 +613,50 @@ // ///////////////////////////////////////////////////////////////////////////// +QString BufferedSocketDevice::ReadLine( int msecs ) +{ + MythTimer timer; + QString sLine; + + if ( CanReadLine() ) + return( ReadLine() ); + + // ---------------------------------------------------------------------- + // If the user supplied a timeout, lets loop until we can read a line + // or timeout. + // ---------------------------------------------------------------------- + + if ( msecs > 0) + { + bool bTimeout = false; + + timer.start(); + + while ( !CanReadLine() && !bTimeout ) + { +// VERBOSE( VB_UPNP, "BufferedSocketDevice::ReadLine - Can't Read Line... Waiting for more." ); + + WaitForMore( msecs, &bTimeout ); + + if ( timer.elapsed() >= msecs ) + { + bTimeout = true; + VERBOSE( VB_UPNP, "BufferedSocketDeviceRequest::ReadLine - Exceeded Total Elapsed Wait Time." ); + } + + } + + if (CanReadLine()) + sLine = ReadLine(); + } + + return( sLine ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + Q_UINT16 BufferedSocketDevice::Port() const { if (m_pSocket) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/bufferedsocketdevice.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/bufferedsocketdevice.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/bufferedsocketdevice.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/bufferedsocketdevice.h 2007-08-23 11:09:51.000000000 -0500 @@ -12,6 +12,9 @@ #define __BUFFEREDSOCKETDEVICE_H__ #include +#include + +#include "mythcontext.h" #include "private/qinternal_p.h" @@ -41,13 +44,14 @@ QMembuf m_bufRead; QPtrList m_bufWrite; - void ReadBytes ( ); + int ReadBytes ( ); bool ConsumeWriteBuf( Q_ULONG nbytes ); public: BufferedSocketDevice( int nSocket ); - BufferedSocketDevice( QSocketDevice *pSocket = NULL ); + BufferedSocketDevice( QSocketDevice *pSocket = NULL, + bool bTakeOwnership = false ); virtual ~BufferedSocketDevice( ); @@ -57,6 +61,7 @@ void SetDestAddress ( QHostAddress hostAddress, Q_UINT16 nPort ); + bool Connect ( const QHostAddress & addr, Q_UINT16 port ); void Close (); void Flush (); QIODevice::Offset Size (); @@ -66,6 +71,7 @@ Q_ULONG BytesAvailable (); Q_ULONG WaitForMore ( int msecs, bool *timeout = NULL ); + Q_ULONG BytesToWrite () const; void ClearPendingData (); void ClearReadBuffer (); @@ -74,14 +80,14 @@ Q_LONG WriteBlock ( const char *data, Q_ULONG len ); Q_LONG WriteBlockDirect ( const char *data, Q_ULONG len ); - Q_LONG ReadLine ( char *data, Q_ULONG maxlen ); - int Getch (); int Putch ( int ); int Ungetch (int); bool CanReadLine (); - virtual QString ReadLine (); + QString ReadLine (); + QString ReadLine ( int msecs ); + Q_LONG ReadLine ( char *data, Q_ULONG maxlen ); Q_UINT16 Port () const; Q_UINT16 PeerPort () const; @@ -95,5 +101,4 @@ int socket () { return( ( m_pSocket ) ? m_pSocket->socket() : 0 ); } }; - #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/configuration.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/configuration.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/configuration.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/configuration.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,319 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: configuration.cpp +// +// Purpose - Configuration file Class +// +// Created By : David Blain Created On : Feb. 12, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "configuration.h" +#include "mythcontext.h" + +#include +#include + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +XmlConfiguration::XmlConfiguration( const QString &sFileName ) +{ + m_sPath = MythContext::GetConfDir(); + m_sFileName = sFileName; + + Load(); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool XmlConfiguration::Load( void ) +{ + QString sName = m_sPath + "/" + m_sFileName; + + QFile file( sName ); + + if (file.exists()) + { + + if ( !file.open( IO_ReadOnly ) ) + return false; + + QString sErrMsg; + int nErrLine = 0; + int nErrCol = 0; + bool bSuccess = m_config.setContent( &file, false, &sErrMsg, &nErrLine, &nErrCol ); + + file.close(); + + if (!bSuccess) + { + VERBOSE(VB_IMPORTANT, QString("Configuration::Load - " + "Error parsing: %1 " + "at line: %2 column: %3") + .arg( sName ) + .arg( nErrLine ) + .arg( nErrCol )); + + VERBOSE(VB_IMPORTANT, QString("Configuration::Load - Error Msg: %1" ) + .arg( sErrMsg )); + return false; + } + + m_rootNode = m_config.namedItem( "Configuration" ); + } + else + { + m_rootNode = m_config.createElement( "Configuration" ); + m_config.appendChild( m_rootNode ); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool XmlConfiguration::Save( void ) +{ + + QString sName = m_sPath + "/" + m_sFileName; + + QFile file( sName ); + + if (!file.exists()) + { + QDir createDir( m_sPath ); + + if (!createDir.exists()) + { + if (!createDir.mkdir( m_sPath, true )) + { + VERBOSE(VB_IMPORTANT, QString("Could not create %1").arg( m_sPath )); + return false; + } + } + } + + if (!file.open( IO_WriteOnly | IO_Truncate )) + { + VERBOSE(VB_IMPORTANT, QString("Could not open settings file %1 " + "for writing").arg( sName )); + + return false; + } + + QTextStream ts( &file ); + + m_config.save( ts, 2 ); + + file.close(); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QDomNode XmlConfiguration::FindNode( const QString &sName, bool bCreate ) +{ + QStringList parts = QStringList::split( "/", sName ); + + return FindNode( parts, m_rootNode, bCreate ); + +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QDomNode XmlConfiguration::FindNode( QStringList &sParts, QDomNode &curNode, bool bCreate ) +{ + if (sParts.empty()) + return curNode; + + QString sName = sParts.front(); + sParts.pop_front(); + + QDomNode child = curNode.namedItem( sName ); + + if (child.isNull() ) + { + if (bCreate) + { + QDomNode newNode = m_config.createElement( sName ); + + child = curNode.appendChild( newNode ); + } + else + sParts.clear(); + } + + return FindNode( sParts, child, bCreate ); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +int XmlConfiguration::GetValue( const QString &sSetting, int nDefault ) +{ + QDomNode node = FindNode( sSetting ); + + if (!node.isNull()) + { + // -=>TODO: This Always assumes firstChild is a Text Node... should change + QDomText oText = node.firstChild().toText(); + + if (!oText.isNull()) + return oText.nodeValue().toInt(); + } + + return nDefault; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QString XmlConfiguration::GetValue( const QString &sSetting, QString sDefault ) +{ + QDomNode node = FindNode( sSetting ); + + if (!node.isNull()) + { + // -=>TODO: This Always assumes firstChild is a Text Node... should change + QDomText oText = node.firstChild().toText(); + + if (!oText.isNull()) + return oText.nodeValue(); + } + + return sDefault; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void XmlConfiguration::SetValue( const QString &sSetting, int nValue ) +{ + QString sValue = QString::number( nValue ); + QDomNode node = FindNode( sSetting, TRUE ); + + if (!node.isNull()) + { + QDomText textNode; + + if (node.hasChildNodes()) + { + // -=>TODO: This Always assumes only child is a Text Node... should change + textNode = node.firstChild().toText(); + textNode.setNodeValue( sValue ); + } + else + { + textNode = m_config.createTextNode( sValue ); + node.appendChild( textNode ); + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void XmlConfiguration::SetValue( const QString &sSetting, QString sValue ) +{ + QDomNode node = FindNode( sSetting, TRUE ); + + if (!node.isNull()) + { + QDomText textNode; + + if (node.hasChildNodes()) + { + // -=>TODO: This Always assumes only child is a Text Node... should change + textNode = node.firstChild().toText(); + textNode.setNodeValue( sValue ); + } + else + { + textNode = m_config.createTextNode( sValue ); + node.appendChild( textNode ); + } + } +} + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// Uses MythContext to access settings in Database +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +DBConfiguration::DBConfiguration( ) +{ +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool DBConfiguration::Load( void ) +{ + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool DBConfiguration::Save( void ) +{ + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +int DBConfiguration::GetValue( const QString &sSetting, int nDefault ) +{ + return gContext->GetNumSetting( sSetting, nDefault ); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QString DBConfiguration::GetValue( const QString &sSetting, QString sDefault ) +{ + return gContext->GetSetting( sSetting, sDefault ); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void DBConfiguration::SetValue( const QString &sSetting, int nValue ) +{ + gContext->SaveSetting( sSetting, nValue ); + +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void DBConfiguration::SetValue( const QString &sSetting, QString sValue ) +{ + gContext->SaveSetting( sSetting, sValue ); +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/configuration.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/configuration.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/configuration.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/configuration.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,99 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: configuration.h +// +// Purpose - XML Configuration file Class +// +// Created By : David Blain Created On : Feb. 12, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __CONFIGURATION_H__ +#define __CONFIGURATION_H__ + +#include +#include + +class Configuration +{ + public: + + virtual ~Configuration() {} + + virtual bool Load ( void ) = 0; + virtual bool Save ( void ) = 0; + + virtual int GetValue( const QString &sSetting, int Default ) = 0; + virtual QString GetValue( const QString &sSetting, QString Default ) = 0; + + virtual void SetValue( const QString &sSetting, int value ) = 0; + virtual void SetValue( const QString &sSetting, QString value ) = 0; + +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// **NOTE: Performance Issue *** +// +// This class loads an XML file into a QDomDocument. All requests for +// getting or setting values navigates the DOM each time. All settings should +// only be read/written once if possible. +// +////////////////////////////////////////////////////////////////////////////// + +class XmlConfiguration : public Configuration +{ + protected: + + QString m_sPath; + QString m_sFileName; + + QDomDocument m_config; + QDomNode m_rootNode; + + QDomNode FindNode( const QString &sName, bool bCreate = FALSE ); + QDomNode FindNode( QStringList &sParts, QDomNode &curNode, bool bCreate = FALSE ); + + public: + + XmlConfiguration( const QString &sFileName ); + + virtual ~XmlConfiguration() {} + + virtual bool Load ( void ); + virtual bool Save ( void ); + + virtual int GetValue( const QString &sSetting, int Default ); + virtual QString GetValue( const QString &sSetting, QString Default ); + + virtual void SetValue( const QString &sSetting, int value ); + virtual void SetValue( const QString &sSetting, QString value ); + +}; + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +class DBConfiguration : public Configuration +{ + public: + + DBConfiguration(); + + virtual ~DBConfiguration() {} + + virtual bool Load ( void ); + virtual bool Save ( void ); + + virtual int GetValue( const QString &sSetting, int Default ); + virtual QString GetValue( const QString &sSetting, QString Default ); + + virtual void SetValue( const QString &sSetting, int value ); + virtual void SetValue( const QString &sSetting, QString value ); + +}; + +#endif + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/eventing.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/eventing.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/eventing.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/eventing.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,384 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: eventing.cpp +// +// Purpose - uPnp Eventing Base Class Implementation +// +// Created By : David Blain Created On : Dec. 22, 2006 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "upnp.h" +#include "eventing.h" +#include "upnptaskevent.h" + +#include "util.h" + +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +Eventing::Eventing( const QString &sExtensionName, const QString &sEventMethodName ) : HttpServerExtension( sExtensionName ) +{ + m_sEventMethodName = sEventMethodName; + m_nSubscriptionDuration = UPnp::g_pConfig->GetValue( "UPnP/SubscriptionDuration", 1800 ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +Eventing::~Eventing() +{ +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +inline short Eventing::HoldEvents() +{ + // -=>TODO: Should use an Atomic increment... + // need to research available functions. + + short nVal; + + m_mutex.lock(); + nVal = (m_nHoldCount++); + m_mutex.unlock(); + + return nVal; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +inline short Eventing::ReleaseEvents() +{ + // -=>TODO: Should use an Atomic decrement... + + short nVal; + + m_mutex.lock(); + nVal = (m_nHoldCount--); + m_mutex.unlock(); + + if (nVal == 0) + Notify(); + + return nVal; +} + + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool Eventing::ProcessRequest( HttpWorkerThread * /*pThread*/, HTTPRequest *pRequest ) +{ + if (pRequest) + { + if ( pRequest->m_sBaseUrl != "/" ) + return false; + + if ( pRequest->m_sMethod != m_sEventMethodName ) + return false; + + VERBOSE( VB_UPNP, QString("Eventing::ProcessRequest - Method (%1)").arg(pRequest->m_sMethod )); + + switch( pRequest->m_eType ) + { + case RequestTypeSubscribe : HandleSubscribe ( pRequest ); break; + case RequestTypeUnsubscribe : HandleUnsubscribe ( pRequest ); break; + default: + UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); + break; + } + } + + return( true ); + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void Eventing::ExecutePostProcess( ) +{ + // Use PostProcessing Hook to perform Initial Notification + // to make sure they receive it AFTER the subscription results + + if (m_pInitializeSubscriber != NULL) + { + NotifySubscriber( m_pInitializeSubscriber ); + + m_pInitializeSubscriber = NULL; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void Eventing::HandleSubscribe( HTTPRequest *pRequest ) +{ + pRequest->m_eResponseType = ResponseTypeXML; + pRequest->m_nResponseStatus = 412; + + QString sCallBack = pRequest->GetHeaderValue( "CALLBACK", "" ); + QString sNT = pRequest->GetHeaderValue( "NT" , "" ); + QString sTimeout = pRequest->GetHeaderValue( "TIMOUT" , "" ); + QString sSID = pRequest->GetHeaderValue( "SID" , "" ); + + SubscriberInfo *pInfo = NULL; + + // ---------------------------------------------------------------------- + // Validate Header Values... + // ---------------------------------------------------------------------- + + // -=>TODO: Need to add support for more than one CallBack URL. + // -=>TODO: Need to handle Timeout header + + if ( sCallBack.length() != 0 ) + { + // ------------------------------------------------------------------ + // New Subscription + // ------------------------------------------------------------------ + + if ( sSID.length() != 0 ) + { + pRequest->m_nResponseStatus = 400; + return; + } + + if ( sNT != "upnp:event" ) + return; + + // ---------------------------------------------------------------------- + // Process Subscription + // ---------------------------------------------------------------------- + + // -=>TODO: Temp code until support for multiple callbacks are supported. + + sCallBack = sCallBack.mid( 1, sCallBack.find( ">" ) - 1); + + pInfo = new SubscriberInfo( sCallBack, m_nSubscriptionDuration ); + + m_Subscribers.insert( pInfo->sUUID, pInfo ); + + // Use PostProcess Hook to Send Initial FULL Notification... + // *** Must send this response first then notify. + + m_pInitializeSubscriber = pInfo; + pRequest->m_pPostProcess = (IPostProcess *)this; + + } + else + { + // ------------------------------------------------------------------ + // Renewal + // ------------------------------------------------------------------ + + if ( sSID.length() != 0 ) + { + sSID = sSID.mid( 5 ); + pInfo = m_Subscribers.find( sSID ); + } + + } + + if (pInfo != NULL) + { + pRequest->m_mapRespHeaders[ "SID" ] = QString( "uuid:%1" ) + .arg( pInfo->sUUID ); + + pRequest->m_mapRespHeaders[ "TIMEOUT"] = QString( "Second-%1" ) + .arg( pInfo->nDuration ); + + pRequest->m_nResponseStatus = 200; + + } + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void Eventing::HandleUnsubscribe( HTTPRequest *pRequest ) +{ + pRequest->m_eResponseType = ResponseTypeXML; + pRequest->m_nResponseStatus = 412; + + QString sCallBack = pRequest->GetHeaderValue( "CALLBACK", "" ); + QString sNT = pRequest->GetHeaderValue( "NT" , "" ); + QString sSID = pRequest->GetHeaderValue( "SID" , "" ); + + if ((sCallBack.length() != 0) || (sNT.length() != 0)) + { + pRequest->m_nResponseStatus = 400; + return; + } + + sSID = sSID.mid( 5 ); + + if (!m_Subscribers.remove( sSID )) + return; + + pRequest->m_nResponseStatus = 200; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void Eventing::Notify() +{ + TaskTime tt; + gettimeofday( &tt, NULL ); + + m_mutex.lock(); + + + for ( SubscriberIterator it( m_Subscribers ); it.current(); ) + { + SubscriberInfo *pInfo = it.current(); + + if ( pInfo != NULL ) + { + // -------------------------------------------------------------- + // Check to see if it's time to expire this Subscription. + // -------------------------------------------------------------- + + if ( tt < pInfo->ttExpires ) + { + // -------------------------------------------------------------- + // Nope... Send Event notification + // -------------------------------------------------------------- + + NotifySubscriber( pInfo ); + + ++it; + } + else + { + // -------------------------------------------------------------- + // Yes... Remove subscriber from list + // (automatically moves iterator to next item) + // -------------------------------------------------------------- + + m_Subscribers.remove( pInfo->sUUID ); + } + } + } + + m_mutex.unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void Eventing::NotifySubscriber( SubscriberInfo *pInfo ) +{ + if (pInfo == NULL) + return; + + int nCount = 0; + QByteArray aBody; + QTextStream tsBody( aBody, IO_WriteOnly ); + + tsBody.setEncoding( QTextStream::UnicodeUTF8 ); + + // ---------------------------------------------------------------------- + // Build Body... Only send if there are changes + // ---------------------------------------------------------------------- + + if (( nCount = BuildNotifyBody( tsBody, pInfo->ttLastNotified )) > 0) + { + + // -=>TODO: Need to add support for more than one CallBack URL. + + QByteArray *pBuffer = new QByteArray(); // UPnpEventTask will delete this pointer. + QTextStream tsMsg( *pBuffer, IO_WriteOnly ); + + tsMsg.setEncoding( QTextStream::UnicodeUTF8 ); + + // ---------------------------------------------------------------------- + // Build Message Header + // ---------------------------------------------------------------------- + + short nPort = pInfo->qURL.hasPort() ? pInfo->qURL.port() : 80; + QString sHost = QString( "%1:%2" ).arg( pInfo->qURL.host() ) + .arg( nPort ); + + tsMsg << "NOTIFY " << pInfo->qURL.path() << " HTTP/1.1\r\n"; + tsMsg << "HOST: " << sHost << "\r\n"; + tsMsg << "CONTENT-TYPE: \"text/xml\"\r\n"; + tsMsg << "Content-Length: " << QString::number( aBody.size() ) << "\r\n"; + tsMsg << "NT: upnp:event\r\n"; + tsMsg << "NTS: upnp:propchange\r\n"; + tsMsg << "SID: uuid:" << pInfo->sUUID << "\r\n"; + tsMsg << "SEQ: " << QString::number( pInfo->nKey ) << "\r\n"; + tsMsg << "\r\n"; + tsMsg.writeRawBytes( aBody.data(), aBody.size() ); + + // ------------------------------------------------------------------ + // Add new EventTask to the TaskQueue to do the actual sending. + // ------------------------------------------------------------------ + + VERBOSE(VB_UPNP, QString("UPnp::Eventing::NotifySubscriber( %1 ) : %2 Variables").arg( sHost ).arg(nCount)); + + UPnpEventTask *pEventTask = new UPnpEventTask( pInfo->qURL.host(), nPort, pBuffer ); + + UPnp::g_pTaskQueue->AddTask( 250, pEventTask ); + + // ------------------------------------------------------------------ + // Update the subscribers Key & last Notified fields + // ------------------------------------------------------------------ + + pInfo->IncrementKey(); + + gettimeofday( &pInfo->ttLastNotified, NULL ); + } + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int Eventing::BuildNotifyBody( QTextStream &ts, TaskTime ttLastNotified ) +{ + int nCount = 0; + + ts << "" << endl + << "" << endl; + + for( StateVariableIterator it( *((StateVariables *)this) ); it.current(); ++it ) + { + StateVariableBase *pBase = it.current(); + + if ( ttLastNotified < pBase->m_ttLastChanged ) + { + nCount++; + + ts << "" << endl; + ts << "<" << pBase->m_sName << ">"; + ts << pBase->ToString(); + ts << "m_sName << ">"; + ts << "" << endl; + } + } + + ts << "" << endl; + + return nCount; +} + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/eventing.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/eventing.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/eventing.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/eventing.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,304 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: Eventing.h +// +// Purpose - +// +// Created By : David Blain Created On : Dec 22, 2006 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef EVENTING_H_ +#define EVENTING_H_ + +#include + +#include +#include +#include +#include +#include +#include + +#include "upnpimpl.h" +#include "upnputil.h" +#include "httpserver.h" + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +class SubscriberInfo +{ + public: + + TaskTime ttExpires; + TaskTime ttLastNotified; + + QString sUUID; + QUrl qURL; + unsigned short nKey; + unsigned long nDuration; // Seconds + + public: + + // ------------------------------------------------------------------ + + SubscriberInfo() + { + nKey = 0; + nDuration = 0; + ttLastNotified.tv_sec = 0; + sUUID = QUuid::createUuid().toString(); + sUUID = sUUID.mid( 1, sUUID.length() - 2); + + + } + + // ------------------------------------------------------------------ + + SubscriberInfo( const QString &url, unsigned long duration ) + { + nKey = 0; + sUUID = QUuid::createUuid().toString(); + sUUID = sUUID.mid( 1, sUUID.length() - 2); + qURL = url; + nDuration = duration; + ttLastNotified.tv_sec = 0; + + SetExpireTime( nDuration ); + } + + // ------------------------------------------------------------------ + + unsigned long IncrementKey() + { + // When key wraps around to zero again... must make it a 1. (upnp spec) + + if ((++nKey) == 0) + nKey = 1; + + return nKey; + } + + protected: + + void SetExpireTime( unsigned long nSecs ) + { + TaskTime tt; + gettimeofday( &tt, NULL ); + + AddMicroSecToTaskTime( tt, (nSecs * 1000000) ); + + ttExpires = tt; + } + + +}; + +////////////////////////////////////////////////////////////////////////////// + +class Subscribers : public QDict< SubscriberInfo > +{ + public: + + Subscribers() + { + setAutoDelete( true ); + } +}; + +typedef QDictIterator< SubscriberInfo > SubscriberIterator; + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +class StateVariableBase +{ + public: + + bool m_bNotify; + QString m_sName; + TaskTime m_ttLastChanged; + + public: + + StateVariableBase( const QString &sName, bool bNotify = FALSE ) + { + m_bNotify = bNotify; + m_sName = sName; + gettimeofday( &m_ttLastChanged, NULL ); + } + + virtual QString ToString() = 0; +}; + +////////////////////////////////////////////////////////////////////////////// + +template< class T > +class StateVariable : public StateVariableBase +{ + private: + + T m_value; + + public: + + // ------------------------------------------------------------------ + + StateVariable( const QString &sName, bool bNotify = FALSE ) : StateVariableBase( sName, bNotify ) + { + } + + // ------------------------------------------------------------------ + + StateVariable( const QString &sName, T value, bool bNotify = FALSE ) : StateVariableBase( sName, bNotify ) + { + m_value = value; + } + + // ------------------------------------------------------------------ + + virtual QString ToString() + { + return QString( "%1" ).arg( m_value ); + } + + // ------------------------------------------------------------------ + + T GetValue() + { + return m_value; + } + + // ------------------------------------------------------------------ + + void SetValue( T value ) + { + if ( m_value != value ) + { + m_value = value; + gettimeofday( &m_ttLastChanged, NULL ); + } + } +}; + +////////////////////////////////////////////////////////////////////////////// + +class StateVariables : public QDict< StateVariableBase > +{ + protected: + + virtual void Notify() = 0; + + public: + + // ------------------------------------------------------------------ + + StateVariables() + { + setAutoDelete( true ); + } + + // ------------------------------------------------------------------ + + void AddVariable( StateVariableBase *pBase ) + { + if (pBase != NULL) + insert( pBase->m_sName, pBase ); + } + + // ------------------------------------------------------------------ + template < class T > + bool SetValue( const QString &sName, T value ) + { + StateVariable< T > *pVariable = dynamic_cast< StateVariable< T > *>( find( sName ) ); + + if (pVariable == NULL) + return false; // It's not the expected type. + + if ( pVariable->GetValue() != value) + { + pVariable->SetValue( value ); + + if (pVariable->m_bNotify) + Notify(); + } + + return true; + } + + // ------------------------------------------------------------------ + + template < class T > + T GetValue( const QString &sName ) + { + StateVariable< T > *pVariable = dynamic_cast< StateVariable< T > *>( find( sName ) ); + + if (pVariable != NULL) + return pVariable->GetValue(); + + return T(0); + } + +}; + +typedef QDictIterator< StateVariableBase > StateVariableIterator; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// Eventing Class Definition +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class Eventing : public HttpServerExtension, + public StateVariables, + public IPostProcess, + public UPnpServiceImpl +{ + + protected: + + QMutex m_mutex; + + QString m_sEventMethodName; + Subscribers m_Subscribers; + + int m_nSubscriptionDuration; + + short m_nHoldCount; + + SubscriberInfo *m_pInitializeSubscriber; + + protected: + + virtual void Notify ( ); + void NotifySubscriber ( SubscriberInfo *pInfo ); + int BuildNotifyBody ( QTextStream &ts, TaskTime ttLastNotified ); + + void HandleSubscribe ( HTTPRequest *pRequest ); + void HandleUnsubscribe( HTTPRequest *pRequest ); + + // Implement UPnpServiceImpl methods that we can + + virtual QString GetServiceEventURL () { return m_sEventMethodName; } + + public: + Eventing ( const QString &sExtensionName, const QString &sEventMethodName ); + virtual ~Eventing ( ); + + virtual bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); + + short HoldEvents ( ); + short ReleaseEvents ( ); + + void ExecutePostProcess( ); + + +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httprequest.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httprequest.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httprequest.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httprequest.cpp 2007-08-23 15:59:44.000000000 -0500 @@ -18,13 +18,16 @@ #include #include "mythconfig.h" -#ifdef CONFIG_DARWIN +#if defined CONFIG_DARWIN || defined CONFIG_CYGWIN || defined(__FreeBSD__) #include "darwin-sendfile.h" #else +#define USE_SETSOCKOPT #include #endif +#include #include #include +#include #include #include @@ -59,10 +62,15 @@ { "zip" , "application/x-tar" }, { "gz" , "application/x-tar" }, { "mpg" , "video/mpeg" }, - { "mpeg", "video/mpeg" } + { "mpeg", "video/mpeg" }, + { "vob", "video/mpeg" }, + { "nuv" , "video/nupplevideo" }, + { "wmv" , "video/x-ms-wmv" } }; -static const int g_nMIMELength = sizeof( g_MIMETypes) / sizeof( MIMETypes ); +static const int g_nMIMELength = sizeof( g_MIMETypes) / sizeof( MIMETypes ); +static const int g_on = 1; +static const int g_off = 0; const char *HTTPRequest::m_szServerHeaders = "Accept-Ranges: bytes\r\n"; @@ -77,7 +85,8 @@ m_bSOAPRequest ( false ), m_eResponseType ( ResponseTypeUnknown), m_nResponseStatus( 200 ), - m_response ( m_aBuffer, IO_WriteOnly ) + m_response ( m_aBuffer, IO_WriteOnly ), + m_pPostProcess ( NULL ) { m_response.setEncoding( QTextStream::UnicodeUTF8 ); } @@ -95,6 +104,7 @@ m_bSOAPRequest = false; m_eResponseType = ResponseTypeUnknown; m_nResponseStatus= 200; + m_pPostProcess = NULL; m_aBuffer.truncate( 0 ); @@ -121,10 +131,19 @@ RequestType HTTPRequest::SetRequestType( const QString &sType ) { - if (sType == "GET" ) return( m_eType = RequestTypeGet ); - if (sType == "HEAD" ) return( m_eType = RequestTypeHead ); - if (sType == "POST" ) return( m_eType = RequestTypePost ); - if (sType == "M-SEARCH") return( m_eType = RequestTypeMSearch ); + if (sType == "GET" ) return( m_eType = RequestTypeGet ); + if (sType == "HEAD" ) return( m_eType = RequestTypeHead ); + if (sType == "POST" ) return( m_eType = RequestTypePost ); + if (sType == "M-SEARCH" ) return( m_eType = RequestTypeMSearch ); + + if (sType == "SUBSCRIBE" ) return( m_eType = RequestTypeSubscribe ); + if (sType == "UNSUBSCRIBE") return( m_eType = RequestTypeUnsubscribe ); + if (sType == "NOTIFY" ) return( m_eType = RequestTypeNotify ); + + if (sType.startsWith( "HTTP/" )) return( m_eType = RequestTypeResponse ); + + VERBOSE( VB_UPNP, QString( "HTTPRequest::SentRequestType( %1 ) - returning Unknown." ) + .arg( sType ) ); return( m_eType = RequestTypeUnknown); } @@ -133,60 +152,111 @@ // ///////////////////////////////////////////////////////////////////////////// +QString HTTPRequest::BuildHeader( long long nSize ) +{ + QString sHeader; + QString sContentType = (m_eResponseType == ResponseTypeOther) ? + m_sResponseTypeText : GetResponseType(); + + sHeader = QString( "HTTP/%1.%2 %3\r\n" + "Date: %4\r\n" + "Server: %5, UPnP/1.0, MythTv %6\r\n" ) + .arg( m_nMajor ) + .arg( m_nMinor ) + .arg( GetResponseStatus() ) + .arg( QDateTime::currentDateTime().toString( "d MMM yyyy hh:mm:ss" ) ) + .arg( HttpServer::g_sPlatform ) + .arg( MYTH_BINARY_VERSION ); + + sHeader += GetAdditionalHeaders(); + + sHeader += QString( "Connection: %1\r\n" + "Content-Type: %2\r\n" + "Content-Length: %3\r\n" ) + .arg( GetKeepAlive() ? "Keep-Alive" : "Close" ) + .arg( sContentType ) + .arg( nSize ); + sHeader += "\r\n"; + + return sHeader; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + long HTTPRequest::SendResponse( void ) { - long nBytes = 0; - QCString sHeader; + long nBytes = 0; + QCString sHeader; switch( m_eResponseType ) { + case ResponseTypeUnknown: case ResponseTypeNone: - return( 0 ); +// VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponse( None ) :%1 -> %2:") +// .arg(GetResponseStatus()) +// .arg(GetPeerAddress())); + return( -1 ); case ResponseTypeFile: +// VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponse( File ) :%1 -> %2:") +// .arg(GetResponseStatus()) +// .arg(GetPeerAddress())); + return( SendResponseFile( m_sFileName )); case ResponseTypeXML: case ResponseTypeHTML: - { - QString sDate = QDateTime::currentDateTime() - .toString( "d MMM yyyy hh:mm:ss" ); - - sHeader = QString("HTTP/1.1 %1\r\n" - "DATE: %2\r\n" ) - .arg( GetResponseStatus()) - .arg( sDate ).utf8(); - - QString sAddlHeaders = GetAdditionalHeaders(); + case ResponseTypeOther: + default: + break; + } - sHeader += sAddlHeaders.utf8(); - - sHeader += QString( "Content-Type: %1\r\n" - "Content-Length: %2\r\n" ) - .arg( GetResponseType() ) - .arg( m_aBuffer.size() ).utf8(); - - sHeader += QString("\r\n").utf8(); + // VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponse(xml/html) :%1 -> %2:") + // .arg(GetResponseStatus()) + // .arg(GetPeerAddress())); - //cout << "Response =====" << endl; - //cout << sHeader; + // ---------------------------------------------------------------------- + // Make it so the header is sent with the data + // ---------------------------------------------------------------------- - // Write out Header. +#ifdef USE_SETSOCKOPT + // Never send out partially complete segments + setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_on, sizeof( g_on )); +#endif - nBytes = WriteBlock( sHeader.data(), sHeader.length() ); + // ---------------------------------------------------------------------- + // Write out Header. + // ---------------------------------------------------------------------- - break; - } - default: - break; - } + sHeader = BuildHeader ( m_aBuffer.size() ).utf8(); + nBytes = WriteBlockDirect( sHeader.data(), sHeader.length() ); + // ---------------------------------------------------------------------- // Write out Response buffer. + // ---------------------------------------------------------------------- if (( m_eType != RequestTypeHead ) && ( m_aBuffer.size() > 0 )) - nBytes += WriteBlock( m_aBuffer.data(), m_aBuffer.size() ); + { +/* + VERBOSE(VB_UPNP,QString("HTTPRequest::SendResponse : DATA : %1 : ").arg( m_aBuffer.size() )); + + for (int i=0; i 0) { if ( bRange = ParseRange( sRange, llSize, &llStart, &llEnd ) ) { - // sContentType="video/x-msvideo"; m_nResponseStatus = 206; - m_mapRespHeaders[ "Content-Range" ] = QString("%1-%2/%3") + m_mapRespHeaders[ "Content-Range" ] = QString("bytes %1-%2/%3") .arg( llStart ) .arg( llEnd ) .arg( llSize ); - //llSize = (llEnd - llStart) + 1; - llSize = (llEnd - llStart); - + llSize = (llEnd - llStart) + 1; } } - if (bRange == false) - { - // DSM-?20 specific response headers + // DSM-?20 specific response headers + if (bRange == false) m_mapRespHeaders[ "User-Agent" ] = "redsonic"; - } // ------------------------------------------------------------------ // @@ -260,29 +345,18 @@ else m_nResponseStatus = 404; - QString sDate = QDateTime::currentDateTime().toString( "d MMM yyyy hh:mm:ss" ); - - sHeader = QString("HTTP/%1.%2 %3\r\n" - "Date: %4\r\n" - "Content-Type: %5\r\n" - "Content-Length: %6\r\n" ) - .arg( m_nMajor ) - .arg( m_nMinor ) - .arg( GetResponseStatus()) - .arg( sDate ) - .arg( sContentType ) - .arg( llSize ).utf8(); - - - sHeader += GetAdditionalHeaders() + "\r\n"; + // -=>TODO: Should set "Content-Length: *" if file is still recording + // ---------------------------------------------------------------------- // Write out Header. + // ---------------------------------------------------------------------- - nBytes = WriteBlock( sHeader.data(), sHeader.length() ); - - Flush(); + sHeader = BuildHeader( llSize ).utf8(); + nBytes = WriteBlockDirect( sHeader.data(), sHeader.length() ); + // ---------------------------------------------------------------------- // Write out File. + // ---------------------------------------------------------------------- if (( m_eType != RequestTypeHead ) && (llSize != 0)) { @@ -296,56 +370,68 @@ // The loop is needed in any case. sent = sendfile64( getSocketHandle(), file, &offset, - (size_t)(llSize > INT_MAX ? INT_MAX : llSize)); + (size_t)(llSize > INT_MAX ? INT_MAX : llSize)); - llSize -= sent; + llSize = llEnd - offset; } while (( sent >= 0 ) && ( llSize > 0 )); + if (sent == -1) + { + VERBOSE(VB_UPNP,QString("SendResponseFile( %1 ) Error: %2 [%3]" ) + .arg( sFileName ) + .arg( errno ) + .arg( strerror( errno ) )); + + nBytes = -1; + } + close( file ); } + // ---------------------------------------------------------------------- + // Turn off the option so any small remaining packets will be sent + // ---------------------------------------------------------------------- + +#ifdef USE_SETSOCKOPT + setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_off, sizeof( g_off )); +#endif + // -=>TODO: Only returns header length... // should we change to return total bytes? - return( nBytes ); + return nBytes; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -void HTTPRequest::FormatErrorReponse( long nCode, const QString &sDesc ) +void HTTPRequest::FormatErrorResponse( bool bServerError, + const QString &sFaultString, + const QString &sDetails ) { m_eResponseType = ResponseTypeXML; m_nResponseStatus = 500; m_response << ""; + QString sWhere = ( bServerError ) ? "s:Server" : "s:Client"; + if (m_bSOAPRequest) { m_mapRespHeaders[ "EXT" ] = ""; - m_mapRespHeaders[ "SERVER" ] = QString( "%1, UPnP/1.0, MythTv %2" ) - .arg( UPnp::g_sPlatform ) - .arg( MYTH_BINARY_VERSION ); - m_response << "" - "s:Client" - "UPnPError"; + m_response << SOAP_ENVELOPE_BEGIN + << "" + << "" << sWhere << "" + << "" << sFaultString << ""; } - m_response << ""; - - if (m_bSOAPRequest) - m_response << ""; - - m_response << "" << nCode << ""; - m_response << "" << sDesc << ""; - - if (m_bSOAPRequest) - m_response << ""; - - m_response << ""; + if (sDetails.length() > 0) + { + m_response << "" << sDetails << ""; + } if (m_bSOAPRequest) m_response << "" << SOAP_ENVELOPE_END; @@ -355,7 +441,7 @@ // ///////////////////////////////////////////////////////////////////////////// -void HTTPRequest::FormatActionReponse( NameValueList *pArgs ) +void HTTPRequest::FormatActionResponse( NameValueList *pArgs ) { m_eResponseType = ResponseTypeXML; m_nResponseStatus = 200; @@ -365,9 +451,6 @@ if (m_bSOAPRequest) { m_mapRespHeaders[ "EXT" ] = ""; - m_mapRespHeaders[ "SERVER" ] = QString( "%1, UPnP/1.0, MythTv %2" ) - .arg( UPnp::g_sPlatform ) - .arg( MYTH_BINARY_VERSION ); m_response << SOAP_ENVELOPE_BEGIN << "\r\n"; @@ -377,8 +460,26 @@ for (NameValue *pNV = pArgs->first(); pNV != NULL; pNV = pArgs->next()) { - m_response << "<" << pNV->sName << ">"; - m_response << pNV->sValue; + m_response << "<" << pNV->sName; + + if (pNV->pAttributes != NULL) + { + + for (NameValue *pAttr = pNV->pAttributes->first(); + pAttr != NULL; + pAttr = pNV->pAttributes->next()) + { + m_response << " " << pAttr->sName << "='" << Encode( pAttr->sValue ) << "'"; + } + } + + m_response << ">"; + + if (m_bSOAPRequest) + m_response << Encode( pNV->sValue ); + else + m_response << pNV->sValue; + m_response << "sName << ">\r\n"; } @@ -392,6 +493,26 @@ } ///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void HTTPRequest::FormatFileResponse( const QString &sFileName ) +{ + m_eResponseType = ResponseTypeHTML; + m_nResponseStatus = 404; + + m_sFileName = sFileName; + + if (QFile::exists( m_sFileName )) + { + + m_eResponseType = ResponseTypeFile; + m_nResponseStatus = 200; + m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + } +} + +///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// @@ -476,9 +597,13 @@ QString HTTPRequest::GetMimeType( const QString &sFileExtension ) { + QString ext; + for (int i = 0; i < g_nMIMELength; i++) { - if ( sFileExtension == g_MIMETypes[i].pszExtension ) + ext = g_MIMETypes[i].pszExtension; + + if ( sFileExtension.upper() == ext.upper() ) return( g_MIMETypes[i].pszType ); } @@ -652,8 +777,9 @@ if (nPayloadSize > 0) { char *pszPayload = new char[ nPayloadSize + 2 ]; + long nBytes = 0; - if ( ReadBlock( pszPayload, nPayloadSize, 5000 ) == nPayloadSize ) + if (( nBytes = ReadBlock( pszPayload, nPayloadSize, 5000 )) == nPayloadSize ) { m_sPayload = QString::fromUtf8( pszPayload, nPayloadSize ); @@ -663,9 +789,14 @@ GetParameters( m_sPayload, m_mapParams ); } else + { + VERBOSE( VB_IMPORTANT, QString( "HTTPRequest::ParseRequest - Unable to read entire payload (read %1 of %2 bytes" ) + .arg( nBytes ) + .arg( nPayloadSize ) ); bSuccess = false; + } - delete pszPayload; + delete [] pszPayload; } // Check to see if this is a SOAP encoded message @@ -676,6 +807,15 @@ bSuccess = ProcessSOAPPayload( sSOAPAction ); else ExtractMethodFromURL(); + +/* + if (m_sMethod != "*" ) + VERBOSE( VB_UPNP, QString("HTTPRequest::ParseRequest - Socket (%1) Base (%2) Method (%3) - Bytes in Socket Buffer (%4)") + .arg( getSocketHandle() ) + .arg( m_sBaseUrl ) + .arg( m_sMethod ) + .arg( BytesAvailable())); +*/ } catch( ... ) { @@ -691,40 +831,63 @@ void HTTPRequest::ProcessRequestLine( const QString &sLine ) { + m_sRawRequest = sLine; + QString sToken; QStringList tokens = QStringList::split(QRegExp("[ \r\n][ \r\n]*"), sLine ); + int nCount = tokens.count(); - for (unsigned int nIdx = 0; nIdx < tokens.count(); nIdx++) + // ---------------------------------------------------------------------- + + if ( sLine.startsWith( "HTTP/" )) + m_eType = RequestTypeResponse; + else + m_eType = RequestTypeUnknown; + + // ---------------------------------------------------------------------- + // if this is actually a response, then sLine's format will be: + // HTTP/m.n + // otherwise: + // HTTP/m.n + // ---------------------------------------------------------------------- + + if (m_eType != RequestTypeResponse) { - switch( nIdx ) - { - case 0: - { - SetRequestType( tokens[0].stripWhiteSpace() ); - break; - } + // ------------------------------------------------------------------ + // Process as a request + // ------------------------------------------------------------------ - case 1: - { - m_sBaseUrl = tokens[1].section( '?', 0, 0).stripWhiteSpace(); + if (nCount > 0) + SetRequestType( tokens[0].stripWhiteSpace() ); - // Process any Query String Parameters + if (nCount > 1) + { + m_sBaseUrl = tokens[1].section( '?', 0, 0).stripWhiteSpace(); - QString sQueryStr = tokens[1].section( '?', 1, 1 ); + // Process any Query String Parameters - if (sQueryStr.length() > 0) - GetParameters( sQueryStr, m_mapParams ); + QString sQueryStr = tokens[1].section( '?', 1, 1 ); - break; - } + if (sQueryStr.length() > 0) + GetParameters( sQueryStr, m_mapParams ); - case 2: - { - SetRequestProtocol( tokens[2].stripWhiteSpace() ); - break; - } } + + if (nCount > 2) + SetRequestProtocol( tokens[2].stripWhiteSpace() ); + } + else + { + // ------------------------------------------------------------------ + // Process as a Response + // ------------------------------------------------------------------ + + if (nCount > 0) + SetRequestProtocol( tokens[0].stripWhiteSpace() ); + + if (nCount > 1) + m_nResponseStatus = tokens[1].toInt(); } } @@ -771,7 +934,7 @@ QStringList parts = QStringList::split( "-", ranges[0], true ); - if (parts.count() != 2) + if (parts.count() != 2) return false; if (parts[0].isNull() && parts[1].isNull()) @@ -818,6 +981,8 @@ return false; } + //cout << getSocketHandle() << "Range Requested " << *pllStart << " - " << *pllEnd << endl; + return true; } @@ -838,6 +1003,7 @@ } m_sBaseUrl = "/" + sList.join( "/" ); + //VERBOSE(VB_UPNP, QString("ExtractMethodFromURL : %1 : ").arg(m_sMethod)); } ///////////////////////////////////////////////////////////////////////////// @@ -852,6 +1018,7 @@ // Open Supplied XML uPnp Description file. // ---------------------------------------------------------------------- +// VERBOSE(VB_UPNP, QString("HTTPRequest::ProcessSOAPPayload : %1 : ").arg(sSOAPAction)); QDomDocument doc ( "request" ); QString sErrMsg; @@ -929,28 +1096,27 @@ return( sStr ); } -/* ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // -// QSocketRequest Class Implementation +// BufferedSocketDeviceRequest Class Implementation // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// -QSocketRequest::QSocketRequest( QSocket *pSocket ) +BufferedSocketDeviceRequest::BufferedSocketDeviceRequest( BufferedSocketDevice *pSocket ) { - m_pSocket = pSocket; + m_pSocket = pSocket; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_LONG QSocketRequest::BytesAvailable() +Q_LONG BufferedSocketDeviceRequest::BytesAvailable() { if (m_pSocket) - return( m_pSocket->bytesAvailable() ); + return( m_pSocket->BytesAvailable() ); return( 0 ); } @@ -959,10 +1125,10 @@ // ///////////////////////////////////////////////////////////////////////////// -Q_ULONG QSocketRequest::WaitForMore( int msecs, bool *timeout ) +Q_ULONG BufferedSocketDeviceRequest::WaitForMore( int msecs, bool *timeout ) { if (m_pSocket) - return( m_pSocket->waitForMore( msecs, timeout )); + return( m_pSocket->WaitForMore( msecs, timeout )); return( 0 ); } @@ -971,301 +1137,114 @@ // ///////////////////////////////////////////////////////////////////////////// -QString QSocketRequest::ReadLine( int msecs ) -{ - QString sLine; - - if (m_pSocket) - { - if (CanReadLine()) - return( m_pSocket->readLine() ); - - if (nTimeout != 0) - m_pSocket->waitForMore( msecs, NULL ); - - if (CanReadLine()) - return( m_pSocket->readLine() ); - } - - return( sLine ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -Q_LONG QSocketRequest::ReadBlock( char *pData, Q_ULONG nMaxLen, int msecs ) -{ - if (m_pSocket) - { - if (msecs == 0) - return( m_pSocket->readBlock( pData, nMaxLen )); - - - } - - return( -1 ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -Q_LONG QSocketRequest::WriteBlock( char *pData, Q_ULONG nLen ) +bool BufferedSocketDeviceRequest::CanReadLine() { if (m_pSocket) - return( m_pSocket->writeBlock( pData, nLen )); - - return( -1 ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -QString QSocketRequest::GetHostAddress() -{ - return( m_pSocket->address().toString() ); -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -// QSocketDeviceRequest Class Implementation -// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// + return( m_pSocket->CanReadLine() ); -QSocketDeviceRequest::QSocketDeviceRequest( QSocketDevice *pSocket, - QHostAddress *pHost, - Q_UINT16 nPort ) -{ - m_pSocket = pSocket; - m_pHost = pHost; - m_nPort = nPort; + return( false ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_LONG QSocketDeviceRequest::BytesAvailable() +QString BufferedSocketDeviceRequest::ReadLine( int msecs ) { - return( m_pSocket->bytesAvailable() ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// + QString sLine; -Q_ULONG QSocketDeviceRequest::WaitForMore( int msecs, bool *timeout ) -{ if (m_pSocket) - return( m_pSocket->waitForMore( msecs, timeout )); - - return( 0 ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -bool QSocketDeviceRequest::CanReadLine() -{ - return( BytesAvailable() ); -} + sLine = m_pSocket->ReadLine( msecs ); -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -QString QSocketDeviceRequest::ReadLine() -{ - char szLine[ 1024 ]; - - m_pSocket->readLine( szLine, sizeof( szLine )); - - return( szLine ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -Q_LONG QSocketDeviceRequest::ReadBlock( char *pData, Q_ULONG nMaxLen ) -{ - return( m_pSocket->readBlock( pData, nMaxLen )); - - return( -1 ); + return( sLine ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_LONG QSocketDeviceRequest::WriteBlock( char *pData, Q_ULONG nLen ) +Q_LONG BufferedSocketDeviceRequest::ReadBlock( char *pData, Q_ULONG nMaxLen, int msecs ) { if (m_pSocket) { - if (m_pHost) - return( m_pSocket->writeBlock( pData, nLen, *m_pHost, m_nPort )); + if (msecs == 0) + return( m_pSocket->ReadBlock( pData, nMaxLen )); else - return( m_pSocket->writeBlock( pData, nLen )); - } - - return( -1 ); -} + { + bool bTimeout = false; -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// + while ( (BytesAvailable() < (int)nMaxLen) && !bTimeout ) + m_pSocket->WaitForMore( msecs, &bTimeout ); -QString QSocketDeviceRequest::GetHostAddress() -{ - return( m_pSocket->address().toString() ); -} -*/ + // Just return what we have even if timed out. -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -// BufferedSocketDeviceRequest Class Implementation -// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// + return( m_pSocket->ReadBlock( pData, nMaxLen )); + } + } -BufferedSocketDeviceRequest::BufferedSocketDeviceRequest( BufferedSocketDevice *pSocket ) -{ - m_pSocket = pSocket; + return( -1 ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_LONG BufferedSocketDeviceRequest::BytesAvailable() +Q_LONG BufferedSocketDeviceRequest::WriteBlock( char *pData, Q_ULONG nLen ) { if (m_pSocket) - return( m_pSocket->BytesAvailable() ); + return( m_pSocket->WriteBlock( pData, nLen )); - return( 0 ); + return( -1 ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_ULONG BufferedSocketDeviceRequest::WaitForMore( int msecs, bool *timeout ) +Q_LONG BufferedSocketDeviceRequest::WriteBlockDirect( char *pData, Q_ULONG nLen ) { if (m_pSocket) - return( m_pSocket->WaitForMore( msecs, timeout )); + return( m_pSocket->WriteBlockDirect( pData, nLen )); - return( 0 ); + return( -1 ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -bool BufferedSocketDeviceRequest::CanReadLine() +QString BufferedSocketDeviceRequest::GetHostAddress() { - if (m_pSocket) - return( m_pSocket->CanReadLine() ); - - return( false ); + return( m_pSocket->SocketDevice()->address().toString() ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -QString BufferedSocketDeviceRequest::ReadLine( int msecs ) +QString BufferedSocketDeviceRequest::GetPeerAddress() { - QString sLine; - MythTimer timeout; - - if (m_pSocket) - { - - if (m_pSocket->CanReadLine()) - return( m_pSocket->ReadLine() ); - - // If the user supplied a timeout, lets loop until we can read a line - // or timeout. - - if ( msecs != 0) - { - bool bTimeout = false; - - timeout.start(); - - while ( !m_pSocket->CanReadLine() && !bTimeout ) - { - m_pSocket->WaitForMore( msecs, &bTimeout ); - - if ( timeout.elapsed() >= msecs ) - { - bTimeout = true; - m_pSocket->ClearReadBuffer(); - } - else - usleep(20); - } - - if (!bTimeout) - sLine = m_pSocket->ReadLine(); - - } - } - - return( sLine ); + return( m_pSocket->SocketDevice()->peerAddress().toString() ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_LONG BufferedSocketDeviceRequest::ReadBlock( char *pData, Q_ULONG nMaxLen, int msecs ) +void BufferedSocketDeviceRequest::SetBlocking( bool bBlock ) { if (m_pSocket) - { - if (msecs == 0) - return( m_pSocket->ReadBlock( pData, nMaxLen )); - else - { - bool bTimeout = false; - - while ( (BytesAvailable() < (int)nMaxLen) && !bTimeout ) - m_pSocket->WaitForMore( msecs, &bTimeout ); - - // Just return what we have even if timed out. - - return( m_pSocket->ReadBlock( pData, nMaxLen )); - } - } - - return( -1 ); + return( m_pSocket->SocketDevice()->setBlocking( bBlock )); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -Q_LONG BufferedSocketDeviceRequest::WriteBlock( char *pData, Q_ULONG nLen ) +bool BufferedSocketDeviceRequest::IsBlocking() { if (m_pSocket) - return( m_pSocket->WriteBlockDirect( pData, nLen )); -// return( m_pSocket->WriteBlock( pData, nLen )); + return( m_pSocket->SocketDevice()->blocking()); - return( -1 ); + return false; } - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -QString BufferedSocketDeviceRequest::GetHostAddress() -{ - return( m_pSocket->SocketDevice()->address().toString() ); -} - diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httprequest.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httprequest.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httprequest.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httprequest.h 2007-08-23 11:09:51.000000000 -0500 @@ -17,6 +17,7 @@ using namespace std; #include +#include "upnputil.h" #include "bufferedsocketdevice.h" #define SOAP_ENVELOPE_BEGIN " QStringMap; - typedef struct { char *pszExtension; @@ -69,24 +73,11 @@ ///////////////////////////////////////////////////////////////////////////// -typedef struct _NameValue -{ - QString sName; - QString sValue; - - _NameValue( const QString &name, const QString value ) - : sName( name ), sValue( value ) { } - -} NameValue; - -class NameValueList : public QPtrList< NameValue > +class IPostProcess { public: - - NameValueList() - { - setAutoDelete( true ); - } + virtual void ExecutePostProcess( ) = 0; + virtual ~IPostProcess() {} }; ///////////////////////////////////////////////////////////////////////////// @@ -126,9 +117,11 @@ bool m_bSOAPRequest; QString m_sNameSpace; - // Reponse + // Response ResponseType m_eResponseType; + QString m_sResponseTypeText; // used for ResponseTypeOther + long m_nResponseStatus; QStringMap m_mapRespHeaders; @@ -136,6 +129,8 @@ QTextStream m_response; + IPostProcess *m_pPostProcess; + protected: RequestType SetRequestType ( const QString &sType ); @@ -157,6 +152,8 @@ long long *pllStart, long long *pllEnd ); + QString BuildHeader ( long long nSize ); + public: HTTPRequest (); @@ -166,8 +163,12 @@ bool ParseRequest (); - void FormatErrorReponse ( long nCode, const QString &sDesc ); - void FormatActionReponse( NameValueList *pArgs ); + void FormatErrorResponse ( bool bServerError, + const QString &sFaultString, + const QString &sDetails ); + + void FormatActionResponse( NameValueList *pArgs ); + void FormatFileResponse ( const QString &sFileName ); long SendResponse ( void ); long SendResponseFile( QString sFileName ); @@ -189,78 +190,20 @@ virtual QString ReadLine ( int msecs = 0 ) = 0; virtual Q_LONG ReadBlock ( char *pData, Q_ULONG nMaxLen, int msecs = 0 ) = 0; virtual Q_LONG WriteBlock ( char *pData, Q_ULONG nLen ) = 0; + virtual Q_LONG WriteBlockDirect( char *pData, Q_ULONG nLen ) = 0; virtual QString GetHostAddress () = 0; + virtual QString GetPeerAddress () = 0; virtual void Flush () = 0; virtual bool IsValid () = 0; virtual int getSocketHandle () = 0; -}; - -/* -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -class QSocketRequest : public HTTPRequest -{ - public: - - QSocket *m_pSocket; - - public: - - QSocketRequest ( QSocket *pSocket ); - virtual ~QSocketRequest () {}; - - virtual Q_LONG BytesAvailable (); - virtual Q_ULONG WaitForMore ( int msecs, bool *timeout = NULL ); - virtual bool CanReadLine (); - virtual QString ReadLine (); - virtual Q_LONG ReadBlock ( char *pData, Q_ULONG nMaxLen ); - virtual Q_LONG WriteBlock ( char *pData, Q_ULONG nLen ); - virtual QString GetHostAddress (); - virtual void Flush () {m_pSocket->flush(); } - virtual bool IsValid () {return( m_pSocket->state() == QSocket::Connected ); } - virtual int getSocketHandle () {return( m_pSocket->socket() ); } - + virtual void SetBlocking ( bool bBlock ) = 0; + virtual bool IsBlocking () = 0; }; ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -class QSocketDeviceRequest : public HTTPRequest -{ - public: - - QSocketDevice *m_pSocket; - QHostAddress *m_pHost; - Q_UINT16 m_nPort; - - public: - - QSocketDeviceRequest ( QSocketDevice *pSocket, - QHostAddress *pHost, - Q_UINT16 nPort ); - virtual ~QSocketDeviceRequest () {}; - - virtual Q_LONG BytesAvailable (); - virtual Q_ULONG WaitForMore ( int msecs, bool *timeout = NULL ); - virtual bool CanReadLine (); - virtual QString ReadLine (); - virtual Q_LONG ReadBlock ( char *pData, Q_ULONG nMaxLen ); - virtual Q_LONG WriteBlock ( char *pData, Q_ULONG nLen ); - virtual QString GetHostAddress (); - virtual void Flush () { } - virtual bool IsValid () {return( m_pSocket->isValid() ); } - virtual int getSocketHandle () {return( m_pSocket->socket() ); } -}; - -*/ - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - class BufferedSocketDeviceRequest : public HTTPRequest { public: @@ -278,10 +221,15 @@ virtual QString ReadLine ( int msecs = 0 ); virtual Q_LONG ReadBlock ( char *pData, Q_ULONG nMaxLen, int msecs = 0 ); virtual Q_LONG WriteBlock ( char *pData, Q_ULONG nLen ); + virtual Q_LONG WriteBlockDirect( char *pData, Q_ULONG nLen ); virtual QString GetHostAddress (); + virtual QString GetPeerAddress (); virtual void Flush () { m_pSocket->Flush(); } virtual bool IsValid () {return( m_pSocket->IsValid() ); } virtual int getSocketHandle () {return( m_pSocket->socket() ); } + virtual void SetBlocking ( bool bBlock ); + virtual bool IsBlocking (); + }; #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httpserver.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httpserver.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httpserver.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httpserver.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -9,6 +9,8 @@ // ////////////////////////////////////////////////////////////////////////////// +#include + #include #include #include @@ -16,8 +18,12 @@ #include #include +#include + #include "httpserver.h" -#include "upnpglobal.h" +#include "upnputil.h" + +#include "upnp.h" // only needed for Config... remove once config is moved. ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -27,18 +33,31 @@ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// +QString HttpServer::g_sPlatform; + ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// HttpServer::HttpServer( int nPort ) - : QServerSocket( nPort, 1), // 5), + : QServerSocket( nPort, 20 ), //5), ThreadPool( "HTTP" ) { m_extensions.setAutoDelete( true ); InitializeThreads(); + // ---------------------------------------------------------------------- + // Build Platform String + // ---------------------------------------------------------------------- + + struct utsname uname_info; + + uname( &uname_info ); + + g_sPlatform = QString( "%1 %2" ).arg( uname_info.sysname ) + .arg( uname_info.release ); + // -=>TODO: Load Config XML // -=>TODO: Load & initialize - HttpServerExtensions } @@ -80,7 +99,11 @@ void HttpServer::RegisterExtension( HttpServerExtension *pExtension ) { if (pExtension != NULL ) + { + m_mutex.lock(); m_extensions.append( pExtension ); + m_mutex.unlock(); + } } ///////////////////////////////////////////////////////////////////////////// @@ -90,7 +113,11 @@ void HttpServer::UnregisterExtension( HttpServerExtension *pExtension ) { if (pExtension != NULL ) + { + m_mutex.lock(); m_extensions.remove( pExtension ); + m_mutex.unlock(); + } } ///////////////////////////////////////////////////////////////////////////// @@ -101,6 +128,8 @@ { bool bProcessed = false; + m_mutex.lock(); + HttpServerExtension *pExtension = m_extensions.first(); while (( pExtension != NULL ) && !bProcessed ) @@ -117,6 +146,8 @@ pExtension = m_extensions.next(); } + m_mutex.unlock(); + if (!bProcessed) { pRequest->m_eResponseType = ResponseTypeHTML; @@ -140,8 +171,8 @@ WorkerThread( (ThreadPool *)pParent, sName ) { m_pHttpServer = pParent; - m_nSocket = 0; - m_nSocketTimeout = gContext->GetNumSetting( "HTTPKeepAliveTimeoutSecs", 600 ) * 1000; + m_nSocket = 0; + m_nSocketTimeout = UPnp::g_pConfig->GetValue( "HTTP/KeepAliveTimeoutSecs", 10 ) * 1000; m_pData = NULL; } @@ -177,13 +208,9 @@ void HttpWorkerThread::StartWork( int nSocket ) { -//cout << "HttpWorkerThread::StartWork:Begin" << endl; - m_nSocket = nSocket; SignalWork(); - -//cout << "HttpWorkerThread::StartWork:End" << endl; } ///////////////////////////////////////////////////////////////////////////// @@ -192,9 +219,9 @@ void HttpWorkerThread::ProcessWork() { - VERBOSE( VB_UPNP, QString( "HttpWorkerThread::ProcessWork:Begin( %1 ) socket=%2" ) - .arg( (long)QThread::currentThread() ) - .arg( m_nSocket )); +// VERBOSE( VB_UPNP, QString( "HttpWorkerThread::ProcessWork:Begin( %1 ) socket=%2" ) +// .arg( (long)QThread::currentThread() ) +// .arg( m_nSocket )); bool bTimeout = false; bool bKeepAlive = true; @@ -209,6 +236,8 @@ return; } + pSocket->SocketDevice()->setBlocking( true ); + while( !IsTermRequested() && bKeepAlive && pSocket->IsValid()) { bTimeout = 0; @@ -227,8 +256,6 @@ { bKeepAlive = pRequest->GetKeepAlive(); - // cout << "Request: (" << QThread::currentThread() << ") socket=" << m_nSocket << " " << pRequest->m_sRequest; - // ------------------------------------------------------ // Request Parsed... Pass on to Main HttpServer class to // delegate processing to HttpServerExtensions. @@ -256,23 +283,35 @@ } } */ + + // ---------------------------------------------------------- + // Always MUST send a response. + // ---------------------------------------------------------- + + if (pRequest->SendResponse() < 0) + { + bKeepAlive = false; + VERBOSE( VB_UPNP, QString( "HttpWorkerThread::ProcessWork socket(%1) - Error returned from SendResponse... Closing connection" ) + .arg( m_nSocket )); + } + + // ---------------------------------------------------------- + // Check to see if a PostProcess was registered + // ---------------------------------------------------------- + + if ( pRequest->m_pPostProcess != NULL ) + pRequest->m_pPostProcess->ExecutePostProcess(); + + delete pRequest; + pRequest = NULL; + + } else { VERBOSE( VB_IMPORTANT, QString( "HttpWorkerThread::ProcessWork - Error Creating BufferedSocketDeviceRequest" )); - pRequest->m_nResponseStatus = 501; bKeepAlive = false; } - - // ---------------------------------------------------------- - // Always MUST send a response. - // ---------------------------------------------------------- - - pRequest->SendResponse(); - - delete pRequest; - pRequest = NULL; - } else { @@ -293,8 +332,8 @@ delete pSocket; m_nSocket = 0; - VERBOSE( VB_UPNP, QString( "HttpWorkerThread::ProcessWork:End( %1 )") - .arg( (long)QThread::currentThread() )); +// VERBOSE( VB_UPNP, QString( "HttpWorkerThread::ProcessWork:End( %1 )") +// .arg( (long)QThread::currentThread() )); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httpserver.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httpserver.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/httpserver.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/httpserver.h 2007-08-23 11:09:51.000000000 -0500 @@ -26,11 +26,13 @@ #include #include -#include "upnpglobal.h" +#include "upnputil.h" #include "httprequest.h" #include "threadpool.h" #include "refcounted.h" +#include "mythcontext.h" + typedef struct timeval TaskTime; class HttpWorkerThread; @@ -49,10 +51,19 @@ public: QString m_sName; + QString m_sSharePath; public: - HttpServerExtension( const QString &sName ):m_sName( sName ){}; + HttpServerExtension( const QString &sName ):m_sName( sName ) + { + QString sInstallPrefix( PREFIX ), sLibDir; + + GetInstallPrefixPath( sInstallPrefix, sLibDir ); + + m_sSharePath = sInstallPrefix + "/share/mythtv/"; + }; + virtual ~HttpServerExtension() {}; // virtual bool Initialize ( HttpServer *pServer ) = 0; @@ -76,6 +87,7 @@ protected: + QMutex m_mutex; HttpServerExtensionList m_extensions; virtual WorkerThread *CreateWorkerThread( ThreadPool *, const QString &sName ); @@ -83,6 +95,10 @@ public: + static QString g_sPlatform; + + public: + HttpServer( int nPort ); virtual ~HttpServer(); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/libmythupnp.pro /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/libmythupnp.pro --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/libmythupnp.pro 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/libmythupnp.pro 2007-08-23 11:09:51.000000000 -0500 @@ -1,5 +1,6 @@ include ( ../../config.mak ) include ( ../../settings.pro ) +include ( ../../version.pro ) TEMPLATE = lib TARGET = mythupnp-$$LIBVERSION @@ -8,7 +9,7 @@ INSTALLS = target setting.path = $${PREFIX}/share/mythtv/ -setting.files += upnpavcd.xml CDS_scpd.xml CMGR_scpd.xml +setting.files += CDS_scpd.xml CMGR_scpd.xml MSRR_scpd.xml MXML_scpd.xml INSTALLS += setting @@ -17,12 +18,17 @@ # Input HEADERS += httprequest.h upnp.h ssdp.h taskqueue.h -HEADERS += upnpdevice.h upnptasknotify.h upnptasksearch.h threadpool.h upnpglobal.h -HEADERS += httpserver.h upnpcds.h upnpcdsobjects.h bufferedsocketdevice.h +HEADERS += upnpdevice.h upnptasknotify.h upnptasksearch.h threadpool.h upnputil.h +HEADERS += httpserver.h upnpcds.h upnpcdsobjects.h bufferedsocketdevice.h upnpmsrr.h +HEADERS += eventing.h upnpcmgr.h upnptaskevent.h upnptaskcache.h ssdpcache.h +HEADERS += upnpimpl.h multicast.h broadcast.h configuration.h +HEADERS += soapclient.h mythxmlclient.h -SOURCES += httprequest.cpp upnp.cpp ssdp.cpp taskqueue.cpp +SOURCES += httprequest.cpp upnp.cpp ssdp.cpp taskqueue.cpp upnputil.cpp SOURCES += upnpdevice.cpp upnptasknotify.cpp upnptasksearch.cpp threadpool.cpp SOURCES += httpserver.cpp upnpcds.cpp upnpcdsobjects.cpp bufferedsocketdevice.cpp +SOURCES += eventing.cpp upnpcmgr.cpp upnpmsrr.cpp upnptaskevent.cpp ssdpcache.cpp +SOURCES += configuration.cpp soapclient.cpp mythxmlclient.cpp INCLUDEPATH += ../libmyth INCLUDEPATH += ../.. @@ -42,6 +48,10 @@ QMAKE_EXTENSION_LIB=a } +cygwin { + QMAKE_EXTENSION_SHLIB=$${QMAKE_EXTENSION_SHLIB}$${QMAKE_EXTENSION_CYGWIN} +} + TARGETDEPS += ../libmyth/libmyth-$${LIBVERSION}.$${QMAKE_EXTENSION_SHLIB} TARGETDEPS += ../libmythtv/libmythtv-$${LIBVERSION}.$${QMAKE_EXTENSION_SHLIB} TARGETDEPS += ../libavcodec/libmythavcodec-$${LIBVERSION}.$${QMAKE_EXTENSION_SHLIB} @@ -50,11 +60,20 @@ inc.path = $${PREFIX}/include/mythtv/upnp/ inc.files = httprequest.h upnp.h ssdp.h taskqueue.h bufferedsocketdevice.h -inc.files += upnpdevice.h upnptasknotify.h upnptasksearch.h threadpool.h upnpglobal.h +inc.files += upnpdevice.h upnptasknotify.h upnptasksearch.h threadpool.h upnputil.h inc.files += httpserver.h httpstatus.h upnpcds.h upnpcdsobjects.h +inc.files += eventing.h upnpcmgr.h upnptaskevent.h upnptaskcache.h ssdpcache.h +inc.files += upnpimpl.h multicast.h broadcast.h configuration.h +inc.files += soapclient.h mythxmlclient.h INSTALLS += inc +cygwin:HEADERS += darwin-sendfile.h +cygwin:SOURCES += darwin-sendfile.c + +freebsd:HEADERS += darwin-sendfile.h +freebsd:SOURCES += darwin-sendfile.c + macx { HEADERS += darwin-sendfile.h SOURCES += darwin-sendfile.c diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/MSRR_scpd.xml /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/MSRR_scpd.xml --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/MSRR_scpd.xml 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/MSRR_scpd.xml 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,88 @@ + + + + 1 + 0 + + + + IsAuthorized + + + DeviceID + in + A_ARG_TYPE_DeviceID + + + Result + out + A_ARG_TYPE_Result + + + + + RegisterDevice + + + RegistrationReqMsg + in + A_ARG_TYPE_RegistrationReqMsg + + + RegistrationRespMsg + out + A_ARG_TYPE_RegistrationRespMsg + + + + + IsValidated + + + DeviceID + in + A_ARG_TYPE_DeviceID + + + Result + out + A_ARG_TYPE_Result + + + + + + + A_ARG_TYPE_DeviceID + string + + + A_ARG_TYPE_Result + int + + + A_ARG_TYPE_RegistrationReqMsg + bin.base64 + + + A_ARG_TYPE_RegistrationRespMsg + bin.base64 + + + AuthorizationGrantedUpdateID + ui4 + + + AuthorizationDeniedUpdateID + ui4 + + + ValidationSucceededUpdateID + ui4 + + + ValidationRevokedUpdateID + ui4 + + + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/multicast.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/multicast.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/multicast.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/multicast.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,68 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: multicast.h +// +// Purpose - Multicast QSocketDevice Implmenetation +// +// Created By : David Blain Created On : Oct. 1, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MULTICAST_H__ +#define __MULTICAST_H__ + +#include + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// QMulticastSocket Class Definition/Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +// -=>TODO: Need to add support for Multi-Homed machines. + +class QMulticastSocket : public QSocketDevice +{ + public: + + QHostAddress m_address; + Q_UINT16 m_port; + struct ip_mreq m_imr; + + public: + + QMulticastSocket( QString sAddress, Q_UINT16 nPort, u_char ttl = 0 ) + : QSocketDevice( QSocketDevice::Datagram ) + { + m_address.setAddress( sAddress ); + m_port = nPort; + + if (ttl == 0) + ttl = 4; + +// ttl = UPnp::g_pConfig->GetValue( "UPnP/TTL", 4 ); + + m_imr.imr_multiaddr.s_addr = inet_addr( sAddress ); + m_imr.imr_interface.s_addr = htonl(INADDR_ANY); + + if ( setsockopt( socket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &m_imr, sizeof( m_imr )) < 0) + { + VERBOSE(VB_IMPORTANT, QString( "QMulticastSocket: setsockopt - IP_ADD_MEMBERSHIP Error" )); + } + + setsockopt( socket(), IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl) ); + + setAddressReusable( true ); + + bind( m_address, m_port ); + } + + virtual ~QMulticastSocket() + { + setsockopt( socket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &m_imr, sizeof(m_imr)); + } +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/mythxmlclient.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/mythxmlclient.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/mythxmlclient.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/mythxmlclient.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,106 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: mythxmlclient.cpp +// +// Purpose - Myth XML protocol client +// +// Created By : David Blain Created On : Mar. 19, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "mythxmlclient.h" + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +MythXMLClient::MythXMLClient( const QUrl &url, bool bInQtThread ) + : SOAPClient( url, + "urn:schemas-mythtv-org:service:MythTv:1", + "/Myth") +{ + m_bInQtThread = bInQtThread; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +MythXMLClient::~MythXMLClient() +{ +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnPResultCode MythXMLClient::GetConnectionInfo( const QString &sPin, DatabaseParams *pParams ) +{ + if (pParams == NULL) + return UPnPResult_InvalidArgs; + + int nErrCode = 0; + QString sErrDesc; + QStringMap list; + + list.insert( "Pin", sPin ); + + if (SendSOAPRequest( "GetConnectionInfo", list, nErrCode, sErrDesc, m_bInQtThread )) + { + QString sXml = "" + list[ "Info" ] + ""; + + QDomDocument doc; + + if ( !doc.setContent( sXml, false, &sErrDesc, &nErrCode )) + { + VERBOSE( VB_UPNP, QString( "MythXMLClient::GetConnectionInfo : (%1) - %2" ) + .arg( nErrCode ) + .arg( sErrDesc )); + + return UPnPResult_ActionFailed; + } + + // -------------------------------------------------------------- + // Is this a valid response? + // -------------------------------------------------------------- + + QDomNode infoNode = doc.namedItem( "Info" ); + + if (!infoNode.isNull()) + { + QDomNode dbNode = infoNode.namedItem( "Database" ); + + pParams->dbHostName = GetNodeValue( dbNode, "Host" , QString( "" )); + //pParams->dbPort = GetNodeValue( dbNode, "Port" , 0 ); + pParams->dbUserName = GetNodeValue( dbNode, "UserName" , QString( "" )); + pParams->dbPassword = GetNodeValue( dbNode, "Password" , QString( "" )); + pParams->dbName = GetNodeValue( dbNode, "Name" , QString( "" )); + pParams->dbType = GetNodeValue( dbNode, "Type" , QString( "" )); + + QDomNode wolNode = infoNode.namedItem( "WOL" ); + + pParams->wolEnabled = GetNodeValue( wolNode, "Enabled" , false ); + pParams->wolReconnect = GetNodeValue( wolNode, "Reconnect", 0 ); + pParams->wolRetry = GetNodeValue( wolNode, "Retry" , 0 ); + pParams->wolCommand = GetNodeValue( wolNode, "Command" , QString( "" )); + + return UPnPResult_Success; + } + else + { + VERBOSE( VB_IMPORTANT, QString( "MythXMLClient::GetConnectionInfo Failed : Unexpected Response - %1" ) + .arg( sXml )); + } + } + else + { + VERBOSE( VB_IMPORTANT, QString( "MythXMLClient::GetConnectionInfo Failed -(%1) %2" ) + .arg( nErrCode ) + .arg( sErrDesc )); + + if (nErrCode == UPnPResult_ActionNotAuthorized) + return UPnPResult_ActionNotAuthorized; + } + + return UPnPResult_ActionFailed; +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/mythxmlclient.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/mythxmlclient.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/mythxmlclient.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/mythxmlclient.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,62 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: mythxmlclient.h +// +// Purpose - Myth XML protocol client +// +// Created By : David Blain Created On : Mar. 19, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef MYTHXMLCLIENT_H_ +#define MYTHXMLCLIENT_H_ + +#include +#include + +#include "mythcontext.h" +#include "httpcomms.h" + +#include "upnp.h" +#include "soapclient.h" + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class MythXMLClient : public SOAPClient +{ + protected: + + bool m_bInQtThread; + + public: + + MythXMLClient( const QUrl &url, bool bInQtThread = true ); + virtual ~MythXMLClient( ); + + UPnPResultCode GetConnectionInfo( const QString &sPin, DatabaseParams *pParams ); + + // GetServiceDescription + // GetProgramGuide + // GetHosts + // GetKeys + // GetSetting + // PutSetting + // GetChannelIcon + // GetRecorded + // GetPreviewImage + // GetRecording + // GetMusic + // GetExpiring + // GetProgramDetails + // GetVideo + +}; + +#endif + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/refcounted.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/refcounted.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/refcounted.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/refcounted.h 2007-08-23 11:09:51.000000000 -0500 @@ -11,6 +11,8 @@ #ifndef __REFCOUNTED_H__ #define __REFCOUNTED_H__ +#include + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/soapclient.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/soapclient.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/soapclient.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/soapclient.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,268 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: soapclient.cpp +// +// Purpose - SOAP client base class +// +// Created By : David Blain Created On : Mar. 19, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "soapclient.h" + +#include "mythcontext.h" +#include "httprequest.h" + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +SOAPClient::SOAPClient( const QUrl &url, + const QString &sNamespace, + const QString &sControlPath ) +{ + m_url = url; + m_sNamespace = sNamespace; + m_sControlPath = sControlPath; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +SOAPClient::~SOAPClient() +{ +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QDomNode SOAPClient::FindNode( const QString &sName, QDomNode &baseNode ) +{ + QStringList parts = QStringList::split( "/", sName ); + + return FindNode( parts, baseNode ); + +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +QDomNode SOAPClient::FindNode( QStringList &sParts, QDomNode &curNode ) +{ + if (sParts.empty()) + return curNode; + + QString sName = sParts.front(); + sParts.pop_front(); + + QDomNode child = curNode.namedItem( sName ); + + if (child.isNull() ) + sParts.clear(); + + return FindNode( sParts, child ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int SOAPClient::GetNodeValue( QDomNode &node, const QString &sName, int nDefault ) +{ + QString sValue = GetNodeValue( node, sName, QString::number( nDefault ) ); + + return sValue.toInt(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool SOAPClient::GetNodeValue( QDomNode &node, const QString &sName, bool bDefault ) +{ + QString sDefault = (bDefault) ? "true" : "false"; + QString sValue = GetNodeValue( node, sName, sDefault ); + + if (sValue.startsWith( "T" , false ) || + sValue.startsWith( "Y" , false ) || + sValue.startsWith( "1" , false ) ) + { + return true; + } + + if (sValue.startsWith( "F" , false ) || + sValue.startsWith( "N" , false ) || + sValue.startsWith( "0" , false ) ) + { + return false; + } + + return bDefault; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString SOAPClient::GetNodeValue( QDomNode &node, const QString &sName, const QString &sDefault ) +{ + if (node.isNull()) + return sDefault; + + QString sValue = ""; + QDomNode valNode = FindNode( sName, node ); + + if (!valNode.isNull()) + { + // -=>TODO: Assumes first child is Text Node. + + QDomText oText = valNode.firstChild().toText(); + + if (!oText.isNull()) + sValue = oText.nodeValue(); + + QUrl::decode( sValue ); + + return sValue; + } + + return sDefault; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool SOAPClient::SendSOAPRequest( const QString &sMethod, + QStringMap &list, + int &nErrCode, + QString &sErrDesc, + bool bInQtThread ) +{ + QUrl url( m_url ); + + url.setPath( m_sControlPath ); + + // -------------------------------------------------------------- + // Add appropriate headers + // -------------------------------------------------------------- + + QHttpRequestHeader header; + + header.setValue("CONTENT-TYPE", "text/xml; charset=\"utf-8\"" ); + header.setValue("SOAPACTION" , QString( "\"%1#GetConnectionInfo\"" ) + .arg( m_sNamespace )); + + // -------------------------------------------------------------- + // Build request payload + // -------------------------------------------------------------- + + QByteArray aBuffer; + QTextStream os( aBuffer, IO_WriteOnly ); + + os << "\r\n"; + os << "\r\n"; + os << " \r\n"; + os << " \r\n"; + + // -------------------------------------------------------------- + // Add parameters from list + // -------------------------------------------------------------- + + for ( QStringMap::iterator it = list.begin(); + it != list.end(); + ++it ) + { + os << " <" << it.key() << ">"; + os << HTTPRequest::Encode( it.data() ); + os << "\r\n"; + } + + os << " \r\n"; + os << " \r\n"; + os << "\r\n"; + + // -------------------------------------------------------------- + // Perform Request + // -------------------------------------------------------------- + + QBuffer buff( aBuffer ); + + QString sXml = HttpComms::postHttp( url, + &header, + &buff, + 10000, // ms + 3, // retries + 0, // redirects + false, // allow gzip + NULL, // login + bInQtThread ); + + // -------------------------------------------------------------- + // Parse response + // -------------------------------------------------------------- + + list.clear(); + + QDomDocument doc; + + if ( !doc.setContent( sXml, true, &sErrDesc, &nErrCode )) + { + VERBOSE( VB_UPNP, QString( "MythXMLClient::SendSOAPRequest( %1 ) - Invalid response from %2" ) + .arg( sMethod ) + .arg( url.toString() )); + return false; + } + + // -------------------------------------------------------------- + // Is this a valid response? + // -------------------------------------------------------------- + + QString sResponseName = sMethod + "Response"; + QDomNodeList oNodeList = doc.elementsByTagNameNS( m_sNamespace, sResponseName ); + + if (oNodeList.count() > 0) + { + QDomNode oMethod = oNodeList.item(0); + + if (!oMethod.isNull()) + { + + for ( QDomNode oNode = oMethod.firstChild(); !oNode.isNull(); + oNode = oNode.nextSibling() ) + { + QDomElement e = oNode.toElement(); + + if (!e.isNull()) + { + QString sName = e.tagName(); + QString sValue = ""; + + QDomText oText = oNode.firstChild().toText(); + + if (!oText.isNull()) + sValue = oText.nodeValue(); + + QUrl::decode( sName ); + QUrl::decode( sValue ); + + list.insert( sName, sValue ); + } + } + } + + return true; + } + + // -------------------------------------------------------------- + // Must be a fault... parse it to return reason + // -------------------------------------------------------------- + + nErrCode = GetNodeValue( doc, "Envelope/Body/Fault/detail/UPnPResult/errorCode" , 500 ); + sErrDesc = GetNodeValue( doc, "Envelope/Body/Fault/detail/UPnPResult/errorDescription", "Unknown" ); + + return false; +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/soapclient.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/soapclient.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/soapclient.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/soapclient.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,60 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: soapclient.h +// +// Purpose - SOAP client base class +// +// Created By : David Blain Created On : Mar. 19, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef SOAPCLIENT_H_ +#define SOAPCLIENT_H_ + +#include +#include + +#include "httpcomms.h" +#include "upnputil.h" + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class SOAPClient +{ + protected: + + QString m_sNamespace; + QString m_sControlPath; + QUrl m_url; + + public: + + SOAPClient( const QUrl &url, + const QString &sNamespace, + const QString &sControlPath ); + virtual ~SOAPClient(); + + protected: + + int GetNodeValue( QDomNode &node, const QString &sName, int nDefault ); + bool GetNodeValue( QDomNode &node, const QString &sName, bool bDefault ); + QString GetNodeValue( QDomNode &node, const QString &sName, const QString &sDefault ); + + QDomNode FindNode( const QString &sName , QDomNode &baseNode ); + QDomNode FindNode( QStringList &sParts, QDomNode &curNode ); + + bool SendSOAPRequest( const QString &sMethod, + QStringMap &list, + int &nErrCode, + QString &sErrDesc, + bool bInQtThread ); +}; + +#endif + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdpcache.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdpcache.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdpcache.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdpcache.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,541 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: ssdpcache.cpp +// +// Purpose - SSDP Client Implmenetation +// +// Created By : David Blain Created On : Jan. 8, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "upnp.h" +#include "mythevent.h" + +int SSDPCacheEntries::g_nAllocated = 0; // Debugging only + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// SSDPCacheEntries Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + + +SSDPCacheEntries::SSDPCacheEntries() +{ + // Should be atomic increment + g_nAllocated++; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +SSDPCacheEntries::~SSDPCacheEntries() +{ + Clear(); + + // Should be atomic decrement + g_nAllocated--; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCacheEntries::Clear() +{ + Lock(); + + for (EntryMap::Iterator it = m_mapEntries.begin(); + it != m_mapEntries.end(); + ++it ) + { + DeviceLocation *pEntry = (DeviceLocation *)it.data(); + + if (pEntry != NULL) + pEntry->Release(); + } + + m_mapEntries.clear(); + + Unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// Caller must call AddRef on returned pointer. +///////////////////////////////////////////////////////////////////////////// + +DeviceLocation *SSDPCacheEntries::Find( const QString &sUSN ) +{ + DeviceLocation *pEntry = NULL; + + Lock(); + EntryMap::Iterator it = m_mapEntries.find( sUSN ); + + if ( it != m_mapEntries.end() ) + pEntry = (DeviceLocation *)it.data(); + + Unlock(); + + return pEntry; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCacheEntries::Insert( const QString &sUSN, DeviceLocation *pEntry ) +{ + Lock(); + + pEntry->AddRef(); + + // Since insert overrights anything already there + // we need to see if the key already exists and release + // it's reference if it does. + + EntryMap::Iterator it = m_mapEntries.find( sUSN ); + + if (it != m_mapEntries.end()) + { + if (it.data() != NULL) + ((DeviceLocation *)it.data())->Release(); + } + + m_mapEntries.insert( sUSN, pEntry ); + + Unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCacheEntries::Remove( const QString &sUSN ) +{ + Lock(); + + EntryMap::Iterator it = m_mapEntries.find( sUSN ); + + if (it != m_mapEntries.end()) + { + if (it.data() != NULL) + ((DeviceLocation *)it.data())->Release(); + + // -=>TODO: Need to somehow call SSDPCache::NotifyRemove + + m_mapEntries.remove( it ); + } + + Unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int SSDPCacheEntries::RemoveStale( const TaskTime &ttNow ) +{ + int nCount = 0; + QStringList lstKeys; + + Lock(); + + // ---------------------------------------------------------------------- + // Iterate through all USN's and build list of stale entries keys + // ---------------------------------------------------------------------- + + for (EntryMap::Iterator it = m_mapEntries.begin(); + it != m_mapEntries.end(); + ++it ) + { + DeviceLocation *pEntry = (DeviceLocation *)it.data(); + + if (pEntry != NULL) + { + pEntry->AddRef(); + + if ( pEntry->m_ttExpires < ttNow ) + lstKeys.append( it.key() ); + + pEntry->Release(); + } + } + + Unlock(); + + nCount = lstKeys.count(); + + // ---------------------------------------------------------------------- + // Iterate through list of keys and remove them. (This avoids issues when + // removing from a QMap while iterating it. + // ---------------------------------------------------------------------- + + for ( QStringList::Iterator it = lstKeys.begin(); it != lstKeys.end(); ++it ) + Remove( *it ); + + return nCount; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// SSDPCache Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +SSDPCache::SSDPCache() +{ + VERBOSE( VB_UPNP, "SSDPCache - Constructor" ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +SSDPCache::~SSDPCache() +{ + VERBOSE( VB_UPNP, "SSDPCache - Destructor" ); + + Clear(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCache::Clear() +{ + Lock(); + + for (SSDPCacheEntriesMap::Iterator it = m_cache.begin(); + it != m_cache.end(); + ++it ) + { + SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data(); + + if (pEntries != NULL) + pEntries->Release(); + } + + m_cache.clear(); + + Unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// Caller must call AddRef on returned pointer. +///////////////////////////////////////////////////////////////////////////// + +SSDPCacheEntries *SSDPCache::Find( const QString &sURI ) +{ + SSDPCacheEntries *pEntries = NULL; + + Lock(); + SSDPCacheEntriesMap::Iterator it = m_cache.find( sURI ); + + if ( it != m_cache.end() ) + pEntries = (SSDPCacheEntries *)it.data(); + + Unlock(); + + return pEntries; +} + +///////////////////////////////////////////////////////////////////////////// +// Caller must call AddRef on returned pointer. +///////////////////////////////////////////////////////////////////////////// + +DeviceLocation *SSDPCache::Find( const QString &sURI, const QString &sUSN ) +{ + DeviceLocation *pEntry = NULL; + SSDPCacheEntries *pEntries = Find( sURI ); + + if (pEntries != NULL) + { + pEntries->AddRef(); + pEntry = pEntries->Find( sUSN ); + pEntries->Release(); + } + + return pEntry; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCache::Add( const QString &sURI, + const QString &sUSN, + const QString &sLocation, + long sExpiresInSecs ) +{ + // -------------------------------------------------------------- + // Calculate when this cache entry should expire. + // -------------------------------------------------------------- + + TaskTime ttExpires; + gettimeofday ( &ttExpires, NULL ); + AddSecondsToTaskTime( ttExpires, sExpiresInSecs ); + + // -------------------------------------------------------------- + // Get a Pointer to a Entries QDict... (Create if not found) + // -------------------------------------------------------------- + + SSDPCacheEntries *pEntries = Find( sURI ); + + if (pEntries == NULL) + { + pEntries = new SSDPCacheEntries(); + pEntries->AddRef(); + m_cache.insert( sURI, pEntries ); + } + + pEntries->AddRef(); + + // -------------------------------------------------------------- + // See if the Entries Collection contains our USN... (Create if not found) + // -------------------------------------------------------------- + + DeviceLocation *pEntry = pEntries->Find( sUSN ); + + if (pEntry == NULL) + { + pEntry = new DeviceLocation( sURI, sUSN, sLocation, ttExpires ); + + Lock(); + pEntries->Insert( sUSN, pEntry ); + Unlock(); + + NotifyAdd( sURI, sUSN, sLocation ); + } + else + { + pEntry->AddRef(); + pEntry->m_sLocation = sLocation; + pEntry->m_ttExpires = ttExpires; + pEntry->Release(); + } + + pEntries->Release(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCache::Remove( const QString &sURI, const QString &sUSN ) +{ + Lock(); + + // -------------------------------------------------------------- + // Get a Pointer to a Entries QDict... (Create if not found) + // -------------------------------------------------------------- + + SSDPCacheEntriesMap::Iterator it = m_cache.find( sURI ); + + if (it != m_cache.end()) + { + SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data(); + + if (pEntries != NULL) + { + pEntries->AddRef(); + + pEntries->Remove( sUSN ); + + if (pEntries->Count() == 0) + { + pEntries->Release(); + m_cache.remove( it ); + } + + pEntries->Release(); + } + } + + Unlock(); + + // -=>TODO: Should this only by notifued if we actually had any entry removed? + + NotifyRemove( sURI, sUSN ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int SSDPCache::RemoveStale() +{ + int nCount = 0; + TaskTime ttNow; + QStringList lstKeys; + + gettimeofday( &ttNow, NULL ); + + Lock(); + + // ---------------------------------------------------------------------- + // Iterate through all Type URI's and build list of stale entries keys + // ---------------------------------------------------------------------- + + for (SSDPCacheEntriesMap::Iterator it = m_cache.begin(); + it != m_cache.end(); + ++it ) + { + SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data(); + + if (pEntries != NULL) + { + pEntries->AddRef(); + + nCount += pEntries->RemoveStale( ttNow ); + + if (pEntries->Count() == 0) + lstKeys.append( it.key() ); + + pEntries->Release(); + } + } + + Unlock(); + + nCount = lstKeys.count(); + + // ---------------------------------------------------------------------- + // Iterate through list of keys and remove them. (This avoids issues when + // removing from a QMap while iterating it. + // ---------------------------------------------------------------------- + + for ( QStringList::Iterator itKey = lstKeys.begin(); itKey != lstKeys.end(); ++itKey ) + { + SSDPCacheEntriesMap::Iterator it = m_cache.find( *itKey ); + + if (it != m_cache.end()) + { + SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data(); + + if (pEntries != NULL) + { + pEntries->Release(); + m_cache.remove( it ); + } + } + } + + return nCount; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCache::NotifyAdd( const QString &sURI, + const QString &sUSN, + const QString &sLocation ) +{ + QStringList values; + + values.append( sURI ); + values.append( sUSN ); + values.append( sLocation ); + + MythEvent me( "SSDP_ADD", values ); + + dispatch( me ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCache::NotifyRemove( const QString &sURI, const QString &sUSN ) +{ + QStringList values; + + values.append( sURI ); + values.append( sUSN ); + + MythEvent me( "SSDP_REMOVE", values ); + + dispatch( me ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPCache::Dump() +{ + int nCount = 0; + + if ( print_verbose_messages & VB_UPNP) + { + + Lock(); + + // ---------------------------------------------------------------------- + // Build List of items to be removed + // ---------------------------------------------------------------------- + + VERBOSE( VB_UPNP, "===============================================================================" ); + VERBOSE( VB_UPNP, QString( " URI (type) - Found: %1 Entries - %2 have been Allocated. " ) + .arg( m_cache.count() ) + .arg( SSDPCacheEntries::g_nAllocated )); + VERBOSE( VB_UPNP, " \t\tUSN (unique id)\t\t | Expires\t | Location" ); + VERBOSE( VB_UPNP, "-------------------------------------------------------------------------------" ); + + for (SSDPCacheEntriesMap::Iterator it = m_cache.begin(); + it != m_cache.end(); + ++it ) + { + SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data(); + + if (pEntries != NULL) + { + VERBOSE( VB_UPNP, it.key() ); + + pEntries->Lock(); + + EntryMap *pMap = pEntries->GetEntryMap(); + + for (EntryMap::Iterator itEntry = pMap->begin(); + itEntry != pMap->end(); + ++itEntry ) + { + + DeviceLocation *pEntry = (DeviceLocation *)itEntry.data(); + + if (pEntry != NULL) + { + nCount++; + + pEntry->AddRef(); + + VERBOSE( VB_UPNP, QString( " * \t\t%1\t | %2\t | %3 " ) + .arg( pEntry->m_sUSN ) + .arg( pEntry->ExpiresInSecs() ) + .arg( pEntry->m_sLocation )); + + pEntry->Release(); + } + } + + VERBOSE( VB_UPNP, " "); + + pEntries->Unlock(); + } + } + + VERBOSE( VB_UPNP, "-------------------------------------------------------------------------------" ); + VERBOSE( VB_UPNP, QString( " Found: %1 Entries - %2 have been Allocated. " ) + .arg( nCount ) + .arg( DeviceLocation::g_nAllocated )); + VERBOSE( VB_UPNP, "===============================================================================" ); + + Unlock(); + } +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdpcache.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdpcache.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdpcache.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdpcache.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,117 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: ssdpcache.h +// +// Purpose - SSDP Cache Declaration +// +// Created By : David Blain Created On : Jan. 8, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __SSDPCLIENT_H__ +#define __SSDPCLIENT_H__ + +#include "mythobservable.h" + +#include +#include +#include + +#include "upnpdevice.h" + +typedef QMap< QString, DeviceLocation * > EntryMap; // Key == Unique Service Name (USN) + +///////////////////////////////////////////////////////////////////////////// +// QDict Implementation that uses RefCounted pointers +///////////////////////////////////////////////////////////////////////////// + +class SSDPCacheEntries : public RefCounted +{ + public: + + static int g_nAllocated; // Debugging only + + protected: + + QMutex m_mutex; + EntryMap m_mapEntries; + + protected: + + // Destructor protected to force use of Release Method + + virtual ~SSDPCacheEntries(); + + public: + + SSDPCacheEntries(); + + void Lock () { m_mutex.lock(); } + void Unlock () { m_mutex.unlock(); } + + void Clear ( ); + + int Count ( ) { return m_mapEntries.size(); } + + DeviceLocation *Find ( const QString &sUSN ); + void Insert ( const QString &sUSN, DeviceLocation *pEntry ); + void Remove ( const QString &sUSN ); + int RemoveStale( const TaskTime &ttNow ); + + // Must Lock/Unlock when using. + EntryMap *GetEntryMap() { return &m_mapEntries; } +}; + +typedef QMap< QString, SSDPCacheEntries * > SSDPCacheEntriesMap; // Key == Service Type URI + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class SSDPCache : public QObject, + public MythObservable +{ + Q_OBJECT + + protected: + + QMutex m_mutex; + SSDPCacheEntriesMap m_cache; + + void NotifyAdd ( const QString &sURI, + const QString &sUSN, + const QString &sLocation ); + void NotifyRemove( const QString &sURI, const QString &sUSN ); + + public: + + SSDPCache(); + virtual ~SSDPCache(); + + void Lock () { m_mutex.lock(); } + void Unlock () { m_mutex.unlock(); } + + SSDPCacheEntriesMap::Iterator Begin() { return m_cache.begin(); } + SSDPCacheEntriesMap::Iterator End () { return m_cache.end(); } + + int Count () { return m_cache.count(); } + void Clear (); + void Add ( const QString &sURI, + const QString &sUSN, + const QString &sLocation, + long sExpiresInSecs ); + + void Remove ( const QString &sURI, const QString &sUSN ); + int RemoveStale( ); + + void Dump ( ); + + SSDPCacheEntries *Find( const QString &sURI ); + DeviceLocation *Find( const QString &sURI, const QString &sUSN ); +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdp.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdp.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdp.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdp.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -10,9 +10,13 @@ #include "upnp.h" -#include "upnptasknotify.h" #include "upnptasksearch.h" -#include "mythcontext.h" +#include "upnptaskcache.h" + +#include "multicast.h" +#include "broadcast.h" + +#include #include #include @@ -30,8 +34,48 @@ // ///////////////////////////////////////////////////////////////////////////// -SSDP::SSDP() : m_bTermRequested( false ) +SSDP::SSDP( int nServicePort ) : m_bTermRequested( false ) +{ + + m_nServicePort = nServicePort; + m_nPort = UPnp::g_pConfig->GetValue( "UPnP/SSDP/Port" , SSDP_PORT ); + m_nSearchPort = UPnp::g_pConfig->GetValue( "UPnP/SSDP/SearchPort", SSDP_SEARCHPORT ); + + m_Sockets[ SocketIdx_Search ] = new QSocketDevice( QSocketDevice::Datagram ); + m_Sockets[ SocketIdx_Multicast ] = new QMulticastSocket( SSDP_GROUP, m_nPort ); + m_Sockets[ SocketIdx_Broadcast ] = new QBroadcastSocket( "255.255.255.255", m_nPort ); + + + m_Sockets[ SocketIdx_Search ]->setBlocking( FALSE ); + m_Sockets[ SocketIdx_Multicast ]->setBlocking( FALSE ); + m_Sockets[ SocketIdx_Broadcast ]->setBlocking( FALSE ); + + // Setup SearchSocket + + m_Sockets[ SocketIdx_Search ]->bind( INADDR_ANY, m_nSearchPort ); + + m_pNotifyTask = NULL; + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +SSDP::~SSDP() { + DisableNotifications(); + + if (m_pNotifyTask != NULL) + m_pNotifyTask->Release(); + + for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ ) + { + if (m_Sockets[ nIdx ] != NULL ) + { + delete m_Sockets[ nIdx ]; + } + } } ///////////////////////////////////////////////////////////////////////////// @@ -66,95 +110,275 @@ // ///////////////////////////////////////////////////////////////////////////// -void SSDP::run() +void SSDP::EnableNotifications() { - QMulticastSocket *pMulticastSocket = new QMulticastSocket( SSDP_GROUP, SSDP_PORT ); - BufferedSocketDevice *pSocket = new BufferedSocketDevice( pMulticastSocket ); - UPnpNotifyTask *pNotifyTask = new UPnpNotifyTask(); - HTTPRequest *pRequest = NULL; + if ( m_pNotifyTask == NULL ) + { + m_pNotifyTask = new UPnpNotifyTask( m_nServicePort ); - //-=>TODO: Should add checks for NULL pointers? + // ------------------------------------------------------------------ + // Let's make sure to hold on to a reference of the NotifyTask. + // ------------------------------------------------------------------ - // ---------------------------------------------------------------------- - // Let's make sure to hold on to a reference of the NotifyTask. - // ---------------------------------------------------------------------- + m_pNotifyTask->AddRef(); - pNotifyTask->AddRef(); + // ------------------------------------------------------------------ + // First Send out Notification that we are leaving the network. + // ------------------------------------------------------------------ - // ---------------------------------------------------------------------- + m_pNotifyTask->SetNTS( NTS_byebye ); + m_pNotifyTask->Execute( NULL ); + } + + // ------------------------------------------------------------------ // Add Announcement Task to the Queue - // ---------------------------------------------------------------------- - - UPnp::g_pTaskQueue->AddTask( pNotifyTask ); + // ------------------------------------------------------------------ + + m_pNotifyTask->SetNTS( NTS_alive ); + UPnp::g_pTaskQueue->AddTask( m_pNotifyTask ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDP::DisableNotifications() +{ + if (m_pNotifyTask != NULL) + { + // Send Announcement that we are leaving. + + m_pNotifyTask->SetNTS( NTS_byebye ); + m_pNotifyTask->Execute( NULL ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDP::PerformSearch( const QString &sST ) +{ + QCString sRequest = QString( "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 2\r\n" + "ST: %1\r\n" + "\r\n" ) + .arg( sST ).utf8(); + + QSocketDevice *pSocket = m_Sockets[ SocketIdx_Search ]; + + QHostAddress address; + address.setAddress( SSDP_GROUP ); + + int nSize = sRequest.size(); + + if ( pSocket->writeBlock( sRequest.data(), sRequest.size(), address, SSDP_PORT ) != nSize) + cerr << "SSDP::PerformSearch - did not write entire buffer." << endl; + + usleep( rand() % 250000 ); + + if ( pSocket->writeBlock( sRequest.data(), sRequest.size(), address, SSDP_PORT ) != nSize) + cerr << "SSDP::PerformSearch - did not write entire buffer." << endl; + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDP::run() +{ + fd_set read_set; + struct timeval timeout; // ---------------------------------------------------------------------- - // Listen for new Mutlicast Requests + // Listen for new Requests // ---------------------------------------------------------------------- while (!IsTermRequested()) { - if ( pSocket->WaitForMore( 500 ) > 0) - { - QHostAddress peerAddress = pSocket->PeerAddress(); - Q_UINT16 peerPort = pSocket->PeerPort (); + int nMaxSocket = 0; - pSocket->SetDestAddress( peerAddress, peerPort ); + FD_ZERO( &read_set ); - // -------------------------------------------------------------- - // See if this is a valid request - // -------------------------------------------------------------- - if ((pRequest = new BufferedSocketDeviceRequest( pSocket )) != NULL) + for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ ) + { + if (m_Sockets[ nIdx ] != NULL) { + FD_SET( m_Sockets[ nIdx ]->socket(), &read_set ); + nMaxSocket = max( m_Sockets[ nIdx ]->socket(), nMaxSocket ); - if ( pRequest->ParseRequest() ) +/* + if (m_Sockets[ nIdx ]->bytesAvailable() > 0) { - switch( pRequest->m_eType ) + cout << "Found Extra data before select: " << nIdx << endl; + ProcessData( m_Sockets[ nIdx ] ); + } +*/ + + } + } + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + if ( select( nMaxSocket + 1, &read_set, NULL, NULL, &timeout ) != -1) + { + for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ ) + { + if (m_Sockets[ nIdx ] != NULL) + { + if (FD_ISSET( m_Sockets[ nIdx ]->socket(), &read_set )) { - case RequestTypeMSearch: ProcessSearchRequest( pRequest, peerAddress, peerPort ); break; - default: break; + // cout << "FD_ISSET( " << nIdx << " ) " << endl; + + ProcessData( m_Sockets[ nIdx ] ); } } - - delete pRequest; - pRequest = NULL; } } } +} - // Send Announcement that we are leaving. +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDP::ProcessData( QSocketDevice *pSocket ) +{ + long nBytes = 0; + long nRead = 0; + + while ((nBytes = pSocket->bytesAvailable()) > 0) + { + QCString buffer( nBytes + 1 ); + + nRead = pSocket->readBlock( buffer.data(), nBytes ); + + QHostAddress peerAddress = pSocket->peerAddress(); + Q_UINT16 peerPort = pSocket->peerPort (); + + // ------------------------------------------------------------------ + + QStringList lines = QStringList::split( "\r\n", buffer ); + QString sRequestLine = lines[0]; + + lines.pop_front(); + + // ------------------------------------------------------------------ + // Parse request Type + // ------------------------------------------------------------------ + + // cout << "SSDP::ProcessData - requestLine: " << sRequestLine << endl; - pNotifyTask->SetNTS( NTS_byebye ); - pNotifyTask->Execute( NULL ); - pNotifyTask->Release(); + SSDPRequestType eType = ProcessRequestLine( sRequestLine ); - if (pSocket != NULL) - delete pSocket; + // ------------------------------------------------------------------ + // Read Headers into map + // ------------------------------------------------------------------ + + QStringMap headers; + + for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) + { + QString sLine = *it; + QString sName = sLine.section( ':', 0, 0 ).stripWhiteSpace(); + QString sValue = sLine.section( ':', 1 ); + + sValue.truncate( sValue.length() ); //-2 + + if ((sName.length() != 0) && (sValue.length() !=0)) + headers.insert( sName.lower(), sValue.stripWhiteSpace() ); + } + +// pSocket->SetDestAddress( peerAddress, peerPort ); + + // -------------------------------------------------------------- + // See if this is a valid request + // -------------------------------------------------------------- + + switch( eType ) + { + case SSDP_MSearch : ProcessSearchRequest ( headers, peerAddress, peerPort ); break; + case SSDP_MSearchResp: ProcessSearchResponse( headers ); break; + case SSDP_Notify : ProcessNotify ( headers ); break; + case SSDP_Unknown: + default: + VERBOSE(VB_UPNP, "SSPD::ProcessData - Unknown request Type."); + break; + } + } - if (pMulticastSocket != NULL) - delete pMulticastSocket; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -bool SSDP::ProcessSearchRequest( HTTPRequest *pRequest, - QHostAddress peerAddress, - Q_UINT16 peerPort ) +SSDPRequestType SSDP::ProcessRequestLine( const QString &sLine ) { - QString sMAN = pRequest->GetHeaderValue( "MAN", "" ); - QString sST = pRequest->GetHeaderValue( "ST", "" ); - QString sMX = pRequest->GetHeaderValue( "MX", "" ); + QStringList tokens = QStringList::split(QRegExp("[ \r\n][ \r\n]*"), sLine ); + + // ---------------------------------------------------------------------- + // if this is actually a response, then sLine's format will be: + // HTTP/m.n + // otherwise: + // HTTP/m.n + // ---------------------------------------------------------------------- + + if ( sLine.startsWith( "HTTP/" )) + return SSDP_MSearchResp; + else + { + if (tokens.count() > 0) + { + if (tokens[0] == "M-SEARCH" ) return SSDP_MSearch; + if (tokens[0] == "NOTIFY" ) return SSDP_Notify; + } + } + + return SSDP_Unknown; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString SSDP::GetHeaderValue( const QStringMap &headers, const QString &sKey, const QString &sDefault ) +{ + QStringMap::const_iterator it = headers.find( sKey.lower() ); + + if ( it == headers.end()) + return( sDefault ); + + return( it.data() ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool SSDP::ProcessSearchRequest( const QStringMap &sHeaders, + QHostAddress peerAddress, + Q_UINT16 peerPort ) +{ + QString sMAN = GetHeaderValue( sHeaders, "MAN", "" ); + QString sST = GetHeaderValue( sHeaders, "ST" , "" ); + QString sMX = GetHeaderValue( sHeaders, "MX" , "" ); int nMX = 0; + //cout << "*** SSDP ProcessSearchrequest : [" << sST << "] MX = " << nMX << endl; + // ---------------------------------------------------------------------- // Validate Header Values... // ---------------------------------------------------------------------- if ( UPnp::g_pTaskQueue == NULL ) return false; - if ( pRequest->m_sMethod != "*" ) return false; - if ( pRequest->m_sProtocol != "HTTP" ) return false; - if ( pRequest->m_nMajor != 1 ) return false; +// if ( pRequest->m_sMethod != "*" ) return false; +// if ( pRequest->m_sProtocol != "HTTP" ) return false; +// if ( pRequest->m_nMajor != 1 ) return false; if ( sMAN != "\"ssdp:discover\"" ) return false; if ( sST.length() == 0 ) return false; if ( sMX.length() == 0 ) return false; @@ -175,7 +399,8 @@ if ((sST == "ssdp:all") || (sST == "upnp:rootdevice")) { - UPnpSearchTask *pTask = new UPnpSearchTask( peerAddress, + UPnpSearchTask *pTask = new UPnpSearchTask( m_nServicePort, + peerAddress, peerPort, sST, UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN()); @@ -198,7 +423,8 @@ if (sUDN.length() > 0) { - UPnpSearchTask *pTask = new UPnpSearchTask( peerAddress, + UPnpSearchTask *pTask = new UPnpSearchTask( m_nServicePort, + peerAddress, peerPort, sST, sUDN ); @@ -217,6 +443,72 @@ } ///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool SSDP::ProcessSearchResponse( const QStringMap &headers ) +{ + QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" ); + QString sST = GetHeaderValue( headers, "ST" , "" ); + QString sUSN = GetHeaderValue( headers, "USN" , "" ); + QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" ); + + int nPos = sCache.find( "max-age", 0, false ); + + if (nPos < 0) + return false; + + if ((nPos = sCache.find( "=", nPos, false )) < 0) + return false; + + int nSecs = sCache.mid( nPos+1 ).toInt(); + + UPnp::g_SSDPCache.Add( sST, sUSN, sDescURL, nSecs ); + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool SSDP::ProcessNotify( const QStringMap &headers ) +{ + QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" ); + QString sNTS = GetHeaderValue( headers, "NTS" , "" ); + QString sNT = GetHeaderValue( headers, "NT" , "" ); + QString sUSN = GetHeaderValue( headers, "USN" , "" ); + QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" ); + + if (sNTS.contains( "ssdp:alive")) + { + int nPos = sCache.find( "max-age", 0, false ); + + if (nPos < 0) + return false; + + if ((nPos = sCache.find( "=", nPos, false )) < 0) + return false; + + int nSecs = sCache.mid( nPos+1 ).toInt(); + + UPnp::g_SSDPCache.Add( sNT, sUSN, sDescURL, nSecs ); + + return true; + } + + + if ( sNTS.contains( "ssdp:byebye" ) ) + { + UPnp::g_SSDPCache.Remove( sNT, sUSN ); + + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // SSDPExtension Implemenation @@ -228,11 +520,10 @@ // ///////////////////////////////////////////////////////////////////////////// -SSDPExtension::SSDPExtension( ) : HttpServerExtension( "SSDP" ) +SSDPExtension::SSDPExtension( int nServicePort ) : HttpServerExtension( "SSDP" ) { - QString sSharePath = gContext->GetShareDir(); - - m_sUPnpDescPath = gContext->GetSetting("upnpDescXmlPath", sSharePath); + m_sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath", m_sSharePath ); + m_nServicePort = nServicePort; } ///////////////////////////////////////////////////////////////////////////// @@ -249,9 +540,8 @@ SSDPMethod SSDPExtension::GetMethod( const QString &sURI ) { - if (sURI == "getDeviceDesc") return( SSDPM_GetDeviceDesc ); - if (sURI == "getCMGRDesc" ) return( SSDPM_GetCMGRDesc ); - if (sURI == "getCDSDesc" ) return( SSDPM_GetCDSDesc ); + if (sURI == "getDeviceDesc" ) return( SSDPM_GetDeviceDesc ); + if (sURI == "getDeviceList" ) return( SSDPM_GetDeviceList ); return( SSDPM_Unknown ); } @@ -269,9 +559,9 @@ switch( GetMethod( pRequest->m_sMethod )) { - case SSDPM_GetDeviceDesc: GetDeviceDesc( pRequest ); return( true ); - case SSDPM_GetCDSDesc : GetFile( pRequest, "CDS_scpd.xml" ); return( true ); - case SSDPM_GetCMGRDesc : GetFile( pRequest, "CMGR_scpd.xml" ); return( true ); + case SSDPM_GetDeviceDesc : GetDeviceDesc( pRequest ); return( true ); + case SSDPM_GetDeviceList : GetDeviceList( pRequest ); return( true ); + default: break; } } @@ -287,7 +577,12 @@ { pRequest->m_eResponseType = ResponseTypeXML; - UPnp::g_UPnpDeviceDesc.GetValidXML( pRequest->GetHostAddress(), pRequest->m_response ); + QString sUserAgent = pRequest->GetHeaderValue( "User-Agent", "" ); + + UPnp::g_UPnpDeviceDesc.GetValidXML( pRequest->GetHostAddress(), + m_nServicePort, + pRequest->m_response, + sUserAgent ); } ///////////////////////////////////////////////////////////////////////////// @@ -310,3 +605,76 @@ } } + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void SSDPExtension::GetDeviceList( HTTPRequest *pRequest ) +{ + SSDPCache &cache = UPnp::g_SSDPCache; + int nCount = 0; + NameValueList list; + + cache.Lock(); + + QString sXML; + QTextStream os( sXML, IO_WriteOnly ); + + for (SSDPCacheEntriesMap::Iterator it = cache.Begin(); + it != cache.End(); + ++it ) + { + SSDPCacheEntries *pEntries = (SSDPCacheEntries *)it.data(); + + if (pEntries != NULL) + { + os << "" << endl; + + pEntries->Lock(); + + EntryMap *pMap = pEntries->GetEntryMap(); + + for (EntryMap::Iterator itEntry = pMap->begin(); + itEntry != pMap->end(); + ++itEntry ) + { + + DeviceLocation *pEntry = (DeviceLocation *)itEntry.data(); + + if (pEntry != NULL) + { + nCount++; + + pEntry->AddRef(); + + os << "" << endl; + + pEntry->Release(); + } + } + + os << "" << endl; + + pEntries->Unlock(); + } + } + + list.append( new NameValue( "DeviceCount" , QString::number( cache.Count() ))); + list.append( new NameValue( "DevicesAllocated" , QString::number( SSDPCacheEntries::g_nAllocated ))); + + list.append( new NameValue( "CacheEntriesFound" , QString::number( nCount ))); + list.append( new NameValue( "CacheEntriesAllocated", QString::number( DeviceLocation::g_nAllocated ))); + + list.append( new NameValue( "DeviceList" , sXML)); + + cache.Unlock(); + + pRequest->FormatActionResponse( &list ); + + pRequest->m_eResponseType = ResponseTypeXML; + pRequest->m_nResponseStatus = 200; + +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdp.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdp.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/ssdp.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/ssdp.h 2007-08-23 11:09:51.000000000 -0500 @@ -15,72 +15,32 @@ #include #include -#include "mythcontext.h" - #include "httpserver.h" #include "taskqueue.h" -#define SSDP_GROUP "239.255.255.250" -#define SSDP_PORT 1900 +#include "ssdpcache.h" +#include "upnptasknotify.h" + +#define SSDP_GROUP "239.255.255.250" +#define SSDP_PORT 1900 +#define SSDP_SEARCHPORT 6549 typedef enum { SSDPM_Unknown = 0, SSDPM_GetDeviceDesc = 1, - SSDPM_GetCDSDesc = 2, - SSDPM_GetCMGRDesc = 3 + SSDPM_GetDeviceList = 2 } SSDPMethod; -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -// QMulticastSocket Class Definition/Implementation -// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -// -=>TODO: Need to add support for Multi-Homed machines. - -class QMulticastSocket : public QSocketDevice +typedef enum { - public: + SSDP_Unknown = 0, + SSDP_MSearch = 1, + SSDP_MSearchResp = 2, + SSDP_Notify = 3 - QHostAddress m_address; - Q_UINT16 m_port; - struct ip_mreq m_imr; - - public: - - QMulticastSocket( QString sAddress, Q_UINT16 nPort, u_char ttl = 0 ) - : QSocketDevice( QSocketDevice::Datagram ) - { - m_address.setAddress( sAddress ); - m_port = nPort; - - if (ttl == 0) - ttl = gContext->GetNumSetting( "upnpTTL", 4 ); - - m_imr.imr_multiaddr.s_addr = inet_addr( sAddress ); - m_imr.imr_interface.s_addr = htonl(INADDR_ANY); - - if ( setsockopt( socket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &m_imr, sizeof( m_imr )) < 0) - { - VERBOSE(VB_IMPORTANT, QString( "QMulticastSocket: setsockopt - IP_ADD_MEMBERSHIP Error" )); - } - - setsockopt( socket(), IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl) ); - - setAddressReusable( true ); - - bind( m_address, m_port ); - } - - virtual ~QMulticastSocket() - { - setsockopt( socket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &m_imr, sizeof(m_imr)); - } -}; +} SSDPRequestType; ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -90,27 +50,59 @@ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// +#define SocketIdx_Search 0 +#define SocketIdx_Multicast 1 +#define SocketIdx_Broadcast 2 + +#define NumberOfSockets (sizeof( m_Sockets ) / sizeof( QSocketDevice * )) + class SSDP : public QThread { private: - bool m_bTermRequested; - QMutex m_lock; + QSocketDevice *m_Sockets[3]; + + int m_nPort; + int m_nSearchPort; + int m_nServicePort; + + UPnpNotifyTask *m_pNotifyTask; + + bool m_bTermRequested; + QMutex m_lock; protected: - bool ProcessSearchRequest( HTTPRequest *pRequest, - QHostAddress peerAddress, - Q_UINT16 peerPort ); - bool IsTermRequested (); + bool ProcessSearchRequest ( const QStringMap &sHeaders, + QHostAddress peerAddress, + Q_UINT16 peerPort ); + bool ProcessSearchResponse( const QStringMap &sHeaders ); + bool ProcessNotify ( const QStringMap &sHeaders ); + + bool IsTermRequested (); + + QString GetHeaderValue ( const QStringMap &headers, + const QString &sKey, + const QString &sDefault ); + + void ProcessData ( QSocketDevice *pSocket ); + + SSDPRequestType ProcessRequestLine( const QString &sLine ); public: - SSDP (); + SSDP ( int nServicePort ); + ~SSDP (); + virtual void run (); void RequestTerminate(void); + void EnableNotifications (); + void DisableNotifications(); + + void PerformSearch( const QString &sST ); + }; ///////////////////////////////////////////////////////////////////////////// @@ -126,6 +118,7 @@ private: QString m_sUPnpDescPath; + int m_nServicePort; private: @@ -133,9 +126,10 @@ void GetDeviceDesc( HTTPRequest *pRequest ); void GetFile ( HTTPRequest *pRequest, QString sFileName ); + void GetDeviceList( HTTPRequest *pRequest ); public: - SSDPExtension( ); + SSDPExtension( int nServicePort ); virtual ~SSDPExtension( ); bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/taskqueue.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/taskqueue.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/taskqueue.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/taskqueue.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -1,231 +1,242 @@ -////////////////////////////////////////////////////////////////////////////// -// Program Name: taskqueue.cpp -// -// Purpose - Used to process delayed tasks -// -// Created By : David Blain Created On : Oct. 24, 2005 -// Modified By : Modified On: -// -////////////////////////////////////////////////////////////////////////////// - -#include "mythcontext.h" -#include "taskqueue.h" -#include "sys/time.h" -#include "qdatetime.h" - -///////////////////////////////////////////////////////////////////////////// -// Define Global instance -///////////////////////////////////////////////////////////////////////////// - -TaskQueue *g_pTaskQueue; - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -// Task Implementation -// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -long Task::m_nTaskCount = 0; - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -Task::Task() -{ - m_mutex.lock(); - m_nTaskId = m_nTaskCount++; - m_mutex.unlock(); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -Task::~Task() -{ -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -// Task Queue Implementation -// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -TaskQueue::TaskQueue() : m_bTermRequested( false ) -{ - -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -TaskQueue::~TaskQueue() -{ - Clear(); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -bool TaskQueue::IsTermRequested() -{ - m_mutex.lock(); - bool bTermRequested = m_bTermRequested; - m_mutex.unlock(); - - return( bTermRequested ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void TaskQueue::RequestTerminate( ) -{ - m_mutex.lock(); - m_bTermRequested = true; - m_mutex.unlock(); - - // Wait for thread to terminate. - - wait( 1000 ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void TaskQueue::run( ) -{ - Task *pTask; - - while ( !IsTermRequested() ) - { - // ------------------------------------------------------------------ - // Process Any Tasks that may need to be executed. - // ------------------------------------------------------------------ - - TaskTime ttNow; - gettimeofday( &ttNow, NULL ); - - if ((pTask = GetNextExpiredTask( ttNow )) != NULL) - { - pTask->Execute( this ); - pTask->Release(); - } - // Make sure to throttle our processing. - - msleep( 100 ); - } - -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void TaskQueue::Clear( ) -{ - m_mutex.lock(); - - for ( TaskMap::iterator it = m_mapTasks.begin(); - it != m_mapTasks.end(); - ++it ) - { - if ((*it).second != NULL) - (*it).second->Release(); - } - - m_mapTasks.clear(); - - m_mutex.unlock(); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void TaskQueue::AddTask( long msec, Task *pTask ) -{ - TaskTime tt; - gettimeofday( &tt, NULL ); - - AddMicroSecToTaskTime( tt, (msec * 1000) ); - - AddTask( tt, pTask ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void TaskQueue::AddTask( TaskTime ttKey, Task *pTask ) -{ - - if (pTask != NULL) - { - m_mutex.lock(); - pTask->AddRef(); - m_mapTasks.insert( TaskMap::value_type( ttKey, pTask )); - m_mutex.unlock(); - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void TaskQueue::AddTask( Task *pTask ) -{ - - if (pTask != NULL) - { - TaskTime tt; - gettimeofday( &tt, NULL ); - - AddTask( tt, pTask ); - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -Task *TaskQueue::GetNextExpiredTask( TaskTime tt, long nWithinMilliSecs /*=50*/ ) -{ - Task *pTask = NULL; - - AddMicroSecToTaskTime( tt, nWithinMilliSecs * 1000 ); - - m_mutex.lock(); - - TaskMap::iterator it = m_mapTasks.begin(); - - if (it != m_mapTasks.end()) - { - TaskTime ttTask = (*it).first; - - if (ttTask < tt) - { - // Do not release here... caller must call release. - - pTask = (*it).second; - - m_mapTasks.erase( it ); - } - } - m_mutex.unlock(); - - return pTask; -} +////////////////////////////////////////////////////////////////////////////// +// Program Name: taskqueue.cpp +// +// Purpose - Used to process delayed tasks +// +// Created By : David Blain Created On : Oct. 24, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "taskqueue.h" +#include +#include + +#include + +using std::cerr; + +///////////////////////////////////////////////////////////////////////////// +// Define Global instance +///////////////////////////////////////////////////////////////////////////// + +TaskQueue *g_pTaskQueue; + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// Task Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +long Task::m_nTaskCount = 0; + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +Task::Task() +{ + m_mutex.lock(); + m_nTaskId = m_nTaskCount++; + m_mutex.unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +Task::~Task() +{ +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// Task Queue Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +TaskQueue::TaskQueue() : m_bTermRequested( false ) +{ + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +TaskQueue::~TaskQueue() +{ + Clear(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool TaskQueue::IsTermRequested() +{ + m_mutex.lock(); + bool bTermRequested = m_bTermRequested; + m_mutex.unlock(); + + return( bTermRequested ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void TaskQueue::RequestTerminate( ) +{ + m_mutex.lock(); + m_bTermRequested = true; + m_mutex.unlock(); + + // Wait for thread to terminate. + + wait( 1000 ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void TaskQueue::run( ) +{ + Task *pTask; + + while ( !IsTermRequested() ) + { + // ------------------------------------------------------------------ + // Process Any Tasks that may need to be executed. + // ------------------------------------------------------------------ + + TaskTime ttNow; + gettimeofday( &ttNow, NULL ); + + if ((pTask = GetNextExpiredTask( ttNow )) != NULL) + { + try + { + pTask->Execute( this ); + pTask->Release(); + } + catch( ... ) + { + cerr << "TaskQueue::run - Call to Execute threw an exception."; + } + + } + // Make sure to throttle our processing. + + msleep( 100 ); + } + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void TaskQueue::Clear( ) +{ + m_mutex.lock(); + + for ( TaskMap::iterator it = m_mapTasks.begin(); + it != m_mapTasks.end(); + ++it ) + { + if ((*it).second != NULL) + (*it).second->Release(); + } + + m_mapTasks.clear(); + + m_mutex.unlock(); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void TaskQueue::AddTask( long msec, Task *pTask ) +{ + TaskTime tt; + gettimeofday( &tt, NULL ); + + AddMicroSecToTaskTime( tt, (msec * 1000) ); + + AddTask( tt, pTask ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void TaskQueue::AddTask( TaskTime ttKey, Task *pTask ) +{ + + if (pTask != NULL) + { + m_mutex.lock(); + pTask->AddRef(); + m_mapTasks.insert( TaskMap::value_type( ttKey, pTask )); + m_mutex.unlock(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void TaskQueue::AddTask( Task *pTask ) +{ + + if (pTask != NULL) + { + TaskTime tt; + gettimeofday( &tt, NULL ); + + AddTask( tt, pTask ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +Task *TaskQueue::GetNextExpiredTask( TaskTime tt, long nWithinMilliSecs /*=50*/ ) +{ + Task *pTask = NULL; + + AddMicroSecToTaskTime( tt, nWithinMilliSecs * 1000 ); + + m_mutex.lock(); + + TaskMap::iterator it = m_mapTasks.begin(); + + if (it != m_mapTasks.end()) + { + TaskTime ttTask = (*it).first; + + if (ttTask < tt) + { + // Do not release here... caller must call release. + + pTask = (*it).second; + + m_mapTasks.erase( it ); + } + } + m_mutex.unlock(); + + return pTask; +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/taskqueue.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/taskqueue.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/taskqueue.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/taskqueue.h 2007-08-23 11:09:51.000000000 -0500 @@ -20,7 +20,7 @@ #include #include -#include "upnpglobal.h" +#include "upnputil.h" #include "refcounted.h" class Task; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/threadpool.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/threadpool.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/threadpool.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/threadpool.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -9,6 +9,8 @@ ///////////////////////////////////////////////////////////////////////////// #include "threadpool.h" +#include "util.h" +#include "upnp.h" // only needed for Config... remove once config is moved. ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -116,6 +118,9 @@ m_bTermRequested = false; m_pThreadPool = pThreadPool; m_sName = sName; + m_nIdleTimeoutMS = 60000; + m_bAllowTimeout = false; + } ///////////////////////////////////////////////////////////////////////////// @@ -185,6 +190,23 @@ // ///////////////////////////////////////////////////////////////////////////// +void WorkerThread::SetTimeout( long nIdleTimeout ) +{ + // -=>NOTE: Not Thread safe... should only be called + // before thread is started. + + m_nIdleTimeoutMS = nIdleTimeout; + + if (m_nIdleTimeoutMS == -1 ) + m_bAllowTimeout = false; + else + m_bAllowTimeout = true; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + void WorkerThread::run( void ) { m_mutex.lock(); @@ -193,8 +215,15 @@ m_Initialized.SetEvent(); + MythTimer timer; + + timer.start(); + while( !IsTermRequested() ) { + if (m_bAllowTimeout && (timer.elapsed() > m_nIdleTimeoutMS) ) + break; + if (m_WorkAvailable.WaitForEvent(500)) { m_WorkAvailable.ResetEvent(); @@ -204,6 +233,8 @@ try { ProcessWork(); + + timer.restart(); } catch(...) { @@ -221,6 +252,8 @@ m_pThreadPool->ThreadTerminating( this ); m_pThreadPool = NULL; } + + VERBOSE( VB_UPNP, QString( "WorkerThread:Run - Exiting: %1" ).arg( m_sName )); } ///////////////////////////////////////////////////////////////////////////// @@ -242,8 +275,9 @@ m_lstThreads .setAutoDelete( false ); m_lstAvailableThreads.setAutoDelete( false ); - m_nInitialThreadCount = gContext->GetNumSetting( "ThreadCountInitial_" + m_sName, 1 ); - m_nMaxThreadCount = gContext->GetNumSetting( "ThreadCountMax_" + m_sName, 5 ); + m_nInitialThreadCount = UPnp::g_pConfig->GetValue( "ThreadPool/" + m_sName + "/Initial", 1 ); + m_nMaxThreadCount = UPnp::g_pConfig->GetValue( "ThreadPool/" + m_sName + "/Max" , 5 ); + m_nIdleTimeout = UPnp::g_pConfig->GetValue( "ThreadPool/" + m_sName + "/Timeout", 60000 ); m_nInitialThreadCount = min( m_nInitialThreadCount, m_nMaxThreadCount ); @@ -295,7 +329,7 @@ // -------------------------------------------------------------- for (long nIdx = 0; nIdx < m_nInitialThreadCount; nIdx ++ ) - AddWorkerThread( true ); + AddWorkerThread( true, -1 ); } @@ -335,7 +369,7 @@ // ---------------------------------------------------------- if ( nThreadCount < m_nMaxThreadCount) - pThread = AddWorkerThread( false ); + pThread = AddWorkerThread( false, m_nIdleTimeout ); else { if (m_threadAvail.wait( 5000 ) == false ) @@ -351,14 +385,17 @@ // ///////////////////////////////////////////////////////////////////////////// -WorkerThread *ThreadPool::AddWorkerThread( bool bMakeAvailable ) +WorkerThread *ThreadPool::AddWorkerThread( bool bMakeAvailable, long nTimeout ) { QString sName = m_sName + "_WorkerThread"; + VERBOSE( VB_UPNP, QString( "ThreadPool:AddWorkerThread - %1" ).arg( sName )); + WorkerThread *pThread = CreateWorkerThread( this, sName ); if (pThread != NULL) { + pThread->SetTimeout( nTimeout ); pThread->start(); if (pThread->WaitForInitialized( 5000 )) diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/threadpool.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/threadpool.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/threadpool.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/threadpool.h 2007-08-23 11:09:51.000000000 -0500 @@ -17,8 +17,6 @@ #include #include -#include "mythcontext.h" - class ThreadPool; ///////////////////////////////////////////////////////////////////////////// @@ -71,6 +69,10 @@ bool m_bTermRequested; QString m_sName; + long m_nIdleTimeoutMS; + bool m_bAllowTimeout; + + protected: virtual void run(); @@ -86,6 +88,7 @@ bool WaitForInitialized( unsigned long msecs ); void RequestTerminate (); void SignalWork (); + void SetTimeout ( long nIdleTimeout ); }; @@ -116,12 +119,13 @@ int m_nInitialThreadCount; int m_nMaxThreadCount; + long m_nIdleTimeout; protected: void InitializeThreads(); - WorkerThread *AddWorkerThread ( bool bMakeAvailable ); + WorkerThread *AddWorkerThread ( bool bMakeAvailable, long nTimeout ); void ThreadAvailable ( WorkerThread *pThread ); void ThreadTerminating( WorkerThread *pThread ); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpavcd.xml /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpavcd.xml --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpavcd.xml 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpavcd.xml 1969-12-31 18:00:00.000000000 -0600 @@ -1,31 +0,0 @@ - - - - 1 - 0 - - - - urn:schemas-upnp-org:device:MediaServer:1 - MythTV AV Media Server - - http://www.mythtv.org/ - uuid:a111a1b9-54f6-475a-bb06-57d83161c0b0 - - - urn:schemas-upnp-org:service:ConnectionManager:1 - urn:upnp-org:serviceId:CMGR_1-0 - getCMGRDesc - _CMGR_1-0_control - _CMGR_1-0_event - - - urn:schemas-upnp-org:service:ContentDirectory:1 - urn:upnp-org:serviceId:CDS_1-0 - getCDSDesc - _CDS_1-0_control - _CDS_1-0_event - - - - diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcds.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcds.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcds.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcds.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -8,7 +8,9 @@ // ////////////////////////////////////////////////////////////////////////////// +#include "upnp.h" #include "upnpcds.h" +#include "upnputil.h" #include "util.h" @@ -51,7 +53,7 @@ // ///////////////////////////////////////////////////////////////////////////// -UPnpCDS::UPnpCDS() : HttpServerExtension( "UPnpCDS" ) +UPnpCDS::UPnpCDS( UPnpDevice *pDevice ) : Eventing( "UPnpCDS", "CDS_Event" ) { m_extensions.setAutoDelete( true ); @@ -62,6 +64,22 @@ m_root.m_sClass = "object.container"; m_root.m_bRestricted= true; m_root.m_bSearchable= true; + + AddVariable( new StateVariable< QString >( "TransferIDs" , true ) ); + AddVariable( new StateVariable< QString >( "ContainerUpdateIDs", true ) ); + AddVariable( new StateVariable< unsigned short >( "SystemUpdateID" , true ) ); + + SetValue< unsigned short >( "SystemUpdateID", 1 ); + + QString sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath", m_sSharePath ); + + m_sServiceDescFileName = sUPnpDescPath + "CDS_scpd.xml"; + m_sControlUrl = "/CDS_Control"; + + + // Add our Service Definition to the device. + + RegisterService( pDevice ); } ///////////////////////////////////////////////////////////////////////////// @@ -78,11 +96,12 @@ UPnpCDSMethod UPnpCDS::GetMethod( const QString &sURI ) { - if (sURI == "Browse" ) return( CDSM_Browse ); - if (sURI == "Search" ) return( CDSM_Search ); - if (sURI == "GetSearchCapabilities" ) return( CDSM_GetSearchCapabilities); - if (sURI == "GetSortCapabilities" ) return( CDSM_GetSortCapabilities ); - if (sURI == "GetSystemUpdateID" ) return( CDSM_GetSystemUpdateID ); + if (sURI == "GetServDesc" ) return CDSM_GetServiceDescription; + if (sURI == "Browse" ) return CDSM_Browse ; + if (sURI == "Search" ) return CDSM_Search ; + if (sURI == "GetSearchCapabilities" ) return CDSM_GetSearchCapabilities; + if (sURI == "GetSortCapabilities" ) return CDSM_GetSortCapabilities ; + if (sURI == "GetSystemUpdateID" ) return CDSM_GetSystemUpdateID ; return( CDSM_Unknown ); } @@ -123,43 +142,37 @@ // ///////////////////////////////////////////////////////////////////////////// -bool UPnpCDS::ProcessRequest( HttpWorkerThread * /*pThread*/, HTTPRequest *pRequest ) +bool UPnpCDS::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) { if (pRequest) { - if ( pRequest->m_sBaseUrl != "/_CDS_1-0_control" ) + if (Eventing::ProcessRequest( pThread, pRequest )) + return true; + + if ( pRequest->m_sBaseUrl != m_sControlUrl ) + { +// VERBOSE( VB_UPNP, QString("UPnpCDS::ProcessRequest - BaseUrl (%1) not ours...").arg(pRequest->m_sBaseUrl )); return false; + } switch( GetMethod( pRequest->m_sMethod ) ) { - case CDSM_Browse : HandleBrowse ( pRequest ); break; - case CDSM_Search : HandleSearch ( pRequest ); break; - case CDSM_GetSearchCapabilities: HandleGetSearchCapabilities( pRequest ); break; - case CDSM_GetSortCapabilities : HandleGetSortCapabilities ( pRequest ); break; - case CDSM_GetSystemUpdateID : HandleGetSystemUpdateID ( pRequest ); break; + case CDSM_GetServiceDescription : pRequest->FormatFileResponse( m_sServiceDescFileName ); break; + case CDSM_Browse : HandleBrowse ( pRequest ); break; + case CDSM_Search : HandleSearch ( pRequest ); break; + case CDSM_GetSearchCapabilities : HandleGetSearchCapabilities ( pRequest ); break; + case CDSM_GetSortCapabilities : HandleGetSortCapabilities ( pRequest ); break; + case CDSM_GetSystemUpdateID : HandleGetSystemUpdateID ( pRequest ); break; default: - pRequest->FormatErrorReponse( 401, "Invalid Action" ); + UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); break; } - } - - return( true ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// + return true; + } -QString &UPnpCDS::Encode( QString &sStr ) -{ - sStr.replace(QRegExp( "&"), "&" ); // This _must_ come first - sStr.replace(QRegExp( "<"), "<" ); - sStr.replace(QRegExp( ">"), ">" ); -// sStr.replace(QRegExp("\""), """); -// sStr.replace(QRegExp( "'"), "'"); + return false; - return( sStr ); } ///////////////////////////////////////////////////////////////////////////// @@ -169,7 +182,7 @@ void UPnpCDS::HandleBrowse( HTTPRequest *pRequest ) { UPnpCDSExtensionResults *pResult = NULL; - UPnpCDSBrowseRequest request; + UPnpCDSRequest request; request.m_sObjectId = pRequest->m_mapParams[ "ObjectID" ]; request.m_sParentId = "0"; @@ -179,12 +192,32 @@ request.m_nRequestedCount = pRequest->m_mapParams[ "RequestedCount"].toLong(); request.m_sSortCriteria = pRequest->m_mapParams[ "SortCriteria" ]; - short nErrorCode = 701; - QString sErrorDesc = "No such object"; - short nNumberReturned = 0; - short nTotalMatches = 0; - short nUpdateID = 0; - QString sResultXML; +/* + VERBOSE(VB_UPNP,QString("UPnpCDS::ProcessRequest \n" + ": url = %1 \n" + ": Method = %2 \n" + ": ObjectId = %3 \n" + ": BrowseFlag = %4 \n" + ": Filter = %5 \n" + ": StartingIndex = %6 \n" + ": RequestedCount = %7 \n" + ": SortCriteria = %8 " ) + .arg( pRequest->m_sBaseUrl ) + .arg( pRequest->m_sMethod ) + .arg( request.m_sObjectId ) + .arg( request.m_eBrowseFlag ) + .arg( request.m_sFilter ) + .arg( request.m_nStartingIndex ) + .arg( request.m_nRequestedCount) + .arg( request.m_sSortCriteria )); +*/ + + UPnPResultCode eErrorCode = UPnPResult_CDS_NoSuchObject; + QString sErrorDesc = ""; + short nNumberReturned = 0; + short nTotalMatches = 0; + short nUpdateID = 0; + QString sResultXML; if (request.m_sObjectId == "0") { @@ -200,8 +233,7 @@ // Return Root Object Only // ---------------------------------------------------------------------- - nErrorCode = 0; - sErrorDesc = ""; + eErrorCode = UPnPResult_Success; nNumberReturned = 1; nTotalMatches = 1; nUpdateID = m_root.m_nUpdateId; @@ -219,37 +251,46 @@ // -=>TODO: Need to handle StartingIndex & RequestedCount - nErrorCode = 0; - sErrorDesc = ""; - nNumberReturned = m_extensions.count(); + eErrorCode = UPnPResult_Success; nTotalMatches = m_extensions.count(); nUpdateID = m_root.m_nUpdateId; - UPnpCDSExtension *pExtension = m_extensions.first(); + if (request.m_nRequestedCount == 0) + request.m_nRequestedCount = nTotalMatches; - request.m_sParentId = "0"; - request.m_eBrowseFlag = CDS_BrowseMetadata; - request.m_sFilter = ""; - request.m_nStartingIndex = 0; - request.m_nRequestedCount = 1; - request.m_sSortCriteria = ""; + short nStart = Max( request.m_nStartingIndex, short( 0 )); + short nCount = Min( nTotalMatches, request.m_nRequestedCount ); - while ( pExtension != NULL ) + UPnpCDSExtension *pExtension = m_extensions.at( nStart ); + UPnpCDSRequest childRequest; + + childRequest.m_sParentId = "0"; + childRequest.m_eBrowseFlag = CDS_BrowseMetadata; + childRequest.m_sFilter = ""; + childRequest.m_nStartingIndex = 0; + childRequest.m_nRequestedCount = 1; + childRequest.m_sSortCriteria = ""; + + while (( pExtension != NULL ) && (nNumberReturned < nCount )) { - request.m_sObjectId = pExtension->m_sExtensionId; + childRequest.m_sObjectId = pExtension->m_sExtensionId; - pResult = pExtension->Browse( &request ); + pResult = pExtension->Browse( &childRequest ); if (pResult != NULL) { - if (pResult->m_nErrorCode == 0) + if (pResult->m_eErrorCode == UPnPResult_Success) + { sResultXML += pResult->GetResultXML(); + nNumberReturned ++; + } delete pResult; } pExtension = m_extensions.next(); } + break; } default: break; @@ -275,10 +316,10 @@ if (pResult != NULL) { - nErrorCode = pResult->m_nErrorCode; + eErrorCode = pResult->m_eErrorCode; sErrorDesc = pResult->m_sErrorDesc; - if (nErrorCode == 0) + if (eErrorCode == UPnPResult_Success) { nNumberReturned = pResult->m_List.count(); nTotalMatches = pResult->m_nTotalMatches; @@ -294,7 +335,7 @@ // Output Results of Browse Method // ---------------------------------------------------------------------- - if (nErrorCode == 0) + if (eErrorCode == UPnPResult_Success) { NameValueList list; @@ -302,15 +343,15 @@ sResults += sResultXML; sResults += DIDL_LITE_END; - list.append( new NameValue( "Result" , Encode( sResults ))); + list.append( new NameValue( "Result" , sResults )); list.append( new NameValue( "NumberReturned", QString::number( nNumberReturned ))); list.append( new NameValue( "TotalMatches" , QString::number( nTotalMatches ))); list.append( new NameValue( "UpdateID" , QString::number( nUpdateID ))); - pRequest->FormatActionReponse( &list ); + pRequest->FormatActionResponse( &list ); } else - pRequest->FormatErrorReponse ( nErrorCode, sErrorDesc ); + UPnp::FormatErrorResponse ( pRequest, eErrorCode, sErrorDesc ); } @@ -320,9 +361,131 @@ void UPnpCDS::HandleSearch( HTTPRequest *pRequest ) { - // -=>TODO: Need to implement + UPnpCDSExtensionResults *pResult = NULL; + UPnpCDSRequest request; + + UPnPResultCode eErrorCode = UPnPResult_InvalidAction; + QString sErrorDesc = ""; + short nNumberReturned = 0; + short nTotalMatches = 0; + short nUpdateID = 0; + QString sResultXML; + + request.m_sObjectId = pRequest->m_mapParams[ "ContainerID" ]; + request.m_sFilter = pRequest->m_mapParams[ "Filter" ]; + request.m_nStartingIndex = pRequest->m_mapParams[ "StartingIndex" ].toLong(); + request.m_nRequestedCount = pRequest->m_mapParams[ "RequestedCount"].toLong(); + request.m_sSortCriteria = pRequest->m_mapParams[ "SortCriteria" ]; + request.m_sSearchCriteria = pRequest->m_mapParams[ "SearchCriteria"]; + + // ---------------------------------------------------------------------- + // Break the SearchCriteria into it's parts + // -=>TODO: This DOES NOT handle ('s or other complex expressions + // ---------------------------------------------------------------------- + + QRegExp rMatch( "\\b(or|and)\\b" ); + rMatch.setCaseSensitive( FALSE ); + + request.m_sSearchList = QStringList::split( rMatch, request.m_sSearchCriteria ); + request.m_sSearchClass = "object"; // Default to all objects. + + // ---------------------------------------------------------------------- + // -=>TODO: Need to process all expressions in searchCriteria... for now, + // Just focus on the "upnp:class derivedfrom" expression + // ---------------------------------------------------------------------- + + for ( QStringList::Iterator it = request.m_sSearchList.begin(); + it != request.m_sSearchList.end(); + ++it ) + { + if ( (*it).contains( "upnp:class derivedfrom", FALSE ) > 0) + { + QStringList sParts = QStringList::split( ' ', *it ); + + if (sParts.count() > 2) + { + request.m_sSearchClass = sParts[2].stripWhiteSpace(); + request.m_sSearchClass.remove( '"' ); + + break; + } + } + } + + // ---------------------------------------------------------------------- + +/* + VERBOSE(VB_UPNP,QString("UPnpCDS::ProcessRequest \n" + ": url = %1 \n" + ": Method = %2 \n" + ": ObjectId = %3 \n" + ": SearchCriteria = %4 \n" + ": Filter = %5 \n" + ": StartingIndex = %6 \n" + ": RequestedCount = %7 \n" + ": SortCriteria = %8 \n" + ": SearchClass = %9" ) + .arg( pRequest->m_sBaseUrl ) + .arg( pRequest->m_sMethod ) + .arg( request.m_sObjectId ) + .arg( request.m_sSearchCriteria) + .arg( request.m_sFilter ) + .arg( request.m_nStartingIndex ) + .arg( request.m_nRequestedCount) + .arg( request.m_sSortCriteria ) + .arg( request.m_sSearchClass )); +*/ + + UPnpCDSExtension *pExtension = m_extensions.first(); + + bool bSearchDone = false; + + while (( pExtension != NULL ) && ( !bSearchDone )) + { + + pResult = pExtension->Search( &request ); + + if (pResult != NULL) + { + eErrorCode = pResult->m_eErrorCode; + sErrorDesc = pResult->m_sErrorDesc; + + if (eErrorCode == UPnPResult_Success) + { + nNumberReturned = pResult->m_List.count(); + nTotalMatches = pResult->m_nTotalMatches; + nUpdateID = pResult->m_nUpdateID; + sResultXML = pResult->GetResultXML(); + bSearchDone = true; + } + + delete pResult; + } + + pExtension = m_extensions.next(); + + } + + // nUpdateID = 0; + //VERBOSE(VB_UPNP,sResultXML); + + if (eErrorCode == UPnPResult_Success) + { + NameValueList list; + QString sResults = DIDL_LITE_BEGIN; + sResults += sResultXML; + sResults += DIDL_LITE_END; + + list.append( new NameValue( "Result" , sResults )); + list.append( new NameValue( "NumberReturned", QString::number( nNumberReturned ))); + list.append( new NameValue( "TotalMatches" , QString::number( nTotalMatches ))); + list.append( new NameValue( "UpdateID" , QString::number( nUpdateID ))); + + pRequest->FormatActionResponse( &list ); + } + else + UPnp::FormatErrorResponse( pRequest, eErrorCode, sErrorDesc ); - pRequest->FormatErrorReponse( 401, "Invalid Action" ); } ///////////////////////////////////////////////////////////////////////////// @@ -333,11 +496,15 @@ { NameValueList list; + VERBOSE(VB_UPNP,QString("UPnpCDS::ProcessRequest : %1 : %2") + .arg(pRequest->m_sBaseUrl) + .arg(pRequest->m_sMethod)); + // -=>TODO: Need to implement based on CDS Extension Capabilities list.append( new NameValue( "SearchCaps", "dc:title,dc:creator,dc:date,upnp:class,res@size" )); - pRequest->FormatActionReponse( &list ); + pRequest->FormatActionResponse( &list ); } ///////////////////////////////////////////////////////////////////////////// @@ -348,11 +515,15 @@ { NameValueList list; + VERBOSE(VB_UPNP,QString("UPnpCDS::ProcessRequest : %1 : %2") + .arg(pRequest->m_sBaseUrl) + .arg(pRequest->m_sMethod)); + // -=>TODO: Need to implement based on CDS Extension Capabilities list.append( new NameValue( "SortCaps", "dc:title,dc:creator,dc:date,upnp:class,res@size" )); - pRequest->FormatActionReponse( &list ); + pRequest->FormatActionResponse( &list ); } ///////////////////////////////////////////////////////////////////////////// @@ -363,10 +534,643 @@ { NameValueList list; - // -=>TODO: Need to provide dynamic value + VERBOSE(VB_UPNP,QString("UPnpCDS::ProcessRequest : %1 : %2") + .arg(pRequest->m_sBaseUrl) + .arg(pRequest->m_sMethod)); + + unsigned short nId = GetValue< unsigned short >( "SystemUpdateID" ); + + list.append( new NameValue( "Id", QString::number( nId ) )); + + pRequest->FormatActionResponse( &list ); +} + + + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// UPnpCDSExtension Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::Browse( UPnpCDSRequest *pRequest ) +{ + + // -=>TODO: Need to add Filter & Sorting Support. + // -=>TODO: Need to add Sub-Folder/Category Support!!!!! + + if (! pRequest->m_sObjectId.startsWith( m_sExtensionId, true )) + return( NULL ); + + // ---------------------------------------------------------------------- + // Parse out request object's path + // ---------------------------------------------------------------------- + + QStringList idPath = QStringList::split( "/", pRequest->m_sObjectId.section('=',0,0) ); + + QString key = pRequest->m_sObjectId.section('=',1); + + if (idPath.count() == 0) + return( NULL ); + + // ---------------------------------------------------------------------- + // Process based on location in hierarchy + // ---------------------------------------------------------------------- + + UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults(); + + if (pResults != NULL) + { + if (key) + idPath.last().append(QString("=%1").arg(key)); + + QString sLast = idPath.last(); + + pRequest->m_sParentId = pRequest->m_sObjectId; + + if (sLast == m_sExtensionId ) { return( ProcessRoot ( pRequest, pResults, idPath )); } + if (sLast == "0" ) { return( ProcessAll ( pRequest, pResults, idPath )); } + if (sLast.startsWith( "key" , true )) { return( ProcessKey ( pRequest, pResults, idPath )); } + if (sLast.startsWith( "item", true )) { return( ProcessItem ( pRequest, pResults, idPath )); } + + int nNodeIdx = sLast.toInt(); + + if ((nNodeIdx > 0) && (nNodeIdx < GetRootCount())) + return( ProcessContainer( pRequest, pResults, nNodeIdx, idPath )); + + pResults->m_eErrorCode = UPnPResult_CDS_NoSuchObject; + pResults->m_sErrorDesc = ""; + } + + return( pResults ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::Search( UPnpCDSRequest *pRequest ) +{ + // -=>TODO: Need to add Filter & Sorting Support. + // -=>TODO: Need to add Sub-Folder/Category Support!!!!! + + QStringList sEmptyList; + + if ( !m_sClass.startsWith( pRequest->m_sSearchClass )) + return NULL; + + UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults(); + + CreateItems( pRequest, pResults, 0, "", false ); + + return pResults; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString UPnpCDSExtension::RemoveToken( const QString &sToken, const QString &sStr, int num ) +{ + QString sResult( "" ); + int nPos = -1; + + for (int nIdx=0; nIdx < num; nIdx++) + { + if ((nPos = sStr.findRev( sToken, nPos )) == -1) + break; + } + + if (nPos > 0) + sResult = sStr.left( nPos ); + + return sResult; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::ProcessRoot( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &/*idPath*/ ) +{ + pResults->m_nTotalMatches = 0; + pResults->m_nUpdateID = 1; + + short nRootCount = GetRootCount(); + + switch( pRequest->m_eBrowseFlag ) + { + case CDS_BrowseMetadata: + { + // -------------------------------------------------------------- + // Return Root Object Only + // -------------------------------------------------------------- + + pResults->m_nTotalMatches = 1; + pResults->m_nUpdateID = 1; + + CDSObject *pRoot = CreateContainer( m_sExtensionId, m_sName, "0"); + + pRoot->SetChildCount( nRootCount ); + + pResults->Add( pRoot ); + + break; + } + + case CDS_BrowseDirectChildren: + { + pResults->m_nUpdateID = 1; + pResults->m_nTotalMatches = nRootCount ; + + if ( pRequest->m_nRequestedCount == 0) + pRequest->m_nRequestedCount = nRootCount ; + + short nStart = Max( pRequest->m_nStartingIndex, short( 0 )); + short nEnd = Min( nRootCount, short( nStart + pRequest->m_nRequestedCount)); + + if (nStart < nRootCount) + { + for (short nIdx = nStart; nIdx < nEnd; nIdx++) + { + UPnpCDSRootInfo *pInfo = GetRootInfo( nIdx ); + + if (pInfo != NULL) + { + + QString sId = QString( "%1/%2" ).arg( pRequest->m_sObjectId ) + .arg( nIdx ); + + CDSObject *pItem = CreateContainer( sId, + QObject::tr( pInfo->title ), + m_sExtensionId ); - list.append( new NameValue( "Id", "1" )); + pItem->SetChildCount( GetDistinctCount( pInfo->column ) ); - pRequest->FormatActionReponse( &list ); + pResults->Add( pItem ); + } + } + } + } + + case CDS_BrowseUnknown: + default: + break; + } + + return pResults; } + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::ProcessAll ( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &/*idPath*/ ) +{ + pResults->m_nTotalMatches = 0; + pResults->m_nUpdateID = 1; + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + switch( pRequest->m_eBrowseFlag ) + { + case CDS_BrowseMetadata: + { + // -------------------------------------------------------------- + // Return Container Object Only + // -------------------------------------------------------------- + + UPnpCDSRootInfo *pInfo = GetRootInfo( 0 ); + + if (pInfo != NULL) + { + pResults->m_nTotalMatches = 1; + pResults->m_nUpdateID = 1; + + CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, + QObject::tr( pInfo->title ), + m_sExtensionId ); + + pItem->SetChildCount( GetDistinctCount( pInfo->column ) ); + + pResults->Add( pItem ); + } + + break; + } + + case CDS_BrowseDirectChildren: + { + + CreateItems( pRequest, pResults, 0, "", false ); + + break; + } + + case CDS_BrowseUnknown: + default: + break; + + } + + + return pResults; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::ProcessItem( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &idPath ) +{ + pResults->m_nTotalMatches = 0; + pResults->m_nUpdateID = 1; + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + if ( pRequest->m_eBrowseFlag == CDS_BrowseMetadata ) + { + // -------------------------------------------------------------- + // Return 1 Item + // -------------------------------------------------------------- + + QStringMap mapParams; + QString sParams = idPath.last().section( '?', 1, 1 ); + + sParams.replace(QRegExp( "&"), "&" ); + + HTTPRequest::GetParameters( sParams, mapParams ); + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + BuildItemQuery( query, mapParams ); + + query.exec(); + + if (query.isActive() && query.size() > 0) + { + if ( query.next() ) + { + pRequest->m_sObjectId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); + + AddItem( pRequest->m_sObjectId, pResults, false, query ); + pResults->m_nTotalMatches = 1; + } + } + } + } + + return pResults; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::ProcessKey( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &idPath ) +{ + pResults->m_nTotalMatches = 0; + pResults->m_nUpdateID = 1; + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + QString sKey = idPath.last().section( '=', 1, 1 ); + QUrl::decode( sKey ); + + if (sKey.length() > 0) + { + int nNodeIdx = idPath[ idPath.count() - 2 ].toInt(); + + switch( pRequest->m_eBrowseFlag ) + { + + case CDS_BrowseMetadata: + { + UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx ); + + if (pInfo == NULL) + return pResults; + + pRequest->m_sParentId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); + + // -------------------------------------------------------------- + // Since Key is not always the title, we need to lookup title. + // -------------------------------------------------------------- + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + QString sSQL = QString( pInfo->sql ) + .arg( pInfo->where ); + + // -=>TODO: There is a problem when called for an Item, instead of a container + // sKey = '/item?ChanId' which is incorrect. + + + query.prepare ( sSQL ); + query.bindValue( ":KEY", sKey ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + if ( query.next() ) + { + // ---------------------------------------------- + // Return Container Object Only + // ---------------------------------------------- + + pResults->m_nTotalMatches = 1; + pResults->m_nUpdateID = 1; + + CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, + query.value(1).toString(), + pRequest->m_sParentId ); + + pItem->SetChildCount( GetDistinctCount( pInfo->column )); + + pResults->Add( pItem ); + } + } + } + break; + } + + case CDS_BrowseDirectChildren: + { + CreateItems( pRequest, pResults, nNodeIdx, sKey, true ); + + break; + } + + case CDS_BrowseUnknown: + default: + break; + } + } + + return pResults; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSExtensionResults *UPnpCDSExtension::ProcessContainer( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + int nNodeIdx, + QStringList &/*idPath*/ ) + +{ + pResults->m_nUpdateID = 1; + pResults->m_nTotalMatches = 0; + + UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx ); + + if (pInfo == NULL) + return pResults; + + switch( pRequest->m_eBrowseFlag ) + { + case CDS_BrowseMetadata: + { + // -------------------------------------------------------------- + // Return Container Object Only + // -------------------------------------------------------------- + + pResults->m_nTotalMatches = 1; + pResults->m_nUpdateID = 1; + + CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, + QObject::tr( pInfo->title ), + m_sExtensionId ); + + pItem->SetChildCount( GetDistinctCount( pInfo->column )); + + pResults->Add( pItem ); + + break; + } + + case CDS_BrowseDirectChildren: + { + pResults->m_nTotalMatches = GetDistinctCount( pInfo->column ); + pResults->m_nUpdateID = 1; + + if (pRequest->m_nRequestedCount == 0) + pRequest->m_nRequestedCount = SHRT_MAX; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + // Remove where clause placeholder. + + QString sSQL = pInfo->sql; + + sSQL.replace( "%1", "" ); + + sSQL += QString( " LIMIT %2, %3" ) + .arg( pRequest->m_nStartingIndex ) + .arg( pRequest->m_nRequestedCount ); + + query.prepare( sSQL ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + + while(query.next()) + { + QString sKey = query.value(0).toString(); + QString sTitle = query.value(1).toString(); + long nCount = query.value(2).toInt(); + + if (sTitle.length() == 0) + sTitle = "(undefined)"; + + QString sId = QString( "%1/key=%2" ) + .arg( pRequest->m_sParentId ) + .arg( sKey ); + + CDSObject *pRoot = CreateContainer( sId, sTitle, pRequest->m_sParentId ); + + pRoot->SetChildCount( nCount ); + + pResults->Add( pRoot ); + } + } + } + + break; + } + + case CDS_BrowseUnknown: + break; + + } + + return pResults; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int UPnpCDSExtension::GetDistinctCount( const QString &sColumn ) +{ + int nCount = 0; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + // Note: Tried to use Bind, however it would not allow me to use it + // for column & table names + + QString sSQL; + + if (sColumn == "*") + { + sSQL = QString( "SELECT count( %1 ) FROM %2" ) + .arg( sColumn ) + .arg( GetTableName( sColumn )); + } + else + { + sSQL = QString( "SELECT count( DISTINCT %1 ) FROM %2" ) + .arg( sColumn ) + .arg( GetTableName( sColumn ) ); + } + + query.prepare( sSQL ); + query.exec(); + + if (query.size() > 0) + { + query.next(); + + nCount = query.value(0).toInt(); + } + } + + return( nCount ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int UPnpCDSExtension::GetCount( const QString &sColumn, const QString &sKey ) +{ + int nCount = 0; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + // Note: Tried to use Bind, however it would not allow me to use it + // for column & table names + + QString sSQL; + + if (sColumn == "*") + sSQL = QString( "SELECT count( * ) FROM %1" ).arg( GetTableName( sColumn ) ); + else + sSQL = QString( "SELECT count( %1 ) FROM %2 WHERE %3=:KEY" ) + .arg( sColumn ) + .arg( GetTableName( sColumn ) ) + .arg( sColumn ); + + query.prepare( sSQL ); + query.bindValue( ":KEY", sKey ); + query.exec(); + + if (query.size() > 0) + { + query.next(); + + nCount = query.value(0).toInt(); + } + + } + + return( nCount ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCDSExtension::CreateItems( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + int nNodeIdx, + const QString &sKey, + bool bAddRef ) +{ + pResults->m_nTotalMatches = 0; + pResults->m_nUpdateID = 1; + + UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx ); + + if (pInfo == NULL) + return; + + pResults->m_nTotalMatches = GetCount( pInfo->column, sKey ); + pResults->m_nUpdateID = 1; + + if (pRequest->m_nRequestedCount == 0) + pRequest->m_nRequestedCount = SHRT_MAX; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + QString sWhere( "" ); + + if ( sKey.length() > 0) + { + sWhere = QString( "WHERE %1=:KEY " ) + .arg( pInfo->column ); + } + + QString sSQL = QString( "%1 %2 LIMIT %3, %4" ) + .arg( GetItemListSQL( pInfo->column ) ) + .arg( sWhere ) + .arg( pRequest->m_nStartingIndex ) + .arg( pRequest->m_nRequestedCount ); + + query.prepare ( sSQL ); + query.bindValue(":KEY", sKey ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + while(query.next()) + AddItem( pRequest->m_sObjectId, pResults, bAddRef, query ); + } + } +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcds.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcds.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcds.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcds.h 2007-08-23 11:09:51.000000000 -0500 @@ -14,20 +14,22 @@ #include #include -#include "httpserver.h" -#include "mythcontext.h" +#include "upnp.h" #include "upnpcdsobjects.h" - +#include "eventing.h" +#include "mythdbcon.h" + class UPnpCDS; - + typedef enum { CDSM_Unknown = 0, - CDSM_Browse = 1, - CDSM_Search = 2, - CDSM_GetSearchCapabilities = 3, - CDSM_GetSortCapabilities = 4, - CDSM_GetSystemUpdateID = 5 + CDSM_GetServiceDescription = 1, + CDSM_Browse = 2, + CDSM_Search = 3, + CDSM_GetSearchCapabilities = 4, + CDSM_GetSortCapabilities = 5, + CDSM_GetSystemUpdateID = 6 } UPnpCDSMethod; @@ -41,44 +43,33 @@ ////////////////////////////////////////////////////////////////////////////// -class UPnpCDSBrowseRequest +class UPnpCDSRequest { public: - QString m_sParentId; QString m_sObjectId; - UPnpCDSBrowseFlag m_eBrowseFlag; + QString m_sFilter; short m_nStartingIndex; short m_nRequestedCount; QString m_sSortCriteria; - public: - - UPnpCDSBrowseRequest() : m_eBrowseFlag ( CDS_BrowseUnknown ), - m_nStartingIndex ( 0 ), - m_nRequestedCount( 0 ) - { - } -}; + // Browse specific properties -////////////////////////////////////////////////////////////////////////////// + QString m_sParentId; + UPnpCDSBrowseFlag m_eBrowseFlag; -class UPnpCDSSearchRequest -{ - public: + // Search specific properties - QString m_sContainerID; - QString m_sSearchCriteria; - QString m_sFilter; - short m_nStartingIndex; - short m_nRequestedCount; - QString m_sSortCriteria; + QString m_sSearchCriteria; + QStringList m_sSearchList; + QString m_sSearchClass; public: - UPnpCDSSearchRequest() : m_nStartingIndex ( 0 ), - m_nRequestedCount( 0 ) + UPnpCDSRequest() : m_nStartingIndex ( 0 ), + m_nRequestedCount( 0 ), + m_eBrowseFlag( CDS_BrowseUnknown ) { } }; @@ -90,7 +81,7 @@ public: CDSObjects m_List; - short m_nErrorCode; + UPnPResultCode m_eErrorCode; QString m_sErrorDesc; short m_nTotalMatches; @@ -98,7 +89,7 @@ public: - UPnpCDSExtensionResults() : m_nErrorCode(0), + UPnpCDSExtensionResults() : m_eErrorCode( UPnPResult_Success ), m_nTotalMatches(0), m_nUpdateID(0) { @@ -111,25 +102,89 @@ ////////////////////////////////////////////////////////////////////////////// +typedef struct +{ + char *title; + char *column; + char *sql; + char *where; + +} UPnpCDSRootInfo; + class UPnpCDSExtension { public: QString m_sExtensionId; QString m_sName; + QString m_sClass; + + protected: + + int GetDistinctCount ( const QString &sColumn ); + int GetCount ( const QString &sColumn, const QString &sKey ); + + QString RemoveToken ( const QString &sToken, const QString &sStr, int num ); + + UPnpCDSExtensionResults *ProcessRoot ( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &idPath ); + UPnpCDSExtensionResults *ProcessAll ( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &idPath ); + UPnpCDSExtensionResults *ProcessItem ( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &idPath ); + UPnpCDSExtensionResults *ProcessKey ( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + QStringList &idPath ); + UPnpCDSExtensionResults *ProcessContainer( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + int nNodeIdx, + QStringList &idPath ); + + void CreateItems ( UPnpCDSRequest *pRequest, + UPnpCDSExtensionResults *pResults, + int nNodeIdx, + const QString &sKey, + bool bAddRef ); + + // ------------------------------------------------------------------ + + virtual UPnpCDSRootInfo *GetRootInfo ( int nIdx) = 0; + virtual int GetRootCount ( ) = 0; + virtual QString GetTableName ( QString sColumn ) = 0; + virtual QString GetItemListSQL( QString sColumn = "" ) = 0; + virtual void BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams ) = 0; + + virtual void AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ) = 0; + + virtual CDSObject *CreateContainer( const QString &sId, + const QString &sTitle, + const QString &sParentId ) + { + return CDSObject::CreateContainer( sId, sTitle, sParentId ); + } + public: - UPnpCDSExtension( QString sName, QString sExtensionId ) + UPnpCDSExtension( QString sName, + QString sExtensionId, + QString sClass ) { m_sName = QObject::tr( sName ); m_sExtensionId = sExtensionId; + m_sClass = sClass; } virtual ~UPnpCDSExtension() {} - virtual UPnpCDSExtensionResults *Browse( UPnpCDSBrowseRequest *pRequest ) = 0; - virtual UPnpCDSExtensionResults *Search( UPnpCDSSearchRequest *pRequest ) = 0; + virtual UPnpCDSExtensionResults *Browse( UPnpCDSRequest *pRequest ); + virtual UPnpCDSExtensionResults *Search( UPnpCDSRequest *pRequest ); virtual QString GetSearchCapabilities() { return( "" ); } virtual QString GetSortCapabilities () { return( "" ); } @@ -145,15 +200,16 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -class UPnpCDS : public HttpServerExtension +class UPnpCDS : public Eventing { private: - short m_nSystemUpdateId; - UPnpCDSExtensionList m_extensions; CDSObject m_root; + QString m_sServiceDescFileName; + QString m_sControlUrl; + private: UPnpCDSMethod GetMethod ( const QString &sURI ); @@ -165,16 +221,23 @@ void HandleGetSortCapabilities ( HTTPRequest *pRequest ); void HandleGetSystemUpdateID ( HTTPRequest *pRequest ); - QString &Encode ( QString &sStr ); + protected: + + // Implement UPnpServiceImpl methods that we can + + virtual QString GetServiceType () { return "urn:schemas-upnp-org:service:ContentDirectory:1"; } + virtual QString GetServiceId () { return "urn:upnp-org:serviceId:CDS_1-0"; } + virtual QString GetServiceControlURL() { return m_sControlUrl.mid( 1 ); } + virtual QString GetServiceDescURL () { return m_sControlUrl.mid( 1 ) + "/GetServDesc"; } public: - UPnpCDS(); + UPnpCDS( UPnpDevice *pDevice ); virtual ~UPnpCDS(); void RegisterExtension ( UPnpCDSExtension *pExtension ); void UnregisterExtension( UPnpCDSExtension *pExtension ); - bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); + virtual bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); }; #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcdsobjects.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcdsobjects.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcdsobjects.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcdsobjects.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -422,6 +422,13 @@ pObject->AddProperty( new Property( "language" , "dc" )); pObject->AddProperty( new Property( "relation" , "dc" )); + // Added for Microsoft Media Player Compatibility + + pObject->AddProperty( new Property( "creator" , "dc" )); + pObject->AddProperty( new Property( "artist" , "upnp" )); + pObject->AddProperty( new Property( "album" , "upnp" )); + pObject->AddProperty( new Property( "date" , "dc" )); + return( pObject ); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcmgr.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcmgr.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcmgr.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcmgr.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,185 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: UPnpCMGR.cpp +// +// Purpose - uPnp Connection Manager Service +// +// Created By : David Blain Created On : Dec. 28, 2006 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "upnp.h" +#include "upnpcmgr.h" + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCMGR::UPnpCMGR ( UPnpDevice *pDevice, + const QString &sSourceProtocols, + const QString &sSinkProtocols ) + : Eventing( "UPnpCMGR", "CMGR_Event" ) +{ + AddVariable( new StateVariable< QString >( "SourceProtocolInfo" , true ) ); + AddVariable( new StateVariable< QString >( "SinkProtocolInfo" , true ) ); + AddVariable( new StateVariable< QString >( "CurrentConnectionIDs", true ) ); + + SetValue< QString >( "CurrentConnectionIDs", "0" ); + SetValue< QString >( "SourceProtocolInfo" , sSourceProtocols ); + SetValue< QString >( "SinkProtocolInfo" , sSinkProtocols ); + + QString sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath", m_sSharePath ); + + m_sServiceDescFileName = sUPnpDescPath + "CMGR_scpd.xml"; + m_sControlUrl = "/CMGR_Control"; + + // Add our Service Definition to the device. + + RegisterService( pDevice ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCMGR::~UPnpCMGR() +{ +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCMGR::AddSourceProtocol( const QString &sProtocol ) +{ + QString sValue = GetValue< QString >( "SourceProtocolInfo" ); + + if (sValue.length() > 0 ) + sValue += ","; + + sValue += sProtocol; + + SetValue< QString >( "SourceProtocolInfo", sValue ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCMGR::AddSinkProtocol( const QString &sProtocol ) +{ + QString sValue = GetValue< QString >( "SinkProtocolInfo" ); + + if (sValue.length() > 0 ) + sValue += ","; + + sValue += sProtocol; + + SetValue< QString >( "SinkProtocolInfo", sValue ); +} + + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCMGRMethod UPnpCMGR::GetMethod( const QString &sURI ) +{ + if (sURI == "GetServDesc" ) return CMGRM_GetServiceDescription ; + if (sURI == "GetProtocolInfo" ) return CMGRM_GetProtocolInfo ; + if (sURI == "GetCurrentConnectionInfo" ) return CMGRM_GetCurrentConnectionInfo; + if (sURI == "GetCurrentConnectionIDs" ) return CMGRM_GetCurrentConnectionIDs ; + + return CMGRM_Unknown; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool UPnpCMGR::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) +{ + if (pRequest) + { + if (Eventing::ProcessRequest( pThread, pRequest )) + return true; + + if ( pRequest->m_sBaseUrl != m_sControlUrl ) + { +// VERBOSE( VB_UPNP, QString("UPnpCMGR::ProcessRequest - BaseUrl (%1) not ours...").arg(pRequest->m_sBaseUrl )); + return false; + } + + VERBOSE( VB_UPNP, QString("UPnpCMGR::ProcessRequest - Method (%1)").arg(pRequest->m_sMethod )); + + switch( GetMethod( pRequest->m_sMethod ) ) + { + case CMGRM_GetServiceDescription : pRequest->FormatFileResponse ( m_sServiceDescFileName ); break; + case CMGRM_GetProtocolInfo : HandleGetProtocolInfo ( pRequest ); break; + case CMGRM_GetCurrentConnectionInfo: HandleGetCurrentConnectionInfo( pRequest ); break; + case CMGRM_GetCurrentConnectionIDs : HandleGetCurrentConnectionIDs ( pRequest ); break; + default: + UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); + break; + } + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCMGR::HandleGetProtocolInfo( HTTPRequest *pRequest ) +{ + NameValueList list; + + list.append( new NameValue( "Source", GetValue< QString >( "SourceProtocolInfo"))); + list.append( new NameValue( "Sink" , GetValue< QString >( "SinkProtocolInfo" ))); + + pRequest->FormatActionResponse( &list ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCMGR::HandleGetCurrentConnectionInfo( HTTPRequest *pRequest ) +{ + unsigned short nId = pRequest->m_mapParams[ "ConnectionID" ].toUShort(); + + if ( nId != 0) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_CMGR_InvalidConnectionRef ); + return; + } + + NameValueList list; + + list.append( new NameValue( "RcsID" , "-1" )); + list.append( new NameValue( "AVTransportID" , "-1" )); + list.append( new NameValue( "ProtocolInfo" , "http-get:*:*:*" )); + list.append( new NameValue( "PeerConnectionManager", "/" )); + list.append( new NameValue( "PeerConnectionID" , "-1" )); + list.append( new NameValue( "Direction" , "Output" )); + list.append( new NameValue( "Status" , "Unknown" )); + + pRequest->FormatActionResponse( &list ); + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCMGR::HandleGetCurrentConnectionIDs ( HTTPRequest *pRequest ) +{ + NameValueList list; + + list.append( new NameValue( "ConnectionIDs", GetValue< QString >( "CurrentConnectionIDs" ))); + + pRequest->FormatActionResponse( &list ); + +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcmgr.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcmgr.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpcmgr.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpcmgr.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,82 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: UPnpCMGR.h +// +// Purpose - +// +// Created By : David Blain Created On : Dec. 28, 2006 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef UPnpCMGR_H_ +#define UPnpCMGR_H_ + +#include "httpserver.h" +#include "eventing.h" + +typedef enum +{ + CMGRM_Unknown = 0, + CMGRM_GetServiceDescription = 1, + CMGRM_GetProtocolInfo = 2, + CMGRM_GetCurrentConnectionInfo = 3, + CMGRM_GetCurrentConnectionIDs = 4 + +} UPnpCMGRMethod; + +////////////////////////////////////////////////////////////////////////////// + +typedef enum +{ + CMGRSTATUS_Unknown = 0, + CMGRSTATUS_OK = 1, + CMGRSTATUS_ContentFormatMismatch = 2, + CMGRSTATUS_InsufficientBandwidth = 3, + CMGRSTATUS_UnreliableChannel = 4 + +} UPnpCMGRConnectionStatus; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// UPnpCMGR Class Definition +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class UPnpCMGR : public Eventing +{ + private: + + QString m_sServiceDescFileName; + QString m_sControlUrl; + + UPnpCMGRMethod GetMethod ( const QString &sURI ); + + void HandleGetProtocolInfo ( HTTPRequest *pRequest ); + void HandleGetCurrentConnectionInfo( HTTPRequest *pRequest ); + void HandleGetCurrentConnectionIDs ( HTTPRequest *pRequest ); + + protected: + + // Implement UPnpServiceImpl methods that we can + + virtual QString GetServiceType () { return "urn:schemas-upnp-org:service:ConnectionManager:1"; } + virtual QString GetServiceId () { return "urn:upnp-org:serviceId:CMGR_1-0"; } + virtual QString GetServiceControlURL() { return m_sControlUrl.mid( 1 ); } + virtual QString GetServiceDescURL () { return m_sControlUrl.mid( 1 ) + "/GetServDesc"; } + + public: + UPnpCMGR( UPnpDevice *pDevice, + const QString &sSourceProtocols = "", + const QString &sSinkProtocols = "" ); + + virtual ~UPnpCMGR(); + + void AddSourceProtocol( const QString &sProtocol ); + void AddSinkProtocol ( const QString &sProtocol ); + + virtual bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnp.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnp.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnp.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnp.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -9,27 +9,21 @@ ////////////////////////////////////////////////////////////////////////////// #include "upnp.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "upnpcds.h" +#include "upnptaskcache.h" +#include "multicast.h" +#include "broadcast.h" ////////////////////////////////////////////////////////////////////////////// // Global/Class Static variables ////////////////////////////////////////////////////////////////////////////// -QString UPnp::g_sPlatform; UPnpDeviceDesc UPnp::g_UPnpDeviceDesc; -TaskQueue *UPnp::g_pTaskQueue; -SSDP *UPnp::g_pSSDP; +TaskQueue *UPnp::g_pTaskQueue = NULL; +SSDP *UPnp::g_pSSDP = NULL; +SSDPCache UPnp::g_SSDPCache; +QStringList UPnp::g_IPAddrList; + +Configuration *UPnp::g_pConfig = NULL; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -43,78 +37,115 @@ // ////////////////////////////////////////////////////////////////////////////// -UPnp::UPnp( bool /*bIsMaster */, HttpServer *pHttpServer ) +UPnp::UPnp() { - VERBOSE(VB_UPNP, QString("UPnp::UPnp:Begin")); + VERBOSE( VB_UPNP, "UPnp - Constructor" ); +} - if ((m_pHttpServer = pHttpServer) == NULL) - { - VERBOSE(VB_IMPORTANT, QString( "UPnp::UPnp:Invalid Parameter (pHttpServer == NULL)" )); - return; - } +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// - // ---------------------------------------------------------------------- - // Build Platform String - // ---------------------------------------------------------------------- +UPnp::~UPnp() +{ + VERBOSE( VB_UPNP, "UPnp - Destructor" ); + CleanUp(); +} - struct utsname uname_info; +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// - uname( &uname_info ); +void UPnp::SetConfiguration( Configuration *pConfig ) +{ + if (g_pConfig) + delete g_pConfig; - g_sPlatform = QString( "%1 %2" ).arg( uname_info.sysname ) - .arg( uname_info.release ); + g_pConfig = pConfig; +} - // ---------------------------------------------------------------------- - // Initialize & Start the global Task Queue Processing Thread - // ---------------------------------------------------------------------- +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool UPnp::Initialize( int nServicePort, HttpServer *pHttpServer ) +{ + QStringList sList; - VERBOSE(VB_UPNP, QString( "UPnp::UPnp:Starting TaskQueue" )); + GetIPAddressList( sList ); - g_pTaskQueue = new TaskQueue(); - g_pTaskQueue->start(); + return Initialize( sList, nServicePort, pHttpServer ); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +bool UPnp::Initialize( QStringList &sIPAddrList, int nServicePort, HttpServer *pHttpServer ) +{ + VERBOSE(VB_UPNP, QString("UPnp::Initialize - Begin")); + + if (g_pConfig == NULL) + { + VERBOSE(VB_IMPORTANT, QString( "UPnp::Initialize - Must call SetConfiguration." )); + return false; + } + + if ((m_pHttpServer = pHttpServer) == NULL) + { + VERBOSE(VB_IMPORTANT, QString( "UPnp::Initialize - Invalid Parameter (pHttpServer == NULL)" )); + return false; + } + + g_IPAddrList = sIPAddrList; + m_nServicePort = nServicePort; // ---------------------------------------------------------------------- - // Make sure our device Description is loaded. + // Initialize & Start the global Task Queue Processing Thread // ---------------------------------------------------------------------- - VERBOSE(VB_UPNP, QString( "UPnp::UPnp:Loading UPnp Description" )); + VERBOSE(VB_UPNP, QString( "UPnp::Initialize - Starting TaskQueue" )); - g_UPnpDeviceDesc.Load(); + g_pTaskQueue = new TaskQueue(); + g_pTaskQueue->start(); // ---------------------------------------------------------------------- // Register any HttpServerExtensions // ---------------------------------------------------------------------- - m_pHttpServer->RegisterExtension( new SSDPExtension()); - m_pHttpServer->RegisterExtension( m_pUPnpCDS = new UPnpCDS ()); + m_pHttpServer->RegisterExtension( new SSDPExtension( m_nServicePort )); // ---------------------------------------------------------------------- - // Start up the SSDP (Upnp Discovery) Thread. + // Add Task to keep SSDPCache purged of stale entries. // ---------------------------------------------------------------------- - VERBOSE(VB_UPNP, QString( "UPnp::UPnp:Starting SSDP Thread" )); - - g_pSSDP = new SSDP(); - g_pSSDP->start(); + UPnp::g_pTaskQueue->AddTask( new SSDPCacheTask() ); // ---------------------------------------------------------------------- - // + // Create the SSDP (Upnp Discovery) Thread. // ---------------------------------------------------------------------- - VERBOSE(VB_UPNP, QString( "UPnp::UPnp:Adding Context Listener" )); + VERBOSE(VB_UPNP, QString( "UPnp::Initialize - Creating SSDP Thread" )); + + g_pSSDP = new SSDP( m_nServicePort ); - gContext->addListener( this ); + VERBOSE(VB_UPNP, QString( "UPnp::Initialize - End" )); - VERBOSE(VB_UPNP, QString( "UPnp::UPnp:End" )); + return true; } ////////////////////////////////////////////////////////////////////////////// -// +// Delay startup of Discovery Threads until all Extensions are registered. ////////////////////////////////////////////////////////////////////////////// -UPnp::~UPnp() +void UPnp::Start() { - CleanUp(); + if (g_pSSDP != NULL) + { + VERBOSE(VB_UPNP, QString( "UPnp::UPnp:Starting SSDP Thread (Multicast)" )); + g_pSSDP->start(); + g_pSSDP->EnableNotifications(); + } } ////////////////////////////////////////////////////////////////////////////// @@ -124,12 +155,9 @@ void UPnp::CleanUp( void ) { - // -=>TODO: Need to check to see if calling this more than once is ok. - - gContext->removeListener(this); - if (g_pSSDP) { + g_pSSDP->DisableNotifications(); g_pSSDP->RequestTerminate(); delete g_pSSDP; @@ -149,177 +177,135 @@ g_pTaskQueue = NULL; } + if (g_pConfig) + { + delete g_pConfig; + g_pConfig = NULL; + } + } - + ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// -void UPnp::customEvent( QCustomEvent *e ) +SSDPCacheEntries *UPnp::Find( const QString &sURI ) { - if (MythEvent::Type(e->type()) == MythEvent::MythEventMessage) - { - MythEvent *me = (MythEvent *)e; - QString message = me->Message(); - - //-=>TODO: Need to handle events to notify clients of changes - } + return g_SSDPCache.Find( sURI ); } ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// - -void UPnp::RegisterExtension( UPnpCDSExtension *pExtension ) +DeviceLocation *UPnp::Find( const QString &sURI, const QString &sUSN ) { - m_pUPnpCDS->RegisterExtension( pExtension ); + return g_SSDPCache.Find( sURI, sUSN ); } ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// -void UPnp::UnregisterExtension( UPnpCDSExtension *pExtension ) +UPnpDeviceDesc *UPnp::GetDeviceDesc( QString &sURL, bool bInQtThread ) { - m_pUPnpCDS->UnregisterExtension( pExtension ); + return UPnpDeviceDesc::Retrieve( sURL, bInQtThread ); } ////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// // -// Global Helper Methods... -// -// -=>TODO: Should these functions go someplace else? -// -////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -QString LookupUDN( QString sDeviceType ) +QString UPnp::GetResultDesc( UPnPResultCode eCode ) { - sDeviceType = "upnp:UDN:" + sDeviceType; - - QString sUDN = gContext->GetSetting( sDeviceType, "" ); - - if ( sUDN.length() == 0) + switch( eCode ) { - sUDN = QUuid::createUuid().toString(); - - sUDN = sUDN.mid( 1, sUDN.length() - 2); + case UPnPResult_Success : return "Success"; + case UPnPResult_InvalidAction : return "Invalid Action"; + case UPnPResult_InvalidArgs : return "Invalid Args"; + case UPnPResult_ActionFailed : return "Action Failed"; + case UPnPResult_ArgumentValueInvalid : return "Argument Value Invalid"; + case UPnPResult_ArgumentValueOutOfRange : return "Argument Value Out Of Range"; + case UPnPResult_OptionalActionNotImplemented: return "Optional Action Not Implemented"; + case UPnPResult_OutOfMemory : return "Out Of Memory"; + case UPnPResult_HumanInterventionRequired : return "Human Intervention Required"; + case UPnPResult_StringArgumentTooLong : return "String Argument Too Long"; + case UPnPResult_ActionNotAuthorized : return "Action Not Authorized"; + case UPnPResult_SignatureFailure : return "Signature Failure"; + case UPnPResult_SignatureMissing : return "Signature Missing"; + case UPnPResult_NotEncrypted : return "Not Encrypted"; + case UPnPResult_InvalidSequence : return "Invalid Sequence"; + case UPnPResult_InvalidControlURL : return "Invalid Control URL"; + case UPnPResult_NoSuchSession : return "No Such Session"; + case UPnPResult_MS_AccessDenied : return "Access Denied"; + + case UPnPResult_CDS_NoSuchObject : return "No Such Object"; + case UPnPResult_CDS_InvalidCurrentTagValue : return "Invalid CurrentTagValue"; + case UPnPResult_CDS_InvalidNewTagValue : return "Invalid NewTagValue"; + case UPnPResult_CDS_RequiredTag : return "Required Tag"; + case UPnPResult_CDS_ReadOnlyTag : return "Read Only Tag"; + case UPnPResult_CDS_ParameterMismatch : return "Parameter Mismatch"; + case UPnPResult_CDS_InvalidSearchCriteria : return "Invalid Search Criteria"; + case UPnPResult_CDS_InvalidSortCriteria : return "Invalid Sort Criteria"; + case UPnPResult_CDS_NoSuchContainer : return "No Such Container"; + case UPnPResult_CDS_RestrictedObject : return "Restricted Object"; + case UPnPResult_CDS_BadMetadata : return "Bad Metadata"; + case UPnPResult_CDS_ResrtictedParentObject : return "Resrticted Parent Object"; + case UPnPResult_CDS_NoSuchSourceResource : return "No Such Source Resource"; + case UPnPResult_CDS_ResourceAccessDenied : return "Resource Access Denied"; + case UPnPResult_CDS_TransferBusy : return "Transfer Busy"; + case UPnPResult_CDS_NoSuchFileTransfer : return "No Such File Transfer"; + case UPnPResult_CDS_NoSuchDestRes : return "No Such Destination Resource"; + case UPnPResult_CDS_DestResAccessDenied : return "Destination Resource Access Denied"; + case UPnPResult_CDS_CannotProcessRequest : return "Cannot Process The Request"; + + //case UPnPResult_CMGR_IncompatibleProtocol = 701, + //case UPnPResult_CMGR_IncompatibleDirections = 702, + //case UPnPResult_CMGR_InsufficientNetResources = 703, + //case UPnPResult_CMGR_LocalRestrictions = 704, + //case UPnPResult_CMGR_AccessDenied = 705, + //case UPnPResult_CMGR_InvalidConnectionRef = 706, + case UPnPResult_CMGR_NotInNetwork : return "Not In Network"; - gContext->SaveSetting ( sDeviceType, sUDN ); } - return( sUDN ); + return "Unkown"; } -///////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// -long GetIPAddressList( QStringList &sStrList ) +void UPnp::FormatErrorResponse( HTTPRequest *pRequest, + UPnPResultCode eCode, + const QString &msg ) { - sStrList.clear(); - - QSocketDevice socket( QSocketDevice::Datagram ); - - struct ifreq ifReqs[ 16 ]; - struct ifreq ifReq; - struct ifconf ifConf; - - // ---------------------------------------------------------------------- - // Get Configuration information... - // ---------------------------------------------------------------------- - - ifConf.ifc_len = sizeof( struct ifreq ) * sizeof( ifReqs ); - ifConf.ifc_ifcu.ifcu_req = ifReqs; + QString sMsg( msg ); - if ( ioctl( socket.socket(), SIOCGIFCONF, &ifConf ) < 0) - return( 0 ); - - long nCount = ifConf.ifc_len / sizeof( struct ifreq ); - - // ---------------------------------------------------------------------- - // Loop through looking for IP addresses. - // ---------------------------------------------------------------------- - - for (long nIdx = 0; nIdx < nCount; nIdx++ ) + if (pRequest != NULL) { - // ------------------------------------------------------------------ - // Is this an interface we want? - // ------------------------------------------------------------------ - - strcpy ( ifReq.ifr_name, ifReqs[ nIdx ].ifr_name ); - - if (ioctl ( socket.socket(), SIOCGIFFLAGS, &ifReq ) < 0) - continue; + QString sDetails = ""; - // ------------------------------------------------------------------ - // Skip loopback and down interfaces - // ------------------------------------------------------------------ + if (pRequest->m_bSOAPRequest) + sDetails = ""; - if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) - continue; + if (sMsg.length() == 0) + sMsg = GetResultDesc( eCode ); - if ( ifReqs[ nIdx ].ifr_addr.sa_family == AF_INET) - { - struct sockaddr_in addr; + HTTPRequest::Encode( sMsg ); - // -------------------------------------------------------------- - // Get a pointer to the address - // -------------------------------------------------------------- + sDetails += QString( "%1" + "%2" ) + .arg( eCode ) + .arg( sMsg ); - memcpy (&addr, &(ifReqs[ nIdx ].ifr_addr), sizeof( ifReqs[ nIdx ].ifr_addr )); + if (pRequest->m_bSOAPRequest) + sDetails += ""; - if (addr.sin_addr.s_addr != htonl( INADDR_LOOPBACK )) - { - QHostAddress address( htonl( addr.sin_addr.s_addr )); - sStrList.append( address.toString() ); - } - } - } - - return( sStrList.count() ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -bool operator< ( TaskTime t1, TaskTime t2 ) -{ - if ( (t1.tv_sec < t2.tv_sec) or - ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec < t2.tv_usec))) - { - return true; + pRequest->FormatErrorResponse ( true, // -=>TODO: Should make this dynamic + "UPnPResult", + sDetails ); } - - return false; -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -bool operator== ( TaskTime t1, TaskTime t2 ) -{ - if ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec == t2.tv_usec)) - return true; - - return false; + else + VERBOSE( VB_IMPORTANT, "UPnp::FormatErrorResponse : Response not created - pRequest == NULL" ); } - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void AddMicroSecToTaskTime( TaskTime &t, __suseconds_t uSecs ) -{ - uSecs += t.tv_usec; - - t.tv_sec += (uSecs / 1000000); - t.tv_usec = (uSecs % 1000000); -} - diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpdevice.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpdevice.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpdevice.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpdevice.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -8,10 +8,16 @@ // ////////////////////////////////////////////////////////////////////////////// +#include "upnp.h" #include "upnpdevice.h" +#include "httpcomms.h" + +#include #include +int DeviceLocation::g_nAllocated = 0; // Debugging only + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // @@ -26,6 +32,7 @@ UPnpDeviceDesc::UPnpDeviceDesc() { + VERBOSE( VB_UPNP, "UPnpDeviceDesc - Constructor" ); } ///////////////////////////////////////////////////////////////////////////// @@ -34,25 +41,21 @@ UPnpDeviceDesc::~UPnpDeviceDesc() { + VERBOSE( VB_UPNP, "UPnpDeviceDesc - Destructor" ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -bool UPnpDeviceDesc::Load() +bool UPnpDeviceDesc::Load( const QString &sFileName ) { - QString sSharePath = gContext->GetShareDir(); - - m_sUPnpDescPath = gContext->GetSetting("upnpDescXmlPath", sSharePath); - m_sUPnpDescPath += "upnpavcd.xml"; - // ---------------------------------------------------------------------- // Open Supplied XML uPnp Description file. // ---------------------------------------------------------------------- QDomDocument doc ( "upnp" ); - QFile file( m_sUPnpDescPath ); + QFile file( sFileName ); if ( !file.open( IO_ReadOnly ) ) return false; @@ -69,7 +72,7 @@ VERBOSE(VB_IMPORTANT, QString("UPnpDeviceDesc::Load - " "Error parsing: %1 " "at line: %2 column: %3") - .arg( m_sUPnpDescPath ) + .arg( sFileName ) .arg( nErrLine ) .arg( nErrCol )); @@ -82,7 +85,20 @@ // XML Document Loaded... now parse it into the UPnpDevice Hierarchy // -------------------------------------------------------------- - QDomNode oNode = doc.documentElement(); + return Load( doc ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool UPnpDeviceDesc::Load( const QDomDocument &xmlDevDesc ) +{ + // -------------------------------------------------------------- + // Parse XML into the UPnpDevice Hierarchy + // -------------------------------------------------------------- + + QDomNode oNode = xmlDevDesc.documentElement(); _InternalLoad( oNode.namedItem( "device" ), &m_rootDevice ); @@ -112,17 +128,19 @@ if ( e.tagName() == "serialNumber" ) { SetStrValue( e, pCurDevice->m_sSerialNumber ); continue; } if ( e.tagName() == "UPC" ) { SetStrValue( e, pCurDevice->m_sUPC ); continue; } if ( e.tagName() == "presentationURL" ) { SetStrValue( e, pCurDevice->m_sPresentationURL ); continue; } + if ( e.tagName() == "UDN" ) { SetStrValue( e, pCurDevice->m_sUDN ); continue; } if ( e.tagName() == "iconList" ) { ProcessIconList ( oNode, pCurDevice ); continue; } if ( e.tagName() == "serviceList" ) { ProcessServiceList( oNode, pCurDevice ); continue; } + if ( e.tagName() == "deviceList" ) { ProcessDeviceList ( oNode, pCurDevice ); continue; } - if ( e.tagName() == "device") - { - UPnpDevice *pDevice = new UPnpDevice(); - pCurDevice->m_listDevices.append( pDevice ); + // Not one of the expected element names... add to extra list. - _InternalLoad( e, pDevice ); - } + QString sValue = ""; + + SetStrValue( e, sValue ); + + pCurDevice->m_lstExtra.append( new NameValue( e.tagName(), sValue )); } } } @@ -176,6 +194,33 @@ SetStrValue( e.namedItem( "SCPDURL" ), pService->m_sSCPDURL ); SetStrValue( e.namedItem( "controlURL" ), pService->m_sControlURL ); SetStrValue( e.namedItem( "eventSubURL" ), pService->m_sEventSubURL ); + + VERBOSE(VB_UPNP,QString("ProcessServiceList adding service : %1 : %2 :") + .arg(pService->m_sServiceType) + .arg(pService->m_sServiceId )); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpDeviceDesc::ProcessDeviceList( QDomNode oListNode, UPnpDevice *pDevice ) +{ + for ( QDomNode oNode = oListNode.firstChild(); !oNode.isNull(); oNode = oNode.nextSibling() ) + { + QDomElement e = oNode.toElement(); + + if (!e.isNull()) + { + if ( e.tagName() == "device") + { + UPnpDevice *pNewDevice = new UPnpDevice(); + pDevice->m_listDevices.append( pNewDevice ); + + _InternalLoad( e, pNewDevice ); } } } @@ -215,12 +260,12 @@ // ///////////////////////////////////////////////////////////////////////////// -QString UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress ) +QString UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, int nPort ) { QString sXML; QTextStream os( sXML, IO_WriteOnly ); - GetValidXML( sBaseAddress, os ); + GetValidXML( sBaseAddress, nPort, os ); return( sXML ); } @@ -229,10 +274,8 @@ // ///////////////////////////////////////////////////////////////////////////// -void UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, QTextStream &os ) +void UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent ) { - int nStatusPort = gContext->GetNumSetting("BackendStatusPort", 6544 ); - // os.setEncoding( QTextStream::UnicodeUTF8 ); os << "" @@ -241,9 +284,9 @@ "1" "0" "" - "http://" << sBaseAddress << ":" << nStatusPort << "/"; + "http://" << sBaseAddress << ":" << nPort << "/"; - OutputDevice( os, &m_rootDevice ); + OutputDevice( os, &m_rootDevice, sUserAgent ); os << ""; } @@ -252,32 +295,71 @@ // ///////////////////////////////////////////////////////////////////////////// -void UPnpDeviceDesc::OutputDevice( QTextStream &os, UPnpDevice *pDevice ) +void UPnpDeviceDesc::OutputDevice( QTextStream &os, + UPnpDevice *pDevice, + const QString &sUserAgent ) { if (pDevice == NULL) return; + QString sFriendlyName = QString( "%1: %2" ) + .arg( GetHostName() ) + .arg( pDevice->m_sFriendlyName ); + + // ---------------------------------------------------------------------- + // Only override the root device + // ---------------------------------------------------------------------- + + if (pDevice == &m_rootDevice) + sFriendlyName = UPnp::g_pConfig->GetValue( "UPnP/FriendlyName", sFriendlyName ); + os << ""; - os << FormatValue( "deviceType" , pDevice->m_sDeviceType ); + os << FormatValue( "UDN" , pDevice->GetUDN() ); + os << FormatValue( "friendlyName" , sFriendlyName ); + os << FormatValue( "deviceType" , pDevice->m_sDeviceType ); - QString sFriendlyName = gContext->GetSetting( "upnpFriendlyName", "" ); + // ---------------------------------------------------------------------- + // XBox 360 needs specific values in the Device Description. + // + // -=>TODO: This should be externalized in a more generic/extension + // kind of way. + // ---------------------------------------------------------------------- - if (sFriendlyName.length() > 0) - os << FormatValue( "friendlyName" , sFriendlyName ); + bool bIsXbox360 = sUserAgent.startsWith( "Xbox/2.0", false ) || sUserAgent.startsWith( "Mozilla/4.0", false); + + if ( bIsXbox360 ) + { + os << FormatValue( "manufacturer" , "Microsoft" ); + os << FormatValue( "modelName" , "Windows Media Player Sharing" ); + os << FormatValue( "modelURL" , "http://www.microsoft.com/" ); + } else - os << FormatValue( "friendlyName" , pDevice->m_sFriendlyName ); + { + os << FormatValue( "manufacturer" , pDevice->m_sManufacturer ); + os << FormatValue( "modelName" , pDevice->m_sModelName ); + os << FormatValue( "modelURL" , pDevice->m_sModelURL ); + } - os << FormatValue( "manufacturer" , pDevice->m_sManufacturer ); os << FormatValue( "manufacturerURL" , pDevice->m_sManufacturerURL ); os << FormatValue( "modelDescription" , pDevice->m_sModelDescription); - os << FormatValue( "modelName" , pDevice->m_sModelName ); os << FormatValue( "modelNumber" , pDevice->m_sModelNumber ); - os << FormatValue( "modelURL" , pDevice->m_sModelURL ); os << FormatValue( "serialNumber" , pDevice->m_sSerialNumber ); - os << FormatValue( "UDN" , pDevice->GetUDN() ); os << FormatValue( "UPC" , pDevice->m_sUPC ); os << FormatValue( "presentationURL" , pDevice->m_sPresentationURL ); + for (NameValue *pNV = pDevice->m_lstExtra.first(); + pNV != NULL; + pNV = pDevice->m_lstExtra.next() ) + { + // -=>TODO: Hack to handle one element with attributes... need to + // handle attributes in a more generic way. + + if (pNV->sName == "dlna:X_DLNADOC") + os << QString( "%1" ).arg( pNV->sValue); + else + os << FormatValue( pNV->sName, pNV->sValue ); + } + // ---------------------------------------------------------------------- // Output Any Icons. // ---------------------------------------------------------------------- @@ -308,12 +390,30 @@ if (pDevice->m_listServices.count() > 0) { + // ------------------------------------------------------------------ + // -=>TODO: As a temporary fix don't expose the MSRR service unless we + // as an XBox360 or Windows MediaPlayer. + // + // There is a problem with a DSM-520 with firmware 1.04 and + // the Denon AVR-4306 reciever. + // + // If the MSRR Service is exposed, it won't let us browse + // any media content. + // + // Need to find out real fix and remove this code. + // ------------------------------------------------------------------ + + bool bDSM = sUserAgent.startsWith( "INTEL_NMPR/2.1 DLNADOC/1.00", false ); + os << ""; for ( UPnpService *pService = pDevice->m_listServices.first(); pService != NULL; pService = pDevice->m_listServices.next() ) { + if ( !bIsXbox360 && pService->m_sServiceType.startsWith( "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar", false )) + continue; + os << ""; os << FormatValue( "serviceType", pService->m_sServiceType ); os << FormatValue( "serviceId" , pService->m_sServiceId ); @@ -329,18 +429,24 @@ // Output any Embedded Devices // ---------------------------------------------------------------------- - if (pDevice->m_listDevices.count() > 0) + // -=>Note: XBMC can't handle sub-devices, it's UserAgent is blank. +/* + if (sUserAgent.length() > 0) { - os << ""; - - for ( UPnpDevice *pEmbeddedDevice = pDevice->m_listDevices.first(); - pEmbeddedDevice != NULL; - pEmbeddedDevice = pDevice->m_listDevices.next() ) + if (pDevice->m_listDevices.count() > 0) { - OutputDevice( os, pEmbeddedDevice ); + os << ""; + + for ( UPnpDevice *pEmbeddedDevice = pDevice->m_listDevices.first(); + pEmbeddedDevice != NULL; + pEmbeddedDevice = pDevice->m_listDevices.next() ) + { + OutputDevice( os, pEmbeddedDevice ); + } + os << ""; } - os << ""; } +*/ os << ""; } @@ -405,3 +511,110 @@ return( "" ); } + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpDevice *UPnpDeviceDesc::FindDevice( const QString &sURI ) +{ + return FindDevice( &m_rootDevice, sURI ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpDevice *UPnpDeviceDesc::FindDevice( UPnpDevice *pDevice, const QString &sURI ) + +{ + if ( sURI == pDevice->m_sDeviceType ) + return pDevice; + + // ---------------------------------------------------------------------- + // Check Embedded Devices for a Match + // ---------------------------------------------------------------------- + + for ( UPnpDevice *pEmbeddedDevice = pDevice->m_listDevices.first(); + pEmbeddedDevice != NULL; + pEmbeddedDevice = pDevice->m_listDevices.next() ) + { + UPnpDevice *pFound = FindDevice( pEmbeddedDevice, sURI ); + + if (pFound != NULL) + return pFound; + } + + return NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpDeviceDesc *UPnpDeviceDesc::Retrieve( QString &sURL, bool bInQtThread ) +{ + UPnpDeviceDesc *pDevice = NULL; + + VERBOSE( VB_UPNP, QString( "UPnpDeviceDesc::Retrieve( %1, %2 ) - Requesting Device Description." ) + .arg( sURL ) + .arg( bInQtThread )); + + QString sXml = HttpComms::getHttp( sURL, + 10000, // ms + 3, // retries + 0, // redirects + false, // allow gzip + NULL, // login + bInQtThread ); + + if (sXml.startsWith( "Load( xml ); + + pDevice->m_HostUrl = sURL; + pDevice->m_sHostName = pDevice->m_HostUrl.host(); + } + else + { + VERBOSE( VB_UPNP, QString( "UPnp::Retrieve - Error parsing device description xml [%1]" ) + .arg( sErrorMsg )); + } + } + else + { + VERBOSE( VB_UPNP, QString( "UPnp::Retrieve - Invalid response from %1" ) + .arg( sURL )); + } + + return pDevice; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString UPnpDeviceDesc::GetHostName() +{ + if (m_sHostName.length() == 0) + { + // Get HostName + + char localHostName[1024]; + + if (gethostname(localHostName, 1024)) + VERBOSE(VB_IMPORTANT, "UPnpDeviceDesc: Error, could not determine host name." + ENO); + + return UPnp::g_pConfig->GetValue( "Settings/HostName", QString( localHostName )); + } + + return m_sHostName; +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpdevice.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpdevice.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpdevice.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpdevice.h 2007-08-23 11:09:51.000000000 -0500 @@ -11,10 +11,18 @@ #ifndef __UPNPDEVICE_H__ #define __UPNPDEVICE_H__ -#include "upnpglobal.h" +#include "upnputil.h" +#include "refcounted.h" +#include "mythcontext.h" #include +#include +#include + +extern const char *myth_source_version; + +class UPnpDeviceDesc; class UPnpDevice; class UPnpService; class UPnpIcon; @@ -27,6 +35,7 @@ typedef QPtrList< UPnpService > UPnpServiceList; typedef QPtrList< UPnpIcon > UPnpIconList; + ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// @@ -53,7 +62,7 @@ QString m_sServiceId; QString m_sSCPDURL; QString m_sControlURL; - QString m_sEventSubURL; + QString m_sEventSubURL; UPnpService() {} }; @@ -75,6 +84,9 @@ QString m_sSerialNumber; QString m_sUPC; QString m_sPresentationURL; + QString m_sUDN; + + NameValueList m_lstExtra; UPnpIconList m_listIcons; UPnpServiceList m_listServices; @@ -84,6 +96,9 @@ UPnpDevice() { + m_sModelNumber = MYTH_BINARY_VERSION; + m_sSerialNumber = myth_source_version; + m_listIcons .setAutoDelete( true ); m_listServices.setAutoDelete( true ); m_listDevices .setAutoDelete( true ); @@ -91,7 +106,10 @@ QString GetUDN() { - return( "uuid:" + LookupUDN( m_sDeviceType )); + if (m_sUDN.isEmpty()) + m_sUDN = "uuid:" + LookupUDN( m_sDeviceType ); + + return m_sUDN; } }; @@ -108,8 +126,9 @@ { public: - QString m_sUPnpDescPath; UPnpDevice m_rootDevice; + QString m_sHostName; + QUrl m_HostUrl; protected: @@ -117,8 +136,11 @@ void ProcessIconList ( QDomNode oListNode, UPnpDevice *pDevice ); void ProcessServiceList( QDomNode oListNode, UPnpDevice *pDevice ); + void ProcessDeviceList ( QDomNode oListNode, UPnpDevice *pDevice ); - void OutputDevice( QTextStream &os, UPnpDevice *pDevice ); + void OutputDevice( QTextStream &os, + UPnpDevice *pDevice, + const QString &sUserAgent = "" ); void SetStrValue ( const QDomNode &n, QString &sValue ); void SetNumValue ( const QDomNode &n, int &nValue ); @@ -126,18 +148,113 @@ QString FormatValue ( const QString &sName, const QString &sValue ); QString FormatValue ( const QString &sName, int nValue ); + QString GetHostName (); + public: UPnpDeviceDesc(); virtual ~UPnpDeviceDesc(); - bool Load (); + bool Load ( const QString &sFileName ); + bool Load ( const QDomDocument &xmlDevDesc ); - void GetValidXML( const QString &sBaseAddress, QTextStream &os ); - QString GetValidXML( const QString &sBaseAddress ); + void GetValidXML( const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent = "" ); + QString GetValidXML( const QString &sBaseAddress, int nPort ); QString FindDeviceUDN( UPnpDevice *pDevice, QString sST ); + UPnpDevice *FindDevice( const QString &sURI ); + + static UPnpDevice *FindDevice( UPnpDevice *pDevice, const QString &sURI ); + static UPnpDeviceDesc *Retrieve ( QString &sURL, bool bInQtThread = TRUE ); + +}; + +///////////////////////////////////////////////////////////////////////////// +// DeviceLocation Class Definition/Implementation +///////////////////////////////////////////////////////////////////////////// + +class DeviceLocation : public RefCounted +{ + public: + + static int g_nAllocated; // Debugging only + + protected: + + // ================================================================== + // Destructor protected to force use of Release Method + // ================================================================== + + virtual ~DeviceLocation() + { + // Should be atomic decrement + g_nAllocated--; + + if (m_pDeviceDesc != NULL) + delete m_pDeviceDesc; + } + + UPnpDeviceDesc *m_pDeviceDesc; // We take ownership of this pointer. + + public: + + QString m_sURI; // Service Type URI + QString m_sUSN; // Unique Service Name + QString m_sLocation; // URL to Device Description + TaskTime m_ttExpires; + QString m_sSecurityPin; // Use for MythXML methods needed pin + + public: + + // ================================================================== + + DeviceLocation( const QString &sURI, + const QString &sUSN, + const QString &sLocation, + TaskTime ttExpires ) : m_pDeviceDesc( NULL ), + m_sURI ( sURI ), + m_sUSN ( sUSN ), + m_sLocation ( sLocation ), + m_ttExpires ( ttExpires ) + { + // Should be atomic increment + g_nAllocated++; + } + + // ================================================================== + + int ExpiresInSecs() + { + TaskTime ttNow; + gettimeofday( &ttNow, NULL ); + + return m_ttExpires.tv_sec - ttNow.tv_sec; + } + + // ================================================================== + + UPnpDeviceDesc *GetDeviceDesc( bool bInQtThread = TRUE ) + { + if (m_pDeviceDesc == NULL) + m_pDeviceDesc = UPnpDeviceDesc::Retrieve( m_sLocation, bInQtThread ); + + return m_pDeviceDesc; + } + + // ================================================================== + + QString GetFriendlyName( bool bInQtThread = TRUE ) + { + UPnpDeviceDesc *pDevice = GetDeviceDesc( bInQtThread ); + + if ( pDevice != NULL) + return pDevice->m_rootDevice.m_sFriendlyName; + + return ""; + } + + }; #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpglobal.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpglobal.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpglobal.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpglobal.h 1969-12-31 18:00:00.000000000 -0600 @@ -1,41 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// Program Name: upnpglobal.h -// -// Purpose - -// -// Created By : David Blain Created On : Oct. 24, 2005 -// Modified By : Modified On: -// -////////////////////////////////////////////////////////////////////////////// - -#ifndef __UPNPGLOBAL_H__ -#define __UPNPGLOBAL_H__ - -#include "mythcontext.h" - -// __suseconds_t doesn't exist on some older Unixes. e.g. Darwin/Mac OS X - -#ifndef __suseconds_t_defined -#define __suseconds_t_defined -typedef int32_t __suseconds_t; -#endif - -////////////////////////////////////////////////////////////////////////////// -// Typedefs -////////////////////////////////////////////////////////////////////////////// - -typedef struct timeval TaskTime; - -////////////////////////////////////////////////////////////////////////////// -// Global Function Prototypes -////////////////////////////////////////////////////////////////////////////// - -QString LookupUDN ( QString sDeviceType ); -long GetIPAddressList ( QStringList &sStrList ); - -bool operator< ( TaskTime t1, TaskTime t2 ); -bool operator== ( TaskTime t1, TaskTime t2 ); - -void AddMicroSecToTaskTime( TaskTime &t, __suseconds_t uSecs ); - -#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnp.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnp.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnp.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnp.h 2007-08-23 11:09:51.000000000 -0500 @@ -14,7 +14,7 @@ #include #include -#include "mythcontext.h" +#include "configuration.h" ////////////////////////////////////////////////////////////////////////////// // @@ -24,7 +24,63 @@ #include "taskqueue.h" #include "httpserver.h" #include "ssdp.h" -#include "upnpcds.h" + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +typedef enum +{ + UPnPResult_Success = 0, + + UPnPResult_InvalidAction = 401, + UPnPResult_InvalidArgs = 402, + UPnPResult_ActionFailed = 501, + UPnPResult_ArgumentValueInvalid = 600, + UPnPResult_ArgumentValueOutOfRange = 601, + UPnPResult_OptionalActionNotImplemented = 602, + UPnPResult_OutOfMemory = 603, + UPnPResult_HumanInterventionRequired = 604, + UPnPResult_StringArgumentTooLong = 605, + UPnPResult_ActionNotAuthorized = 606, + UPnPResult_SignatureFailure = 607, + UPnPResult_SignatureMissing = 608, + UPnPResult_NotEncrypted = 609, + UPnPResult_InvalidSequence = 610, + UPnPResult_InvalidControlURL = 611, + UPnPResult_NoSuchSession = 612, + + UPnPResult_CDS_NoSuchObject = 701, + UPnPResult_CDS_InvalidCurrentTagValue = 702, + UPnPResult_CDS_InvalidNewTagValue = 703, + UPnPResult_CDS_RequiredTag = 704, + UPnPResult_CDS_ReadOnlyTag = 705, + UPnPResult_CDS_ParameterMismatch = 706, + UPnPResult_CDS_InvalidSearchCriteria = 708, + UPnPResult_CDS_InvalidSortCriteria = 709, + UPnPResult_CDS_NoSuchContainer = 710, + UPnPResult_CDS_RestrictedObject = 711, + UPnPResult_CDS_BadMetadata = 712, + UPnPResult_CDS_ResrtictedParentObject = 713, + UPnPResult_CDS_NoSuchSourceResource = 714, + UPnPResult_CDS_ResourceAccessDenied = 715, + UPnPResult_CDS_TransferBusy = 716, + UPnPResult_CDS_NoSuchFileTransfer = 717, + UPnPResult_CDS_NoSuchDestRes = 718, + UPnPResult_CDS_DestResAccessDenied = 719, + UPnPResult_CDS_CannotProcessRequest = 720, + + UPnPResult_CMGR_IncompatibleProtocol = 701, + UPnPResult_CMGR_IncompatibleDirections = 702, + UPnPResult_CMGR_InsufficientNetResources = 703, + UPnPResult_CMGR_LocalRestrictions = 704, + UPnPResult_CMGR_AccessDenied = 705, + UPnPResult_CMGR_InvalidConnectionRef = 706, + UPnPResult_CMGR_NotInNetwork = 707, + + UPnPResult_MS_AccessDenied = 801 + +} UPnPResultCode; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -34,33 +90,59 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -class UPnp : public QObject +class UPnp { - Q_OBJECT protected: - - UPnpCDS *m_pUPnpCDS; // Do not delete (auto deleted) + HttpServer *m_pHttpServer; + int m_nServicePort; public: - static QString g_sPlatform; + static Configuration *g_pConfig; static UPnpDeviceDesc g_UPnpDeviceDesc; static TaskQueue *g_pTaskQueue; static SSDP *g_pSSDP; + static SSDPCache g_SSDPCache; + static QStringList g_IPAddrList; public: - UPnp( bool bMaster, HttpServer *pHttpServer ); + UPnp(); virtual ~UPnp(); - void CleanUp( void ); + void SetConfiguration( Configuration *pConfig ); + + bool Initialize( int nServicePort, HttpServer *pHttpServer ); + bool Initialize( QStringList &sIPAddrList, int nServicePort, HttpServer *pHttpServer ); + + virtual void Start(); + + void CleanUp ( void ); + + UPnpDevice *RootDevice() { return &(g_UPnpDeviceDesc.m_rootDevice); } + + static void PerformSearch( const QString &sST ) + { + if (g_pSSDP) + g_pSSDP->PerformSearch( sST ); + } + + HttpServer *GetHttpServer() { return m_pHttpServer; } + + static void AddListener ( QObject *listener ) { g_SSDPCache.addListener ( listener ); } + static void RemoveListener( QObject *listener ) { g_SSDPCache.removeListener( listener ); } + + static SSDPCacheEntries *Find( const QString &sURI ); + static DeviceLocation *Find( const QString &sURI, const QString &sUSN ); - void customEvent( QCustomEvent *e ); + static UPnpDeviceDesc *GetDeviceDesc( QString &sURL, bool bInQtThread = TRUE); - void RegisterExtension ( UPnpCDSExtension *pExtension ); - void UnregisterExtension( UPnpCDSExtension *pExtension ); + static QString GetResultDesc( UPnPResultCode eCode ); + static void FormatErrorResponse( HTTPRequest *pRequest, + UPnPResultCode eCode, + const QString &sMsg = "" ); }; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpimpl.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpimpl.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpimpl.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpimpl.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,48 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: UPnpImpl.h +// +// Purpose - +// +// Created By : David Blain Created On : Jan 15, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + + +#ifndef __UPNPIMPL_H__ +#define __UPNPIMPL_H__ + +#include "upnpdevice.h" + +class UPnpServiceImpl +{ + protected: + + virtual QString GetServiceType () = 0; + virtual QString GetServiceId () = 0; + virtual QString GetServiceControlURL() = 0; + virtual QString GetServiceDescURL () = 0; + virtual QString GetServiceEventURL () { return ""; } + + public: + + UPnpServiceImpl() {} + + void RegisterService( UPnpDevice *pDevice ) + { + if (pDevice != NULL) + { + UPnpService *pService = new UPnpService(); + + pService->m_sServiceType = GetServiceType(); + pService->m_sServiceId = GetServiceId(); + pService->m_sSCPDURL = GetServiceDescURL(); + pService->m_sControlURL = GetServiceControlURL(); + pService->m_sEventSubURL = GetServiceEventURL(); + + pDevice->m_listServices.append( pService ); + } + } +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpmsrr.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpmsrr.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpmsrr.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpmsrr.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,151 @@ +///////////////////////////////////////////////////////////////////////////// +// Program Name: upnpmsrr.cpp +// +// Purpose - uPnp Microsoft Media Receiver Registrar "fake" Service +// +////////////////////////////////////////////////////////////////////////////// + +#include "upnp.h" +#include "upnpmsrr.h" + +#include +#include + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpMSRR::UPnpMSRR( UPnpDevice *pDevice ) : Eventing( "UPnpMSRR", "MSRR_Event" ) +{ + AddVariable( new StateVariable< unsigned short >( "AuthorizationGrantedUpdateID", true ) ); + AddVariable( new StateVariable< unsigned short >( "AuthorizationDeniedUpdateID" , true ) ); + AddVariable( new StateVariable< unsigned short >( "ValidationSucceededUpdateID" , true ) ); + AddVariable( new StateVariable< unsigned short >( "ValidationRevokedUpdateID" , true ) ); + + SetValue< unsigned short >( "AuthorizationGrantedUpdateID", 0 ); + SetValue< unsigned short >( "AuthorizationDeniedUpdateID" , 0 ); + SetValue< unsigned short >( "ValidationSucceededUpdateID" , 0 ); + SetValue< unsigned short >( "ValidationRevokedUpdateID" , 0 ); + + QString sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath", m_sSharePath ); + + m_sServiceDescFileName = sUPnpDescPath + "MSRR_scpd.xml"; + m_sControlUrl = "/MSRR_Control"; + + // Add our Service Definition to the device. + + RegisterService( pDevice ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpMSRR::~UPnpMSRR() +{ +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpMSRRMethod UPnpMSRR::GetMethod( const QString &sURI ) +{ + if (sURI == "GetServDesc" ) return MSRR_GetServiceDescription; + if (sURI == "IsAuthorized" ) return MSRR_IsAuthorized ; + if (sURI == "RegisterDevice" ) return MSRR_RegisterDevice ; + if (sURI == "IsValidated" ) return MSRR_IsValidated ; + + return( MSRR_Unknown ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool UPnpMSRR::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) +{ + if (pRequest) + { + if (Eventing::ProcessRequest( pThread, pRequest )) + return true; + + if ( pRequest->m_sBaseUrl != m_sControlUrl ) + return false; + + VERBOSE(VB_UPNP, QString("UPnpMSRR::ProcessRequest : %1 : %2 :") + .arg( pRequest->m_sBaseUrl ) + .arg( pRequest->m_sMethod )); + + switch( GetMethod( pRequest->m_sMethod ) ) + { + case MSRR_GetServiceDescription : pRequest->FormatFileResponse( m_sServiceDescFileName ); break; + case MSRR_IsAuthorized : HandleIsAuthorized ( pRequest ); break; + case MSRR_RegisterDevice : HandleRegisterDevice ( pRequest ); break; + case MSRR_IsValidated : HandleIsValidated ( pRequest ); break; + + default: + UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); + break; + } + } + + return( true ); + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpMSRR::HandleIsAuthorized( HTTPRequest *pRequest ) +{ + /* Always tell the user they are authorized to access this data */ + VERBOSE(VB_UPNP, QString("UPnpMSRR::HandleIsAuthorized")); + NameValueList list; + + NameValue *pResult = new NameValue( "Result", "1"); + + pResult->AddAttribute( "xmlns:dt", "urn:schemas-microsoft-com:datatypes" ); + pResult->AddAttribute( "dt:dt" , "int" ); + + list.append( pResult ); + + pRequest->FormatActionResponse( &list ); + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpMSRR::HandleRegisterDevice( HTTPRequest *pRequest ) +{ + /* Sure, register, we don't really care */ + VERBOSE(VB_UPNP, QString("UPnpMSRR::HandleRegisterDevice")); + NameValueList list; + + list.append( new NameValue( "Result", "1")); + + pRequest->FormatActionResponse( &list ); + +} + +void UPnpMSRR::HandleIsValidated( HTTPRequest *pRequest ) +{ + /* You are valid sir */ + + VERBOSE(VB_UPNP, QString("UPnpMSRR::HandleIsValidated")); + NameValueList list; + + NameValue *pResult = new NameValue( "Result", "1"); + + pResult->AddAttribute( "xmlns:dt", "urn:schemas-microsoft-com:datatypes" ); + pResult->AddAttribute( "dt:dt" , "int" ); + + list.append( pResult ); + + pRequest->FormatActionResponse( &list ); + +} + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpmsrr.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpmsrr.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnpmsrr.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnpmsrr.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,58 @@ +#ifndef UPnpMSRR_H_ +#define UPnpMSRR_H_ + +#include +#include + +#include "httpserver.h" +#include "eventing.h" + +class UPnpMSRR; + +typedef enum +{ + MSRR_Unknown = 0, + MSRR_GetServiceDescription = 1, + MSRR_IsAuthorized = 2, + MSRR_RegisterDevice = 3, + MSRR_IsValidated = 4 + +} UPnpMSRRMethod; + +////////////////////////////////////////////////////////////////////////////// +// +// UPnpMSRR Class Definition +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class UPnpMSRR : public Eventing +{ + private: + + QString m_sServiceDescFileName; + QString m_sControlUrl; + + UPnpMSRRMethod GetMethod ( const QString &sURI ); + + void HandleIsAuthorized ( HTTPRequest *pRequest ); + void HandleRegisterDevice ( HTTPRequest *pRequest ); + void HandleIsValidated ( HTTPRequest *pRequest ); + + protected: + + // Implement UPnpServiceImpl methods that we can + + virtual QString GetServiceType () { return "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"; } + virtual QString GetServiceId () { return "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"; } + virtual QString GetServiceControlURL() { return m_sControlUrl.mid( 1 ); } + virtual QString GetServiceDescURL () { return m_sControlUrl.mid( 1 ) + "/GetServDesc"; } + + public: + UPnpMSRR( UPnpDevice *pDevice ); + virtual ~UPnpMSRR(); + + bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptaskcache.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptaskcache.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptaskcache.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptaskcache.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,66 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnptaskcache.h +// +// Purpose - UPnp Task to keep SSDPCache free of stale records +// +// Created By : David Blain Created On : Jan. 9, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __UPNPTASKCACHE_H__ +#define __UPNPTASKCACHE_H__ + +#include "upnp.h" + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// SSDPCacheTask Class Definition +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class SSDPCacheTask : public Task +{ + protected: + + int m_nInterval; // Number of ms between executing. + int m_nExecuteCount; // Used for debugging. + + // Destructor protected to force use of Release Method + + virtual ~SSDPCacheTask() {}; + + public: + + SSDPCacheTask() + { + m_nExecuteCount = 0; + m_nInterval = UPnp::g_pConfig->GetValue( "UPnP/SSDP/CacheInterval", 30 ) * 1000; + + } + + virtual QString Name () + { + return( "SSDPCache" ); + } + + virtual void Execute( TaskQueue *pQueue ) + { + m_nExecuteCount++; + + int nCount = UPnp::g_SSDPCache.RemoveStale(); + + if (nCount > 0) + VERBOSE( VB_UPNP, QString( "SSDPCacheTask - Removed %1 stale entries." ).arg( nCount )); + + if ((m_nExecuteCount % 60) == 0) + UPnp::g_SSDPCache.Dump(); + + pQueue->AddTask( m_nInterval, (Task *)this ); + } + +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptaskevent.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptaskevent.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptaskevent.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptaskevent.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,113 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnptaskevent.cpp +// +// Purpose - UPnp Task to notifing subscribers of an event +// +// Created By : David Blain Created On : Dec. 31, 2006 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "upnptaskevent.h" + +#include +#include + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// UPnpEventTaskTask Implementation +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpEventTask::UPnpEventTask( QHostAddress peerAddress, + int nPeerPort, + QByteArray *pPayload ) +{ + m_PeerAddress = peerAddress; + m_nPeerPort = nPeerPort; + m_pPayload = pPayload; // We take ownership of this pointer. +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpEventTask::~UPnpEventTask() +{ + if (m_pPayload != NULL) + delete m_pPayload; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpEventTask::Execute( TaskQueue * /*pQueue*/ ) +{ + if (m_pPayload == NULL) + return; + + QSocketDevice *pSockDev = new QSocketDevice( QSocketDevice::Stream ); + BufferedSocketDevice *pSock = new BufferedSocketDevice( pSockDev ); + + pSockDev->setBlocking( true ); + + if (pSock->Connect( m_PeerAddress, m_nPeerPort )) + { + // ------------------------------------------------------------------ + // Send NOTIFY message + // ------------------------------------------------------------------ + + if (pSock->WriteBlockDirect( m_pPayload->data(), m_pPayload->size() ) != -1) + { + // -------------------------------------------------------------- + // Read first line to determine success/Fail + // -------------------------------------------------------------- + + QString sResponseLine = pSock->ReadLine( 3000 ); + + if ( sResponseLine.length() > 0) + { + //if (sResponseLine.contains("200 OK")) + //{ + VERBOSE( VB_UPNP, QString( "UPnpEventTask::Execute - NOTIFY to %1:%2 returned %3." ) + .arg( m_PeerAddress.toString() ) + .arg( m_nPeerPort ) + .arg( sResponseLine )); + + //} + } + else + { + VERBOSE( VB_UPNP, QString( "UPnpEventTask::Execute - Timeout reading first line of reply from %1:%2." ) + .arg( m_PeerAddress.toString() ) + .arg( m_nPeerPort ) ); + } + } + else + VERBOSE( VB_UPNP, QString( "UPnpEventTask::Execute - Error sending to %1:%2." ) + .arg( m_PeerAddress.toString() ) + .arg( m_nPeerPort ) ); + + pSock->Close(); + } + else + { + VERBOSE( VB_UPNP, QString( "UPnpEventTask::Execute - Error sending to %1:%2." ) + .arg( m_PeerAddress.toString() ) + .arg( m_nPeerPort ) ); + } + + if ( pSock != NULL ) + delete pSock; + + if ( pSockDev != NULL ) + delete pSockDev; +} + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptaskevent.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptaskevent.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptaskevent.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptaskevent.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,53 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnptaskevent.h +// +// Purpose - UPnp Task to notifing subscribers of an event +// +// Created By : David Blain Created On : Dec. 31, 2006 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __UPNPTASKEVENT_H__ +#define __UPNPTASKEVENT_H__ + +#include "upnp.h" +#include "bufferedsocketdevice.h" + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// UPnpEventTask Class Definition +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class UPnpEventTask : public Task +{ + protected: + + QHostAddress m_PeerAddress; + int m_nPeerPort; + QByteArray *m_pPayload; + + QCString m_sPayload; + + protected: + + // Destructor protected to force use of Release Method + + virtual ~UPnpEventTask(); + + public: + + UPnpEventTask( QHostAddress peerAddress, + int nPeerPort, + QByteArray *m_pPayload ); + + virtual QString Name () { return( "Event" ); } + virtual void Execute( TaskQueue * ); + +}; + + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasknotify.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasknotify.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasknotify.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasknotify.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -8,9 +8,10 @@ // ////////////////////////////////////////////////////////////////////////////// -#include "upnpdevice.h" -#include "ssdp.h" -#include "upnptasknotify.h" +#include "upnp.h" +#include "mythcontext.h" +#include "multicast.h" +#include "broadcast.h" #include #include @@ -19,6 +20,7 @@ #include #include + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // @@ -31,10 +33,12 @@ // ///////////////////////////////////////////////////////////////////////////// -UPnpNotifyTask::UPnpNotifyTask( ) : m_nStatusPort( 0 ), - m_nMaxAge( 0 ), - m_eNTS( NTS_alive ) +UPnpNotifyTask::UPnpNotifyTask( int nServicePort ) { + m_nServicePort = nServicePort; + m_eNTS = NTS_alive; + + m_nMaxAge = UPnp::g_pConfig->GetValue( "UPnP/SSDP/MaxAge" , 3600 ); } ///////////////////////////////////////////////////////////////////////////// @@ -49,9 +53,9 @@ // ///////////////////////////////////////////////////////////////////////////// -void UPnpNotifyTask::SendNotifyMsg( QMulticastSocket *pSocket, - QString sNT, - QString sUDN ) +void UPnpNotifyTask::SendNotifyMsg( QSocketDevice *pSocket, + QString sNT, + QString sUDN ) { QString sUSN; @@ -60,38 +64,45 @@ else sUSN = sNT; - QString sData = QString ( "SERVER: %1, UPnP/1.0, MythTv %2\r\n" + QString sData = QString ( "Server: %1, UPnP/1.0, MythTv %2\r\n" "NTS: %3\r\n" "NT: %4\r\n" "USN: %5\r\n" "CACHE-CONTROL: max-age=%6\r\n" "Content-Length: 0\r\n\r\n" ) - .arg( UPnp::g_sPlatform ) + .arg( HttpServer::g_sPlatform ) .arg( MYTH_BINARY_VERSION ) .arg( GetNTSString() ) .arg( sNT ) .arg( sUSN ) .arg( m_nMaxAge ); +// VERBOSE(VB_UPNP, QString("UPnpNotifyTask::SendNotifyMsg : %1 : %2 : %3") +// .arg( pSocket->address().toString() ) +// .arg( sNT ) +// .arg( sUSN )); + for ( QStringList::Iterator it = m_addressList.begin(); it != m_addressList.end(); ++it ) - { - QString sHeader = QString ( "NOTIFY * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "LOCATION: http://%1:%2/getDeviceDesc\r\n" ) - .arg( *it ) - .arg( m_nStatusPort); + { //239.255.255.250:1900\r\n" + QString sHeader = QString( "NOTIFY * HTTP/1.1\r\n" + "HOST: %1:%2\r\n" + "LOCATION: http://%3:%4/getDeviceDesc\r\n" ) + .arg( SSDP_GROUP ) // pSocket->address().toString() ) + .arg( SSDP_PORT ) // pSocket->port() ) + .arg( *it ) + .arg( m_nServicePort); QString sPacket = sHeader + sData; QCString scPacket = sPacket.utf8(); // ------------------------------------------------------------------ - // Send Packet to Multicast Socket (Send same packet twice) + // Send Packet to Socket (Send same packet twice) // ------------------------------------------------------------------ pSocket->writeBlock( scPacket, scPacket.length(), pSocket->address(), pSocket->port() ); - usleep( 500000 ); + usleep( rand() % 250000 ); pSocket->writeBlock( scPacket, scPacket.length(), pSocket->address(), pSocket->port() ); } @@ -103,20 +114,15 @@ void UPnpNotifyTask::Execute( TaskQueue *pQueue ) { - // ---------------------------------------------------------------------- - // Reload in case they have changed. - // ---------------------------------------------------------------------- - m_nStatusPort = gContext->GetNumSetting("BackendStatusPort", 6544 ); - m_nMaxAge = gContext->GetNumSetting("upnpMaxAge" , 3600 ); - - QMulticastSocket *pSocket = new QMulticastSocket( SSDP_GROUP, SSDP_PORT ); + QSocketDevice *pMulticast = new QMulticastSocket( SSDP_GROUP , SSDP_PORT ); +// QSocketDevice *pBroadcast = new QBroadcastSocket( "255.255.255.255", SSDP_PORT ); // ---------------------------------------------------------------------- // Refresh IP Address List in case of changes // ---------------------------------------------------------------------- - GetIPAddressList( m_addressList ); + m_addressList = UPnp::g_IPAddrList; // ---------------------------------------------------------------------- // Must send rootdevice Notification for first device. @@ -124,20 +130,25 @@ UPnpDevice &device = UPnp::g_UPnpDeviceDesc.m_rootDevice; - SendNotifyMsg( pSocket, "upnp:rootdevice", device.GetUDN() ); + SendNotifyMsg( pMulticast, "upnp:rootdevice", device.GetUDN() ); +// SendNotifyMsg( pBroadcast, "upnp:rootdevice", device.GetUDN() ); // ---------------------------------------------------------------------- // Process rest of notifications // ---------------------------------------------------------------------- - ProcessDevice( pSocket, &device ); + ProcessDevice( pMulticast, &device ); +// ProcessDevice( pBroadcast, &device ); // ---------------------------------------------------------------------- // Clean up and reshedule task if needed (timeout = m_nMaxAge / 2). // ---------------------------------------------------------------------- - delete pSocket; - pSocket = NULL; + delete pMulticast; +// delete pBroadcast; + + pMulticast = NULL; +// pBroadcast = NULL; m_mutex.lock(); @@ -152,7 +163,7 @@ // ///////////////////////////////////////////////////////////////////////////// -void UPnpNotifyTask::ProcessDevice( QMulticastSocket *pSocket, UPnpDevice *pDevice ) +void UPnpNotifyTask::ProcessDevice( QSocketDevice *pSocket, UPnpDevice *pDevice ) { // ---------------------------------------------------------------------- // Loop for each device and send the 2 required messages diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasknotify.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasknotify.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasknotify.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasknotify.h 2007-08-23 11:09:51.000000000 -0500 @@ -19,6 +19,7 @@ #include #include "upnp.h" +#include "multicast.h" ///////////////////////////////////////////////////////////////////////////// // Typedefs @@ -46,7 +47,7 @@ QMutex m_mutex; QString m_sMasterIP; - int m_nStatusPort; + int m_nServicePort; int m_nMaxAge; UPnpNotifyNTS m_eNTS; @@ -60,12 +61,12 @@ virtual ~UPnpNotifyTask(); - void ProcessDevice( QMulticastSocket *pSocket, UPnpDevice *pDevice ); - void SendNotifyMsg( QMulticastSocket *pSocket, QString sNT, QString sUDN ); + void ProcessDevice( QSocketDevice *pSocket, UPnpDevice *pDevice ); + void SendNotifyMsg( QSocketDevice *pSocket, QString sNT, QString sUDN ); public: - UPnpNotifyTask(); + UPnpNotifyTask( int nServicePort ); virtual QString Name () { return( "Notify" ); } virtual void Execute( TaskQueue * ); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasksearch.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasksearch.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasksearch.cpp 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasksearch.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -8,8 +8,8 @@ // ////////////////////////////////////////////////////////////////////////////// -#include "upnpdevice.h" -#include "ssdp.h" +#include "upnp.h" +#include "mythcontext.h" #include "upnptasksearch.h" #include @@ -31,7 +31,8 @@ // ///////////////////////////////////////////////////////////////////////////// -UPnpSearchTask::UPnpSearchTask( QHostAddress peerAddress, +UPnpSearchTask::UPnpSearchTask( int nServicePort, + QHostAddress peerAddress, int nPeerPort, QString sST, QString sUDN ) @@ -40,6 +41,9 @@ m_nPeerPort = nPeerPort; m_sST = sST; m_sUDN = sUDN; + m_nServicePort= nServicePort; + m_nMaxAge = UPnp::g_pConfig->GetValue( "UPnP/SSDP/MaxAge" , 3600 ); + } ///////////////////////////////////////////////////////////////////////////// @@ -60,7 +64,7 @@ { QString sUSN; - if ( sUDN.length() > 0) + if (( sUDN.length() > 0) && ( sUDN != sST )) sUSN = sUDN + "::" + sST; else sUSN = sST; @@ -70,17 +74,23 @@ QString sData = QString ( "CACHE-CONTROL: max-age=%1\r\n" "DATE: %2\r\n" "EXT:\r\n" - "SERVER: %3, UPnP/1.0, MythTv %4\r\n" + "Server: %3, UPnP/1.0, MythTv %4\r\n" "ST: %5\r\n" "USN: %6\r\n" "Content-Length: 0\r\n\r\n" ) .arg( m_nMaxAge ) .arg( sDate ) - .arg( UPnp::g_sPlatform ) + .arg( HttpServer::g_sPlatform ) .arg( MYTH_BINARY_VERSION ) .arg( sST ) .arg( sUSN ); +// VERBOSE(VB_UPNP, QString("UPnpSearchTask::SendMsg : %1 : %2 ") +// .arg( sST ) +// .arg( sUSN )); + +//cout << "UPnpSearchTask::SendMsg m_PeerAddress = " << m_PeerAddress.toString() << " Port=" << m_nPeerPort << endl; + for ( QStringList::Iterator it = m_addressList.begin(); it != m_addressList.end(); ++it ) @@ -88,7 +98,7 @@ QString sHeader = QString ( "HTTP/1.1 200 OK\r\n" "LOCATION: http://%1:%2/getDeviceDesc\r\n" ) .arg( *it ) - .arg( m_nStatusPort); + .arg( m_nServicePort); QString sPacket = sHeader + sData; @@ -99,7 +109,7 @@ // ------------------------------------------------------------------ pSocket->writeBlock( scPacket, scPacket.length(), m_PeerAddress, m_nPeerPort ); - usleep( 500000 ); + usleep( rand() % 250000 ); pSocket->writeBlock( scPacket, scPacket.length(), m_PeerAddress, m_nPeerPort ); } @@ -111,20 +121,13 @@ void UPnpSearchTask::Execute( TaskQueue * /*pQueue*/ ) { - // ---------------------------------------------------------------------- - // Reload in case they have changed. - // ---------------------------------------------------------------------- - - m_nStatusPort = gContext->GetNumSetting("BackendStatusPort", 6544 ); - m_nMaxAge = gContext->GetNumSetting("upnpMaxAge" , 3600 ); - QSocketDevice *pSocket = new QSocketDevice( QSocketDevice::Datagram ); // ---------------------------------------------------------------------- // Refresh IP Address List in case of changes // ---------------------------------------------------------------------- - GetIPAddressList( m_addressList ); + m_addressList = UPnp::g_IPAddrList; // ---------------------------------------------------------------------- // Check to see if this is a rootdevice or all request. diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasksearch.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasksearch.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnptasksearch.h 2007-01-22 03:08:35.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnptasksearch.h 2007-08-23 11:09:51.000000000 -0500 @@ -33,7 +33,7 @@ protected: QStringList m_addressList; - int m_nStatusPort; + int m_nServicePort; int m_nMaxAge; QHostAddress m_PeerAddress; @@ -55,7 +55,8 @@ public: - UPnpSearchTask( QHostAddress peerAddress, + UPnpSearchTask( int nServicePort, + QHostAddress peerAddress, int nPeerPort, QString sST, QString sUDN ); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnputil.cpp /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnputil.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnputil.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnputil.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,224 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnputil.cpp +// +// Purpose - Global Helper Methods... +// +// Created By : David Blain Created On : Jan 15. 24, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "mythconfig.h" +#include "upnputil.h" +#include "upnp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_GETIFADDRS +#include +#endif + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString LookupUDN( QString sDeviceType ) +{ + QStringList sList = QStringList::split( ":", sDeviceType ); + + QString sName = "UPnP/UDN/" + sList[ sList.size() - 2 ]; + + // cout << "lookupUDN: " << sName << endl; + + QString sUDN = UPnp::g_pConfig->GetValue( sName, "" ); + + if ( sUDN.length() == 0) + { + sUDN = QUuid::createUuid().toString(); + + sUDN = sUDN.mid( 1, sUDN.length() - 2); + + UPnp::g_pConfig->SetValue( sName, sUDN ); + + UPnp::g_pConfig->Save(); + } + + return( sUDN ); +} + +///////////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_GETIFADDRS + +long GetIPAddressList(QStringList &sStrList) +{ + struct ifaddrs *list, *ifa; + + + sStrList.clear(); + + if (getifaddrs(&list) == -1) + { + VERBOSE(VB_UPNP, QString("GetIPAddressList() - getifaddrs failed: ") + + strerror(errno)); + return 0; + } + + for (ifa=list; ifa; ifa=ifa->ifa_next) + { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + if (ifa->ifa_flags & IFF_LOOPBACK) + continue; + if (!(ifa->ifa_flags & IFF_UP)) + continue; + + + char address[15]; + + if (inet_ntop(ifa->ifa_addr->sa_family, + &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + address, sizeof(address)) == NULL) + { + VERBOSE(VB_UPNP, QString("GetIPAddressList() - inet_ntop failed: ") + + strerror(errno)); + continue; + } + + sStrList.append(address); + //VERBOSE(VB_UPNP, QString("GetIPAddressList() - Added %1 as %2") + // .arg(ifa->ifa_name).arg(address)); + } + + return(sStrList.count()); +} + +#else // HAVE_GETIFADDRS + +// On some Unixes (e.g. Darwin), this implementation is buggy because +// struct ifreq is variable size. Luckily, most of them have getifaddrs() + +long GetIPAddressList( QStringList &sStrList ) +{ + sStrList.clear(); + + QSocketDevice socket( QSocketDevice::Datagram ); + + struct ifreq ifReqs[ 16 ]; + struct ifreq ifReq; + struct ifconf ifConf; + + // ---------------------------------------------------------------------- + // Get Configuration information... + // ---------------------------------------------------------------------- + + ifConf.ifc_len = sizeof( struct ifreq ) * sizeof( ifReqs ); + ifConf.ifc_ifcu.ifcu_req = ifReqs; + + if ( ioctl( socket.socket(), SIOCGIFCONF, &ifConf ) < 0) + return( 0 ); + + long nCount = ifConf.ifc_len / sizeof( struct ifreq ); + + // ---------------------------------------------------------------------- + // Loop through looking for IP addresses. + // ---------------------------------------------------------------------- + + for (long nIdx = 0; nIdx < nCount; nIdx++ ) + { + // ------------------------------------------------------------------ + // Is this an interface we want? + // ------------------------------------------------------------------ + + strcpy ( ifReq.ifr_name, ifReqs[ nIdx ].ifr_name ); + + if (ioctl ( socket.socket(), SIOCGIFFLAGS, &ifReq ) < 0) + continue; + + // ------------------------------------------------------------------ + // Skip loopback and down interfaces + // ------------------------------------------------------------------ + + if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) + continue; + + if ( ifReqs[ nIdx ].ifr_addr.sa_family == AF_INET) + { + struct sockaddr_in addr; + + // -------------------------------------------------------------- + // Get a pointer to the address + // -------------------------------------------------------------- + + memcpy (&addr, &(ifReqs[ nIdx ].ifr_addr), sizeof( ifReqs[ nIdx ].ifr_addr )); + + if (addr.sin_addr.s_addr != htonl( INADDR_LOOPBACK )) + { + QHostAddress address( htonl( addr.sin_addr.s_addr )); + + sStrList.append( address.toString() ); + } + } + } + + return( sStrList.count() ); +} + +#endif // HAVE_GETIFADDRS + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool operator< ( TaskTime t1, TaskTime t2 ) +{ + if ( (t1.tv_sec < t2.tv_sec) or + ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec < t2.tv_usec))) + { + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool operator== ( TaskTime t1, TaskTime t2 ) +{ + if ((t1.tv_sec == t2.tv_sec) && (t1.tv_usec == t2.tv_usec)) + return true; + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void AddMicroSecToTaskTime( TaskTime &t, __suseconds_t uSecs ) +{ + uSecs += t.tv_usec; + + t.tv_sec += (uSecs / 1000000); + t.tv_usec = (uSecs % 1000000); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void AddSecondsToTaskTime( TaskTime &t, long nSecs ) +{ + t.tv_sec += nSecs; +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnputil.h /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnputil.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/libs/libmythupnp/upnputil.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/libs/libmythupnp/upnputil.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnputil.h +// +// Purpose - +// +// Created By : David Blain Created On : Oct. 24, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __UPNPUTIL_H__ +#define __UPNPUTIL_H__ + +#include +#include +#include + +// __suseconds_t doesn't exist on some older Unixes. e.g. Darwin/Mac OS X + +#if defined(__FreeBSD__) +#define __suseconds_t_defined // It exists on FreeBSD, but doesn't define this +#endif + +#ifndef __suseconds_t_defined +#define __suseconds_t_defined +typedef int32_t __suseconds_t; +#endif + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +template inline const T& Min( const T &x, const T &y ) +{ + return( ( x < y ) ? x : y ); +} + +template inline const T& Max( const T &x, const T &y ) +{ + return( ( x > y ) ? x : y ); +} +////////////////////////////////////////////////////////////////////////////// +// Typedefs +////////////////////////////////////////////////////////////////////////////// + +typedef struct timeval TaskTime; +typedef QMap< QString, QString > QStringMap; + +///////////////////////////////////////////////////////////////////////////// + +class NameValue; + +class NameValueList : public QPtrList< NameValue > +{ + public: + + NameValueList() + { + setAutoDelete( true ); + } +}; + +class NameValue +{ + public: + + QString sName; + QString sValue; + + NameValueList *pAttributes; + + NameValue() + : sName( NULL ), sValue( NULL ), pAttributes( NULL ) { } + + NameValue( const QString &name, const QString &value ) + : sName( name ), sValue( value ), pAttributes( NULL ) { } + + NameValue( const QString &name, char *value ) + : sName( name ), sValue( value ), pAttributes( NULL ) { } + + NameValue( const QString &name, int value ) + : sName( name ), sValue( QString::number( value )), pAttributes( NULL ) { } + + NameValue( const QString &name, bool value ) + : sName( name ), sValue( (value) ? "1" : "0" ), pAttributes( NULL ) { } + + ~NameValue() + { + if (pAttributes != NULL) + delete pAttributes; + } + + void AddAttribute( const QString &name, const QString &value ) + { + if (pAttributes == NULL) + pAttributes = new NameValueList(); + + pAttributes->append( new NameValue( name, value )); + } + +}; + +////////////////////////////////////////////////////////////////////////////// +// Global Function Prototypes +////////////////////////////////////////////////////////////////////////////// + +QString LookupUDN ( QString sDeviceType ); +long GetIPAddressList ( QStringList &sStrList ); + +bool operator< ( TaskTime t1, TaskTime t2 ); +bool operator== ( TaskTime t1, TaskTime t2 ); + +void AddMicroSecToTaskTime( TaskTime &t, __suseconds_t uSecs ); +void AddSecondsToTaskTime ( TaskTime &t, long nSecs ); + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/devicemaster.xml /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/devicemaster.xml --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/devicemaster.xml 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/devicemaster.xml 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,34 @@ + + + + 1 + 0 + + + + MythTV AV Media Server + urn:schemas-upnp-org:device:MediaServer:1 + MythTV + http://www.mythtv.org/ + MythTV AV Media Server + + MythTV AV Media Server + http://www.mythtv.org/ + DMS-1.00 + + + + + MythTV Master Media Server + urn:schemas-mythtv-org:device:MasterMediaServer:1 + MythTV + http://www.mythtv.org/ + MythTV Master Media Server + + MythTV AV Media Server + http://www.mythtv.org/ + + + + + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/deviceslave.xml /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/deviceslave.xml --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/deviceslave.xml 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/deviceslave.xml 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,19 @@ + + + + 1 + 0 + + + + MythTV Slave Media Server + urn:schemas-mythtv-org:device:SlaveMediaServer:1 + MythTV + http://www.mythtv.org/ + MythTV Slave Media Server + + MythTV Slave Media Server + http://www.mythtv.org/ + + + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/httpstatus.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/httpstatus.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/httpstatus.cpp 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/httpstatus.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -9,6 +9,7 @@ ////////////////////////////////////////////////////////////////////////////// #include "httpstatus.h" +#include "mythxml.h" #include "libmyth/mythcontext.h" #include "libmyth/util.h" @@ -18,6 +19,7 @@ #include #include #include +#include #include #include "../../config.h" @@ -57,1023 +59,48 @@ HttpStatusMethod HttpStatus::GetMethod( const QString &sURI ) { if (sURI == "" ) return( HSM_GetStatusHTML ); - if (sURI == "getStatusHTML" ) return( HSM_GetStatusHTML ); - if (sURI == "getStatus" ) return( HSM_GetStatusXML ); + if (sURI == "GetStatusHTML" ) return( HSM_GetStatusHTML ); + if (sURI == "GetStatus" ) return( HSM_GetStatusXML ); if (sURI == "xml" ) return( HSM_GetStatusXML ); - if (sURI == "getProgramGuide" ) return( HSM_GetProgramGuide ); - if (sURI == "getProgramDetails" ) return( HSM_GetProgramDetails); - - if (sURI == "getHosts" ) return( HSM_GetHosts ); - if (sURI == "getKeys" ) return( HSM_GetKeys ); - if (sURI == "getSetting" ) return( HSM_GetSetting ); - if (sURI == "putSetting" ) return( HSM_PutSetting ); - - if (sURI == "getChannelIcon" ) return( HSM_GetChannelIcon ); - if (sURI == "getRecorded" ) return( HSM_GetRecorded ); - if (sURI == "getExpiring" ) return( HSM_GetExpiring ); - if (sURI == "getPreviewImage" ) return( HSM_GetPreviewImage ); - if (sURI == "getRecording" ) return( HSM_GetRecording ); - if (sURI == "getMusic" ) return( HSM_GetMusic ); - - if (sURI == "getDeviceDesc" ) return( HSM_GetDeviceDesc ); - if (sURI == "getCDSDesc" ) return( HSM_GetCDSDesc ); - if (sURI == "getCMGRDesc" ) return( HSM_GetCMGRDesc ); - - if (sURI == "*" ) return( HSM_Asterisk ); - - return( HSM_Unknown ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -bool HttpStatus::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) -{ - try - { - if (pRequest) - { - if (pRequest->m_sBaseUrl != "/") - return( false ); - - switch( GetMethod( pRequest->m_sMethod )) - { - case HSM_GetStatusXML : GetStatusXML ( pRequest ); return( true ); - case HSM_GetStatusHTML : GetStatusHTML ( pRequest ); return( true ); - - case HSM_GetProgramGuide: GetProgramGuide( pRequest ); return( true ); - - case HSM_GetProgramDetails: GetProgramDetails( pRequest ); return( true ); - - case HSM_GetHosts : GetHosts ( pRequest ); return( true ); - case HSM_GetKeys : GetKeys ( pRequest ); return( true ); - case HSM_GetSetting : GetSetting ( pRequest ); return( true ); - case HSM_PutSetting : PutSetting ( pRequest ); return( true ); - - case HSM_GetChannelIcon : GetChannelIcon ( pRequest ); return( true ); - case HSM_GetRecorded : GetRecorded ( pRequest ); return( true ); - case HSM_GetExpiring : GetExpiring ( pRequest ); return( true ); - case HSM_GetPreviewImage: GetPreviewImage( pRequest ); return( true ); - - case HSM_GetRecording : GetRecording ( pThread, pRequest ); return( true ); - case HSM_GetMusic : GetMusic ( pThread, pRequest ); return( true ); - default: break; - } - } - } - catch( ... ) - { - cerr << "HttpStatus::ProcessRequest() - Unexpected Exception" << endl; - } - - return( false ); -} - -// ========================================================================== -// Request handler Methods -// ========================================================================== - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetHosts( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - query.prepare("SELECT DISTINCTROW hostname " - "FROM settings WHERE (not isNull( hostname ));" ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - pRequest->m_response << ""; - - while(query.next()) - { - QString sHost = query.value(0).toString(); - - pRequest->m_response << "" - << HTTPRequest::Encode( sHost ) - << ""; - } - - pRequest->m_response << ""; - - } - } - else - { - QString sMsg = "Database not open while trying to load list of hosts"; - - VERBOSE(VB_IMPORTANT, sMsg ); - - pRequest->m_response << "" + sMsg + ""; - pRequest->m_nResponseStatus = 500; - - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetKeys( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - query.prepare("SELECT DISTINCTROW value FROM settings;" ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - pRequest->m_response << ""; - - while(query.next()) - { - QString sKey = query.value(0).toString(); - - pRequest->m_response << "" - << HTTPRequest::Encode( sKey ) - << ""; - } - - pRequest->m_response << ""; - - } - } - else - { - QString sMsg = QString("Database not open while trying to load setting: %1") - .arg( pRequest->m_mapParams[ "Key" ] ); - - VERBOSE(VB_IMPORTANT, sMsg ); - - pRequest->m_response << "" + HTTPRequest::Encode( sMsg ) + ""; - pRequest->m_nResponseStatus = 500; - - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetSetting( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - - QString sKey = pRequest->m_mapParams[ "Key" ]; - QString sHostName = pRequest->m_mapParams[ "HostName" ]; - QString sValue; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Was a Key Supplied? - - if (sKey.length() > 0) - { - query.prepare("SELECT data, hostname from settings " - "WHERE value = :KEY AND " - "(hostname = :HOSTNAME OR hostname IS NULL) " - "ORDER BY hostname DESC;" ); - - query.bindValue(":KEY" , sKey ); - query.bindValue(":HOSTNAME", sHostName ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - query.next(); - - if ( (sHostName.length() == 0) || - ((sHostName.length() > 0) && - (sHostName == query.value(1).toString()))) - { - sValue = query.value(0).toString(); - sHostName = query.value(1).toString(); - - pRequest->m_response << "" - << HTTPRequest::Encode( sValue ) - << ""; - return; - } - } - - } - else - { - - VERBOSE(VB_IMPORTANT, "HostName:" ); - VERBOSE(VB_IMPORTANT, sHostName ); - - if (sHostName.length() == 0) - { - query.prepare("SELECT value, data FROM settings " - "WHERE (hostname IS NULL)" ); - } - else - { - query.prepare("SELECT value, data FROM settings " - "WHERE (hostname = :HOSTNAME)" ); - query.bindValue(":HOSTNAME", sHostName ); - } - - query.exec(); - - if (query.isActive() && query.size() > 0) - { - pRequest->m_response << ""; - - while(query.next()) - { - sKey = query.value(0).toString(); - sValue = query.value(1).toString(); - - pRequest->m_response << "" - << HTTPRequest::Encode( sValue ) - << ""; - } - - pRequest->m_response << ""; - - return; - } - - } - - // Not found, so return the supplied default value - - pRequest->m_response << "" - << HTTPRequest::Encode( pRequest->m_mapParams[ "Default" ] ) - << ""; - } - else - { - QString sMsg = QString("Database not open while trying to load setting: %1") - .arg( pRequest->m_mapParams[ "Key" ] ); - - VERBOSE(VB_IMPORTANT, sMsg ); - - pRequest->m_response << "" << HTTPRequest::Encode( sMsg ) << ""; - pRequest->m_nResponseStatus = 500; - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::PutSetting( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - - QString sHostName = pRequest->m_mapParams[ "HostName" ]; - QString sKey = pRequest->m_mapParams[ "Key" ]; - QString sValue = pRequest->m_mapParams[ "Value" ]; - - if (sKey.length() > 0) - { - // -=>TODO: MythContext::SaveSettingOnHost does not handle NULL hostnames. - // This code should be removed when fixed in MythContext. - - if (sHostName.length() == 0) - { - - MSqlQuery query(MSqlQuery::InitCon()); - - query.prepare("DELETE FROM settings WHERE value = :KEY " - "AND hostname IS NULL;"); - query.bindValue(":KEY", sKey); - - if (!query.exec() || !query.isActive()) - MythContext::DBError("Clear setting", query); - } - - // End hack. - - gContext->SaveSettingOnHost( sKey, sValue, sHostName ); - - pRequest->m_response << ""; - } - else - pRequest->m_response << "Key Required"; - -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetProgramGuide( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - - QString sStartTime = pRequest->m_mapParams[ "StartTime" ]; - QString sEndTime = pRequest->m_mapParams[ "EndTime" ]; - int iNumOfChannels = pRequest->m_mapParams[ "NumOfChannels"].toInt(); - int iStartChanId = pRequest->m_mapParams[ "StartChanId" ].toInt(); - bool bDetails = pRequest->m_mapParams[ "Details" ].toInt(); - int iEndChanId = iStartChanId; - - QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); - QDateTime dtEnd = QDateTime::fromString( sEndTime , Qt::ISODate ); - - if (!dtStart.isValid()) - { - pRequest->m_response << "StartTime is invalid"; - return; - } - - if (!dtEnd.isValid()) - { - pRequest->m_response << "EndTime is invalid"; - return; - } - - if (dtEnd < dtStart) - { - pRequest->m_response << "EndTime is before StartTime"; - return; - } - - if (iNumOfChannels == 0) - iNumOfChannels = 1; - - if (iNumOfChannels == -1) - iNumOfChannels = SHRT_MAX; - - // Find the ending channel Id - - MSqlQuery query(MSqlQuery::InitCon()); - - query.prepare( "SELECT chanid FROM channel WHERE (chanid >= :STARTCHANID )" - " ORDER BY chanid LIMIT :NUMCHAN" ); - - query.bindValue(":STARTCHANID", iStartChanId ); - query.bindValue(":NUMCHAN" , iNumOfChannels ); - - if (!query.exec() || !query.isActive()) - MythContext::DBError("Select ChanId", query); - - query.first(); iStartChanId = query.value(0).toInt(); - query.last(); iEndChanId = query.value(0).toInt(); - - // Build add'l SQL statement for Program Listing - - MSqlBindings bindings; - QString sSQL = "WHERE program.chanid >= :StartChanId " - "AND program.chanid <= :EndChanId " - "AND program.starttime >= :StartDate " - "AND program.endtime <= :EndDate " - "GROUP BY program.starttime, channel.channum, " - "channel.callsign, program.title " - "ORDER BY program.chanid "; - - bindings[":StartChanId"] = iStartChanId; - bindings[":EndChanId" ] = iEndChanId; - bindings[":StartDate" ] = dtStart.toString( Qt::ISODate ); - bindings[":EndDate" ] = dtEnd.toString( Qt::ISODate ); - - // Get all Pending Scheduled Programs - - RecList recList; - ProgramList schedList; - - if (m_pSched) - m_pSched->getAllPending( &recList); - - // ---------------------------------------------------------------------- - // We need to convert from a RecList to a ProgramList - // (ProgramList will autodelete ProgramInfo pointers) - // ---------------------------------------------------------------------- - - for (RecIter itRecList = recList.begin(); - itRecList != recList.end(); itRecList++) - { - schedList.append( *itRecList ); - } - - // ---------------------------------------------------------------------- - - ProgramList progList; - - progList.FromProgram( sSQL, bindings, schedList ); - - // Build Response XML - - QDomDocument doc( "ProgramGuide" ); - - QDomElement root = doc.createElement("ProgramGuide"); - doc.appendChild(root); - - root.setAttribute("asOf" , QDateTime::currentDateTime().toString( Qt::ISODate )); - root.setAttribute("version" , MYTH_BINARY_VERSION ); - root.setAttribute("protoVer" , MYTH_PROTO_VERSION ); - root.setAttribute("totalCount", progList.count() ); - - // ---------------------------------------------------------------------- - - QDomElement channels = doc.createElement("Channels"); - root.appendChild( channels ); - - int iChanCount = 0; - QDomElement channel; - QString sCurChanId = ""; - ProgramInfo *pInfo = progList.first(); - - while (pInfo != NULL) - { - if ( sCurChanId != pInfo->chanid ) - { - iChanCount++; - - sCurChanId = pInfo->chanid; - - // Ouput new Channel Node - - channel = doc.createElement( "Channel" ); - channels.appendChild( channel ); - - FillChannelInfo( channel, pInfo, bDetails ); - } - - FillProgramInfo( &doc, channel, pInfo, false, bDetails ); - - pInfo = progList.next(); - - } - - root.setAttribute("startTime" , sStartTime ); - root.setAttribute("endTime" , sEndTime ); - root.setAttribute("startChanId" , iStartChanId ); - root.setAttribute("endChanId" , iEndChanId ); - root.setAttribute("numOfChannels", iChanCount ); - root.setAttribute("details" , bDetails ); - - pRequest->m_response << doc.toString(); -} - - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetProgramDetails( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - - QString sStartTime = pRequest->m_mapParams[ "StartTime" ]; - QString sChanId = pRequest->m_mapParams[ "ChanId" ]; - - QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); - - if (!dtStart.isValid()) - { - pRequest->m_response << "StartTime is invalid"; - return; - } - - // ---------------------------------------------------------------------- - // -=>TODO: Add support for getting Recorded Program Info - // ---------------------------------------------------------------------- - - // Build add'l SQL statement for Program Listing - - MSqlBindings bindings; - QString sSQL = "WHERE program.chanid = :ChanId " - "AND program.starttime = :StartTime "; - - bindings[":ChanId" ] = sChanId; - bindings[":StartTime"] = dtStart.toString( Qt::ISODate ); - - // Get all Pending Scheduled Programs - - RecList recList; - ProgramList schedList; - - if (m_pSched) - m_pSched->getAllPending( &recList); - - // ---------------------------------------------------------------------- - // We need to convert from a RecList to a ProgramList - // (ProgramList will autodelete ProgramInfo pointers) - // ---------------------------------------------------------------------- - - for (RecIter itRecList = recList.begin(); - itRecList != recList.end(); itRecList++) - { - schedList.append( *itRecList ); - } - - // ---------------------------------------------------------------------- - - ProgramList progList; - - progList.FromProgram( sSQL, bindings, schedList ); - - ProgramInfo *pInfo = progList.first(); - - if (pInfo==NULL) - { - pRequest->m_response << "Error Reading Program Info"; - return; - } - - // Build Response XML - - QDomDocument doc( "ProgramDetails" ); - - QDomElement root = doc.createElement("ProgramDetails"); - doc.appendChild(root); - - root.setAttribute("asOf" , QDateTime::currentDateTime().toString( Qt::ISODate )); - root.setAttribute("version" , MYTH_BINARY_VERSION ); - root.setAttribute("protoVer" , MYTH_PROTO_VERSION ); - root.setAttribute("totalCount", 1 ); - root.setAttribute("startTime" , sStartTime ); - root.setAttribute("chanId" , sChanId ); - - FillProgramInfo( &doc, root, pInfo, true ); - - pRequest->m_response << doc.toString(); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetChannelIcon( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeFile; - - int iChanId = pRequest->m_mapParams[ "ChanId" ].toInt(); - - // Read Icon file path from database - - MSqlQuery query(MSqlQuery::InitCon()); - - query.prepare( "SELECT icon FROM channel WHERE (chanid = :CHANID )" ); - query.bindValue(":CHANID", iChanId ); - - if (!query.exec() || !query.isActive()) - MythContext::DBError("Select ChanId", query); - - if (query.size() > 0) - { - query.first(); - - pRequest->m_sFileName = query.value(0).toString(); - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetRecorded( HTTPRequest *pRequest ) -{ - bool bDescending = pRequest->m_mapParams[ "Descending" ].toInt(); - - // Get all Pending Scheduled Programs - - RecList recList; - ProgramList schedList; - - if (m_pSched) - m_pSched->getAllPending( &recList); - - // ---------------------------------------------------------------------- - // We need to convert from a RecList to a ProgramList - // (ProgramList will autodelete ProgramInfo pointers) - // ---------------------------------------------------------------------- - - for (RecIter itRecList = recList.begin(); - itRecList != recList.end(); itRecList++) - { - schedList.append( *itRecList ); - } - - // ---------------------------------------------------------------------- - - ProgramList progList; - - progList.FromRecorded( bDescending, &schedList ); - - // Build Response XML - - QDomDocument doc( "Recorded" ); - - QDomElement root = doc.createElement("Recorded"); - doc.appendChild(root); - - root.setAttribute("asOf" , QDateTime::currentDateTime().toString( Qt::ISODate )); - root.setAttribute("version" , MYTH_BINARY_VERSION ); - root.setAttribute("protoVer" , MYTH_PROTO_VERSION ); - root.setAttribute("totalCount", progList.count() ); - - ProgramInfo *pInfo = progList.first(); - - while (pInfo != NULL) - { - FillProgramInfo( &doc, root, pInfo, true ); - - pInfo = progList.next(); - } - - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_response << doc.toString(); + return( HSM_Unknown ); } ///////////////////////////////////////////////////////////////////////////// -// +// ///////////////////////////////////////////////////////////////////////////// -void HttpStatus::GetExpiring( HTTPRequest *pRequest ) +bool HttpStatus::ProcessRequest( HttpWorkerThread * /* pThread */, HTTPRequest *pRequest ) { - pginfolist_t list; - - m_pExpirer->GetAllExpiring( list ); - - // Build Response XML - - QDomDocument doc( "Expiring" ); - - QDomElement root = doc.createElement("Expiring"); - doc.appendChild(root); - - root.setAttribute("asOf" , QDateTime::currentDateTime().toString( Qt::ISODate )); - root.setAttribute("version" , MYTH_BINARY_VERSION ); - root.setAttribute("protoVer" , MYTH_PROTO_VERSION ); - root.setAttribute("totalCount", list.size() ); - - pginfolist_t::iterator it = list.begin(); - for (; it !=list.end(); it++) + try { - ProgramInfo *pInfo = (*it); - - if (pInfo != NULL) + if (pRequest) { - FillProgramInfo( &doc, root, pInfo, true ); - delete pInfo; - } - } - - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - pRequest->m_eResponseType = ResponseTypeXML; - pRequest->m_response << doc.toString(); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetPreviewImage( HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeHTML; - pRequest->m_nResponseStatus = 404; - - // -=>TODO: Add Parameters to allow various sizes & times - - QString sChanId = pRequest->m_mapParams[ "ChanId" ]; - QString sStartTime= pRequest->m_mapParams[ "StartTime" ]; - - QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); - - if (!dtStart.isValid()) - return; - - // ---------------------------------------------------------------------- - // Read Recording From Database - // ---------------------------------------------------------------------- - - ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart ); - - if (pInfo==NULL) - return; - - if ( pInfo->hostname != gContext->GetHostName()) - { - // We only handle requests for local resources - - delete pInfo; - - return; - } - - // ---------------------------------------------------------------------- - // check to see if preview image is already created. - // ---------------------------------------------------------------------- - - QString sFileName = pInfo->GetRecordFilename( gContext->GetFilePrefix() ); - pRequest->m_sFileName = sFileName + ".png"; - - if (!QFile::exists( pRequest->m_sFileName )) - { - // Must generate Preview Image - - // Find first Local encoder - - EncoderLink *pEncoder = NULL; + if (pRequest->m_sBaseUrl != "/") + return( false ); - for ( QMap::Iterator it = m_pEncoders->begin(); - it != m_pEncoders->end(); - ++it ) - { - if (it.data()->IsLocal()) + switch( GetMethod( pRequest->m_sMethod )) { - pEncoder = it.data(); - break; - } - } - - if ( pEncoder == NULL) - { - delete pInfo; - return; - } - - // ------------------------------------------------------------------ - // Generate Preview Image and save. - // ------------------------------------------------------------------ - - int len = 0; - int width = 0, height = 0; - float aspect = 0; - int secondsin = gContext->GetNumSetting("PreviewPixmapOffset", 64) + - gContext->GetNumSetting("RecordPreRoll",0); - - unsigned char *data = (unsigned char *)pEncoder->GetScreenGrab(pInfo, - sFileName, - secondsin, - len, width, - height, aspect); - - - if (!data) - { - delete pInfo; - return; - } - - QImage img(data, width, height, 32, NULL, 65536 * 65536, QImage::LittleEndian); - - float ppw = gContext->GetNumSetting("PreviewPixmapWidth", 160); - float pph = gContext->GetNumSetting("PreviewPixmapHeight", 120); - - if (aspect <= 0) - aspect = ((float) width) / height; - - if (aspect > ppw / pph) - pph = rint(ppw / aspect); - else - ppw = rint(pph * aspect); - - img = img.smoothScale((int) ppw, (int) pph); - - img.save( pRequest->m_sFileName.ascii(), "PNG" ); - } - - if (pInfo) - delete pInfo; - - if (QFile::exists( pRequest->m_sFileName )) - { - pRequest->m_eResponseType = ResponseTypeFile; - pRequest->m_nResponseStatus = 200; - } - -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetRecording( HttpWorkerThread *pThread, - HTTPRequest *pRequest ) -{ - bool bIndexFile = false; - - pRequest->m_eResponseType = ResponseTypeHTML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - pRequest->m_nResponseStatus = 404; - - QString sChanId = pRequest->m_mapParams[ "ChanId" ]; - QString sStartTime= pRequest->m_mapParams[ "StartTime" ]; - - if (sStartTime.length() == 0) - return; - - // ---------------------------------------------------------------------- - // DSM-320 & DSM-520 Special File Request for Index file of MPEG - // - // -=>TODO: Need to reverse Engineer File Format & create on the fly - // from the RecordedMarkup Table. - // ---------------------------------------------------------------------- - - int nIdxPos = sStartTime.findRev( ".idx", -1, FALSE ); - - if (nIdxPos >=0 ) - { - bIndexFile = true; - sStartTime = sStartTime.left( nIdxPos ); - } - - // ---------------------------------------------------------------------- - // Check to see if this is another request for the same recording... - // ---------------------------------------------------------------------- - - ThreadData *pData = (ThreadData *)pThread->GetWorkerData(); - - if (pData != NULL) - { - if ((pData->m_eType == ThreadData::DT_Recording) && - pData->IsSameRecording( sChanId, sStartTime )) - { - pRequest->m_sFileName = pData->m_sFileName; - - } - else - pData = NULL; - } - - // ---------------------------------------------------------------------- - // New request if pData == NULL - // ---------------------------------------------------------------------- - - if (pData == NULL) - { - // ------------------------------------------------------------------ - // Load Program Information & build FileName - // ------------------------------------------------------------------ - - QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); - - if (!dtStart.isValid()) - return; - - // ------------------------------------------------------------------ - // Read Recording From Database - // ------------------------------------------------------------------ - - ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart ); - - if (pInfo==NULL) - return; - - if ( pInfo->hostname != gContext->GetHostName()) - { - // We only handle requests for local resources - - delete pInfo; - - return; - } - - pRequest->m_sFileName = pInfo->GetRecordFilename( gContext->GetFilePrefix() ); - - delete pInfo; - - // ------------------------------------------------------------------ - // Store File information in WorkerThread Storage for next request (cache) - // ------------------------------------------------------------------ - - pData = new ThreadData( sChanId, sStartTime, pRequest->m_sFileName ); - - pThread->SetWorkerData( pData ); - } - - // ---------------------------------------------------------------------- - // DSM-?20 Seek table support. - // ---------------------------------------------------------------------- - - if (bIndexFile) - pRequest->m_sFileName += ".idx"; - - // ---------------------------------------------------------------------- - // check to see if the file exists - // ---------------------------------------------------------------------- - - if (QFile::exists( pRequest->m_sFileName )) - { - pRequest->m_eResponseType = ResponseTypeFile; - pRequest->m_nResponseStatus = 200; - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::GetMusic( HttpWorkerThread *pThread, - HTTPRequest *pRequest ) -{ - pRequest->m_eResponseType = ResponseTypeHTML; - pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; - pRequest->m_nResponseStatus = 404; - - QString sId = pRequest->m_mapParams[ "Id" ]; - - if (sId.length() == 0) - return; + case HSM_GetStatusXML : GetStatusXML ( pRequest ); return true; + case HSM_GetStatusHTML : GetStatusHTML ( pRequest ); return true; - int nTrack = sId.toInt(); - - // ---------------------------------------------------------------------- - // Check to see if this is another request for the same recording... - // ---------------------------------------------------------------------- - - ThreadData *pData = (ThreadData *)pThread->GetWorkerData(); - - if (pData != NULL) - { - if ((pData->m_eType == ThreadData::DT_Music) && - (pData->m_nTrackNumber == nTrack)) - { - pRequest->m_sFileName = pData->m_sFileName; - - } - else - pData = NULL; - } - - // ---------------------------------------------------------------------- - // New request if pData == NULL - // ---------------------------------------------------------------------- - - if (pData == NULL) - { - QString sBasePath = gContext->GetSetting( "MusicLocation", ""); - - // ------------------------------------------------------------------ - // Load Track's FileName - // ------------------------------------------------------------------ - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - query.prepare("SELECT filename FROM musicmetadata WHERE intid = :KEY" ); - query.bindValue(":KEY", nTrack ); - query.exec(); + default: + { + pRequest->m_eResponseType = ResponseTypeHTML; + pRequest->m_nResponseStatus = 200; - if (query.isActive() && query.size() > 0) - { - query.first(); - pRequest->m_sFileName = QString( "%1/%2" ) - .arg( sBasePath ) - .arg( query.value(0).toString() ); + break; + } } } - - // ------------------------------------------------------------------ - // Store information in WorkerThread Storage for next request (cache) - // ------------------------------------------------------------------ - - pData = new ThreadData( nTrack, pRequest->m_sFileName ); - - pThread->SetWorkerData( pData ); } - - // ---------------------------------------------------------------------- - // check to see if the file exists - // ---------------------------------------------------------------------- - - if (QFile::exists( pRequest->m_sFileName )) + catch( ... ) { - pRequest->m_eResponseType = ResponseTypeFile; - pRequest->m_nResponseStatus = 200; + cerr << "HttpStatus::ProcessRequest() - Unexpected Exception" << endl; } -} + return( false ); +} ///////////////////////////////////////////////////////////////////////////// // @@ -1172,7 +199,7 @@ if (pInfo) { - FillProgramInfo(pDoc, encoder, pInfo); + MythXML::FillProgramInfo(pDoc, encoder, pInfo); delete pInfo; } @@ -1207,7 +234,7 @@ ((*itProg)->recstartts >= QDateTime::currentDateTime())) { iNumRecordings++; - FillProgramInfo(pDoc, scheduled, *itProg); + MythXML::FillProgramInfo(pDoc, scheduled, *itProg); } } @@ -1257,6 +284,7 @@ job.setAttribute("flags" , it.data().flags ); job.setAttribute("status" , it.data().status ); job.setAttribute("statusTime", it.data().statustime.toString(Qt::ISODate)); +// job.setAttribute("schedTime" , it.data().schedruntime.toString(Qt::ISODate)); job.setAttribute("args" , it.data().args ); if (it.data().hostname == "") @@ -1267,7 +295,7 @@ QDomText textNode = pDoc->createTextNode(it.data().comment); job.appendChild(textNode); - FillProgramInfo(pDoc, job, pInfo); + MythXML::FillProgramInfo(pDoc, job, pInfo); delete pInfo; } @@ -1289,67 +317,67 @@ mInfo.appendChild(guide ); // drive space --------------------- - - long long iTotal = -1, iUsed = -1, iAvail = -1; - - iAvail = getDiskSpace( gContext->GetFilePrefix(), iTotal, iUsed); - - storage.setAttribute("_local_total", (int)(iTotal>>10)); - storage.setAttribute("_local_used" , (int)(iUsed>>10)); - storage.setAttribute("_local_free" , (int)(iAvail>>10)); - - if (m_bIsMaster) - { - long long mTotal = 0, mUsed = 0, mAvail = 0; - long long gTotal = 0, gUsed = 0, gAvail = 0; - QString hosts = "_local"; - QMap backendsCounted; - QString encoderHost; - QMap::Iterator eit; - - gTotal = iTotal; - gUsed = iUsed; - gAvail = iAvail; - - for (eit = m_pEncoders->begin(); eit != m_pEncoders->end(); ++eit) - { - encoderHost = eit.data()->GetHostName(); - if (eit.data()->IsConnected() && - !eit.data()->IsLocal() && - !backendsCounted.contains(encoderHost)) - { - backendsCounted[encoderHost] = true; - hosts += "," + encoderHost; - - eit.data()->GetFreeDiskSpace(mTotal, mUsed); - mAvail = mTotal - mUsed; - - storage.setAttribute(encoderHost + "_total", (int)(mTotal>>10)); - storage.setAttribute(encoderHost + "_used" , (int)(mUsed>>10)); - storage.setAttribute(encoderHost + "_free" , (int)(mAvail>>10)); - - if ((mTotal == iTotal) && - (absLongLong(mAvail - iAvail) < (iAvail * 0.05))) - { - storage.setAttribute(encoderHost + "_shared" , 1); - } - else - { - storage.setAttribute(encoderHost + "_shared" , 0); - gTotal += mTotal; - gUsed += mUsed; - gAvail += mAvail; - } - } - } - storage.setAttribute("_total_total", (int)(gTotal>>10)); - storage.setAttribute("_total_used" , (int)(gUsed>>10)); - storage.setAttribute("_total_free" , (int)(gAvail>>10)); - - if (hosts != "") - hosts += ",_total"; - storage.setAttribute("slaves", hosts); - } + + long long iTotal = -1, iUsed = -1, iAvail = -1; + + iAvail = getDiskSpace( gContext->GetFilePrefix(), iTotal, iUsed); + + storage.setAttribute("_local_total", (int)(iTotal>>10)); + storage.setAttribute("_local_used" , (int)(iUsed>>10)); + storage.setAttribute("_local_free" , (int)(iAvail>>10)); + + if (m_bIsMaster) + { + long long mTotal = 0, mUsed = 0, mAvail = 0; + long long gTotal = 0, gUsed = 0, gAvail = 0; + QString hosts = "_local"; + QMap backendsCounted; + QString encoderHost; + QMap::Iterator eit; + + gTotal = iTotal; + gUsed = iUsed; + gAvail = iAvail; + + for (eit = m_pEncoders->begin(); eit != m_pEncoders->end(); ++eit) + { + encoderHost = eit.data()->GetHostName(); + if (eit.data()->IsConnected() && + !eit.data()->IsLocal() && + !backendsCounted.contains(encoderHost)) + { + backendsCounted[encoderHost] = true; + hosts += "," + encoderHost; + + eit.data()->GetFreeDiskSpace(mTotal, mUsed); + mAvail = mTotal - mUsed; + + storage.setAttribute(encoderHost + "_total", (int)(mTotal>>10)); + storage.setAttribute(encoderHost + "_used" , (int)(mUsed>>10)); + storage.setAttribute(encoderHost + "_free" , (int)(mAvail>>10)); + + if ((mTotal == iTotal) && + (absLongLong(mAvail - iAvail) < (iAvail * 0.05))) + { + storage.setAttribute(encoderHost + "_shared" , 1); + } + else + { + storage.setAttribute(encoderHost + "_shared" , 0); + gTotal += mTotal; + gUsed += mUsed; + gAvail += mAvail; + } + } + } + storage.setAttribute("_total_total", (int)(gTotal>>10)); + storage.setAttribute("_total_used" , (int)(gUsed>>10)); + storage.setAttribute("_total_free" , (int)(gAvail>>10)); + + if (hosts != "") + hosts += ",_total"; + storage.setAttribute("slaves", hosts); + } // load average --------------------- @@ -1400,7 +428,8 @@ } #ifdef HAVE_LMSENSORS - tempSettingLock.lock(); + m_settingLock.lock(); + if (!found_acpi) { int chip_nr, a, b; @@ -1434,7 +463,7 @@ } sensors_cleanup(); } - tempSettingLock.unlock(); + m_settingLock.unlock(); #endif // Guide Data --------------------- @@ -1442,7 +471,7 @@ QDateTime GuideDataThrough; MSqlQuery query(MSqlQuery::InitCon()); - query.prepare("SELECT max(endtime) FROM program;"); + query.prepare("SELECT MAX(endtime) FROM program WHERE manualid = 0;"); if (query.exec() && query.isActive() && query.size()) { @@ -1472,119 +501,6 @@ guide.appendChild(dataDirectMessage); } -void HttpStatus::FillProgramInfo(QDomDocument *pDoc, - QDomElement &e, - ProgramInfo *pInfo, - bool bIncChannel /* = true */, - bool bDetails /* = true */) -{ - if ((pDoc == NULL) || (pInfo == NULL)) - return; - - // Build Program Element - - QDomElement program = pDoc->createElement( "Program" ); - e.appendChild( program ); - - program.setAttribute( "startTime" , pInfo->startts.toString(Qt::ISODate)); - program.setAttribute( "endTime" , pInfo->endts.toString(Qt::ISODate)); - program.setAttribute( "title" , pInfo->title ); - program.setAttribute( "subTitle" , pInfo->subtitle ); - program.setAttribute( "category" , pInfo->category ); - program.setAttribute( "catType" , pInfo->catType ); - program.setAttribute( "repeat" , pInfo->repeat ); - - if (bDetails) - { - - program.setAttribute( "seriesId" , pInfo->seriesid ); - program.setAttribute( "programId" , pInfo->programid ); - program.setAttribute( "stars" , pInfo->stars ); - program.setAttribute( "fileSize" , longLongToString( pInfo->filesize )); - program.setAttribute( "lastModified", pInfo->lastmodified.toString(Qt::ISODate) ); - program.setAttribute( "programFlags", pInfo->programflags ); - program.setAttribute( "hostname" , pInfo->hostname ); - - if (pInfo->hasAirDate) - program.setAttribute( "airdate" , pInfo->originalAirDate.toString(Qt::ISODate) ); - - QDomText textNode = pDoc->createTextNode( pInfo->description ); - program.appendChild( textNode ); - - } - - if ( bIncChannel ) - { - // Build Channel Child Element - - QDomElement channel = pDoc->createElement( "Channel" ); - program.appendChild( channel ); - - FillChannelInfo( channel, pInfo, bDetails ); - } - - // Build Recording Child Element - - if ( pInfo->recstatus != rsUnknown ) - { - QDomElement recording = pDoc->createElement( "Recording" ); - program.appendChild( recording ); - - recording.setAttribute( "recStatus" , pInfo->recstatus ); - recording.setAttribute( "recPriority" , pInfo->recpriority ); - recording.setAttribute( "recStartTs" , pInfo->recstartts.toString(Qt::ISODate)); - recording.setAttribute( "recEndTs" , pInfo->recendts.toString(Qt::ISODate)); - - if (bDetails) - { - recording.setAttribute( "recordId" , pInfo->recordid ); - recording.setAttribute( "recGroup" , pInfo->recgroup ); - recording.setAttribute( "playGroup" , pInfo->playgroup ); - recording.setAttribute( "recType" , pInfo->rectype ); - recording.setAttribute( "dupInType" , pInfo->dupin ); - recording.setAttribute( "dupMethod" , pInfo->dupmethod ); - recording.setAttribute( "encoderId" , pInfo->cardid ); - recording.setAttribute( "recProfile" , pInfo->GetProgramRecordingProfile()); - recording.setAttribute( "preRollSeconds", m_nPreRollSeconds ); - } - } -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -void HttpStatus::FillChannelInfo( QDomElement &channel, - ProgramInfo *pInfo, - bool bDetails /* = true */ ) -{ - if (pInfo) - { -/* - QString sHostName = gContext->GetHostName(); - QString sPort = gContext->GetSettingOnHost( "BackendStatusPort", sHostName); - QString sIconURL = QString( "http://%1:%2/getChannelIcon?ChanId=%3" ) - .arg( sHostName ) - .arg( sPort ) - .arg( pInfo->chanid ); -*/ - - channel.setAttribute( "chanId" , pInfo->chanid ); - channel.setAttribute( "chanNum" , pInfo->chanstr ); - channel.setAttribute( "callSign" , pInfo->chansign ); -// channel.setAttribute( "iconURL" , sIconURL ); - channel.setAttribute( "channelName", pInfo->channame ); - - if (bDetails) - { - channel.setAttribute( "chanFilters", pInfo->chanOutputFilters ); - channel.setAttribute( "sourceId" , pInfo->sourceid ); - channel.setAttribute( "inputId" , pInfo->inputid ); - channel.setAttribute( "commFree" , pInfo->chancommfree); - } - } -} - ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// @@ -1679,7 +595,6 @@ << " div.loadstatus {\r\n" << " width:325px;\r\n" << " height:7em;\r\n" - << " float:right;\r\n" << " }\r\n" << " .jobfinished { color: #0000ff; }\r\n" << " .jobaborted { color: #7f0000; }\r\n" @@ -2087,6 +1002,7 @@ QDateTime endTs = QDateTime::fromString( p.attribute( "endTime" ,"" ), Qt::ISODate ); QDateTime recStartTs = QDateTime::fromString( r.attribute( "recStartTs","" ), Qt::ISODate ); QDateTime statusTime = QDateTime::fromString( e.attribute( "statusTime","" ), Qt::ISODate ); +// QDateTime schedRunTime = QDateTime::fromString( e.attribute( "schedTime","" ), Qt::ISODate ); QString sHostname = e.attribute( "hostname", "master" ); QString sComment = ""; @@ -2107,8 +1023,14 @@ if ( !sSubTitle.isNull() && !sSubTitle.isEmpty()) os << "" << sSubTitle << "

"; - os << "Job: " << JobQueue::JobText( nType ) << "
" - << "Status: " + os << "Job: " << JobQueue::JobText( nType ) << "
"; +/* + if (schedRunTime > QDateTime::currentDateTime()) + os << "Scheduled Run Time: " + << schedRunTime.toString(timeDateFormat) + << "
"; +*/ + os << "Status: " << JobQueue::StatusText( nStatus ) << "
" << "Status Time: " @@ -2181,70 +1103,70 @@ // local drive space --------------------- - node = info.namedItem( "Storage" ); + node = info.namedItem( "Storage" ); - if (!node.isNull()) - { - QDomElement e = node.toElement(); + if (!node.isNull()) + { + QDomElement e = node.toElement(); - if (!e.isNull()) - { - QString slaves = e.attribute("slaves", "_local"); - QStringList tokens = QStringList::split(",", slaves); + if (!e.isNull()) + { + QString slaves = e.attribute("slaves", "_local"); + QStringList tokens = QStringList::split(",", slaves); - os << " Disk Usage:
\r\n"; - os << "
    \r\n"; + os << " Disk Usage:
    \r\n"; + os << "
      \r\n"; - for (unsigned int i = 0; i < tokens.size(); i++) - { - int nFree = e.attribute(tokens[i] + "_free" , "0" ).toInt(); - int nTotal= e.attribute(tokens[i] + "_total", "0" ).toInt(); - int nUsed = e.attribute(tokens[i] + "_used" , "0" ).toInt(); + for (unsigned int i = 0; i < tokens.size(); i++) + { + int nFree = e.attribute(tokens[i] + "_free" , "0" ).toInt(); + int nTotal= e.attribute(tokens[i] + "_total", "0" ).toInt(); + int nUsed = e.attribute(tokens[i] + "_used" , "0" ).toInt(); - if (slaves == "_local") - { - // do nothing - } - else if (tokens[i] == "_local") - { - os << "
    • Master Backend:\r\n" - << "
        \r\n"; - } - else if (tokens[i] == "_total") - { - os << "
      • Total Disk Space:\r\n" - << "
          \r\n"; - } - else - { - os << "
        • " << tokens[i] << ": "; + if (slaves == "_local") + { + // do nothing + } + else if (tokens[i] == "_local") + { + os << "
        • Master Backend:\r\n" + << "
            \r\n"; + } + else if (tokens[i] == "_total") + { + os << "
          • Total Disk Space:\r\n" + << "
              \r\n"; + } + else + { + os << "
            • " << tokens[i] << ": "; - if (e.attribute(tokens[i] + "shared", "0").toInt()) - os << " (Shared with master)"; + if (e.attribute(tokens[i] + "shared", "0").toInt()) + os << " (Shared with master)"; - os << "\r\n" - << "
                \r\n"; - } + os << "\r\n" + << "
                  \r\n"; + } - os << "
                • Total Space: "; - sRep.sprintf( "%d,%03d MB ", (nTotal) / 1000, (nTotal) % 1000); - os << sRep << "
                • \r\n"; - - os << "
                • Space Used: "; - sRep.sprintf( "%d,%03d MB ", (nUsed) / 1000, (nUsed) % 1000); - os << sRep << "
                • \r\n"; - - os << "
                • Space Free: "; - sRep.sprintf( "%d,%03d MB ", (nFree) / 1000, (nFree) % 1000); - os << sRep << "
                • \r\n"; - - if (slaves != "_local_") - os << "
                \r\n" - << " \r\n"; - } - os << "
              \r\n"; - } - } + os << "
            • Total Space: "; + sRep.sprintf( "%d,%03d MB ", (nTotal) / 1000, (nTotal) % 1000); + os << sRep << "
            • \r\n"; + + os << "
            • Space Used: "; + sRep.sprintf( "%d,%03d MB ", (nUsed) / 1000, (nUsed) % 1000); + os << sRep << "
            • \r\n"; + + os << "
            • Space Free: "; + sRep.sprintf( "%d,%03d MB ", (nFree) / 1000, (nFree) % 1000); + os << sRep << "
            • \r\n"; + + if (slaves != "_local_") + os << "
            \r\n" + << "
          • \r\n"; + } + os << "
          \r\n"; + } + } // ACPI temperature ------------------ diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/httpstatus.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/httpstatus.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/httpstatus.h 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/httpstatus.h 2007-08-23 11:09:51.000000000 -0500 @@ -26,29 +26,7 @@ { HSM_Unknown = 0, HSM_GetStatusHTML = 1, - HSM_GetStatusXML = 2, - HSM_GetProgramGuide = 3, - - HSM_GetHosts = 4, - HSM_GetKeys = 5, - HSM_GetSetting = 6, - HSM_PutSetting = 7, - - HSM_GetChannelIcon = 8, - HSM_GetRecorded = 9, - HSM_GetPreviewImage = 10, - - HSM_GetRecording = 11, - HSM_GetMusic = 12, - - HSM_GetDeviceDesc = 13, - HSM_GetCDSDesc = 14, - HSM_GetCMGRDesc = 15, - - HSM_Asterisk = 16, - - HSM_GetExpiring = 17, - HSM_GetProgramDetails = 18 + HSM_GetStatusXML = 2 } HttpStatusMethod; @@ -60,66 +38,6 @@ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// -class ThreadData : public HttpWorkerData -{ - public: - - typedef enum - { - DT_Unknown = 0, - DT_Recording = 1, - DT_Music = 2 - - } ThreadDataType; - - - ThreadDataType m_eType; - - QString m_sChanId; - QString m_sStartTime; - QString m_sFileName; - - int m_nTrackNumber; - - public: - - ThreadData( long nTrackNumber, const QString &sFileName ) - { - m_eType = DT_Music; - m_nTrackNumber = nTrackNumber; - m_sFileName = sFileName; - } - - ThreadData( const QString &sChanId, - const QString &sStartTime, - const QString &sFileName ) - { - m_eType = DT_Recording; - m_sChanId = sChanId; - m_sStartTime = sStartTime; - m_sFileName = sFileName; - } - - virtual ~ThreadData() - { - } - - bool IsSameRecording( const QString &sChanId, - const QString &sStartTime ) - { - return( (sChanId == m_sChanId ) && (sStartTime == m_sStartTime )); - } -}; - - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -// -// -// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - class HttpStatus : public HttpServerExtension { private: @@ -129,55 +47,22 @@ AutoExpire *m_pExpirer; bool m_bIsMaster; int m_nPreRollSeconds; + QMutex m_settingLock; private: HttpStatusMethod GetMethod( const QString &sURI ); - void GetStatusXML ( HTTPRequest *pRequest ); - void GetStatusHTML ( HTTPRequest *pRequest ); - - void GetProgramGuide( HTTPRequest *pRequest ); - void GetProgramDetails( HTTPRequest *pRequest ); - - void GetHosts ( HTTPRequest *pRequest ); - void GetKeys ( HTTPRequest *pRequest ); - void GetSetting ( HTTPRequest *pRequest ); - void PutSetting ( HTTPRequest *pRequest ); - - void GetChannelIcon ( HTTPRequest *pRequest ); - void GetRecorded ( HTTPRequest *pRequest ); - void GetPreviewImage( HTTPRequest *pRequest ); - - void GetExpiring ( HTTPRequest *pRequest ); - - void GetRecording ( HttpWorkerThread *pThread, - HTTPRequest *pRequest ); - - void GetMusic ( HttpWorkerThread *pThread, - HTTPRequest *pRequest ); - - void GetDeviceDesc ( HTTPRequest *pRequest ); - void GetFile ( HTTPRequest *pRequest, QString sFileName ); - - void FillProgramInfo ( QDomDocument *pDoc, - QDomElement &e, - ProgramInfo *pInfo, - bool bIncChannel = true, - bool bDetails = true ); - - void FillStatusXML ( QDomDocument *pDoc); - void FillChannelInfo ( QDomElement &channel, - ProgramInfo *pInfo, - bool bDetails = true ); + void GetStatusXML ( HTTPRequest *pRequest ); + void GetStatusHTML ( HTTPRequest *pRequest ); + void FillStatusXML ( QDomDocument *pDoc); void PrintStatus ( QTextStream &os, QDomDocument *pDoc ); int PrintEncoderStatus( QTextStream &os, QDomElement encoders ); int PrintScheduled ( QTextStream &os, QDomElement scheduled ); int PrintJobQueue ( QTextStream &os, QDomElement jobs ); int PrintMachineInfo ( QTextStream &os, QDomElement info ); - QMutex tempSettingLock; public: HttpStatus( QMap *tvList, Scheduler *sched, AutoExpire *expirer, bool bIsMaster ); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/main.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/main.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/main.cpp 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/main.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -34,10 +34,8 @@ #include "libmythtv/programinfo.h" #include "libmythtv/dbcheck.h" #include "libmythtv/jobqueue.h" -#include "libmythupnp/upnp.h" -#include "upnpcdstv.h" -#include "upnpcdsmusic.h" +#include "mediaserver.h" #include "httpstatus.h" QMap tvList; @@ -49,8 +47,7 @@ HouseKeeper *housekeeping = NULL; QString logfile = ""; -HttpServer *g_pHttpServer = NULL; -UPnp *g_pUPnp = NULL; +MediaServer *g_pUPnp = NULL; bool setupTVs(bool ismaster, bool &error) { @@ -210,9 +207,6 @@ if (g_pUPnp) delete g_pUPnp; - if (g_pHttpServer) - delete g_pHttpServer; - if (pidfile != "") unlink(pidfile.ascii()); @@ -402,10 +396,12 @@ else if (!strcmp(a.argv()[argpos],"--version")) { extern const char *myth_source_version; - cout << "Library API version: " << MYTH_BINARY_VERSION << endl; - cout << "Source code version: " << myth_source_version << endl; + extern const char *myth_source_path; + cout << "Library API version : " << MYTH_BINARY_VERSION << endl; + cout << "Source code version : " << myth_source_version << endl; + cout << "SVN Branch : " << myth_source_path << endl; #ifdef MYTH_BUILD_CONFIG - cout << "Options compiled in:" <GetNumSetting("BackendServerPort", 6543); - int statusport = gContext->GetNumSetting("BackendStatusPort", 6544); QString myip = gContext->GetSetting("BackendServerIP"); QString masterip = gContext->GetSetting("MasterServerIP"); @@ -616,39 +611,13 @@ else jobqueue = new JobQueue(ismaster); - // Initialize & Start the Mini HttpServer - VERBOSE(VB_IMPORTANT, "Main::Starting HttpServer"); - - g_pHttpServer = new HttpServer(statusport); + // Start UPnP Services - if (!g_pHttpServer->ok()) - { - VERBOSE(VB_IMPORTANT, "Main::HttpServer Create Error"); - // exit(BACKEND_BUGGY_EXIT_NO_BIND_STATUS); - } + g_pUPnp = new MediaServer( ismaster, noupnp ); VERBOSE(VB_IMPORTANT, "Main::Registering HttpStatus Extension"); - g_pHttpServer->RegisterExtension(new HttpStatus(&tvList, sched, expirer, ismaster )); - - // Start UPnP Services For Master Backends Only - if (ismaster && noupnp) - cerr << "********* The UPNP service has been DISABLED with the " - "--noupnp option *********\n"; - - if (ismaster && !noupnp) - { - g_pUPnp = new UPnp(ismaster, g_pHttpServer); - - VERBOSE(VB_UPNP, "Main::Registering UPnpCDSTv Extension"); - - g_pUPnp->RegisterExtension(new UPnpCDSTv()); - - VERBOSE(VB_UPNP, "Main::Registering UPnpCDSMusic Extension"); - - g_pUPnp->RegisterExtension(new UPnpCDSMusic()); - } - // End uPnP & Mini HttpServer Initialization + g_pUPnp->GetHttpServer()->RegisterExtension(new HttpStatus(&tvList, sched, expirer, ismaster )); VERBOSE(VB_IMPORTANT, QString("%1 version: %2 www.mythtv.org") .arg(binname).arg(MYTH_BINARY_VERSION)); @@ -676,7 +645,7 @@ } } - new MainServer(ismaster, port, statusport, &tvList, sched, expirer); + new MainServer(ismaster, port, &tvList, sched, expirer); if (ismaster) { diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mainserver.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mainserver.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mainserver.cpp 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mainserver.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -152,7 +152,7 @@ bool threadlives; }; -MainServer::MainServer(bool master, int port, int /*statusport*/, +MainServer::MainServer(bool master, int port, QMap *tvList, Scheduler *sched, AutoExpire *expirer) { @@ -1064,7 +1064,7 @@ "LEFT JOIN channel ON recorded.chanid = channel.chanid " "LEFT JOIN recordedprogram ON " " ( recorded.chanid = recordedprogram.chanid AND " - " recorded.starttime = recordedprogram.starttime ) " + " recorded.progstart = recordedprogram.starttime ) " "WHERE ( recorded.deletepending = 0 OR " " DATE_ADD(recorded.lastmodified, INTERVAL 5 MINUTE) <= NOW() " " ) " @@ -1233,7 +1233,8 @@ proginfo->filesize = size; - proginfo->SetFilesize(size); + if (proginfo->recendts < QDateTime::currentDateTime()) + proginfo->SetFilesize(size); } } else @@ -1255,7 +1256,8 @@ { slave->FillProgramInfo(proginfo, playbackhost); - proginfo->SetFilesize(proginfo->filesize); + if (proginfo->recendts < QDateTime::currentDateTime()) + proginfo->SetFilesize(proginfo->filesize); } else { @@ -2947,22 +2949,23 @@ MythSocket *pbssock = pbs->getSocket(); int recnum = commands[1].toInt(); + QStringList retlist; QMap::Iterator iter = encoderList->find(recnum); if (iter == encoderList->end()) { VERBOSE(VB_IMPORTANT, "MainServer: " + QString("HandleRemoteEncoder(cmd %1) ").arg(slist[1]) + - QString("Unknown encoder: %1, exiting").arg(recnum)); - exit(BACKEND_BUGGY_EXIT_UNKNOWN_ENC); + QString("Unknown encoder: %1").arg(recnum)); + retlist << QString::number((int) kState_Error); + SendResponse(pbssock, retlist); + return; } EncoderLink *enc = iter.data(); QString command = slist[1]; - QStringList retlist; - if (command == "GET_STATE") { retlist << QString::number((int)enc->GetState()); @@ -3232,18 +3235,20 @@ int recnum = commands[1].toInt(); + QStringList retlist; FileTransfer *ft = getFileTransferByID(recnum); if (!ft) { VERBOSE(VB_IMPORTANT, QString("Unknown file transfer socket: %1") .arg(recnum)); - exit(BACKEND_BUGGY_EXIT_UNKNOWN_FILE_SOCK); + retlist << QString("ERROR: Unknown file transfer socket: %1") + .arg(recnum); + SendResponse(pbssock, retlist); + return; } QString command = slist[1]; - QStringList retlist; - ft->UpRef(); if (command == "IS_OPEN") diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mainserver.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mainserver.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mainserver.h 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mainserver.h 2007-08-23 11:09:51.000000000 -0500 @@ -25,7 +25,7 @@ { Q_OBJECT public: - MainServer(bool master, int port, int statusport, + MainServer(bool master, int port, QMap *tvList, Scheduler *sched, AutoExpire *expirer); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mediaserver.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mediaserver.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mediaserver.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mediaserver.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,218 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: mediaserver.cpp +// +// Purpose - uPnp Media Server main Class +// +// Created By : David Blain Created On : Jan. 15, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "mediaserver.h" +#include "mythxml.h" + +#include "upnpcdstv.h" +#include "upnpcdsmusic.h" +#include "upnpcdsvideo.h" + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// UPnp Class implementaion +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +MediaServer::MediaServer( bool bIsMaster, bool bDisableUPnp /* = FALSE */ ) +{ + VERBOSE(VB_UPNP, QString("MediaServer::Begin")); + + // ---------------------------------------------------------------------- + // Initialize Configuration class (Database for Servers) + // ---------------------------------------------------------------------- + + SetConfiguration( new DBConfiguration() ); + + // ---------------------------------------------------------------------- + // Create mini HTTP Server + // ---------------------------------------------------------------------- + + int nPort = g_pConfig->GetValue( "BackendStatusPort", 6544 ); + QString sIP = g_pConfig->GetValue( "BackendServerIP" , "" ); + + if (sIP.isEmpty()) + { + VERBOSE(VB_IMPORTANT, "MediaServer::No BackendServerIP Address defined"); + return; + } + + + m_pHttpServer = new HttpServer( nPort ); + + if (!m_pHttpServer->ok()) + { + VERBOSE(VB_IMPORTANT, "MediaServer::HttpServer Create Error"); + // exit(BACKEND_BUGGY_EXIT_NO_BIND_STATUS); + return; + } + + if (bDisableUPnp) + { + cerr << "********* The UPNP service has been DISABLED with the " + "--noupnp option *********\n"; + return; + } + + // ---------------------------------------------------------------------- + // BackendServerIP is only one IP address at this time... Doing Split anyway + // ---------------------------------------------------------------------- + + QStringList sIPAddrList = QStringList::split( ";", sIP ); + + // ---------------------------------------------------------------------- + // Initialize UPnp Stack + // ---------------------------------------------------------------------- + + if (Initialize( sIPAddrList, nPort, m_pHttpServer )) + { + + QString sSharePath = gContext->GetShareDir(); + QString sFileName = g_pConfig->GetValue( "upnpDescXmlPath", sSharePath ); + QString sDeviceType; + + if ( bIsMaster ) + { + sFileName += "devicemaster.xml"; + sDeviceType = "urn:schemas-mythtv-org:device:MasterMediaServer:1"; + } + else + { + sFileName += "deviceslave.xml"; + sDeviceType = "urn:schemas-mythtv-org:device:SlaveMediaServer:1"; + } + + // ------------------------------------------------------------------ + // Make sure our device Description is loaded. + // ------------------------------------------------------------------ + + VERBOSE(VB_UPNP, QString( "MediaServer::Loading UPnp Description" )); + + g_UPnpDeviceDesc.Load( sFileName ); + + UPnpDevice *pMythDevice = UPnpDeviceDesc::FindDevice( RootDevice(), + sDeviceType ); + + // ------------------------------------------------------------------ + // Register the MythXML protocol... + // ------------------------------------------------------------------ + + VERBOSE(VB_UPNP, QString( "MediaServer::Registering MythXML Service." )); + + m_pHttpServer->RegisterExtension( new MythXML( pMythDevice )); + + // ------------------------------------------------------------------ + // Register any HttpServerExtensions... Only The Master Backend + // ------------------------------------------------------------------ + + if (bIsMaster) + { + QString sSourceProtocols = "http-get:*:image/gif:*," + "http-get:*:image/jpeg:*," + "http-get:*:image/png:*," + "http-get:*:video/avi:*," + "http-get:*:audio/mpeg:*," + "http-get:*:audio/wav:*," + "http-get:*:video/mpeg:*," + "http-get:*:video/nupplevideo:*," + "http-get:*:video/x-ms-wmv:*"; + + VERBOSE(VB_UPNP, QString( "MediaServer::Registering MSRR Service." )); + + m_pHttpServer->RegisterExtension( new UPnpMSRR( RootDevice() )); + + VERBOSE(VB_UPNP, QString( "MediaServer::Registering CMGR Service." )); + + m_pHttpServer->RegisterExtension( m_pUPnpCMGR= new UPnpCMGR( RootDevice(), sSourceProtocols )); + + VERBOSE(VB_UPNP, QString( "MediaServer::Registering CDS Service." )); + + m_pHttpServer->RegisterExtension( m_pUPnpCDS = new UPnpCDS ( RootDevice() )); + + // ------------------------------------------------------------------ + // Register CDS Extensions + // ------------------------------------------------------------------ + + VERBOSE(VB_UPNP, "MediaServer::Registering UPnpCDSTv Extension"); + + RegisterExtension(new UPnpCDSTv()); + + VERBOSE(VB_UPNP, "MediaServer::Registering UPnpCDSMusic Extension"); + + RegisterExtension(new UPnpCDSMusic()); + + VERBOSE(VB_UPNP, "MediaServer::Registering UPnpCDSVideo Extension"); + + RegisterExtension(new UPnpCDSVideo()); + } + + // VERBOSE(VB_UPNP, QString( "MediaServer::Adding Context Listener" )); + + // gContext->addListener( this ); + + Start(); + + } + + VERBOSE(VB_UPNP, QString( "MediaServer::End" )); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +MediaServer::~MediaServer() +{ + // -=>TODO: Need to check to see if calling this more than once is ok. + +// gContext->removeListener(this); + + if (m_pHttpServer) + delete m_pHttpServer; +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// +/* +void MediaServer::customEvent( QCustomEvent *e ) +{ + if (MythEvent::Type(e->type()) == MythEvent::MythEventMessage) + { + MythEvent *me = (MythEvent *)e; + QString message = me->Message(); + + //-=>TODO: Need to handle events to notify clients of changes + } +} +*/ +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void MediaServer::RegisterExtension( UPnpCDSExtension *pExtension ) +{ + m_pUPnpCDS->RegisterExtension( pExtension ); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +void MediaServer::UnregisterExtension( UPnpCDSExtension *pExtension ) +{ + m_pUPnpCDS->UnregisterExtension( pExtension ); +} diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mediaserver.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mediaserver.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mediaserver.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mediaserver.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,50 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: mediaserver.h +// +// Purpose - uPnp Media Server main Class +// +// Created By : David Blain Created On : Jan. 15, 2007 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MEDIASERVER_H__ +#define __MEDIASERVER_H__ + +#include +#include + +#include "libmythupnp/upnp.h" + +#include "libmythupnp/upnpcds.h" +#include "libmythupnp/upnpcmgr.h" +#include "libmythupnp/upnpmsrr.h" + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class MediaServer : public UPnp +{ + + protected: + + UPnpCDS *m_pUPnpCDS; // Do not delete (auto deleted) + UPnpCMGR *m_pUPnpCMGR; // Do not delete (auto deleted) + + public: + MediaServer( bool bMaster, bool bDisableUPnp = FALSE ); + virtual ~MediaServer(); + +// void customEvent( QCustomEvent *e ); + + void RegisterExtension ( UPnpCDSExtension *pExtension ); + void UnregisterExtension( UPnpCDSExtension *pExtension ); + +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/MXML_scpd.xml /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/MXML_scpd.xml --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/MXML_scpd.xml 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/MXML_scpd.xml 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,413 @@ + + + + 1 + 0 + + + + GetAlbumArt + + + Id + in + A_ARG_TYPE_Id + + + Height + in + A_ARG_TYPE_Height + + + Width + in + A_ARG_TYPE_Width + + + + + GetChannelIcon + + + ChanId + in + A_ARG_TYPE_ChanId + + + Height + in + A_ARG_TYPE_Height + + + Width + in + A_ARG_TYPE_Width + + + + + GetConnectionInfo + + + Pin + in + A_ARG_TYPE_Value + + + Info + out + A_ARG_TYPE_XML + + + + + GetExpiring + + + Count + out + A_ARG_TYPE_Count + + + AsOf + out + A_ARG_TYPE_Time + + + Version + out + A_ARG_TYPE_Version + + + ProtoVer + out + A_ARG_TYPE_ProtoVer + + + Expiring + out + A_ARG_TYPE_XML + + + + + GetHosts + + + Count + out + A_ARG_TYPE_Count + + + Hosts + out + A_ARG_TYPE_XML + + + + + GetKeys + + + Count + out + A_ARG_TYPE_Count + + + Keys + out + A_ARG_TYPE_XML + + + + + GetMusic + + + Id + in + A_ARG_TYPE_Id + + + + + GetPreviewImage + + + ChanId + in + A_ARG_TYPE_ChanId + + + StartTime + in + A_ARG_TYPE_Time + + + + + GetProgramDetails + + + ChanId + in + A_ARG_TYPE_ChanId + + + StartTime + in + A_ARG_TYPE_Time + + + Count + out + A_ARG_TYPE_Count + + + AsOf + out + A_ARG_TYPE_Time + + + Version + out + A_ARG_TYPE_Version + + + ProtoVer + out + A_ARG_TYPE_ProtoVer + + + ProgramDetails + out + A_ARG_TYPE_XML + + + + + GetProgramGuide + + + StartTime + in + A_ARG_TYPE_Time + + + EndTime + in + A_ARG_TYPE_Time + + + StartChanId + in + A_ARG_TYPE_ChanId + + + NumOfCannels + in + A_ARG_TYPE_Count + + + Details + in + A_ARG_TYPE_Bool + + + Count + out + A_ARG_TYPE_Count + + + AsOf + out + A_ARG_TYPE_Time + + + Version + out + A_ARG_TYPE_Version + + + ProtoVer + out + A_ARG_TYPE_ProtoVer + + + ProgramGuide + out + A_ARG_TYPE_XML + + + + + GetRecorded + + + Descending + in + A_ARG_TYPE_Bool + + + Count + out + A_ARG_TYPE_Count + + + AsOf + out + A_ARG_TYPE_Time + + + Version + out + A_ARG_TYPE_Version + + + ProtoVer + out + A_ARG_TYPE_ProtoVer + + + Recorded + out + A_ARG_TYPE_XML + + + + + GetRecording + + + ChanId + in + A_ARG_TYPE_ChanId + + + StartTime + in + A_ARG_TYPE_Time + + + + + GetSetting + + + Key + in + A_ARG_TYPE_Key + + + HostName + in + A_ARG_TYPE_HostName + + + Default + in + A_ARG_TYPE_Value + + + Count + out + A_ARG_TYPE_Count + + + Values + out + A_ARG_TYPE_XML + + + + + GetVideo + + + Id + in + A_ARG_TYPE_Id + + + + + PutSetting + + + Key + in + A_ARG_TYPE_Key + + + HostName + in + A_ARG_TYPE_HostName + + + Value + in + A_ARG_TYPE_Value + + + Result + out + A_ARG_TYPE_Bool + + + + + + + A_ARG_TYPE_Version + string + + + A_ARG_TYPE_Time + string + + + A_ARG_TYPE_ChanId + string + + + A_ARG_TYPE_Value + string + + + A_ARG_TYPE_Key + string + + + A_ARG_TYPE_ProtoVer + string + + + A_ARG_TYPE_Id + i2 + + + A_ARG_TYPE_Bool + boolean + + + A_ARG_TYPE_XML + string + + + A_ARG_TYPE_Count + i2 + + + A_ARG_TYPE_HostName + string + + + A_ARG_TYPE_Height + i2 + + + A_ARG_TYPE_Width + i2 + + + \ No newline at end of file diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mythbackend.pro /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mythbackend.pro --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mythbackend.pro 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mythbackend.pro 2007-08-23 11:09:51.000000000 -0500 @@ -9,17 +9,24 @@ target.path = $${PREFIX}/bin INSTALLS = target +setting.path = $${PREFIX}/share/mythtv/ +setting.files += devicemaster.xml deviceslave.xml MXML_scpd.xml + +INSTALLS += setting + QMAKE_CLEAN += $(TARGET) # Input HEADERS += autoexpire.h encoderlink.h filetransfer.h httpstatus.h mainserver.h HEADERS += playbacksock.h scheduler.h server.h housekeeper.h -HEADERS += upnpcdstv.h upnpcdsmusic.h +HEADERS += upnpcdstv.h upnpcdsmusic.h upnpcdsvideo.h mediaserver.h +HEADERS += mythxml.h SOURCES += autoexpire.cpp encoderlink.cpp filetransfer.cpp httpstatus.cpp SOURCES += main.cpp mainserver.cpp playbacksock.cpp scheduler.cpp server.cpp SOURCES += housekeeper.cpp -SOURCES += upnpcdstv.cpp upnpcdsmusic.cpp +SOURCES += upnpcdstv.cpp upnpcdsmusic.cpp upnpcdsvideo.cpp mediaserver.cpp +SOURCES += mythxml.cpp using_oss:DEFINES += USING_OSS diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mythxml.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mythxml.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mythxml.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mythxml.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,1615 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: MythXML.cpp +// +// Purpose - Html & XML status HttpServerExtension +// +// Created By : David Blain Created On : Oct. 24, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#include "mythxml.h" + +#include "libmyth/mythcontext.h" +#include "libmyth/util.h" +#include "libmyth/mythdbcon.h" + +#include +#include +#include +#include +#include +#include + +#include "../../config.h" +#ifdef HAVE_LMSENSORS + #define LMSENSOR_DEFAULT_CONFIG_FILE "/etc/sensors.conf" + #include + #include +#endif + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +MythXML::MythXML( UPnpDevice *pDevice ) : Eventing( "MythXML", "MYTHTV_Event" ) +{ + m_pEncoders = &tvList; + m_pSched = sched; + m_pExpirer = expirer; + + m_nPreRollSeconds = gContext->GetNumSetting("RecordPreRoll", 0); + + // Add any event variables... + + // --- none at this time. + + QString sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath", m_sSharePath ); + + m_sServiceDescFileName = sUPnpDescPath + "MXML_scpd.xml"; + m_sControlUrl = "/Myth"; + + // Add our Service Definition to the device. + + RegisterService( pDevice ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +MythXML::~MythXML() +{ +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +MythXMLMethod MythXML::GetMethod( const QString &sURI ) +{ + if (sURI == "GetServDesc" ) return MXML_GetServiceDescription; + + if (sURI == "GetProgramGuide" ) return MXML_GetProgramGuide; + if (sURI == "GetProgramDetails" ) return MXML_GetProgramDetails; + + if (sURI == "GetHosts" ) return MXML_GetHosts; + if (sURI == "GetKeys" ) return MXML_GetKeys; + if (sURI == "GetSetting" ) return MXML_GetSetting; + if (sURI == "PutSetting" ) return MXML_PutSetting; + + if (sURI == "GetChannelIcon" ) return MXML_GetChannelIcon; + if (sURI == "GetAlbumArt" ) return MXML_GetAlbumArt; + if (sURI == "GetRecorded" ) return MXML_GetRecorded; + if (sURI == "GetExpiring" ) return MXML_GetExpiring; + if (sURI == "GetPreviewImage" ) return MXML_GetPreviewImage; + if (sURI == "GetRecording" ) return MXML_GetRecording; + if (sURI == "GetVideo" ) return MXML_GetVideo; + if (sURI == "GetMusic" ) return MXML_GetMusic; + if (sURI == "GetConnectionInfo" ) return MXML_GetConnectionInfo; + + return( MXML_Unknown ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +bool MythXML::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) +{ + try + { + if (pRequest) + { + if (pRequest->m_sBaseUrl != m_sControlUrl) + return( false ); + + switch( GetMethod( pRequest->m_sMethod )) + { + case MXML_GetServiceDescription: pRequest->FormatFileResponse( m_sServiceDescFileName ); return true; + + case MXML_GetProgramGuide : GetProgramGuide( pRequest ); return true; + case MXML_GetProgramDetails : GetProgramDetails( pRequest ); return true; + + case MXML_GetHosts : GetHosts ( pRequest ); return true; + case MXML_GetKeys : GetKeys ( pRequest ); return true; + case MXML_GetSetting : GetSetting ( pRequest ); return true; + case MXML_PutSetting : PutSetting ( pRequest ); return true; + + case MXML_GetChannelIcon : GetChannelIcon ( pRequest ); return true; + case MXML_GetRecorded : GetRecorded ( pRequest ); return true; + case MXML_GetExpiring : GetExpiring ( pRequest ); return true; + case MXML_GetPreviewImage : GetPreviewImage( pRequest ); return true; + + case MXML_GetRecording : GetRecording ( pThread, pRequest ); return true; + case MXML_GetMusic : GetMusic ( pThread, pRequest ); return true; + case MXML_GetVideo : GetVideo ( pThread, pRequest ); return true; + + case MXML_GetConnectionInfo : GetConnectionInfo( pRequest ); return true; + case MXML_GetAlbumArt : GetAlbumArt ( pRequest ); return true; + + default: + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); + + return true; + } + } + } + } + catch( ... ) + { + VERBOSE( VB_IMPORTANT, "MythXML::ProcessRequest() - Unexpected Exception" ); + } + + return( false ); +} + +// ========================================================================== +// Request handler Methods +// ========================================================================== + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetHosts( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + query.prepare("SELECT DISTINCTROW hostname " + "FROM settings WHERE (not isNull( hostname ));" ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + QString sHosts; + QTextStream os( &sHosts, IO_WriteOnly ); + + while(query.next()) + { + QString sHost = query.value(0).toString(); + + os << "" + << HTTPRequest::Encode( sHost ) + << ""; + } + + NameValueList list; + + list.append( new NameValue( "Count", query.size() )); + list.append( new NameValue( "Hosts", sHosts )); + + pRequest->FormatActionResponse( &list ); + + } + } + else + UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, "Database not open while trying to load list of hosts" ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetKeys( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + query.prepare("SELECT DISTINCTROW value FROM settings;" ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + QString sKeys; + QTextStream os( &sKeys, IO_WriteOnly ); + + while(query.next()) + { + QString sKey = query.value(0).toString(); + + os << "" + << HTTPRequest::Encode( sKey ) + << ""; + } + + NameValueList list; + + list.append( new NameValue( "Count", query.size() )); + list.append( new NameValue( "Keys" , sKeys )); + + pRequest->FormatActionResponse( &list ); + } + } + else + UPnp::FormatErrorResponse( pRequest, + UPnPResult_ActionFailed, + QString("Database not open while trying to load setting: %1") + .arg( pRequest->m_mapParams[ "Key" ] )); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetSetting( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + QString sKey = pRequest->m_mapParams[ "Key" ]; + QString sHostName = pRequest->m_mapParams[ "HostName" ]; + QString sValue; + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + NameValueList list; + + // Was a Key Supplied? + + QString sXml; + QTextStream os( &sXml, IO_WriteOnly ); + + if (sKey.length() > 0) + { + query.prepare("SELECT data, hostname from settings " + "WHERE value = :KEY AND " + "(hostname = :HOSTNAME OR hostname IS NULL) " + "ORDER BY hostname DESC;" ); + + query.bindValue(":KEY" , sKey ); + query.bindValue(":HOSTNAME", sHostName ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + query.next(); + + if ( (sHostName.length() == 0) || + ((sHostName.length() > 0) && + (sHostName == query.value(1).toString()))) + { + sValue = query.value(0).toString(); + sHostName = query.value(1).toString(); + + os << "" + << HTTPRequest::Encode( sValue ) + << ""; + + list.append( new NameValue( "Count" , 1 )); + list.append( new NameValue( "HostName", sHostName )); + list.append( new NameValue( "Values", sXml )); + + pRequest->FormatActionResponse( &list ); + + return; + } + } + + } + else + { + + if (sHostName.length() == 0) + { + query.prepare("SELECT value, data FROM settings " + "WHERE (hostname IS NULL)" ); + } + else + { + query.prepare("SELECT value, data FROM settings " + "WHERE (hostname = :HOSTNAME)" ); + query.bindValue(":HOSTNAME", sHostName ); + } + + query.exec(); + + if (query.isActive() && query.size() > 0) + { + + while(query.next()) + { + sKey = query.value(0).toString(); + sValue = query.value(1).toString(); + + os << "" + << HTTPRequest::Encode( sValue ) + << ""; + } + + + list.append( new NameValue( "Count" , query.size())); + list.append( new NameValue( "HostName", sHostName )); + list.append( new NameValue( "Values" , sXml )); + + pRequest->FormatActionResponse( &list ); + + return; + } + + } + + // Not found, so return the supplied default value + + os << "" + << HTTPRequest::Encode( pRequest->m_mapParams[ "Default" ] ) + << ""; + + + list.append( new NameValue( "Count" , 1 )); + list.append( new NameValue( "HostName", sHostName)); + list.append( new NameValue( "Values", sXml )); + + pRequest->FormatActionResponse( &list ); + } + else + UPnp::FormatErrorResponse( pRequest, + UPnPResult_ActionFailed, + QString("Database not open while trying to load setting: %1") + .arg( pRequest->m_mapParams[ "Key" ] )); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::PutSetting( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + QString sHostName = pRequest->m_mapParams[ "HostName" ]; + QString sKey = pRequest->m_mapParams[ "Key" ]; + QString sValue = pRequest->m_mapParams[ "Value" ]; + + if (sKey.length() > 0) + { + NameValueList list; + + gContext->SaveSettingOnHost( sKey, sValue, sHostName ); + + list.append( new NameValue( "Result", "True" )); + + pRequest->FormatActionResponse( &list ); + } + else + UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidArgs, "Key Required" ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetProgramGuide( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + QString sStartTime = pRequest->m_mapParams[ "StartTime" ]; + QString sEndTime = pRequest->m_mapParams[ "EndTime" ]; + int iNumOfChannels = pRequest->m_mapParams[ "NumOfChannels"].toInt(); + int iStartChanId = pRequest->m_mapParams[ "StartChanId" ].toInt(); + bool bDetails = pRequest->m_mapParams[ "Details" ].toInt(); + int iEndChanId = iStartChanId; + + QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); + QDateTime dtEnd = QDateTime::fromString( sEndTime , Qt::ISODate ); + + if (!dtStart.isValid()) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid, "StartTime is invalid" ); + return; + } + + if (!dtEnd.isValid()) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid, "EndTime is invalid" ); + return; + } + + if (dtEnd < dtStart) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid, "EndTime is before StartTime"); + return; + } + + if (iNumOfChannels == 0) + iNumOfChannels = 1; + + if (iNumOfChannels == -1) + iNumOfChannels = SHRT_MAX; + + // Find the ending channel Id + + MSqlQuery query(MSqlQuery::InitCon()); + + query.prepare( "SELECT chanid FROM channel WHERE (chanid >= :STARTCHANID )" + " ORDER BY chanid LIMIT :NUMCHAN" ); + + query.bindValue(":STARTCHANID", iStartChanId ); + query.bindValue(":NUMCHAN" , iNumOfChannels ); + + if (!query.exec() || !query.isActive()) + MythContext::DBError("Select ChanId", query); + + query.first(); iStartChanId = query.value(0).toInt(); + query.last(); iEndChanId = query.value(0).toInt(); + + // Build add'l SQL statement for Program Listing + + MSqlBindings bindings; + QString sSQL = "WHERE program.chanid >= :StartChanId " + "AND program.chanid <= :EndChanId " + "AND program.starttime >= :StartDate " + "AND program.endtime <= :EndDate " + "GROUP BY program.starttime, channel.channum, " + "channel.callsign, program.title " + "ORDER BY program.chanid "; + + bindings[":StartChanId"] = iStartChanId; + bindings[":EndChanId" ] = iEndChanId; + bindings[":StartDate" ] = dtStart.toString( Qt::ISODate ); + bindings[":EndDate" ] = dtEnd.toString( Qt::ISODate ); + + // Get all Pending Scheduled Programs + + RecList recList; + ProgramList schedList; + + if (m_pSched) + m_pSched->getAllPending( &recList); + + // ---------------------------------------------------------------------- + // We need to convert from a RecList to a ProgramList + // (ProgramList will autodelete ProgramInfo pointers) + // ---------------------------------------------------------------------- + + for (RecIter itRecList = recList.begin(); + itRecList != recList.end(); itRecList++) + { + schedList.append( *itRecList ); + } + + // ---------------------------------------------------------------------- + + ProgramList progList; + + progList.FromProgram( sSQL, bindings, schedList ); + + // Build Response + + QDomDocument doc; + + QDomElement channels = doc.createElement("Channels"); + doc.appendChild( channels ); + + int iChanCount = 0; + QDomElement channel; + QString sCurChanId = ""; + ProgramInfo *pInfo = progList.first(); + + while (pInfo != NULL) + { + if ( sCurChanId != pInfo->chanid ) + { + iChanCount++; + + sCurChanId = pInfo->chanid; + + // Ouput new Channel Node + + channel = doc.createElement( "Channel" ); + channels.appendChild( channel ); + + FillChannelInfo( channel, pInfo, bDetails ); + } + + FillProgramInfo( &doc, channel, pInfo, false, bDetails ); + + pInfo = progList.next(); + + } + + // ---------------------------------------------------------------------- + + NameValueList list; + + list.append( new NameValue( "StartTime" , sStartTime )); + list.append( new NameValue( "EndTime" , sEndTime )); + list.append( new NameValue( "StartChanId" , iStartChanId )); + list.append( new NameValue( "EndChanId" , iEndChanId )); + list.append( new NameValue( "NumOfChannels", iChanCount )); + list.append( new NameValue( "Details" , bDetails )); + + list.append( new NameValue( "Count" , (int)progList.count() )); + list.append( new NameValue( "AsOf" , QDateTime::currentDateTime().toString( Qt::ISODate ))); + list.append( new NameValue( "Version" , MYTH_BINARY_VERSION )); + list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION )); + list.append( new NameValue( "ProgramGuide" , doc.toString() )); + + pRequest->FormatActionResponse( &list ); + +} + + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetProgramDetails( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + QString sStartTime = pRequest->m_mapParams[ "StartTime" ]; + QString sChanId = pRequest->m_mapParams[ "ChanId" ]; + + QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); + + if (!dtStart.isValid()) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid, "StartTime is invalid" ); + return; + } + + // ---------------------------------------------------------------------- + // -=>TODO: Add support for getting Recorded Program Info + // ---------------------------------------------------------------------- + + // Build add'l SQL statement for Program Listing + + MSqlBindings bindings; + QString sSQL = "WHERE program.chanid = :ChanId " + "AND program.starttime = :StartTime "; + + bindings[":ChanId" ] = sChanId; + bindings[":StartTime"] = dtStart.toString( Qt::ISODate ); + + // Get all Pending Scheduled Programs + + RecList recList; + ProgramList schedList; + + if (m_pSched) + m_pSched->getAllPending( &recList); + + // ---------------------------------------------------------------------- + // We need to convert from a RecList to a ProgramList + // (ProgramList will autodelete ProgramInfo pointers) + // ---------------------------------------------------------------------- + + for (RecIter itRecList = recList.begin(); + itRecList != recList.end(); itRecList++) + { + schedList.append( *itRecList ); + } + + // ---------------------------------------------------------------------- + + ProgramList progList; + + progList.FromProgram( sSQL, bindings, schedList ); + + ProgramInfo *pInfo = progList.first(); + + if (pInfo==NULL) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, "Error Reading Program Info" ); + return; + } + + // Build Response XML + + QDomDocument doc; + +// QDomElement root = doc.createElement("Programs"); +// doc.appendChild(root); + + FillProgramInfo( &doc, doc, pInfo, true ); + + // ---------------------------------------------------------------------- + + NameValueList list; + + list.append( new NameValue( "StartTime" , sStartTime )); + list.append( new NameValue( "ChanId" , sChanId )); + + list.append( new NameValue( "Count" , 1 )); + list.append( new NameValue( "AsOf" , QDateTime::currentDateTime().toString( Qt::ISODate ))); + list.append( new NameValue( "Version" , MYTH_BINARY_VERSION )); + list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION )); + + list.append( new NameValue( "ProgramDetails" , doc.toString() )); + + pRequest->FormatActionResponse( &list ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetChannelIcon( HTTPRequest *pRequest ) +{ + bool bDefaultPixmap = false; + + pRequest->m_eResponseType = ResponseTypeFile; + + int iChanId = pRequest->m_mapParams[ "ChanId" ].toInt(); + + // Optional Parameters + + int nWidth = pRequest->m_mapParams[ "Width" ].toInt(); + int nHeight = pRequest->m_mapParams[ "Height" ].toInt(); + + // Read Icon file path from database + + MSqlQuery query(MSqlQuery::InitCon()); + + query.prepare( "SELECT icon FROM channel WHERE (chanid = :CHANID )" ); + query.bindValue(":CHANID", iChanId ); + + if (!query.exec() || !query.isActive()) + MythContext::DBError("Select ChanId", query); + + if (query.size() > 0) + { + query.first(); + + pRequest->m_sFileName = query.value(0).toString(); + } + + if ((nWidth == 0) && (nHeight == 0)) + { + bDefaultPixmap = true; + } + + QString sFileName; + + if (bDefaultPixmap) + { + return; + } + else + sFileName = QString( "%1.%2x%3.png" ) + .arg( pRequest->m_sFileName ) + .arg( nWidth ) + .arg( nHeight ); + + // ---------------------------------------------------------------------- + // check to see if image is already created. + // ---------------------------------------------------------------------- + + if (QFile::exists( sFileName )) + { + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; + pRequest->m_sFileName = sFileName; + return; + } + + float fAspect = 0.0; + + QImage *pImage = new QImage(pRequest->m_sFileName); + + if (!pImage) + return; + + if (fAspect <= 0) + fAspect = (float)(pImage->width()) / pImage->height(); + + if ( nWidth == 0 ) + nWidth = (int)rint(nHeight * fAspect); + + if ( nHeight == 0 ) + nHeight = (int)rint(nWidth / fAspect); + + QImage img = pImage->smoothScale( nWidth, nHeight); + + img.save( sFileName.ascii(), "PNG" ); + + delete pImage; + + pRequest->m_sFileName = sFileName; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetAlbumArt( HTTPRequest *pRequest ) +{ + bool bDefaultPixmap = false; + + pRequest->m_eResponseType = ResponseTypeFile; + + int iId = pRequest->m_mapParams[ "Id" ].toInt(); + + // Optional Parameters + + int nWidth = pRequest->m_mapParams[ "Width" ].toInt(); + int nHeight = pRequest->m_mapParams[ "Height" ].toInt(); + + // Read AlbumArt file path from database + + MSqlQuery query(MSqlQuery::InitCon()); + query.prepare("SELECT CONCAT_WS('/', music_directories.path, " + "music_albumart.filename) FROM music_albumart " + "LEFT JOIN music_directories ON " + "music_directories.directory_id=music_albumart.directory_id " + "WHERE music_albumart.albumart_id = :ARTID;"); + query.bindValue(":ARTID", iId ); + + if (!query.exec() || !query.isActive()) + MythContext::DBError("Select ArtId", query); + + QString musicbasepath = gContext->GetSetting("MusicLocation", ""); + + if (query.size() > 0) + { + query.first(); + + pRequest->m_sFileName = musicbasepath + query.value(0).toString(); + } + + if ((nWidth == 0) && (nHeight == 0)) + { + bDefaultPixmap = true; + } + + QString sFileName; + + if (bDefaultPixmap) + { + return; + } + else + sFileName = QString( "%1.%2x%3.png" ) + .arg( pRequest->m_sFileName ) + .arg( nWidth ) + .arg( nHeight ); + + // ---------------------------------------------------------------------- + // check to see if albumart image is already created. + // ---------------------------------------------------------------------- + + if (QFile::exists( sFileName )) + { + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; + pRequest->m_sFileName = sFileName; + return; + } + + // ------------------------------------------------------------------ + // Must generate Albumart Image, Generate Image and save. + // ------------------------------------------------------------------ + + float fAspect = 0.0; + + QImage *pImage = new QImage(pRequest->m_sFileName); + + if (!pImage) + return; + + if (fAspect <= 0) + fAspect = (float)(pImage->width()) / pImage->height(); + + if ( nWidth == 0 ) + nWidth = (int)rint(nHeight * fAspect); + + if ( nHeight == 0 ) + nHeight = (int)rint(nWidth / fAspect); + + QImage img = pImage->smoothScale( nWidth, nHeight); + + img.save( sFileName.ascii(), "PNG" ); + + delete pImage; + + pRequest->m_sFileName = sFileName; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetRecorded( HTTPRequest *pRequest ) +{ + bool bDescending = pRequest->m_mapParams[ "Descending" ].toInt(); + + // Get all Pending Scheduled Programs + + RecList recList; + ProgramList schedList; + + if (m_pSched) + m_pSched->getAllPending( &recList); + + // ---------------------------------------------------------------------- + // We need to convert from a RecList to a ProgramList + // (ProgramList will autodelete ProgramInfo pointers) + // ---------------------------------------------------------------------- + + for (RecIter itRecList = recList.begin(); + itRecList != recList.end(); itRecList++) + { + schedList.append( *itRecList ); + } + + // ---------------------------------------------------------------------- + + ProgramList progList; + + progList.FromRecorded( bDescending, &schedList ); + + // Build Response XML + + QDomDocument doc; + + QDomElement root = doc.createElement("Programs"); + doc.appendChild(root); + + ProgramInfo *pInfo = progList.first(); + + while (pInfo != NULL) + { + FillProgramInfo( &doc, root, pInfo, true ); + + pInfo = progList.next(); + } + + // ---------------------------------------------------------------------- + + NameValueList list; + + list.append( new NameValue( "Count" , (int)progList.count())); + list.append( new NameValue( "AsOf" , QDateTime::currentDateTime().toString( Qt::ISODate ))); + list.append( new NameValue( "Version" , MYTH_BINARY_VERSION )); + list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION )); + + list.append( new NameValue( "Recorded" , doc.toString() )); + + pRequest->FormatActionResponse( &list ); + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetExpiring( HTTPRequest *pRequest ) +{ + pginfolist_t infoList; + + m_pExpirer->GetAllExpiring( infoList ); + + // Build Response XML + + QDomDocument doc; + + QDomElement root = doc.createElement("Programs"); + doc.appendChild(root); + + pginfolist_t::iterator it = infoList.begin(); + for (; it !=infoList.end(); it++) + { + ProgramInfo *pInfo = (*it); + + if (pInfo != NULL) + { + FillProgramInfo( &doc, root, pInfo, true ); + delete pInfo; + } + } + + // ---------------------------------------------------------------------- + + NameValueList list; + + list.append( new NameValue( "Count" , (int)infoList.size())); + list.append( new NameValue( "AsOf" , QDateTime::currentDateTime().toString( Qt::ISODate ))); + list.append( new NameValue( "Version" , MYTH_BINARY_VERSION )); + list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION )); + + list.append( new NameValue( "Expiring" , doc.toString() )); + + pRequest->FormatActionResponse( &list ); + +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetPreviewImage( HTTPRequest *pRequest ) +{ + bool bDefaultPixmap = false; + + pRequest->m_eResponseType = ResponseTypeHTML; + pRequest->m_nResponseStatus = 404; + + QString sChanId = pRequest->m_mapParams[ "ChanId" ]; + QString sStartTime= pRequest->m_mapParams[ "StartTime" ]; + + // Optional Parameters + + int nWidth = pRequest->m_mapParams[ "Width" ].toInt(); + int nHeight = pRequest->m_mapParams[ "Height" ].toInt(); + int nSecsIn = pRequest->m_mapParams[ "SecsIn" ].toInt(); + + QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); + + if (!dtStart.isValid()) + return; + + // ---------------------------------------------------------------------- + // Read Recording From Database + // ---------------------------------------------------------------------- + + ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart ); + + if (pInfo==NULL) + return; + + if ( pInfo->hostname != gContext->GetHostName()) + { + // We only handle requests for local resources + + delete pInfo; + + return; + } + + // ---------------------------------------------------------------------- + + if ((nWidth == 0) && (nHeight == 0)) + { + bDefaultPixmap = true; + nWidth = gContext->GetNumSetting("PreviewPixmapWidth", 160); + nHeight = gContext->GetNumSetting("PreviewPixmapHeight", 120); + } + + // ---------------------------------------------------------------------- + // Determine Time the image should be extracted from + // ---------------------------------------------------------------------- + + if (nSecsIn == 0) + { + nSecsIn = gContext->GetNumSetting("PreviewPixmapOffset", 64) + + gContext->GetNumSetting("RecordPreRoll",0); + } + + // ---------------------------------------------------------------------- + // If a specific size/time is requested, don't use cached image. + // ---------------------------------------------------------------------- + // -=>TODO: should cache custom sized images... + // would need to decide how to delete + // ---------------------------------------------------------------------- + + QString sFileName = pInfo->GetPlaybackURL(); + + if (bDefaultPixmap) + pRequest->m_sFileName = sFileName + ".png"; + else + pRequest->m_sFileName = QString( "%1.%2x%3x%4.png" ) + .arg( sFileName ) + .arg( nWidth ) + .arg( nHeight ) + .arg( nSecsIn ); + + // ---------------------------------------------------------------------- + // check to see if preview image is already created. + // ---------------------------------------------------------------------- + + if (QFile::exists( pRequest->m_sFileName )) + { + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; + return; + } + + // ------------------------------------------------------------------ + // Must generate Preview Image, Generate Image and save. + // ------------------------------------------------------------------ + + float fAspect = 0.0; + + QImage *pImage = GeneratePreviewImage( pInfo, + sFileName, + nSecsIn, + fAspect ); + + if (pImage == NULL) + { + delete pInfo; + return; + } + + // ------------------------------------------------------------------ + + if (bDefaultPixmap) + { + + if (fAspect <= 0) + fAspect = (float)(nWidth) / nHeight; + + if (fAspect > nWidth / nHeight) + nHeight = (int)rint(nWidth / fAspect); + else + nWidth = (int)rint(nHeight * fAspect); + } + else + { + if ( nWidth == 0 ) + nWidth = (int)rint(nHeight * fAspect); + + if ( nHeight == 0 ) + nHeight = (int)rint(nWidth / fAspect); + + /* + QByteArray aBytes; + QBuffer buffer( aBytes ); + + buffer.open( IO_WriteOnly ); + img.save( &buffer, "PNG" ); + + pRequest->m_eResponseType = ResponseTypeOther; + pRequest->m_sResponseTypeText = pRequest->GetMimeType( "png" ); + pRequest->m_nResponseStatus = 200; + + pRequest->m_response.writeRawBytes( aBytes.data(), aBytes.size() ); + */ + } + + QImage img = pImage->smoothScale( nWidth, nHeight); + + img.save( pRequest->m_sFileName.ascii(), "PNG" ); + + delete pImage; + + if (pInfo) + delete pInfo; + + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QImage *MythXML::GeneratePreviewImage( ProgramInfo *pInfo, + const QString &sFileName, + int nSecsIn, + float &fAspect ) +{ + + // Find first Local encoder + + EncoderLink *pEncoder = NULL; + + for ( QMap::Iterator it = m_pEncoders->begin(); + it != m_pEncoders->end(); + ++it ) + { + if (it.data()->IsLocal()) + { + pEncoder = it.data(); + break; + } + } + + if ( pEncoder == NULL) + return NULL; + + // ------------------------------------------------------------------ + // Generate Preview Image and save. + // ------------------------------------------------------------------ + + int nLen = 0, nWidth = 0, nHeight = 0; + + unsigned char *pData = (unsigned char *)pEncoder->GetScreenGrab( pInfo, + sFileName, + nSecsIn, + nLen, + nWidth, + nHeight, + fAspect); + if (!pData) + return NULL; + + return new QImage( pData, nWidth, nHeight, 32, NULL, 65536 * 65536, QImage::LittleEndian ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetRecording( HttpWorkerThread *pThread, + HTTPRequest *pRequest ) +{ + bool bIndexFile = false; + + pRequest->m_eResponseType = ResponseTypeHTML; + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + pRequest->m_nResponseStatus = 404; + + QString sChanId = pRequest->m_mapParams[ "ChanId" ]; + QString sStartTime= pRequest->m_mapParams[ "StartTime" ]; + + if (sStartTime.length() == 0) + { + VERBOSE( VB_UPNP, "MythXML::GetRecording - StartTime missing."); + return; + } + + // ---------------------------------------------------------------------- + // DSM-320 & DSM-520 Special File Request for Index file of MPEG + // + // -=>TODO: Need to reverse Engineer File Format & create on the fly + // from the RecordedMarkup Table. + // ---------------------------------------------------------------------- + + int nIdxPos = sStartTime.findRev( ".idx", -1, FALSE ); + + if (nIdxPos >=0 ) + { + bIndexFile = true; + sStartTime = sStartTime.left( nIdxPos ); + } + + // ---------------------------------------------------------------------- + // Check to see if this is another request for the same recording... + // ---------------------------------------------------------------------- + + ThreadData *pData = (ThreadData *)pThread->GetWorkerData(); + + if (pData != NULL) + { + if ((pData->m_eType == ThreadData::DT_Recording) && + pData->IsSameRecording( sChanId, sStartTime )) + { + pRequest->m_sFileName = pData->m_sFileName; + + } + else + pData = NULL; + } + + // ---------------------------------------------------------------------- + // New request if pData == NULL + // ---------------------------------------------------------------------- + + if (pData == NULL) + { + // ------------------------------------------------------------------ + // Load Program Information & build FileName + // ------------------------------------------------------------------ + + QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate ); + + if (!dtStart.isValid()) + { + VERBOSE( VB_UPNP, "MythXML::GetRecording - StartTime Invalid."); + return; + } + + // ------------------------------------------------------------------ + // Read Recording From Database + // ------------------------------------------------------------------ + + ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart ); + + if (pInfo==NULL) + { + VERBOSE( VB_UPNP, QString( "MythXML::GetRecording - GetProgramFromRecorded( %1, %2 ) returned NULL" ) + .arg( sChanId ) + .arg( sStartTime )); + return; + } + + if ( pInfo->hostname != gContext->GetHostName()) + { + // We only handle requests for local resources + + VERBOSE( VB_UPNP, QString( "MythXML::GetRecording - To access this recording, send request to %1." ) + .arg( pInfo->hostname )); + + delete pInfo; + + return; + } + + pRequest->m_sFileName = pInfo->GetPlaybackURL(); + + delete pInfo; + + // ------------------------------------------------------------------ + // Store File information in WorkerThread Storage for next request (cache) + // ------------------------------------------------------------------ + + pData = new ThreadData( sChanId, sStartTime, pRequest->m_sFileName ); + + pThread->SetWorkerData( pData ); + } + + // ---------------------------------------------------------------------- + // DSM-?20 Seek table support. + // ---------------------------------------------------------------------- + + if (bIndexFile) + pRequest->m_sFileName += ".idx"; + + // ---------------------------------------------------------------------- + // check to see if the file exists + // ---------------------------------------------------------------------- + + if (QFile::exists( pRequest->m_sFileName )) + { + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetMusic( HttpWorkerThread *pThread, + HTTPRequest *pRequest ) +{ + pRequest->m_eResponseType = ResponseTypeHTML; + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + pRequest->m_nResponseStatus = 404; + + QString sId = pRequest->m_mapParams[ "Id" ]; + + if (sId.length() == 0) + return; + + int nTrack = sId.toInt(); + + // ---------------------------------------------------------------------- + // Check to see if this is another request for the same recording... + // ---------------------------------------------------------------------- + + ThreadData *pData = (ThreadData *)pThread->GetWorkerData(); + + if (pData != NULL) + { + if ((pData->m_eType == ThreadData::DT_Music) && + (pData->m_nTrackNumber == nTrack)) + { + pRequest->m_sFileName = pData->m_sFileName; + + } + else + pData = NULL; + } + + // ---------------------------------------------------------------------- + // New request if pData == NULL + // ---------------------------------------------------------------------- + + if (pData == NULL) + { + QString sBasePath = gContext->GetSetting( "MusicLocation", ""); + + // ------------------------------------------------------------------ + // Load Track's FileName + // ------------------------------------------------------------------ + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + query.prepare("SELECT music_songs.filename AS filename FROM music_songs " + "WHERE music_songs.song_id = :KEY"); + + query.bindValue(":KEY", nTrack ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + query.first(); + pRequest->m_sFileName = QString( "%1/%2" ) + .arg( sBasePath ) + .arg( query.value(0).toString() ); + } + } + + // ------------------------------------------------------------------ + // Store information in WorkerThread Storage for next request (cache) + // ------------------------------------------------------------------ + + pData = new ThreadData( nTrack, pRequest->m_sFileName ); + + pThread->SetWorkerData( pData ); + } + + // ---------------------------------------------------------------------- + // check to see if the file exists + // ---------------------------------------------------------------------- + + if (QFile::exists( pRequest->m_sFileName )) + { + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetVideo( HttpWorkerThread *pThread, + HTTPRequest *pRequest ) +{ + pRequest->m_eResponseType = ResponseTypeHTML; + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + pRequest->m_nResponseStatus = 404; + + QString sId = pRequest->m_mapParams[ "Id" ]; + + if (sId.length() == 0) + return; + + // ---------------------------------------------------------------------- + // Check to see if this is another request for the same recording... + // ---------------------------------------------------------------------- + + ThreadData *pData = (ThreadData *)pThread->GetWorkerData(); + + if (pData != NULL) + { + if ((pData->m_eType == ThreadData::DT_Video) && (pData->m_sVideoID == sId )) + { + pRequest->m_sFileName = pData->m_sFileName; + + } + else + pData = NULL; + } + + // ---------------------------------------------------------------------- + // New request if pData == NULL + // ---------------------------------------------------------------------- + + if (pData == NULL) + { + QString sBasePath = ""; + + // ------------------------------------------------------------------ + // Load Track's FileName + // ------------------------------------------------------------------ + + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + query.prepare("SELECT filename FROM videometadata WHERE intid = :KEY" ); + query.bindValue(":KEY", sId ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + query.first(); + pRequest->m_sFileName = QString( "%1/%2" ) + .arg( sBasePath ) + .arg( query.value(0).toString() ); + } + } + + // ------------------------------------------------------------------ + // Store information in WorkerThread Storage for next request (cache) + // ------------------------------------------------------------------ + + pData = new ThreadData( sId, pRequest->m_sFileName ); + + pThread->SetWorkerData( pData ); + } + + // ---------------------------------------------------------------------- + // check to see if the file exists + // ---------------------------------------------------------------------- + + if (QFile::exists( pRequest->m_sFileName )) + { + pRequest->m_eResponseType = ResponseTypeFile; + pRequest->m_nResponseStatus = 200; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::GetConnectionInfo( HTTPRequest *pRequest ) +{ + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + QString sPin = pRequest->m_mapParams[ "Pin" ]; + QString sSecurityPin = gContext->GetSetting( "SecurityPin", ""); + + if (( sSecurityPin.length() != 0 ) && ( sPin != sSecurityPin )) + { + UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionNotAuthorized ); + return; + } + + DatabaseParams params = gContext->GetDatabaseParams(); + + // Check for DBHostName of "localhost" and change to public name or IP + + QString sServerIP = gContext->GetSetting( "BackendServerIP", "localhost" ); + QString sPeerIP = pRequest->GetPeerAddress(); + + if ((params.dbHostName == "localhost") && + (sServerIP != "localhost") && + (sServerIP != sPeerIP )) + { + params.dbHostName = sServerIP; + } + + QString sXml; + QTextStream os( &sXml, IO_WriteOnly ); + + os << ""; + os << "" << params.dbHostName << ""; +// os << "" << params.dbPort << ""; + os << "" << params.dbUserName << ""; + os << "" << params.dbPassword << ""; + os << "" << params.dbName << ""; + os << "" << params.dbType << ""; + os << ""; + + os << ""; + os << "" << params.wolEnabled << ""; + os << "" << params.wolReconnect << ""; + os << "" << params.wolRetry << ""; + os << "" << params.wolCommand << ""; + os << ""; + + NameValueList list; + + list.append( new NameValue( "Info", sXml )); + + pRequest->FormatActionResponse( &list ); + + return; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// Static Methods +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::FillProgramInfo(QDomDocument *pDoc, + QDomNode &node, + ProgramInfo *pInfo, + bool bIncChannel /* = true */, + bool bDetails /* = true */) +{ + if ((pDoc == NULL) || (pInfo == NULL)) + return; + + // Build Program Element + + QDomElement program = pDoc->createElement( "Program" ); + node.appendChild( program ); + + program.setAttribute( "startTime" , pInfo->startts.toString(Qt::ISODate)); + program.setAttribute( "endTime" , pInfo->endts.toString(Qt::ISODate)); + program.setAttribute( "title" , pInfo->title ); + program.setAttribute( "subTitle" , pInfo->subtitle ); + program.setAttribute( "category" , pInfo->category ); + program.setAttribute( "catType" , pInfo->catType ); + program.setAttribute( "repeat" , pInfo->repeat ); + + if (bDetails) + { + + program.setAttribute( "seriesId" , pInfo->seriesid ); + program.setAttribute( "programId" , pInfo->programid ); + program.setAttribute( "stars" , pInfo->stars ); + program.setAttribute( "fileSize" , longLongToString( pInfo->filesize )); + program.setAttribute( "lastModified", pInfo->lastmodified.toString(Qt::ISODate) ); + program.setAttribute( "programFlags", pInfo->programflags ); + program.setAttribute( "hostname" , pInfo->hostname ); + + if (pInfo->hasAirDate) + program.setAttribute( "airdate" , pInfo->originalAirDate.toString(Qt::ISODate) ); + + QDomText textNode = pDoc->createTextNode( pInfo->description ); + program.appendChild( textNode ); + + } + + if ( bIncChannel ) + { + // Build Channel Child Element + + QDomElement channel = pDoc->createElement( "Channel" ); + program.appendChild( channel ); + + FillChannelInfo( channel, pInfo, bDetails ); + } + + // Build Recording Child Element + + if ( pInfo->recstatus != rsUnknown ) + { + QDomElement recording = pDoc->createElement( "Recording" ); + program.appendChild( recording ); + + recording.setAttribute( "recStatus" , pInfo->recstatus ); + recording.setAttribute( "recPriority" , pInfo->recpriority ); + recording.setAttribute( "recStartTs" , pInfo->recstartts.toString(Qt::ISODate)); + recording.setAttribute( "recEndTs" , pInfo->recendts.toString(Qt::ISODate)); + + if (bDetails) + { + recording.setAttribute( "recordId" , pInfo->recordid ); + recording.setAttribute( "recGroup" , pInfo->recgroup ); + recording.setAttribute( "playGroup" , pInfo->playgroup ); + recording.setAttribute( "recType" , pInfo->rectype ); + recording.setAttribute( "dupInType" , pInfo->dupin ); + recording.setAttribute( "dupMethod" , pInfo->dupmethod ); + recording.setAttribute( "encoderId" , pInfo->cardid ); + recording.setAttribute( "recProfile" , pInfo->GetProgramRecordingProfile()); + // recording.setAttribute( "preRollSeconds", m_nPreRollSeconds ); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void MythXML::FillChannelInfo( QDomElement &channel, + ProgramInfo *pInfo, + bool bDetails /* = true */ ) +{ + if (pInfo) + { +/* + QString sHostName = gContext->GetHostName(); + QString sPort = gContext->GetSettingOnHost( "BackendStatusPort", sHostName); + QString sIconURL = QString( "http://%1:%2/getChannelIcon?ChanId=%3" ) + .arg( sHostName ) + .arg( sPort ) + .arg( pInfo->chanid ); +*/ + + channel.setAttribute( "chanId" , pInfo->chanid ); + channel.setAttribute( "chanNum" , pInfo->chanstr ); + channel.setAttribute( "callSign" , pInfo->chansign ); +// channel.setAttribute( "iconURL" , sIconURL ); + channel.setAttribute( "channelName", pInfo->channame ); + + if (bDetails) + { + channel.setAttribute( "chanFilters", pInfo->chanOutputFilters ); + channel.setAttribute( "sourceId" , pInfo->sourceid ); + channel.setAttribute( "inputId" , pInfo->inputid ); + channel.setAttribute( "commFree" , pInfo->chancommfree); + } + } +} + +// vim:set shiftwidth=4 tabstop=4 expandtab: diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mythxml.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mythxml.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/mythxml.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/mythxml.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,219 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: mythxml.h +// +// Purpose - Myth XML protocol HttpServerExtension +// +// Created By : David Blain Created On : Oct. 24, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef MYTHXML_H_ +#define MYTHXML_H_ + +#include +#include + +#include "upnp.h" +//#include "httpserver.h" +#include "mainserver.h" +#include "eventing.h" + +#include "autoexpire.h" +#include "mythcontext.h" +#include "jobqueue.h" +#include "programinfo.h" + +extern QMap tvList; +extern AutoExpire *expirer; +extern Scheduler *sched; + +typedef enum +{ + MXML_Unknown = 0, + MXML_GetServiceDescription = 1, + + MXML_GetProgramGuide = 2, + MXML_GetHosts = 3, + MXML_GetKeys = 4, + MXML_GetSetting = 5, + MXML_PutSetting = 6, + + MXML_GetChannelIcon = 7, + MXML_GetRecorded = 8, + MXML_GetPreviewImage = 9, + + MXML_GetRecording = 10, + MXML_GetMusic = 11, + + MXML_GetExpiring = 12, + MXML_GetProgramDetails = 13, + MXML_GetVideo = 14, + + MXML_GetConnectionInfo = 15, + MXML_GetAlbumArt = 16 + +} MythXMLMethod; + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class MythXML : public Eventing +{ + private: + + QString m_sControlUrl; + QString m_sServiceDescFileName; + + Scheduler *m_pSched; + QMap *m_pEncoders; + AutoExpire *m_pExpirer; + bool m_bIsMaster; + int m_nPreRollSeconds; + + protected: + + // Implement UPnpServiceImpl methods that we can + + virtual QString GetServiceType () { return "urn:schemas-mythtv-org:service:MythTv:1"; } + virtual QString GetServiceId () { return "urn:mythtv-org:serviceId:MYTHTV_1-0"; } + virtual QString GetServiceControlURL() { return m_sControlUrl.mid( 1 ); } + virtual QString GetServiceDescURL () { return m_sControlUrl.mid( 1 ) + "/GetServDesc"; } + + private: + + MythXMLMethod GetMethod( const QString &sURI ); + + void GetProgramGuide( HTTPRequest *pRequest ); + void GetProgramDetails( HTTPRequest *pRequest ); + + void GetHosts ( HTTPRequest *pRequest ); + void GetKeys ( HTTPRequest *pRequest ); + void GetSetting ( HTTPRequest *pRequest ); + void PutSetting ( HTTPRequest *pRequest ); + + void GetChannelIcon ( HTTPRequest *pRequest ); + void GetRecorded ( HTTPRequest *pRequest ); + void GetPreviewImage( HTTPRequest *pRequest ); + + void GetConnectionInfo( HTTPRequest *pRequest ); + void GetAlbumArt ( HTTPRequest *pRequest ); + + QImage *GeneratePreviewImage( ProgramInfo *pInfo, + const QString &sFileName, + int nSecsIn, + float &fAspect ); + + void GetExpiring ( HTTPRequest *pRequest ); + + void GetRecording ( HttpWorkerThread *pThread, + HTTPRequest *pRequest ); + + void GetMusic ( HttpWorkerThread *pThread, + HTTPRequest *pRequest ); + + void GetVideo ( HttpWorkerThread *pThread, + HTTPRequest *pRequest ); + + + void GetDeviceDesc ( HTTPRequest *pRequest ); + void GetFile ( HTTPRequest *pRequest, QString sFileName ); + + public: + MythXML( UPnpDevice *pDevice ); + virtual ~MythXML(); + + bool ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ); + + // Static methods shared with HttpStatus + + static void FillProgramInfo ( QDomDocument *pDoc, + QDomNode &node, + ProgramInfo *pInfo, + bool bIncChannel = true, + bool bDetails = true ); + + static void FillChannelInfo ( QDomElement &channel, + ProgramInfo *pInfo, + bool bDetails = true ); + +}; + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class ThreadData : public HttpWorkerData +{ + public: + + typedef enum + { + DT_Unknown = 0, + DT_Recording = 1, + DT_Music = 2, + DT_Video = 3 + + + } ThreadDataType; + + + ThreadDataType m_eType; + + QString m_sChanId; + QString m_sStartTime; + QString m_sFileName; + QString m_sVideoID; + + int m_nTrackNumber; + + public: + + ThreadData( long nTrackNumber, const QString &sFileName ) + { + m_eType = DT_Music; + m_nTrackNumber = nTrackNumber; + m_sFileName = sFileName; + } + + ThreadData( const QString &sChanId, + const QString &sStartTime, + const QString &sFileName ) + { + m_eType = DT_Recording; + m_sChanId = sChanId; + m_sStartTime = sStartTime; + m_sFileName = sFileName; + } + + ThreadData( const QString &sVideoID, + const QString &sFileName ) + { + m_eType = DT_Video; + m_sVideoID = sVideoID; + m_sFileName = sFileName; + } + + + virtual ~ThreadData() + { + } + + bool IsSameRecording( const QString &sChanId, + const QString &sStartTime ) + { + return( (sChanId == m_sChanId ) && (sStartTime == m_sStartTime )); + } +}; + + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/scheduler.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/scheduler.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/scheduler.cpp 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/scheduler.cpp 2007-03-22 21:27:14.000000000 -0500 @@ -1552,7 +1552,7 @@ if (recIter != reclist.end()) { ProgramInfo *nextRecording = (*recIter); - QDateTime restarttime = nextRecording->startts.addSecs((-1) * + QDateTime restarttime = nextRecording->recstartts.addSecs((-1) * prerollseconds); int add = gContext->GetNumSetting("StartupSecsBeforeRecording", 240); @@ -1989,17 +1989,15 @@ { MSqlQuery epicnt(dbConn); - epicnt.prepare("SELECT count(*) FROM recorded " - "WHERE title = :TITLE AND duplicate <> 0;"); - epicnt.bindValue(":TITLE", qtitle.utf8()); + epicnt.prepare("SELECT DISTINCT chanid, progstart, progend " + "FROM recorded " + "WHERE recordid = :RECID AND preserve = 0 " + "AND recgroup <> 'LiveTV';"); + epicnt.bindValue(":RECID", recid); - epicnt.exec(); - - if (epicnt.isActive() && epicnt.size() > 0) + if (epicnt.exec() && epicnt.isActive()) { - epicnt.next(); - - if (epicnt.value(0).toInt() >= maxEpisodes) + if ((epicnt.size() > 0) && (epicnt.size() >= maxEpisodes)) { tooManyMap[recid] = true; checkTooMany = true; diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsmusic.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsmusic.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsmusic.cpp 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsmusic.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -16,14 +16,14 @@ /* Music Music - All Music Music/All - + Music/All/track?Id=1 + + Music/All/item?Id=1 + + - PlayLists - By Artist Music/artist - Music/artist/artistKey=Pink Floyd - Music/artist/artistKey=Pink Floyd/album/albumKey=The Wall - + Music/artist/artistKey=Pink Floyd/album/albumKey=The Wall/track?Id=1 + + Music/artist/artistKey=Pink Floyd/album/albumKey=The Wall/item?Id=1 + - By Album - @@ -36,670 +36,145 @@ - By Artist Music/artist - Music/artist/artistKey=Pink Floyd - Music/artist/artistKey=Pink Floyd/album/albumKey=The Wall - + Music/artist/artistKey=Pink Floyd/album/albumKey=The Wall/track?Id=1 + + Music/artist/artistKey=Pink Floyd/album/albumKey=The Wall/item?Id=1 + */ - -typedef struct -{ - char *title; - char *column; - char *sql; - char *where; - -} RootInfo; - -static RootInfo g_RootNodes[] = +UPnpCDSRootInfo UPnpCDSMusic::g_RootNodes[] = { - { "All Music", + { "All Music", "*", - "SELECT intid as id, " - "title as name, " + "SELECT song_id as id, " + "name, " "1 as children " - "FROM musicmetadata " + "FROM music_songs song " "%1 " - "ORDER BY title", + "ORDER BY name", "" }, +/* +This is currently broken... need to handle list of items with single parent (like 'All Music') + { "Recently Added", "*", - "SELECT intid id, " - "title as name, " + "SELECT song_id id, " + "name, " "1 as children " - "FROM musicmetadata " + "FROM music_songs song " "%1 " - "ORDER BY title", - "WHERE (DATEDIFF( CURDATE(), date_added ) <= 30 ) " }, - - { "By Album", - "album", - "SELECT album as id, " - "album as name, " - "count( album ) as children " - "FROM musicmetadata " + "ORDER BY name", + "WHERE (DATEDIFF( CURDATE(), date_modified ) <= 30 ) " }, +*/ + { "By Album", + "song.album_id", + "SELECT a.album_id as id, " + "a.album_name as name, " + "count( song.album_id ) as children " + "FROM music_songs song join music_albums a on a.album_id = song.album_id " "%1 " - "GROUP BY album " - "ORDER BY album", - "WHERE album=:KEY" }, - + "GROUP BY a.album_id " + "ORDER BY a.album_name", + "WHERE song.album_id=:KEY" }, /* - { "By Artist", - "artist", - "SELECT artist as id, " - "artist as name, " - "count( distinct album ) as children " - "FROM musicmetadata " + { "By Artist", + "artist_id", + "SELECT a.artist_id as id, " + "a.artist_name as name, " + "count( distinct song.artist_id ) as children " + "FROM music_songs song join music_artists a on a.artist_id = song.artist_id " "%1 " - "GROUP BY artist " - "ORDER BY artist", - "WHERE artist=:KEY" }, + "GROUP BY a.artist_id " + "ORDER BY a.artist_name", + "WHERE song.artist_id=:KEY" }, */ /* - { "By Genre", - "genre", - "SELECT genre as id, " +{ "By Genre", + "genre_id", + "SELECT g.genre_id as id, " "genre as name, " - "count( distinct artist ) as children " - "FROM musicmetadata " + "count( distinct song.genre_id ) as children " + "FROM music_songs song join music_genres g on g.genre_id = song.genre_id " "%1 " - "GROUP BY genre " - "ORDER BY genre", - "WHERE genre=:KEY" }, + "GROUP BY g.genre_id " + "ORDER BY g.genre", + "WHERE song.genre_id=:KEY" }, */ }; -static const short g_nRootNodeLength = sizeof( g_RootNodes ) / sizeof( RootInfo ); - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -#define SHARED_MUSIC_SQL "SELECT intid, artist, album, title, " \ - "genre, year, tracknum, description, " \ - "filename, length " \ - "FROM musicmetadata " - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -template inline const T& Min( const T &x, const T &y ) -{ - return( ( x < y ) ? x : y ); -} - -template inline const T& Max( const T &x, const T &y ) -{ - return( ( x > y ) ? x : y ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -UPnpCDSExtensionResults *UPnpCDSMusic::Browse( UPnpCDSBrowseRequest *pRequest ) -{ - - // -=>TODO: Need to add Filter & Sorting Support. - // -=>TODO: Need to add Sub-Folder/Category Support!!!!! - - if (! pRequest->m_sObjectId.startsWith( m_sExtensionId, true )) - return( NULL ); - - // ---------------------------------------------------------------------- - // Parse out request object's path - // ---------------------------------------------------------------------- - - QStringList idPath = QStringList::split( "/", pRequest->m_sObjectId.section('=',0,0) ); - - QString key = pRequest->m_sObjectId.section('=',1); - - if (idPath.count() == 0) - return( NULL ); - - // ---------------------------------------------------------------------- - // Process based on location in hierarchy - // ---------------------------------------------------------------------- - - UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults(); - - if (pResults != NULL) - { - - if (key) - idPath.last().append(QString("=%1").arg(key)); - - QString sLast = idPath.last(); - - pRequest->m_sParentId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); - - if (sLast == m_sExtensionId ) { return( ProcessRoot ( pRequest, pResults, idPath )); } - if (sLast == "0" ) { return( ProcessAll ( pRequest, pResults, idPath, 0 )); } - if (sLast == "1" ) { return( ProcessAll ( pRequest, pResults, idPath, 1 )); } - if (sLast.startsWith( "key" , true )) { return( ProcessKey ( pRequest, pResults, idPath )); } - if (sLast.startsWith( "track", true )) { return( ProcessTrack( pRequest, pResults, idPath )); } - - int nNodeIdx = sLast.toInt(); - - if ((nNodeIdx > 1) && (nNodeIdx < g_nRootNodeLength)) - return( ProcessContainer( pRequest, pResults, nNodeIdx, idPath )); - - delete pResults; - } - - return( NULL ); - -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -QString UPnpCDSMusic::RemoveToken( const QString &sToken, const QString &sStr, int num ) -{ - QString sResult( "" ); - int nPos = -1; - - for (int nIdx=0; nIdx < num; nIdx++) - { - if ((nPos = sStr.findRev( sToken, nPos )) == -1) - break; - } - - if (nPos > 0) - sResult = sStr.left( nPos ); - - return sResult; -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -UPnpCDSExtensionResults *UPnpCDSMusic::ProcessRoot( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &/*idPath*/ ) -{ - - - switch( pRequest->m_eBrowseFlag ) - { - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Return Root Object Only - // -------------------------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pRoot = CDSObject::CreateContainer( m_sExtensionId, - m_sName, - "0"); - - pRoot->SetChildCount( g_nRootNodeLength ); - - pResults->Add( pRoot ); - - break; - } - - case CDS_BrowseDirectChildren: - { - pResults->m_nUpdateID = 1; - pResults->m_nTotalMatches = g_nRootNodeLength; - - if ( pRequest->m_nRequestedCount == 0) - pRequest->m_nRequestedCount = g_nRootNodeLength; - - short nStart = Max( pRequest->m_nStartingIndex, short( 0 )); - short nEnd = Min( g_nRootNodeLength, short( nStart + pRequest->m_nRequestedCount)); - - if (nStart < g_nRootNodeLength) - { - for (short nIdx = nStart; nIdx < nEnd; nIdx++) - { - QString sId = QString( "%1/%2" ).arg( pRequest->m_sObjectId ) - .arg( nIdx ); - - CDSObject *pItem = CDSObject::CreateContainer( sId, - QObject::tr( g_RootNodes[ nIdx ].title ), - pRequest->m_sParentId ); - - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nIdx ].column ) ); - - pResults->Add( pItem ); - } - } - } - - case CDS_BrowseUnknown: - default: - break; - } - - return( pResults ); -} - - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -UPnpCDSExtensionResults *UPnpCDSMusic::ProcessAll ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &/*idPath*/, - int nNodeIdx ) -{ - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - switch( pRequest->m_eBrowseFlag ) - { - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Return Container Object Only - // -------------------------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pItem = CDSObject::CreateContainer( pRequest->m_sObjectId, - QObject::tr( g_RootNodes[ nNodeIdx ].title ), - m_sExtensionId ); - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nNodeIdx ].column, - g_RootNodes[ nNodeIdx ].where ) ); - - pResults->Add( pItem ); - - break; - } - - case CDS_BrowseDirectChildren: - { - - CreateMusicTracks( pRequest, pResults, nNodeIdx, "", false ); - - break; - } - - case CDS_BrowseUnknown: - default: - break; - } - - - return( pResults ); -} +int UPnpCDSMusic::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -UPnpCDSExtensionResults *UPnpCDSMusic::ProcessTrack( UPnpCDSBrowseRequest */* pRequest */, - UPnpCDSExtensionResults *pResults, - QStringList &/* idPath */) -{ - pResults->m_nTotalMatches = 0; - pResults->m_nUpdateID = 1; +UPnpCDSRootInfo *UPnpCDSMusic::GetRootInfo( int nIdx ) +{ + if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) + return &(g_RootNodes[ nIdx ]); -/* - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - if ( pRequest->m_eBrowseFlag == CDS_BrowseMetadata ) - { - // -------------------------------------------------------------- - // Return 1 VideoItem - // -------------------------------------------------------------- - - QStringMap mapParams; - QString sParams = idPath.last().section( '?', 1, 1 ); - - sParams.replace(QRegExp( "&"), "&" ); - - HTTPRequest::GetParameters( sParams, mapParams ); - - int nChanId = mapParams[ "ChanId" ].toInt(); - QString sStartTime = mapParams[ "StartTime" ]; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - query.prepare( SHARED_MUSIC_SQL - "WHERE chanid=:CHANID and starttime=:STARTTIME " ); - - query.bindValue(":CHANID" , (int)nChanId ); - query.bindValue(":STARTTIME", sStartTime ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - if ( query.next() ) - { - pRequest->m_sObjectId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); - - AddVideoItem( pRequest, pResults, false, query ); - pResults->m_nTotalMatches = 1; - } - } - } - } -*/ - - return( pResults ); + return NULL; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -UPnpCDSExtensionResults *UPnpCDSMusic::ProcessKey( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ) +int UPnpCDSMusic::GetRootCount() { - pResults->m_nTotalMatches = 0; - pResults->m_nUpdateID = 1; - - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - QString sKey = idPath.last().section( '=', 1, 1 ); - - if (sKey.length() > 0) - { - QUrl::decode( sKey ); - - int nNodeIdx = idPath[ idPath.count() - 2 ].toInt(); - - switch( pRequest->m_eBrowseFlag ) - { - - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Since Key is not always the title, we need to lookup title. - // -------------------------------------------------------------- - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - QString sSQL = QString( g_RootNodes[ nNodeIdx ].sql ) - .arg( g_RootNodes[ nNodeIdx ].where ); - - query.prepare ( sSQL ); - query.bindValue( ":KEY", sKey ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - if ( query.next() ) - { - // ---------------------------------------------- - // Return Container Object Only - // ---------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pItem = CDSObject::CreateContainer( pRequest->m_sObjectId, - query.value(1).toString(), - pRequest->m_sParentId ); - - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nNodeIdx ].column )); - - pResults->Add( pItem ); - } - } - } - break; - } - - case CDS_BrowseDirectChildren: - { - CreateMusicTracks( pRequest, pResults, nNodeIdx, sKey, true ); - - break; - } - - case CDS_BrowseUnknown: - default: - break; - - } - } - - return( pResults ); + return g_nRootCount; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -UPnpCDSExtensionResults *UPnpCDSMusic::ProcessContainer( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - QStringList &/*idPath*/ ) - +QString UPnpCDSMusic::GetTableName( QString sColumn ) { - pResults->m_nUpdateID = 1; - pResults->m_nTotalMatches = 0; - - switch( pRequest->m_eBrowseFlag ) - { - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Return Container Object Only - // -------------------------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pItem = CDSObject::CreateContainer( pRequest->m_sObjectId, - QObject::tr( g_RootNodes[ nNodeIdx ].title ), - m_sExtensionId ); - - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nNodeIdx ].column )); - - pResults->Add( pItem ); - - break; - } - - case CDS_BrowseDirectChildren: - { - pResults->m_nTotalMatches = GetDistinctCount( g_RootNodes[ nNodeIdx ].column ); - pResults->m_nUpdateID = 1; - - if (pRequest->m_nRequestedCount == 0) - pRequest->m_nRequestedCount = SHRT_MAX; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Remove where clause placeholder. - - QString sSQL = g_RootNodes[ nNodeIdx ].sql; - sSQL.replace( "%1", "" ); - - sSQL += QString( " LIMIT %2, %3" ) - .arg( pRequest->m_nStartingIndex ) - .arg( pRequest->m_nRequestedCount ); - - query.prepare( sSQL ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - - while(query.next()) - { - QString sKey = query.value(0).toString(); - QString sTitle = query.value(1).toString(); - long nCount = query.value(2).toInt(); - - if (sTitle.length() == 0) - sTitle = "(undefined)"; - - QString sId = QString( "%1/%2/key=%3" ) - .arg( pRequest->m_sParentId ) - .arg( nNodeIdx ) - .arg( sKey ); - - CDSObject *pRoot = CDSObject::CreateContainer( sId, sTitle, pRequest->m_sParentId ); - - pRoot->SetChildCount( nCount ); - - pResults->Add( pRoot ); - } - } - } - - break; - } - - case CDS_BrowseUnknown: - default: - break; - - } - - return( pResults ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -int UPnpCDSMusic::GetDistinctCount( const QString &sColumn, const QString &sWhere ) -{ - int nCount = 0; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Note: Tried to use Bind, however it would not allow me to use it - // for column & table names - - QString sSQL; - - if (sColumn == "*") - sSQL = QString( "SELECT count( * ) FROM musicmetadata %1" ).arg( sWhere ); - else - sSQL = QString( "SELECT count( DISTINCT %1 ) FROM musicmetadata" ).arg( sColumn ); - - query.prepare( sSQL ); - query.exec(); - - if (query.size() > 0) - { - query.next(); - - nCount = query.value(0).toInt(); - } - } - - return( nCount ); + return "music_songs song"; } - ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -int UPnpCDSMusic::GetCount( int nNodeIdx , const QString &sKey ) +QString UPnpCDSMusic::GetItemListSQL( QString /* sColumn */ ) { - int nCount = 0; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Note: Tried to use Bind, however it would not allow me to use it - // for column & table names - - QString sSQL = QString( "SELECT count( %1 ) FROM musicmetadata %2" ) - .arg( g_RootNodes[ nNodeIdx ].column ) - .arg( g_RootNodes[ nNodeIdx ].where ); - - query.prepare( sSQL ); - query.bindValue( ":KEY", sKey ); - query.exec(); - - if (query.size() > 0) - { - query.next(); - - nCount = query.value(0).toInt(); - } - - } - - return( nCount ); + return "SELECT song.song_id as intid, artist.artist_name as artist, " \ + "album.album_name as album, song.name as title, " \ + "genre.genre, song.year, song.track as tracknum, " \ + "song.description, song.filename, song.length " \ + "FROM music_songs song " \ + " join music_artists artist on artist.artist_id = song.artist_id " \ + " join music_albums album on album.album_id = song.album_id " \ + " join music_genres genre on genre.genre_id = song.genre_id "; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -void UPnpCDSMusic::CreateMusicTracks( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - const QString &sKey, - bool bAddRef ) +void UPnpCDSMusic::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams ) { + int nId = mapParams[ "Id" ].toInt(); - pResults->m_nTotalMatches = GetCount( nNodeIdx, sKey ); - pResults->m_nUpdateID = 1; - - if (pRequest->m_nRequestedCount == 0) - pRequest->m_nRequestedCount = SHRT_MAX; + QString sSQL = QString( "%1 WHERE song.song_id=:ID " ) + .arg( GetItemListSQL() ); - MSqlQuery query(MSqlQuery::InitCon()); + query.prepare( sSQL ); - if (query.isConnected()) - { - QString sSQL = QString( SHARED_MUSIC_SQL - "%1 LIMIT %2, %3" ) - .arg( g_RootNodes[ nNodeIdx ].where ) - .arg( pRequest->m_nStartingIndex ) - .arg( pRequest->m_nRequestedCount ); - - query.prepare ( sSQL ); - query.bindValue(":KEY", sKey ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { -// pResults->m_nTotalMatches = query.size(); - - while(query.next()) - AddMusicTrack( pRequest, pResults, bAddRef, nNodeIdx, query ); - } - } + query.bindValue(":ID" , (int)nId ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -void UPnpCDSMusic::AddMusicTrack( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - bool bAddRef, - int /*nNodeIdx*/, - MSqlQuery &query ) +void UPnpCDSMusic::AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ) { QString sName; @@ -739,14 +214,14 @@ // if (!m_mapBackendPort.contains( sHostName )) // m_mapBackendPort[ sHostName ] = gContext->GetSettingOnHost("BackendStatusPort", sHostName); - QString sServerIp = gContext->GetSetting( "BackendServerIp" ); - QString sPort = gContext->GetSetting("BackendStatusPort" ); + QString sServerIp = gContext->GetSetting( "BackendServerIp" ); + QString sPort = gContext->GetSetting( "BackendStatusPort" ); // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- - QString sURIBase = QString( "http://%1:%2/" ) + QString sURIBase = QString( "http://%1:%2/Myth/" ) .arg( sServerIp ) .arg( sPort ); @@ -754,20 +229,20 @@ .arg( nId ); - QString sId = QString( "%1/track%2") - .arg( pRequest->m_sObjectId ) + QString sId = QString( "%1/item%2") + .arg( sObjectId ) .arg( sURIParams ); CDSObject *pItem = CDSObject::CreateMusicTrack( sId, sName, - pRequest->m_sParentId ); + sObjectId ); pItem->m_bRestricted = true; pItem->m_bSearchable = true; pItem->m_sWriteStatus = "NOT_WRITABLE"; if ( bAddRef ) { - QString sRefId = QString( "%1/0/track%2") + QString sRefId = QString( "%1/0/item%2") .arg( m_sExtensionId ) .arg( sURIParams ); @@ -807,8 +282,8 @@ QFileInfo fInfo( sFileName ); QString sMimeType = HTTPRequest::GetMimeType( fInfo.extension( FALSE )); - QString sProtocol = QString( "http-get:*:%1:*" ).arg( sMimeType ); - QString sURI = QString( "%1getMusic%2").arg( sURIBase ) + QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.OR G_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); + QString sURI = QString( "%1GetMusic%2").arg( sURIBase ) .arg( sURIParams ); Resource *pRes = pItem->AddResource( sProtocol, sURI ); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsmusic.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsmusic.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsmusic.h 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsmusic.h 2007-08-23 11:09:51.000000000 -0500 @@ -14,10 +14,6 @@ #include "mainserver.h" #include "upnpcds.h" -//using namespace UPnp; - -typedef QMap< QString, QString > StringMap; - ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// @@ -26,64 +22,31 @@ { private: - QString RemoveToken ( const QString &sToken, const QString &sStr, int num ); - - int GetDistinctCount ( const QString &sColumn, const QString &sWhere = "" ); - int GetCount ( int nNodeIdx, const QString &sKey ); - - void BuildContainerChildren( UPnpCDSExtensionResults *pResults, - int nNodeIdx, - const QString &sParentId, - short nStartingIndex, - short nRequestedCount ); - - void CreateMusicTracks ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - const QString &sKey, - bool bAddRef ); - - void AddMusicTrack ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - bool bAddRef, - int nNodeIdx, - MSqlQuery &query ); - - UPnpCDSExtensionResults *ProcessRoot ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessAll ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath, - int nNodeIdx ); - UPnpCDSExtensionResults *ProcessTrack ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessKey ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessContainer( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - QStringList &idPath ); + static UPnpCDSRootInfo g_RootNodes[]; + static int g_nRootCount; + protected: + virtual UPnpCDSRootInfo *GetRootInfo (int nIdx); + virtual int GetRootCount ( ); + virtual QString GetTableName ( QString sColumn ); + virtual QString GetItemListSQL( QString sColumn = "" ); + + virtual void BuildItemQuery( MSqlQuery &query, + const QStringMap &mapParams ); + + virtual void AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ); public: - UPnpCDSMusic( ) : UPnpCDSExtension( "Music", "Music" ) + UPnpCDSMusic( ) : UPnpCDSExtension( "Music", "Music", + "object.item.audioItem.musicTrack" ) { } virtual ~UPnpCDSMusic() {} - - virtual UPnpCDSExtensionResults *Browse( UPnpCDSBrowseRequest *pRequest ); - virtual UPnpCDSExtensionResults *Search( UPnpCDSSearchRequest * /* pRequest */ ) - { - return( NULL ); - } - - virtual QString GetSearchCapabilities() { return( "" ); } - virtual QString GetSortCapabilities () { return( "" ); } }; #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdstv.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdstv.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdstv.cpp 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdstv.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -16,6 +16,7 @@ #include #include "util.h" + /* Recordings RecTv - All Programs RecTv/All @@ -33,16 +34,7 @@ */ -typedef struct -{ - char *title; - char *column; - char *sql; - char *where; - -} RootInfo; - -static RootInfo g_RootNodes[] = +UPnpCDSRootInfo UPnpCDSTv::g_RootNodes[] = { { "All Recordings", "*", @@ -77,15 +69,15 @@ "WHERE category=:KEY" }, { "By Date", - "DATE( starttime )", - "SELECT DATE( starttime ) as id, " + "DATE_FORMAT(starttime, '%Y-%m-%d')", + "SELECT DATE_FORMAT(starttime, '%Y-%m-%d') as id, " "DATE_FORMAT(starttime, '%Y-%m-%d %W') as name, " "count( DATE_FORMAT(starttime, '%Y-%m-%d %W') ) as children " "FROM recorded " "%1 " "GROUP BY name " "ORDER BY starttime DESC", - "WHERE DATE( starttime )=:KEY" }, + "WHERE DATE_FORMAT(starttime, '%Y-%m-%d') =:KEY" }, { "By Channel", "chanid", @@ -111,596 +103,77 @@ "WHERE recgroup=:KEY" } }; -static const short g_nRootNodeLength = sizeof( g_RootNodes ) / sizeof( RootInfo ); - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -#define SHARED_RECORDED_SQL "SELECT chanid, starttime, endtime, title, " \ - "subtitle, description, category, " \ - "hostname, recgroup, filesize, " \ - "basename, progstart, progend " \ - "FROM recorded " - - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -template inline const T& Min( const T &x, const T &y ) -{ - return( ( x < y ) ? x : y ); -} - -template inline const T& Max( const T &x, const T &y ) -{ - return( ( x > y ) ? x : y ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -UPnpCDSExtensionResults *UPnpCDSTv::Browse( UPnpCDSBrowseRequest *pRequest ) -{ - - // -=>TODO: Need to add Filter & Sorting Support. - // -=>TODO: Need to add Sub-Folder/Category Support!!!!! - - if (! pRequest->m_sObjectId.startsWith( m_sExtensionId, true )) - return( NULL ); - - // ---------------------------------------------------------------------- - // Parse out request object's path - // ---------------------------------------------------------------------- - - QStringList idPath = QStringList::split( "/", pRequest->m_sObjectId.section('=',0,0) ); - - QString key = pRequest->m_sObjectId.section('=',1); - - if (idPath.count() == 0) - return( NULL ); - - // ---------------------------------------------------------------------- - // Process based on location in hierarchy - // ---------------------------------------------------------------------- - - UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults(); - - if (pResults != NULL) - { - if (key) - idPath.last().append(QString("=%1").arg(key)); - - QString sLast = idPath.last(); - - pRequest->m_sParentId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); - - if (sLast == m_sExtensionId ) { return( ProcessRoot ( pRequest, pResults, idPath )); } - if (sLast == "0" ) { return( ProcessAll ( pRequest, pResults, idPath )); } - if (sLast.startsWith( "key" , true )) { return( ProcessKey ( pRequest, pResults, idPath )); } - if (sLast.startsWith( "item", true )) { return( ProcessItem ( pRequest, pResults, idPath )); } - - int nNodeIdx = sLast.toInt(); - - if ((nNodeIdx > 0) && (nNodeIdx < g_nRootNodeLength)) - return( ProcessContainer( pRequest, pResults, nNodeIdx, idPath )); - - delete pResults; - } - - return( NULL ); - -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -QString UPnpCDSTv::RemoveToken( const QString &sToken, const QString &sStr, int num ) -{ - QString sResult( "" ); - int nPos = -1; - - for (int nIdx=0; nIdx < num; nIdx++) - { - if ((nPos = sStr.findRev( sToken, nPos )) == -1) - break; - } - - if (nPos > 0) - sResult = sStr.left( nPos ); - - return sResult; -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -UPnpCDSExtensionResults *UPnpCDSTv::ProcessRoot( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &/*idPath*/ ) -{ - pResults->m_nTotalMatches = 0; - pResults->m_nUpdateID = 1; - - switch( pRequest->m_eBrowseFlag ) - { - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Return Root Object Only - // -------------------------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pRoot = CDSObject::CreateContainer( m_sExtensionId, - m_sName, - "0"); - - pRoot->SetChildCount( g_nRootNodeLength ); - - pResults->Add( pRoot ); - - break; - } - - case CDS_BrowseDirectChildren: - { - pResults->m_nUpdateID = 1; - pResults->m_nTotalMatches = g_nRootNodeLength; - - if ( pRequest->m_nRequestedCount == 0) - pRequest->m_nRequestedCount = g_nRootNodeLength; - - short nStart = Max( pRequest->m_nStartingIndex, short( 0 )); - short nEnd = Min( g_nRootNodeLength, short( nStart + pRequest->m_nRequestedCount)); - - if (nStart < g_nRootNodeLength) - { - for (short nIdx = nStart; nIdx < nEnd; nIdx++) - { - QString sId = QString( "%1/%2" ).arg( pRequest->m_sObjectId ) - .arg( nIdx ); - - CDSObject *pItem = CDSObject::CreateContainer( sId, - QObject::tr( g_RootNodes[ nIdx ].title ), - pRequest->m_sParentId ); - - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nIdx ].column ) ); - - pResults->Add( pItem ); - } - } - } - - case CDS_BrowseUnknown: - default: - break; - } - - return( pResults ); -} - - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -UPnpCDSExtensionResults *UPnpCDSTv::ProcessAll ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &/*idPath*/ ) -{ - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - switch( pRequest->m_eBrowseFlag ) - { - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Return Container Object Only - // -------------------------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pItem = CDSObject::CreateContainer( pRequest->m_sObjectId, - QObject::tr( g_RootNodes[ 0 ].title ), - m_sExtensionId ); - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ 0 ].column ) ); - - pResults->Add( pItem ); - - break; - } - - case CDS_BrowseDirectChildren: - { - - CreateVideoItems( pRequest, pResults, 0, "", false ); - - break; - } - - case CDS_BrowseUnknown: - default: - break; - - } - - - return( pResults ); -} +int UPnpCDSTv::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -UPnpCDSExtensionResults *UPnpCDSTv::ProcessItem( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ) -{ - pResults->m_nTotalMatches = 0; - pResults->m_nUpdateID = 1; - - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - if ( pRequest->m_eBrowseFlag == CDS_BrowseMetadata ) - { - // -------------------------------------------------------------- - // Return 1 VideoItem - // -------------------------------------------------------------- - - QStringMap mapParams; - QString sParams = idPath.last().section( '?', 1, 1 ); - - sParams.replace(QRegExp( "&"), "&" ); - - HTTPRequest::GetParameters( sParams, mapParams ); - - int nChanId = mapParams[ "ChanId" ].toInt(); - QString sStartTime = mapParams[ "StartTime" ]; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - query.prepare( SHARED_RECORDED_SQL - "WHERE chanid=:CHANID and starttime=:STARTTIME " ); - - query.bindValue(":CHANID" , (int)nChanId ); - query.bindValue(":STARTTIME", sStartTime ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - if ( query.next() ) - { - pRequest->m_sObjectId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); - - AddVideoItem( pRequest, pResults, false, query ); - pResults->m_nTotalMatches = 1; - } - } - } - } +UPnpCDSRootInfo *UPnpCDSTv::GetRootInfo( int nIdx ) +{ + if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) + return &(g_RootNodes[ nIdx ]); - return( pResults ); + return NULL; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -UPnpCDSExtensionResults *UPnpCDSTv::ProcessKey( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ) +int UPnpCDSTv::GetRootCount() { - pResults->m_nTotalMatches = 0; - pResults->m_nUpdateID = 1; - - // ---------------------------------------------------------------------- - // - // ---------------------------------------------------------------------- - - QString sKey = idPath.last().section( '=', 1, 1 ); - QUrl::decode( sKey ); - - if (sKey.length() > 0) - { - int nNodeIdx = idPath[ idPath.count() - 2 ].toInt(); - - switch( pRequest->m_eBrowseFlag ) - { - - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Since Key is not always the title, we need to lookup title. - // -------------------------------------------------------------- - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - QString sSQL = QString( g_RootNodes[ nNodeIdx ].sql ) - .arg( g_RootNodes[ nNodeIdx ].where ); - - query.prepare ( sSQL ); - query.bindValue( ":KEY", sKey ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - if ( query.next() ) - { - // ---------------------------------------------- - // Return Container Object Only - // ---------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pItem = CDSObject::CreateContainer( pRequest->m_sObjectId, - query.value(1).toString(), - pRequest->m_sParentId ); - - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nNodeIdx ].column )); - - pResults->Add( pItem ); - } - } - } - break; - } - - case CDS_BrowseDirectChildren: - { - CreateVideoItems( pRequest, pResults, nNodeIdx, sKey, true ); - - break; - } - - case CDS_BrowseUnknown: - default: - break; - } - } - - return( pResults ); + return g_nRootCount; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -UPnpCDSExtensionResults *UPnpCDSTv::ProcessContainer( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - QStringList &/*idPath*/ ) - +QString UPnpCDSTv::GetTableName( QString /* sColumn */) { - pResults->m_nUpdateID = 1; - pResults->m_nTotalMatches = 0; - - switch( pRequest->m_eBrowseFlag ) - { - case CDS_BrowseMetadata: - { - // -------------------------------------------------------------- - // Return Container Object Only - // -------------------------------------------------------------- - - pResults->m_nTotalMatches = 1; - pResults->m_nUpdateID = 1; - - CDSObject *pItem = CDSObject::CreateContainer( pRequest->m_sObjectId, - QObject::tr( g_RootNodes[ nNodeIdx ].title ), - m_sExtensionId ); - - pItem->SetChildCount( GetDistinctCount( g_RootNodes[ nNodeIdx ].column )); - - pResults->Add( pItem ); - - break; - } - - case CDS_BrowseDirectChildren: - { - pResults->m_nTotalMatches = GetDistinctCount( g_RootNodes[ nNodeIdx ].column ); - pResults->m_nUpdateID = 1; - - if (pRequest->m_nRequestedCount == 0) - pRequest->m_nRequestedCount = SHRT_MAX; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Remove where clause placeholder. - - QString sSQL = g_RootNodes[ nNodeIdx ].sql; - sSQL.replace( "%1", "" ); - - sSQL += QString( " LIMIT %2, %3" ) - .arg( pRequest->m_nStartingIndex ) - .arg( pRequest->m_nRequestedCount ); - - query.prepare( sSQL ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - - while(query.next()) - { - QString sKey = query.value(0).toString(); - QString sTitle = query.value(1).toString(); - long nCount = query.value(2).toInt(); - - if (sTitle.length() == 0) - sTitle = "(undefined)"; - - QString sId = QString( "%1/%2/key=%3" ) - .arg( pRequest->m_sParentId ) - .arg( nNodeIdx ) - .arg( sKey ); - - CDSObject *pRoot = CDSObject::CreateContainer( sId, sTitle, pRequest->m_sParentId ); - - pRoot->SetChildCount( nCount ); - - pResults->Add( pRoot ); - } - } - } - - break; - } - } - - return( pResults ); -} - -///////////////////////////////////////////////////////////////////////////// -// -///////////////////////////////////////////////////////////////////////////// - -int UPnpCDSTv::GetDistinctCount( const QString &sColumn ) -{ - int nCount = 0; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Note: Tried to use Bind, however it would not allow me to use it - // for column & table names - - QString sSQL; - - if (sColumn == "*") - sSQL = QString( "SELECT count( %1 ) FROM recorded" ).arg( sColumn ); - else - sSQL = QString( "SELECT count( DISTINCT %1 ) FROM recorded" ).arg( sColumn ); - - query.prepare( sSQL ); - query.exec(); - - if (query.size() > 0) - { - query.next(); - - nCount = query.value(0).toInt(); - } - } - - return( nCount ); + return "recorded"; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -int UPnpCDSTv::GetCount( const QString &sColumn, const QString &sKey ) +QString UPnpCDSTv::GetItemListSQL( QString /* sColumn */ ) { - int nCount = 0; - - MSqlQuery query(MSqlQuery::InitCon()); - - if (query.isConnected()) - { - // Note: Tried to use Bind, however it would not allow me to use it - // for column & table names - - QString sSQL; - - if (sColumn == "*") - sSQL = "SELECT count( * ) FROM recorded"; - else - sSQL = QString( "SELECT count( %1 ) FROM recorded WHERE %2=:KEY" ) - .arg( sColumn ) - .arg( sColumn ); - - query.prepare( sSQL ); - query.bindValue( ":KEY", sKey ); - query.exec(); - - if (query.size() > 0) - { - query.next(); - - nCount = query.value(0).toInt(); - } - - } - - return( nCount ); + return "SELECT chanid, starttime, endtime, title, " \ + "subtitle, description, category, " \ + "hostname, recgroup, filesize, " \ + "basename, progstart, progend " \ + "FROM recorded "; } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -void UPnpCDSTv::CreateVideoItems( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - const QString &sKey, - bool bAddRef ) +void UPnpCDSTv::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams ) { + int nChanId = mapParams[ "ChanId" ].toInt(); + QString sStartTime = mapParams[ "StartTime" ]; - pResults->m_nTotalMatches = GetCount( g_RootNodes[ nNodeIdx ].column, sKey ); - pResults->m_nUpdateID = 1; - - if (pRequest->m_nRequestedCount == 0) - pRequest->m_nRequestedCount = SHRT_MAX; + QString sSQL = QString( "%1 WHERE chanid=:CHANID and starttime=:STARTTIME " ) + .arg( GetItemListSQL() ); - MSqlQuery query(MSqlQuery::InitCon()); + query.prepare( sSQL ); - if (query.isConnected()) - { - QString sWhere( "" ); - - if ( sKey.length() > 0) - { - sWhere = QString( "WHERE %1=:KEY " ) - .arg( g_RootNodes[ nNodeIdx ].column ); - } - - QString sSQL = QString( SHARED_RECORDED_SQL - "%1 " - "LIMIT %2, %3" ) - .arg( sWhere ) - .arg( pRequest->m_nStartingIndex ) - .arg( pRequest->m_nRequestedCount ); - - query.prepare ( sSQL ); - query.bindValue(":KEY", sKey ); - query.exec(); - - if (query.isActive() && query.size() > 0) - { - while(query.next()) - AddVideoItem( pRequest, pResults, bAddRef, query ); - } - } + query.bindValue(":CHANID" , (int)nChanId ); + query.bindValue(":STARTTIME", sStartTime ); } ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// -void UPnpCDSTv::AddVideoItem( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - bool bAddRef, - MSqlQuery &query ) +void UPnpCDSTv::AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ) { int nChanid = query.value( 0).toInt(); QDateTime dtStartTime = query.value( 1).toDateTime(); @@ -713,7 +186,6 @@ QString sRecGroup = query.value( 8).toString(); long long nFileSize = stringToLongLong( query.value( 9).toString() ); QString sBaseName = query.value(10).toString(); - //VERBOSE(VB_UPNP, QString(" %1 : %2 - %3 - %4").arg(sTitle).arg(sSubtitle).arg(sBaseName).arg(nFileSize)); QDateTime dtProgStart = query.value(11).toDateTime(); QDateTime dtProgEnd = query.value(12).toDateTime(); @@ -734,7 +206,7 @@ QString sName = sTitle + ": " + sSubtitle; - QString sURIBase = QString( "http://%1:%2/" ) + QString sURIBase = QString( "http://%1:%2/Myth/" ) .arg( m_mapBackendIp [ sHostName ] ) .arg( m_mapBackendPort[ sHostName ] ); @@ -743,12 +215,12 @@ .arg( dtStartTime.toString(Qt::ISODate)); QString sId = QString( "%1/item%2") - .arg( pRequest->m_sObjectId ) + .arg( sObjectId ) .arg( sURIParams ); CDSObject *pItem = CDSObject::CreateVideoItem( sId, sName, - pRequest->m_sParentId ); + sObjectId ); pItem->m_bRestricted = false; pItem->m_bSearchable = true; pItem->m_sWriteStatus = "WRITABLE"; @@ -775,6 +247,16 @@ //pItem->SetPropValue( "relation" , ); //pItem->SetPropValue( "region" , ); + // ---------------------------------------------------------------------- + // Needed for Microsoft Media Player Compatibility + // (Won't display correct Title without them) + // ---------------------------------------------------------------------- + + pItem->SetPropValue( "creator" , "[Unknown Author]" ); + pItem->SetPropValue( "artist" , "[Unknown Author]" ); + pItem->SetPropValue( "album" , "[Unknown Series]" ); + pItem->SetPropValue( "actor" , "[Unknown Author]" ); + pResults->Add( pItem ); // ---------------------------------------------------------------------- @@ -784,8 +266,9 @@ QFileInfo fInfo( sBaseName ); QString sMimeType = HTTPRequest::GetMimeType( fInfo.extension( FALSE )); - QString sProtocol = QString( "http-get:*:%1:*" ).arg( sMimeType ); - QString sURI = QString( "%1getRecording%2").arg( sURIBase ) + // DLNA string below is temp fix for ps3 seeking. + QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.OR G_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); + QString sURI = QString( "%1GetRecording%2").arg( sURIBase ) .arg( sURIParams ); Resource *pRes = pItem->AddResource( sProtocol, sURI ); @@ -794,6 +277,7 @@ uint uiEnd = dtProgEnd.toTime_t(); uint uiDur = uiEnd - uiStart; + QString sDur = QString( "%1:%2:%3" ) .arg( (uiDur / 3600) % 24, 2 ) .arg( (uiDur / 60) % 60 , 2 ) @@ -802,7 +286,7 @@ pRes->AddAttribute( "duration" , sDur ); pRes->AddAttribute( "size" , longLongToString( nFileSize) ); - +/* // ---------------------------------------------------------------------- // Add Video Resource Element based on File extension (mythtv) // ---------------------------------------------------------------------- @@ -816,14 +300,15 @@ pRes->AddAttribute( "duration" , sDur ); pRes->AddAttribute( "size" , longLongToString( nFileSize) ); - +*/ // ---------------------------------------------------------------------- // Add Thumbnail Resource // ---------------------------------------------------------------------- - sURI = QString( "%1getPreviewImage%2").arg( sURIBase ) - .arg( sURIParams ); + //sURI = QString( "%1GetPreviewImage%2").arg( sURIBase ) + // .arg( sURIParams ); + + //pItem->AddResource( "http-get:*:image/png:*" , sURI ); - pItem->AddResource( "http-get:*:image/png:*" , sURI ); } diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdstv.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdstv.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdstv.h 2007-01-22 03:08:40.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdstv.h 2007-08-23 11:09:51.000000000 -0500 @@ -14,10 +14,6 @@ #include "mainserver.h" #include "upnpcds.h" -//using namespace UPnp; - -typedef QMap< QString, QString > StringMap; - ////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////// @@ -26,67 +22,35 @@ { private: - StringMap m_mapBackendIp; - StringMap m_mapBackendPort; - - private: - - QString RemoveToken ( const QString &sToken, const QString &sStr, int num ); + static UPnpCDSRootInfo g_RootNodes[]; + static int g_nRootCount; - int GetDistinctCount ( const QString &sColumn ); - int GetCount ( const QString &sColumn, const QString &sKey ); + QStringMap m_mapBackendIp; + QStringMap m_mapBackendPort; - void BuildContainerChildren( UPnpCDSExtensionResults *pResults, - int nNodeIdx, - const QString &sParentId, - short nStartingIndex, - short nRequestedCount ); - - void CreateVideoItems ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - const QString &sKey, - bool bAddRef ); - - void AddVideoItem ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - bool bAddRef, - MSqlQuery &query ); - - UPnpCDSExtensionResults *ProcessRoot ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessAll ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessItem ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessKey ( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - QStringList &idPath ); - UPnpCDSExtensionResults *ProcessContainer( UPnpCDSBrowseRequest *pRequest, - UPnpCDSExtensionResults *pResults, - int nNodeIdx, - QStringList &idPath ); + protected: + virtual UPnpCDSRootInfo *GetRootInfo (int nIdx); + virtual int GetRootCount ( ); + virtual QString GetTableName ( QString sColumn ); + virtual QString GetItemListSQL( QString sColumn = "" ); + + virtual void BuildItemQuery( MSqlQuery &query, + const QStringMap &mapParams ); + + virtual void AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ); public: - UPnpCDSTv( ) : UPnpCDSExtension( "Recordings", "RecTv" ) + UPnpCDSTv( ) : UPnpCDSExtension( "Recordings", "RecTv", + "object.item.videoItem" ) { } virtual ~UPnpCDSTv() {} - - virtual UPnpCDSExtensionResults *Browse( UPnpCDSBrowseRequest *pRequest ); - virtual UPnpCDSExtensionResults *Search( UPnpCDSSearchRequest * /* pRequest */ ) - { - return( NULL ); - } - - virtual QString GetSearchCapabilities() { return( "" ); } - virtual QString GetSortCapabilities () { return( "" ); } }; #endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsvideo.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsvideo.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsvideo.cpp 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsvideo.cpp 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,330 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnpcdsvideo.cpp +// +// Purpose - uPnp Content Directory Extention for MythVideo Videos +// +////////////////////////////////////////////////////////////////////////////// + +#include "upnpcdsvideo.h" +#include "httprequest.h" +#include +#include +#include +#include +#include "util.h" + +UPnpCDSRootInfo UPnpCDSVideo::g_RootNodes[] = +{ + { "All Videos", + "*", + "SELECT 0 as key, " + "title as name, " + "1 as children " + "FROM videometadata " + "%1 " + "ORDER BY title DESC", + "" }, + + { "By Genre", + "idgenre", + "SELECT intid as id, " + "genre as name, " + "count( genre ) as children " + "FROM videogenre " + "%1 " + "GROUP BY genre " + "ORDER BY genre", + "WHERE genre=:KEY" }, + + { "By Country", + "idcountry", + "SELECT intid as id, " + "country as name, " + "count( country ) as children " + "FROM videocountry " + "%1 " + "GROUP BY country " + "ORDER BY country", + "WHERE country=:KEY" } + + +}; + +int UPnpCDSVideo::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo ); + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +UPnpCDSRootInfo *UPnpCDSVideo::GetRootInfo( int nIdx ) +{ + if ((nIdx >=0 ) && ( nIdx < g_nRootCount )) + return &(g_RootNodes[ nIdx ]); + + return NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +int UPnpCDSVideo::GetRootCount() +{ + return g_nRootCount; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString UPnpCDSVideo::GetTableName( QString sColumn ) +{ + if (sColumn == "idgenre" ) return "videometadatagenre"; + if (sColumn == "idcountry") return "videometadatacountry"; + + return "videometadata"; +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +QString UPnpCDSVideo::GetItemListSQL( QString sColumn ) +{ + if ( sColumn == "idgenre" ) + { + return "SELECT intid, title, director, plot, year, " \ + "userrating, length, filename, coverfile, " \ + "category, idgenre FROM videometadata " \ + "LEFT JOIN (videometadatagenre) ON " \ + "(videometadatagenre.idvideo = videometadata.intid) "; + } + + if ( sColumn == "idcountry" ) + { + return "SELECT intid, title, director, plot, year, " \ + "userrating, length, filename, coverfile, " \ + "category, idcountry FROM videometadata " \ + "LEFT JOIN (videometadatacountry) ON " \ + "(videometadatacountry.idvideo = videometadata.intid) "; + } + + return "SELECT intid, title, director, plot, year, " \ + "userrating, length, filename, coverfile, " \ + "category FROM videometadata "; +} + +/* +#define SHARED_ALLVIDEO_SQL "SELECT intid, title, director, plot, year, " \ + "userrating, length, filename, coverfile, " \ + "category, idgenre, idcountry FROM videometadata " \ + "LEFT JOIN (videometadatagenre) ON " \ + "(videometadatagenre.idvideo = videometadata.intid) " \ + "LEFT JOIN (videometadatacountry) ON " \ + "(videometadatacountry.idvideo = videometadata.intid) " +*/ + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCDSVideo::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams ) +{ + int nVideoID = mapParams[ "Id" ].toInt(); + + QString sSQL = QString( "%1 WHERE intid=:VIDEOID " ).arg( GetItemListSQL( ) ); + + query.prepare( sSQL ); + + query.bindValue( ":VIDEOID", (int)nVideoID ); +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +// -- TODO -- Going to improve this later, just a rough in right now + +void UPnpCDSVideo::FillMetaMaps(void) +{ + MSqlQuery query(MSqlQuery::InitCon()); + + if (query.isConnected()) + { + QString sSQL = "SELECT intid, genre FROM videogenre"; + + query.prepare ( sSQL ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + while(query.next()) + m_mapGenreNames[query.value(0).toInt()] = query.value(1).toString(); + } + + sSQL = "SELECT intid, country FROM videocountry"; + + query.prepare ( sSQL ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + while(query.next()) + m_mapCountryNames[query.value(0).toInt()] = query.value(1).toString(); + } + + // Forcibly map "0" to "Unknown" + m_mapGenreNames[0] = "Unknown"; + m_mapCountryNames[0] = "Unknown"; + + + int nVidID,nGenreID,nCountryID = 0; + + sSQL = GetItemListSQL( "idgenre" ); + + query.prepare ( sSQL ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + while(query.next()) + { + nVidID = query.value(0).toInt(); + nGenreID = query.value(10).toInt(); + + if (m_mapGenre.contains(nVidID)) + m_mapGenre[nVidID] = QString("%1 / %2") + .arg(m_mapGenre[nVidID]) + .arg(m_mapGenreNames[nGenreID]); + else + m_mapGenre[nVidID] = m_mapGenreNames[nGenreID]; + + } + } + + sSQL = GetItemListSQL( "idcountry" ); + + query.prepare ( sSQL ); + query.exec(); + + if (query.isActive() && query.size() > 0) + { + while(query.next()) + { + nVidID = query.value(0).toInt(); + nCountryID = query.value(10).toInt(); + + if (m_mapCountry.contains(nVidID)) + m_mapCountry[nVidID] = QString("%1 / %2") + .arg(m_mapCountry[nVidID]) + .arg(m_mapCountryNames[nCountryID]); + else + m_mapCountry[nVidID] = m_mapCountryNames[nCountryID]; + + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +///////////////////////////////////////////////////////////////////////////// + +void UPnpCDSVideo::AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ) +{ + int nVidID = query.value( 0).toInt(); + QString sTitle = query.value( 1).toString(); + QString sDirector = query.value( 2).toString(); + QString sDescription = query.value( 3).toString(); + QString sYear = query.value( 4).toString(); + QString sUserRating = query.value( 5).toString(); + //long long nFileSize = stringToLongLong( query.value( 6).toString() ); + QString sFileName = query.value( 7).toString(); + QString sCover = query.value( 8).toString(); + //int nCategory = query.value( 9).toInt(); + + // This should be a lookup from acache of the videometagenre and such tables. + QString sGenre = m_mapGenre[nVidID]; + QString sCountry = m_mapCountry[nVidID]; + + // ---------------------------------------------------------------------- + // Cache Host ip Address & Port + // ---------------------------------------------------------------------- + QString sServerIp = gContext->GetSetting( "BackendServerIp" ); + QString sPort = gContext->GetSetting("BackendStatusPort" ); + + // ---------------------------------------------------------------------- + // Build Support Strings + // ---------------------------------------------------------------------- + + QString sName = sTitle; + + QString sURIBase = QString( "http://%1:%2/Myth/" ) + .arg( sServerIp ) + .arg( sPort ); + + QString sURIParams = QString( "?Id=%1&" ) + .arg( nVidID ); + + QString sId = QString( "%1/item%2") + .arg( sObjectId ) + .arg( sURIParams ); + + CDSObject *pItem = CDSObject::CreateVideoItem( sId, + sName, + sObjectId ); + pItem->m_bRestricted = false; + pItem->m_bSearchable = true; + pItem->m_sWriteStatus = "WRITABLE"; + + if ( bAddRef ) + { + QString sRefId = QString( "%1/0/item%2") + .arg( m_sExtensionId ) + .arg( sURIParams ); + + pItem->SetPropValue( "refID", sRefId ); + } + + pItem->SetPropValue( "country" , sCountry ); + pItem->SetPropValue( "director" , sDirector ); + pItem->SetPropValue( "year" , sYear ); + pItem->SetPropValue( "genre" , sGenre ); + //cerr << QString("Setting GENRE : %1 for item (%3) %2").arg(sGenre).arg(sTitle).arg(nVidID) << endl; + pItem->SetPropValue( "longDescription", sDescription ); + pItem->SetPropValue( "description" , sDescription ); + + //pItem->SetPropValue( "producer" , ); + //pItem->SetPropValue( "rating" , ); + //pItem->SetPropValue( "actor" , ); + //pItem->SetPropValue( "director" , ); + //pItem->SetPropValue( "publisher" , ); + //pItem->SetPropValue( "language" , ); + //pItem->SetPropValue( "relation" , ); + //pItem->SetPropValue( "region" , ); + + pResults->Add( pItem ); + + // ---------------------------------------------------------------------- + // Add Video Resource Element based on File extension (HTTP) + // ---------------------------------------------------------------------- + + QFileInfo fInfo( sFileName ); + + QString sMimeType = HTTPRequest::GetMimeType( fInfo.extension( FALSE )); + QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.OR G_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); + QString sURI = QString( "%1GetVideo%2").arg( sURIBase ) + .arg( sURIParams ); + + Resource *pRes = pItem->AddResource( sProtocol, sURI ); + + // Wonder if we should add the filesize to videometadata. would make this cleaner since we + // are hitting the db for everything else. + + pRes->AddAttribute( "size" , QString("%1").arg(fInfo.size()) ); + +} + diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsvideo.h /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsvideo.h --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythbackend/upnpcdsvideo.h 1969-12-31 18:00:00.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythbackend/upnpcdsvideo.h 2007-08-23 11:09:51.000000000 -0500 @@ -0,0 +1,68 @@ +////////////////////////////////////////////////////////////////////////////// +// Program Name: upnpcdstv.h +// +// Purpose - uPnp Content Directory Extention for Recorded TV +// +// Created By : David Blain Created On : Jan. 24, 2005 +// Modified By : Modified On: +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef UPnpCDSVIDEO_H_ +#define UPnpCDSVIDEO_H_ + +#include "mainserver.h" +#include "upnpcds.h" + +typedef QMap IntMap; + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +class UPnpCDSVideo : public UPnpCDSExtension +{ + private: + + static UPnpCDSRootInfo g_RootNodes[]; + static int g_nRootCount; + + QStringMap m_mapBackendIp; + QStringMap m_mapBackendPort; + + IntMap m_mapGenreNames; + IntMap m_mapCountryNames; + IntMap m_mapGenre; + IntMap m_mapCountry; + + protected: + + virtual UPnpCDSRootInfo *GetRootInfo (int nIdx); + virtual int GetRootCount ( ); + virtual QString GetTableName ( QString sColumn ); + virtual QString GetItemListSQL( QString sColumn = ""); + + virtual void BuildItemQuery( MSqlQuery &query, + const QStringMap &mapParams ); + + virtual void AddItem( const QString &sObjectId, + UPnpCDSExtensionResults *pResults, + bool bAddRef, + MSqlQuery &query ); + + private: + + void FillMetaMaps (void); + + public: + + UPnpCDSVideo( ) : UPnpCDSExtension( "Videos", "Videos", + "object.item.videoItem" ) + { + FillMetaMaps(); + } + + virtual ~UPnpCDSVideo() {} +}; + +#endif diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythcommflag/ClassicCommDetector.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythcommflag/ClassicCommDetector.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythcommflag/ClassicCommDetector.cpp 2007-01-22 03:08:38.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythcommflag/ClassicCommDetector.cpp 2007-03-20 21:59:19.000000000 -0500 @@ -52,7 +52,8 @@ recordingStartedAt(recordingStartedAt_in), recordingStopsAt(recordingStopsAt_in), framesProcessed(0),preRoll(0),postRoll(0), - logoDetector(0) + logoDetector(0), + sceneChangeDetector(0) { stillRecording = recordingStopsAt > QDateTime::currentDateTime(); diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythfilldatabase/filldata.cpp /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythfilldatabase/filldata.cpp --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythfilldatabase/filldata.cpp 2007-01-22 03:08:41.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythfilldatabase/filldata.cpp 2007-08-23 17:39:10.000000000 -0500 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ #include "cardutil.h" #include "sourceutil.h" #include "remoteutil.h" +#include "videosource.h" // for is_grabber.. using namespace std; @@ -54,6 +56,7 @@ bool isNorthAmerica = false; bool isJapan = false; bool interrupted = false; +bool endofdata = false; bool refresh_today = false; bool refresh_tomorrow = true; bool refresh_second = false; @@ -65,6 +68,7 @@ bool channel_updates = false; bool channel_update_run = false; bool remove_new_channels = false; +bool filter_new_channels = true; bool only_update_channels = false; bool need_post_grab_proc = true; QString logged_in = ""; @@ -212,6 +216,11 @@ QString userid; QString password; QString lineupid; + bool xmltvgrabber_baseline; + bool xmltvgrabber_manualconfig; + bool xmltvgrabber_cache; + QString xmltvgrabber_prefmethod; + vector dd_dups; }; @@ -841,18 +850,22 @@ bool insert_channels = insert_chan(source.id); int new_channels = DataDirectProcessor::UpdateChannelsSafe( - source.id, insert_channels); + source.id, insert_channels, filter_new_channels); // User must pass "--do-channel-updates" for these updates if (channel_updates) - DataDirectProcessor::UpdateChannelsUnsafe(source.id); + { + DataDirectProcessor::UpdateChannelsUnsafe( + source.id, filter_new_channels); + } // TODO delete any channels which no longer exist in listings source if (update_icons) UpdateSourceIcons(source.id); // Unselect channels not in users lineup for DVB, HDTV - if (!insert_channels && (new_channels > 0)) + if (!insert_channels && (new_channels > 0) && + is_grabber_labs(source.xmltvgrabber)) { bool ok0 = (logged_in == source.userid); bool ok1 = (raw_lineup == source.id); @@ -877,14 +890,33 @@ bool DataDirectUpdateChannels(Source source) { - ddprocessor.SetListingsProvider(DD_ZAP2IT); + if (source.xmltvgrabber == "datadirect") + ddprocessor.SetListingsProvider(DD_ZAP2IT); + else if (source.xmltvgrabber == "schedulesdirect1") + ddprocessor.SetListingsProvider(DD_SCHEDULES_DIRECT); + else + { + VERBOSE(VB_IMPORTANT, + "FillData: We only support DataDirectUpdateChannels with " + "TMS Labs and Schedules Direct."); + return false; + } + ddprocessor.SetUserID(source.userid); ddprocessor.SetPassword(source.password); - bool ok = ddprocessor.GrabFullLineup( - source.lineupid, true, insert_chan(source.id)/*only sel*/); - logged_in = source.userid; - raw_lineup = source.id; + bool ok = true; + if (!is_grabber_labs(source.xmltvgrabber)) + { + ok = ddprocessor.GrabLineupsOnly(); + } + else + { + ok = ddprocessor.GrabFullLineup(source.lineupid, true, + insert_chan(source.id)/*only sel*/); + logged_in = source.userid; + raw_lineup = source.id; + } if (ok) DataDirectStationUpdate(source, false); @@ -976,6 +1008,25 @@ bool grabDDData(Source source, int poffset, QDate pdate, int ddSource) { + if (source.dd_dups.empty()) + ddprocessor.SetCacheData(false); + else + { + VERBOSE(VB_GENERAL, QString( + "This DataDirect listings source is " + "shared by %1 MythTV lineups") + .arg(source.dd_dups.size()+1)); + if (source.id > source.dd_dups[0]) + { + VERBOSE(VB_IMPORTANT, "We should use cached data for this one"); + } + else if (source.id < source.dd_dups[0]) + { + VERBOSE(VB_IMPORTANT, "We should keep data around after this one"); + } + ddprocessor.SetCacheData(true); + } + ddprocessor.SetListingsProvider(ddSource); ddprocessor.SetUserID(source.userid); ddprocessor.SetPassword(source.password); @@ -2505,7 +2556,17 @@ return false; handleChannels(id, &chanlist); - handlePrograms(id, &proglist); + if (proglist.count() == 0) + { + VERBOSE(VB_GENERAL, + QString("No programs found in data.")); + endofdata = true; + } + else + { + handlePrograms(id, &proglist); + } + return true; } @@ -2531,12 +2592,9 @@ if (xmltv_grabber == "datadirect") return grabDDData(source, offset, *qCurrentDate, DD_ZAP2IT); - else if (xmltv_grabber == "technovera") - { - VERBOSE(VB_ALL, "This grabber is no longer supported"); - exit(FILLDB_EXIT_INVALID_CMDLINE); - } - + if (xmltv_grabber == "schedulesdirect1") + return grabDDData(source, offset, *qCurrentDate, DD_SCHEDULES_DIRECT); + char tempfilename[] = "/tmp/mythXXXXXX"; if (mkstemp(tempfilename) == -1) { @@ -2551,127 +2609,32 @@ QString home = QDir::homeDirPath(); QString configfile = QString("%1/%2.xmltv").arg(MythContext::GetConfDir()) .arg(source.name); - QString command; - - if (xmltv_grabber == "tv_grab_uk") - command.sprintf("nice %s --days 7 --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_uk_rt") - command.sprintf("nice %s --days 14 --config-file '%s' --output %s", - xmltv_grabber.ascii(), - configfile.ascii(), filename.ascii()); - else if (xmltv_grabber == "tv_grab_au") - command.sprintf("nice %s --days 7 --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_de_tvtoday") - command.sprintf("nice %s --slow --days 1 --config-file '%s' --offset %d --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - offset, filename.ascii()); - else if (xmltv_grabber == "tv_grab_fr") - command.sprintf("nice %s --days 7 --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_nl") - command.sprintf("nice %s --output %s", - xmltv_grabber.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_fi") - // Use the default of 10 days for Finland's grabber - command.sprintf("nice %s --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_es" || - xmltv_grabber == "tv_grab_es_laguiatv") - // Use fixed interval of 3 days for Spanish grabber - command.sprintf("nice %s --days=4 --config-file '%s' --output %s", - xmltv_grabber.ascii(), - configfile.ascii(), filename.ascii()); - else if (xmltv_grabber == "tv_grab_jp") - { - // Use fixed interval of 7 days for Japanese grabber - command.sprintf("nice %s --days 7 --enable-readstr --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - isJapan = true; - } - else if (xmltv_grabber == "tv_grab_no") - command.sprintf("nice %s --days 1 --offset %d --config-file '%s' --output %s", - xmltv_grabber.ascii(), offset, configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_se_swedb") - command.sprintf("nice %s --days 1 --offset %d --config-file '%s' --output %s", - xmltv_grabber.ascii(), offset, configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_dk") - // Use fixed interval of 7 days for Danish grabber - command.sprintf("nice %s --days 7 --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_pt") - // Use fixed interval of 3 days for Portuguese grabber - command.sprintf("nice %s --days=4 --config-file '%s' --output %s", - xmltv_grabber.ascii(), - configfile.ascii(), filename.ascii()); - else if (xmltv_grabber == "tv_grab_be_tvb") - command.sprintf("nice %s --days 1 --offset %d --config-file '%s' --output %s", - xmltv_grabber.ascii(), offset, configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_be_tlm") - command.sprintf("nice %s --days 1 --offset %d --config-file '%s' --output %s", - xmltv_grabber.ascii(), offset, configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_ee") - // Estonian grabber returns all known data by default - command.sprintf("nice %s --output %s", - xmltv_grabber.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_il") - // Israeli grabber returns all known data by default - command.sprintf("nice %s --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else if (xmltv_grabber == "tv_grab_ru") - // Russian grabber returns all known data by default - command.sprintf("nice %s --config-file '%s' --output %s", - xmltv_grabber.ascii(), configfile.ascii(), - filename.ascii()); - else - { - isNorthAmerica = true; - command.sprintf("nice %s --days 1 --offset %d --config-file '%s' " - "--output %s", xmltv_grabber.ascii(), - offset, configfile.ascii(), filename.ascii()); - } - - if (((print_verbose_messages & VB_GENERAL) == 0) && - (xmltv_grabber == "tv_grab_na" || - xmltv_grabber == "tv_grab_de_tvtoday" || - xmltv_grabber == "tv_grab_fi" || - xmltv_grabber == "tv_grab_es" || - xmltv_grabber == "tv_grab_es_laguiatv" || - xmltv_grabber == "tv_grab_se_swedb" || - xmltv_grabber == "tv_grab_no" || - xmltv_grabber == "tv_grab_dk" || - xmltv_grabber == "tv_grab_uk" || - xmltv_grabber == "tv_grab_uk_rt" || - xmltv_grabber == "tv_grab_nl" || - xmltv_grabber == "tv_grab_fr" || - xmltv_grabber == "tv_grab_fi" || - xmltv_grabber == "tv_grab_jp" || - xmltv_grabber == "tv_grab_pt" || - xmltv_grabber == "tv_grab_be_tvb" || - xmltv_grabber == "tv_grab_be_tlm" || - xmltv_grabber == "tv_grab_ee" || - xmltv_grabber == "tv_grab_ru")) - command += " --quiet"; + QString command = QString("nice %1 --config-file '%2' --output %3") + .arg(xmltv_grabber.ascii()) + .arg(configfile.ascii()) + .arg(filename.ascii()); + + // The one concession to grabber specific behaviour. + // Will be removed when the grabber allows. + if (xmltv_grabber == "tv_grab_jp") + { + command += QString(" --enable-readstr"); + isJapan = true; + } + else if (source.xmltvgrabber_prefmethod != "allatonce") + { + // XMLTV Docs don't recommend grabbing one day at a + // time but the current myth code is heavily geared + // that way so until it is re-written behave as + // we always have done. + command += QString(" --days 1 --offset %1").arg(offset); + } command += graboptions; - VERBOSE(VB_GENERAL, - "----------------- Start of XMLTV output -----------------"); + if (! (print_verbose_messages & VB_GENERAL)) + command += " --quiet"; QDateTime qdtNow = QDateTime::currentDateTime(); MSqlQuery query(MSqlQuery::InitCon()); @@ -2685,10 +2648,18 @@ "WHERE value='mythfilldatabaseLastRunStatus'") .arg(status)); + VERBOSE(VB_GENERAL, QString("Grabber Command: %1").arg(command)); + + VERBOSE(VB_GENERAL, + "----------------- Start of XMLTV output -----------------"); + int systemcall_status = system(command.ascii()); bool succeeded = WIFEXITED(systemcall_status) && WEXITSTATUS(systemcall_status) == 0; + VERBOSE(VB_GENERAL, + "------------------ End of XMLTV output ------------------"); + qdtNow = QDateTime::currentDateTime(); query.exec(QString("UPDATE settings SET data ='%1' " "WHERE value='mythfilldatabaseLastRunEnd'") @@ -2704,13 +2675,13 @@ query.exec(QString("UPDATE settings SET data ='%1' " "WHERE value='mythfilldatabaseLastRunStatus'") .arg(status)); + + VERBOSE(VB_GENERAL, status); + if (WIFSIGNALED(systemcall_status) && (WTERMSIG(systemcall_status) == SIGINT || WTERMSIG(systemcall_status) == SIGQUIT)) interrupted = true; } - - VERBOSE(VB_GENERAL, - "------------------ End of XMLTV output ------------------"); grabDataFromFile(source.id, filename); @@ -2784,7 +2755,7 @@ query.prepare("DELETE FROM record WHERE (type = :SINGLE " "OR type = :OVERRIDE OR type = :DONTRECORD) " - "AND enddate < NOW();"); + "AND enddate < CURDATE();"); query.bindValue(":SINGLE", kSingleRecord); query.bindValue(":OVERRIDE", kOverrideRecord); query.bindValue(":DONTRECORD", kDontRecord); @@ -2827,6 +2798,7 @@ bool fillData(QValueList &sourcelist) { QValueList::Iterator it; + QValueList::Iterator it2; QString status, querystr; MSqlQuery query(MSqlQuery::InitCon()); @@ -2834,45 +2806,223 @@ int failures = 0; int externally_handled = 0; int total_sources = sourcelist.size(); - - query.exec(QString("SELECT MAX(endtime) FROM program WHERE manualid=0;")); - if (query.isActive() && query.size() > 0) - { - query.next(); - - if (!query.isNull(0)) - GuideDataBefore = QDateTime::fromString(query.value(0).toString(), - Qt::ISODate); - } + int source_channels = 0; QString sidStr = QString("Updating source #%1 (%2) with grabber %3"); need_post_grab_proc = false; + int nonewdata = 0; + bool has_dd_source = false; + + // find all DataDirect duplicates, so we only data download once. + for (it = sourcelist.begin(); it != sourcelist.end(); ++it) + { + if (!is_grabber_datadirect((*it).xmltvgrabber)) + continue; + + has_dd_source = true; + for (it2 = sourcelist.begin(); it2 != sourcelist.end(); ++it2) + { + if (((*it).id != (*it2).id) && + ((*it).xmltvgrabber == (*it2).xmltvgrabber) && + ((*it).userid == (*it2).userid) && + ((*it).password == (*it2).password)) + { + (*it).dd_dups.push_back((*it2).id); + } + } + } + if (has_dd_source) + ddprocessor.CreateTempDirectory(); for (it = sourcelist.begin(); it != sourcelist.end(); ++it) { + + query.prepare("SELECT MAX(endtime) FROM program p LEFT JOIN channel c " + "ON p.chanid=c.chanid WHERE c.sourceid= :SRCID " + "AND manualid = 0;"); + query.bindValue(":SRCID", (*it).id); + query.exec(); + if (query.isActive() && query.size() > 0) + { + query.next(); + + if (!query.isNull(0)) + GuideDataBefore = QDateTime::fromString(query.value(0).toString(), + Qt::ISODate); + } + channel_update_run = false; + endofdata = false; + + QString xmltv_grabber = (*it).xmltvgrabber; + + if (xmltv_grabber == "eitonly") + { + VERBOSE(VB_GENERAL, + QString("Source %1 configured to use only the " + "broadcasted guide data. Skipping.") + .arg((*it).id)); + + externally_handled++; + query.exec(QString("UPDATE settings SET data ='%1' " + "WHERE value='mythfilldatabaseLastRunStart' OR " + "value = 'mythfilldatabaseLastRunEnd'") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"))); + continue; + } + else if (xmltv_grabber == "/bin/true" || + xmltv_grabber == "none" || + xmltv_grabber == "") + { + VERBOSE(VB_GENERAL, + QString("Source %1 configured with no grabber. " + "Nothing to do.").arg((*it).id)); + + externally_handled++; + query.exec(QString("UPDATE settings SET data ='%1' " + "WHERE value='mythfilldatabaseLastRunStart' OR " + "value = 'mythfilldatabaseLastRunEnd'") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"))); + continue; + } + VERBOSE(VB_GENERAL, sidStr.arg((*it).id) .arg((*it).name) - .arg((*it).xmltvgrabber)); + .arg(xmltv_grabber)); - QString xmltv_grabber = (*it).xmltvgrabber; - need_post_grab_proc |= (xmltv_grabber != "datadirect"); + query.prepare( + "SELECT COUNT(chanid) FROM channel WHERE sourceid = " + ":SRCID AND xmltvid != ''"); + query.bindValue(":SRCID", (*it).id); + query.exec(); - if (xmltv_grabber == "tv_grab_uk" || xmltv_grabber == "tv_grab_uk_rt" || - xmltv_grabber == "tv_grab_fi" || xmltv_grabber == "tv_grab_es" || - xmltv_grabber == "tv_grab_es_laguiatv" || - xmltv_grabber == "tv_grab_nl" || xmltv_grabber == "tv_grab_au" || - xmltv_grabber == "tv_grab_fr" || xmltv_grabber == "tv_grab_jp" || - xmltv_grabber == "tv_grab_pt" || xmltv_grabber == "tv_grab_ee" || - xmltv_grabber == "tv_grab_dk") + if (query.isActive() && query.size() > 0) { - // These don't support the --offset option, so just grab the max. - // TODO: tv_grab_fi/dk/is seems to support --offset, maybe more. Needs verification. - if (!grabData(*it, 0)) - ++failures; + query.next(); + source_channels = query.value(0).toInt(); + if (source_channels > 0) + { + VERBOSE(VB_GENERAL, QString("Found %1 channels for " + "source %2 which use grabber") + .arg(source_channels).arg((*it).id)); + } + else + { + VERBOSE(VB_GENERAL, QString("No channels are " + "configured to use grabber.")); + } + } + else { + source_channels = 0; + VERBOSE(VB_GENERAL, + QString("Can't get a channel count for source id %1") + .arg((*it).id)); + } + + bool hasprefmethod = false; + + if (is_grabber_external(xmltv_grabber)) + { + + QProcess grabber_capabilities_proc(xmltv_grabber); + grabber_capabilities_proc.addArgument(QString("--capabilities")); + if ( grabber_capabilities_proc.start() ) + { + + int i=0; + // Assume it shouldn't take more than 10 seconds + // Broken versions of QT cause QProcess::start + // and QProcess::isRunning to return true even + // when the executable doesn't exist + while (grabber_capabilities_proc.isRunning() && i < 100) + { + usleep(100000); + ++i; + } + + if (grabber_capabilities_proc.normalExit()) + { + QString capabilites = ""; + + while (grabber_capabilities_proc.canReadLineStdout()) + { + QString capability + = grabber_capabilities_proc.readLineStdout(); + capabilites += capability + " "; + + if (capability == "baseline") + (*it).xmltvgrabber_baseline = true; + + if (capability == "manualconfig") + (*it).xmltvgrabber_manualconfig = true; + + if (capability == "cache") + (*it).xmltvgrabber_cache = true; + + if (capability == "preferredmethod") + hasprefmethod = true; + } + + VERBOSE(VB_GENERAL, QString("Grabber has capabilities: %1") + .arg(capabilites)); + } + else { + VERBOSE(VB_IMPORTANT, "%1 --capabilities failed or we " + "timed out waiting. You may need to upgrade your " + "xmltv grabber"); + } + } + else { + QString error = grabber_capabilities_proc.readLineStdout(); + VERBOSE(VB_IMPORTANT, QString("Failed to run %1 " + "--capabilities").arg(xmltv_grabber)); + } + } + + + if (hasprefmethod) + { + + QProcess grabber_method_proc(xmltv_grabber); + grabber_method_proc.addArgument("--preferredmethod"); + if ( grabber_method_proc.start() ) + { + int i=0; + // Assume it shouldn't take more than 10 seconds + // Broken versions of QT cause QProcess::start + // and QProcess::isRunning to return true even + // when the executable doesn't exist + while (grabber_method_proc.isRunning() && i < 100) + { + usleep(100000); + ++i; + } + + if (grabber_method_proc.normalExit()) + { + (*it).xmltvgrabber_prefmethod = + grabber_method_proc.readLineStdout(); + } + else { + VERBOSE(VB_IMPORTANT, "%1 --preferredmethod failed or we " + "timed out waiting. You may need to upgrade your " + "xmltv grabber"); + } + + VERBOSE(VB_GENERAL, QString("Grabber prefers method: %1") + .arg((*it).xmltvgrabber_prefmethod)); + } + else { + QString error = grabber_method_proc.readLineStdout(); + VERBOSE(VB_IMPORTANT, QString("Failed to run %1 --preferredmethod") + .arg(xmltv_grabber)); + } } - else if ((xmltv_grabber == "datadirect") && dd_grab_all) + + need_post_grab_proc |= !is_grabber_datadirect(xmltv_grabber); + + if (is_grabber_datadirect(xmltv_grabber) && dd_grab_all) { if (only_update_channels) DataDirectUpdateChannels(*it); @@ -2882,47 +3032,32 @@ grabData(*it, 0, &qCurrentDate); } } - else if (xmltv_grabber == "datadirect" || - xmltv_grabber == "tv_grab_se_swedb" || - xmltv_grabber == "tv_grab_no" || - xmltv_grabber == "tv_grab_de_tvtoday" || - xmltv_grabber == "tv_grab_be_tvb" || - xmltv_grabber == "tv_grab_be_tlm" || - xmltv_grabber == "tv_grab_is" || - xmltv_grabber == "tv_grab_br" || - xmltv_grabber == "tv_grab_cz" || - xmltv_grabber == "tv_grab_ru") + else if ((*it).xmltvgrabber_prefmethod == "allatonce") + { + if (!grabData(*it, 0)) + ++failures; + } + else if ((*it).xmltvgrabber_baseline || + is_grabber_datadirect(xmltv_grabber)) { - // Grabbers supporting the --offset option - - if (xmltv_grabber == "tv_grab_no") - listing_wrap_offset = 6 * 3600; QDate qCurrentDate = QDate::currentDate(); - int grabdays = 9; + // We'll keep grabbing until it returns nothing + // Max days currently supported is 21 + int grabdays = 21; - // Grab different amount of days for the different grabbers, - // often decided by the person maintaining the grabbers. if (maxDays > 0) // passed with --max-days grabdays = maxDays; - else if (xmltv_grabber == "datadirect") + else if (is_grabber_datadirect(xmltv_grabber)) grabdays = 14; - else if (xmltv_grabber == "tv_grab_se_swedb") - grabdays = 10; - else if (xmltv_grabber == "tv_grab_no" || - xmltv_grabber == "tv_grab_de_tvtoday") - grabdays = 7; - else if (xmltv_grabber == "tv_grab_be_tvb" || - xmltv_grabber == "tv_grab_be_tlm") - grabdays = 5; grabdays = (only_update_channels) ? 1 : grabdays; if (grabdays == 1) refresh_today = true; - if ((xmltv_grabber == "datadirect") && only_update_channels) + if (is_grabber_datadirect(xmltv_grabber) && only_update_channels) { DataDirectUpdateChannels(*it); grabdays = 0; @@ -2961,16 +3096,21 @@ (i == 2 && refresh_second)) { // Always refresh if the user specified today/tomorrow/second. - download_needed = true; - } - else if (xmltv_grabber == "tv_grab_se_swedb") - { - // Since tv_grab_se_swedb handles caching internally, - // let it do its job and always grab new data. - VERBOSE(VB_GENERAL, - "Data Refresh needed because the grabber relies on " - "internal caching."); - download_needed = true; + if (refresh_today) + { + VERBOSE(VB_GENERAL, + "Data Refresh needed because user specified --refresh-today"); + } + else if (refresh_second) + { + VERBOSE(VB_GENERAL, + "Data Refresh needed because user specified --refresh-second"); + } + else + { + VERBOSE(VB_GENERAL, + "Data Refresh always needed for tomorrow"); + } } else { @@ -2987,9 +3127,9 @@ "INTERVAL '%1' DAY), INTERVAL '18' HOUR) " " AND starttime < DATE_ADD(CURRENT_DATE(), " "INTERVAL '%2' DAY) " - "WHERE c.sourceid = %3 " + "WHERE c.sourceid = %3 AND c.xmltvid != '' " "GROUP BY c.chanid;"; - + if (query.exec(querystr.arg(i-1).arg(i).arg((*it).id)) && query.isActive()) { @@ -3086,7 +3226,7 @@ "offset day %2.").arg(i-1).arg(i)); download_needed = true; } - } + } if (download_needed) { @@ -3100,6 +3240,14 @@ break; } } + + if (endofdata) + { + VERBOSE(VB_GENERAL, + QString("Grabber is no longer returning program data, finishing")); + break; + } + } else { @@ -3109,61 +3257,51 @@ } } } - else if (xmltv_grabber == "eitonly") - { - VERBOSE(VB_IMPORTANT, "Source configured to use only the " - "broadcasted guide data. Skipping."); - externally_handled++; - query.exec(QString("UPDATE settings SET data ='%1' " - "WHERE value='mythfilldatabaseLastRunStart' OR " - "value = 'mythfilldatabaseLastRunEnd'") - .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"))); - } - else if (xmltv_grabber == "/bin/true" || - xmltv_grabber == "none" || - xmltv_grabber == "") - { - VERBOSE(VB_IMPORTANT, - "Source configured with no grabber. Nothing to do."); - externally_handled++; - query.exec(QString("UPDATE settings SET data ='%1' " - "WHERE value='mythfilldatabaseLastRunStart' OR " - "value = 'mythfilldatabaseLastRunEnd'") - .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm"))); - } else { VERBOSE(VB_IMPORTANT, QString("Grabbing XMLTV data using ") + xmltv_grabber + - " is not verified as working."); + " is not supported. You may need to upgrade to" + " the latest version of XMLTV."); } if (interrupted) { break; } - } - if (only_update_channels && !need_post_grab_proc) - return true; + query.prepare("SELECT MAX(endtime) FROM program p LEFT JOIN channel c " + "ON p.chanid=c.chanid WHERE c.sourceid= :SRCID " + "AND manualid = 0;"); + query.bindValue(":SRCID", (*it).id); + query.exec(); + if (query.isActive() && query.size() > 0) + { + query.next(); - query.exec(QString("SELECT MAX(endtime) FROM program WHERE manualid=0;")); - if (query.isActive() && query.size() > 0) - { - query.next(); + if (!query.isNull(0)) + GuideDataAfter = QDateTime::fromString(query.value(0).toString(), + Qt::ISODate); + } - if (!query.isNull(0)) - GuideDataAfter = QDateTime::fromString(query.value(0).toString(), - Qt::ISODate); + if (GuideDataAfter == GuideDataBefore) + { + nonewdata++; + } } + if (only_update_channels && !need_post_grab_proc) + return true; + if (failures == 0) { - if ((GuideDataAfter == GuideDataBefore) && + if (nonewdata > 0 && (total_sources != externally_handled)) - status = "mythfilldatabase ran, but did not insert " - "any new data into the Guide. This can indicate a " - "potential grabber failure."; + status = QString("mythfilldatabase ran, but did not insert " + "any new data into the Guide for %1 of %2 sources. " + "This can indicate a potential grabber failure.") + .arg(nonewdata) + .arg(total_sources); else status = "Successful."; @@ -3330,7 +3468,7 @@ int fromxawfile_id = 1; QString fromxawfile_name; - bool usingDataDirect = false; + bool usingDataDirect = false, usingDataDirectLabs = false; bool grab_data = true; bool export_iconmap = false; @@ -3444,6 +3582,10 @@ { remove_new_channels = true; } + else if (!strcmp(a.argv()[argpos], "--do-not-filter-new-channels")) + { + filter_new_channels = false; + } else if (!strcmp(a.argv()[argpos], "--graboptions")) { if (((argpos + 1) >= a.argc())) @@ -3680,6 +3822,13 @@ cout << " option. New channels are automatically removed\n"; cout << " for DVB and HDTV sources that use DataDirect.\n"; cout << "\n"; + cout << "--do-not-filter-new-channels\n"; + cout << " Normally MythTV tries to avoid adding ATSC channels\n"; + cout << " to NTSC channel lineups. This option restores the\n"; + cout << " behaviour of adding every channel in the downloaded\n"; + cout << " channel lineup to MythTV's lineup, in case MythTV's\n"; + cout << " smarts fail you.\n"; + cout << "\n"; cout << "--graboptions <\"options\">\n"; cout << " Pass options to grabber\n"; cout << "\n"; @@ -3694,7 +3843,7 @@ cout << "--refresh-today\n"; cout << "--refresh-second\n"; cout << "--refresh-all\n"; - cout << " (Only valid for grabbers: DataDirect, se_swedb, no, ee, de_tvtoday)\n"; + cout << " (Only valid for selected grabbers: e.g. DataDirect)\n"; cout << " Force a refresh today or two days (or every day) from now,\n"; cout << " to catch the latest changes\n"; cout << "--dont-refresh-tomorrow\n"; @@ -3843,7 +3992,7 @@ "FROM videosource ") + where + QString(" ORDER BY sourceid;"); sourcequery.exec(querystr); - + if (sourcequery.isActive()) { if (sourcequery.size() > 0) @@ -3851,7 +4000,7 @@ while (sourcequery.next()) { Source newsource; - + newsource.id = sourcequery.value(0).toInt(); newsource.name = sourcequery.value(1).toString(); newsource.xmltvgrabber = sourcequery.value(2).toString(); @@ -3859,9 +4008,16 @@ newsource.password = sourcequery.value(4).toString(); newsource.lineupid = sourcequery.value(5).toString(); + newsource.xmltvgrabber_baseline = false; + newsource.xmltvgrabber_manualconfig = false; + newsource.xmltvgrabber_cache = false; + newsource.xmltvgrabber_prefmethod = ""; + sourcelist.append(newsource); - if (newsource.xmltvgrabber == "datadirect") - usingDataDirect = true; + usingDataDirect |= + is_grabber_datadirect(newsource.xmltvgrabber); + usingDataDirectLabs |= + is_grabber_labs(newsource.xmltvgrabber); } } else @@ -4095,6 +4251,12 @@ ddprocessor.GrabNextSuggestedTime(); } + if (usingDataDirectLabs || + !gContext->GetNumSetting("MythFillFixProgramIDsHasRunOnce", 0)) + { + DataDirectProcessor::FixProgramIDs(); + } + VERBOSE(VB_IMPORTANT, "\n" "===============================================================\n" "| Attempting to contact the master backend for rescheduling. |\n" diff -Nru /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythfrontend/library.xml /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythfrontend/library.xml --- /tmp/I6LedcdNLH/mythtv-0.20-svn20070122/programs/mythfrontend/library.xml 2007-01-22 03:08:36.000000000 -0600 +++ /tmp/jxFKjogze3/mythtv-0.20.2/programs/mythfrontend/library.xml 2007-08-07 09:41:51.000000000 -0500 @@ -106,6 +106,13 @@ + + + +