Index: configure
===================================================================
--- configure	(revision 10247)
+++ configure	(working copy)
@@ -54,6 +54,7 @@
 lirc="yes"
 joystick_menu="yes"
 firewire_cable_box="yes"
+freebox_box="yes"
 dbox2_dvb_box="yes"
 ip_network_recorder="yes"
 hdhomerun_box="yes"
@@ -178,6 +179,9 @@
 echo "  --disable-lirc           disable lirc support (Infrared Remotes)"
 echo "  --disable-joystick-menu  disable joystick menu"
 echo "  --disable-firewire       disable support for FireWire cable boxes"
+echo "  --disable-freebox        disable support for Freebox"
+echo "  --livelibdir=DIR         location of Live streaming library"
+echo "  --liveincludedir=DIR     location of Live streaming include files"
 echo "  --disable-dbox2          disable support for Nokia DBOX2 DVB boxes (or compatibles)"
 echo "  --disable-hdhomerun      disable support for HDHomeRun boxes"
 echo "  --disable-crciprec       disable support for Network Recorder"
@@ -819,6 +823,14 @@
   ;;
   --disable-dbox2) dbox2_dvb_box="no"
   ;;
+  --enable-freebox) freebox_box="yes"
+  ;;
+  --disable-freebox) freebox_box="no"
+  ;;
+  --livelibdir=*) live_lib_dir=`echo $opt | cut -d '=' -f 2`
+  ;;
+  --liveincludedir=*) live_include_dir=`echo $opt | cut -d '=' -f 2`
+  ;;
   --enable-hdhomerun) hdhomerun_box="yes"
   ;;
   --disable-hdhomerun) hdhomerun_box="no"
@@ -2041,6 +2053,42 @@
     fi
 fi
 
+if test x"$freebox_box" = x"yes" ; then
+    if test x"$live_lib_dir" = x""; then
+        if has_library libliveMedia; then
+            CONFIG_LIVE_LIBS="-lliveMedia -lgroupsock -lBasicUsageEnvironment -lUsageEnvironment"
+        else
+            echo "Unable to find Live Media library."
+            freebox_box="no"
+        fi
+    else
+        if test ! -f "$live_lib_dir/liveMedia/libliveMedia.a"; then
+            echo "Unable to find Live Media library."
+            freebox_box="no"
+        fi
+        CONFIG_LIVE_LIBS="-L$live_lib_dir/liveMedia -L$live_lib_dir/UsageEnvironment -L$live_lib_dir/BasicUsageEnvironment -L$live_lib_dir/groupsock -lliveMedia -lgroupsock -lBasicUsageEnvironment -lUsageEnvironment"
+    fi
+    if test "x$live_include_dir" = "x"; then
+        if has_header liveMedia.hh; then
+            true
+        else
+            echo "Unable to find Live Media headers."
+            freebox_box="no"
+        fi
+    else
+        if test -f "$live_include_dir/liveMedia/include/liveMedia.hh"; then
+            LIVE_INCLUDES="$live_include_dir/liveMedia/include $live_include_dir/UsageEnvironment/include $live_include_dir/BasicUsageEnvironment/include $live_include_dir/groupsock/include"
+        else
+            if test -f "$live_include_dir/liveMedia/liveMedia.hh"; then
+                LIVE_INCLUDES="$live_include_dir/liveMedia $live_include_dir/UsageEnvironment $live_include_dir/BasicUsageEnvironment $live_include_dir/groupsock"
+            else
+                echo "Unable to find Live Media headers."
+                freebox_box="no"
+            fi
+        fi
+    fi
+fi
+
 lamemp3="no"
 if has_library libmp3lame ; then
     if has_header lame/lame.h ; then
@@ -2327,6 +2375,7 @@
   echo "FireWire support $firewire_cable_box"
   echo "DVB support      $dvb [$dvb_path]"
   echo "DBox2 support    $dbox2_dvb_box"
+  echo "freebox support  $freebox_box"
   echo "HDHomeRun sup.   $hdhomerun_box"
   echo "CRC Ip Rec sup.  $ip_network_recorder"
 fi
@@ -2990,6 +3039,13 @@
   CCONFIG="$CCONFIG using_ip_rec"
 fi
 
+if test x"$freebox_box" = x"yes" ; then
+    CCONFIG="$CCONFIG using_freebox"
+    CONFIG_DEFINES="$CONFIG_DEFINES USING_FREEBOX"
+    echo "CONFIG_LIVE_LIBS=$CONFIG_LIVE_LIBS" >> $MYTH_CONFIG_MAK
+    CONFIG_INCLUDEPATH="$CONFIG_INCLUDEPATH $LIVE_INCLUDES"
+fi
+
 if test x"$lirc" = x"yes" ; then
   CCONFIG="$CCONFIG using_lirc"
   echo "CONFIG_LIRC_LIBS=-llirc_client" >> $MYTH_CONFIG_MAK
Index: libs/libmythtv/freeboxchannel.h
===================================================================
--- libs/libmythtv/freeboxchannel.h	(revision 0)
+++ libs/libmythtv/freeboxchannel.h	(revision 0)
@@ -0,0 +1,55 @@
+/** -*- Mode: c++ -*-
+ *  FreeboxChannel
+ *  Copyright (c) 2006 by Laurent Arnal, Benjamin Lerman & Mickaël Remars
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#ifndef FREEBOXCHANNEL_H
+#define FREEBOXCHANNEL_H
+
+#include "channelbase.h"
+#include "freeboxchannelinfo.h"
+
+#include <qmutex.h>
+
+class FreeboxRecorder;
+
+class FreeboxChannel : public ChannelBase
+{
+    friend class FreeboxRecorderImpl;
+  public:
+    FreeboxChannel(TVRec *parent, const QString &videodev, int cardid);
+    ~FreeboxChannel(void) { }
+
+    bool Open(void);
+    void Close(void);
+
+    bool IsOpen(void) const;
+
+    bool SwitchToInput(const QString &inputname, const QString &channum);
+    bool SwitchToInput(int inputNum, bool setstarting);
+    bool SetChannelByString(const QString &channum);
+
+    void SetRecorder(FreeboxRecorder *rec);
+
+  private:
+    FreeboxChannelInfo GetChanInfo(const QString& channum,
+                                   uint           sourceid = 0) const;
+    FreeboxChannelInfo GetCurrentChanInfo(void) const
+        { return GetChanInfo(curchannelname); }
+
+    QString               m_videodev;
+    FreeboxRecorder      *m_recorder;
+    fbox_chan_map_t       m_freeboxchannels;
+    mutable QMutex        m_lock;
+    int                   m_cardid;
+
+  private:
+    FreeboxChannel& operator=(const FreeboxChannel&); //< avoid default impl
+    FreeboxChannel(const FreeboxChannel&);            //< avoid default impl
+    FreeboxChannel();                                 //< avoid default impl
+};
+
+#endif // FREEBOXCHANNEL_H
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/freeboxchannelfetcher.h
===================================================================
--- libs/libmythtv/freeboxchannelfetcher.h	(revision 0)
+++ libs/libmythtv/freeboxchannelfetcher.h	(revision 0)
@@ -0,0 +1,19 @@
+#ifndef _FREEBOXCHANNELFETCHER_H_
+#define _FREEBOXCHANNELFETCHER_H_
+
+#include "freeboxchannelinfo.h"
+
+class FreeboxChannelFetcher
+{
+  private:
+    FreeboxChannelFetcher();
+    ~FreeboxChannelFetcher();
+
+  public:
+    static QString DownloadPlaylist(const QString& url);
+    static fbox_chan_map_t ParsePlaylist(const QString& rawdata);
+};
+
+#endif//_FREEBOXCHANNELFETCHER_H_
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/libmythtv.pro
===================================================================
--- libs/libmythtv/libmythtv.pro	(revision 10247)
+++ libs/libmythtv/libmythtv.pro	(working copy)
@@ -374,6 +374,18 @@
     using_dbox2:SOURCES += dbox2recorder.cpp dbox2channel.cpp dbox2epg.cpp
     using_dbox2:HEADERS += dbox2recorder.h dbox2channel.h dbox2epg.h
     using_dbox2:DEFINES += USING_DBOX2
+    
+    # Support for freebox (http://adsl.free.fr/)
+    using_freebox:SOURCES += freeboxrecorder.cpp
+    using_freebox:SOURCES += freeboxchannel.cpp
+    using_freebox:SOURCES += freeboxchannelfetcher.cpp
+    using_freebox:SOURCES += freeboxmediasink.cpp
+    using_freebox:HEADERS += freeboxrecorder.h
+    using_freebox:HEADERS += freeboxchannel.h
+    using_freebox:HEADERS += freeboxchannelfetcher.h
+    using_freebox:HEADERS += freeboxchannelinfo.h
+    using_freebox:HEADERS += freeboxmediasink.h
+    using_freebox:DEFINES += USING_FREEBOX
 
     # Support for HDHomeRun box
     using_hdhr {
Index: libs/libmythtv/freeboxchannel.cpp
===================================================================
--- libs/libmythtv/freeboxchannel.cpp	(revision 0)
+++ libs/libmythtv/freeboxchannel.cpp	(revision 0)
@@ -0,0 +1,190 @@
+/** -*- Mode: c++ -*-
+ *  FreeboxChannel
+ *  Copyright (c) 2006 by Laurent Arnal, Benjamin Lerman & Mickaël Remars
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#include "freeboxchannel.h"
+
+#include "libmythtv/freeboxchannelfetcher.h"
+#include "libmythtv/freeboxrecorder.h"
+#include "libmythtv/tv_rec.h"
+
+#define LOC QString("FBChan(%1): ").arg(GetCardID())
+#define LOC_ERR QString("FBChan(%1), Error: ").arg(GetCardID())
+
+FreeboxChannel::FreeboxChannel(TVRec*         parent,
+                               const QString& videodev,
+                               int            cardid)
+    : ChannelBase(parent),
+      m_videodev(videodev),
+      m_recorder(NULL),
+      m_lock(TRUE),
+      m_cardid(cardid)
+{
+}
+
+bool FreeboxChannel::Open(void)
+{
+    QMutexLocker locker(&m_lock);
+
+    if (!InitializeInputs())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+            QString("InitializeInputs() failed"));
+        return false;
+    }
+    
+    if (m_freeboxchannels.size() == 0)
+    {
+        QString content = FreeboxChannelFetcher::DownloadPlaylist(m_videodev);
+        m_freeboxchannels = FreeboxChannelFetcher::ParsePlaylist(content);
+        VERBOSE(VB_IMPORTANT, LOC + QString("Loaded %1 channels from %2")
+            .arg(m_freeboxchannels.size())
+            .arg(m_videodev));
+    }
+
+    return IsOpen();
+}
+
+void FreeboxChannel::Close(void)
+{
+    QMutexLocker locker(&m_lock);
+    m_freeboxchannels.clear();
+}
+
+bool FreeboxChannel::IsOpen(void) const
+{
+    QMutexLocker locker(&m_lock);
+    return m_freeboxchannels.size() > 0;
+}
+
+bool FreeboxChannel::SwitchToInput(const QString &inputname,
+                                   const QString &channum)
+{
+    QMutexLocker locker(&m_lock);
+
+    int inputNum = GetInputByName(inputname);
+    if (inputNum < 0)
+        return false;
+
+    return SetChannelByString(channum);
+}
+
+bool FreeboxChannel::SwitchToInput(int inputNum, bool setstarting)
+{
+    QMutexLocker locker(&m_lock);
+
+    InputMap::const_iterator it = inputs.find(inputNum);
+    if (it == inputs.end())
+        return false;
+
+    QString channum = (*it)->startChanNum;
+
+    if (setstarting)
+        return SetChannelByString(channum);
+
+    return true;
+}
+
+bool FreeboxChannel::SetChannelByString(const QString &channum)
+{
+    QMutexLocker locker(&m_lock);
+
+    // Verify that channel exists
+    if (!GetChanInfo(channum).isValid())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                QString("SetChannelByString(%1)").arg(channum) +
+                " Invalid channel");
+        return false;
+    }
+
+    // Set the channel..
+    curchannelname = channum;
+
+    // Send signal to recorder that channel has changed.
+    if (m_recorder)
+    {
+        FreeboxChannelInfo chaninfo(GetCurrentChanInfo());
+        m_recorder->ChannelChanged(chaninfo);
+    }
+
+    return true;
+}
+
+FreeboxChannelInfo FreeboxChannel::GetChanInfo(const QString& channum,
+                                               uint           sourceid) const
+{
+    QMutexLocker locker(&m_lock);
+
+    FreeboxChannelInfo dummy;
+    QString msg = LOC_ERR + QString("GetChanInfo(%1) failed").arg(channum);
+
+    if (channum.isEmpty())
+    {
+        VERBOSE(VB_IMPORTANT, msg);
+        return dummy;
+    }
+
+    if (!sourceid)
+    {
+        InputMap::const_iterator it = inputs.find(currentInputID);
+        if (it == inputs.end())
+        {
+            VERBOSE(VB_IMPORTANT, msg);
+            return dummy;
+        }
+        sourceid = (*it)->sourceid;
+    }
+
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare(
+        "SELECT freqid, name "
+        "FROM channel "
+        "WHERE channum  = :CHANNUM AND "
+        "      sourceid = :SOURCEID");
+
+    query.bindValue(":CHANNUM",  channum);
+    query.bindValue(":SOURCEID", sourceid);
+
+    if (!query.exec() || !query.isActive())
+    {
+        MythContext::DBError("fetching chaninfo", query);
+        VERBOSE(VB_IMPORTANT, msg);
+        return dummy;
+    }
+
+    while (query.next())
+    {
+        // Try to map freqid or name to a channel in the map
+        const QString freqid = query.value(0).toString();
+        fbox_chan_map_t::const_iterator it;
+        if (!freqid.isEmpty())
+        {
+            it = m_freeboxchannels.find(freqid);
+            if (it != m_freeboxchannels.end())
+                return *it;
+        }
+
+        // Try to map name to a channel in the map
+        const QString name    = query.value(1).toString();
+        for (it = m_freeboxchannels.begin();
+             it != m_freeboxchannels.end(); ++it)
+        {
+            if ((*it).m_name == name)
+                return *it;
+        }
+    }
+
+    VERBOSE(VB_IMPORTANT, msg);
+    return dummy;
+}
+
+void FreeboxChannel::SetRecorder(FreeboxRecorder *rec)
+{
+    QMutexLocker locker(&m_lock);
+    m_recorder = rec;
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/freeboxmediasink.cpp
===================================================================
--- libs/libmythtv/freeboxmediasink.cpp	(revision 0)
+++ libs/libmythtv/freeboxmediasink.cpp	(revision 0)
@@ -0,0 +1,73 @@
+/**
+ *  FreeboxMediaSink
+ *  Copyright (c) 2006 by Laurent Arnal, Benjamin Lerman & Mickaël Remars
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#include "freeboxmediasink.h"
+
+#include "freeboxrecorder.h"
+
+
+
+FreeboxMediaSink::FreeboxMediaSink(UsageEnvironment& pEnv,
+                                   FreeboxRecorder&  pRecorder,
+                                   unsigned          bufferSize) :
+    MediaSink(pEnv),
+    fBufferSize(bufferSize),
+    env(pEnv),
+    recorder(pRecorder)
+{
+    // Setup the data buffer
+    fBuffer = new unsigned char[fBufferSize];
+}
+
+FreeboxMediaSink::~FreeboxMediaSink()
+{
+    // free the data buffer
+    delete[] fBuffer;
+}
+
+FreeboxMediaSink* FreeboxMediaSink::createNew(UsageEnvironment& env,
+                                              FreeboxRecorder&  pRecorder,
+                                              unsigned          bufferSize)
+{
+    return new FreeboxMediaSink(env, pRecorder, bufferSize);
+}
+
+Boolean FreeboxMediaSink::continuePlaying()
+{
+    if (fSource == NULL) return False;
+
+    fSource->getNextFrame(fBuffer, fBufferSize,
+                          afterGettingFrame, this,
+                          onSourceClosure, this);
+    return True;
+}
+
+void FreeboxMediaSink::afterGettingFrame(
+        void*          clientData,
+        unsigned       frameSize,
+        unsigned       /*numTruncatedBytes*/,
+        struct timeval presentationTime,
+        unsigned       /*durationInMicroseconds*/)
+{
+    FreeboxMediaSink* sink = (FreeboxMediaSink*)clientData;
+    sink->afterGettingFrame1(frameSize, presentationTime);
+}
+
+void FreeboxMediaSink::afterGettingFrame1(unsigned       frameSize,
+                                          struct timeval presentationTime)
+{
+    addData(fBuffer, frameSize, presentationTime);
+    continuePlaying();
+}
+
+void FreeboxMediaSink::addData(unsigned char* data,
+                               unsigned       dataSize,
+                               struct timeval presentationTime)
+{
+    recorder.addData(data, dataSize, presentationTime);
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/dbcheck.cpp
===================================================================
--- libs/libmythtv/dbcheck.cpp	(revision 10247)
+++ libs/libmythtv/dbcheck.cpp	(working copy)
@@ -10,7 +10,7 @@
 #include "mythdbcon.h"
 
 /// This is the DB schema version expected by the running MythTV instance.
-const QString currentDatabaseVersion = "1145";
+const QString currentDatabaseVersion = "1146";
 
 static bool UpdateDBVersionNumber(const QString &newnumber);
 static bool performActualUpdate(const QString updates[], QString version,
@@ -2308,6 +2308,18 @@
 //"ALTER TABLE capturecard DROP COLUMN dvb_hw_decoder;" in 0.21
 //"ALTER TABLE cardinput DROP COLUMN  preference;" in 0.22
 
+  if (dbver == "1145")
+    {
+        const QString updates[] = {
+"INSERT INTO profilegroups SET name = 'Freebox Input', cardtype = 'Freebox', is_default = 1;",
+""
+};
+
+        if (!performActualUpdate(updates, "1146", dbver))
+            return false;
+    }
+
+
     return true;
 }
 
@@ -2934,6 +2946,7 @@
 "INSERT INTO profilegroups VALUES (8,"
 " 'USB Mpeg-4 Encoder (Plextor ConvertX, etc)','GO7007',1,NULL);",
 "INSERT INTO profilegroups VALUES (9,'DBOX2 Input','DBOX2',1,NULL);",
+"INSERT INTO `profilegroups` VALUES (10,'Freebox Input','Freebox',1,NULL);",
 "INSERT INTO recordingprofiles VALUES (1,'Default',NULL,NULL,1);",
 "INSERT INTO recordingprofiles VALUES (2,'Live TV',NULL,NULL,1);",
 "INSERT INTO recordingprofiles VALUES (3,'High Quality',NULL,NULL,1);",
Index: libs/libmythtv/freeboxchannelinfo.h
===================================================================
--- libs/libmythtv/freeboxchannelinfo.h	(revision 0)
+++ libs/libmythtv/freeboxchannelinfo.h	(revision 0)
@@ -0,0 +1,29 @@
+#ifndef _FREEBOXCHANNELINFO_H_
+#define _FREEBOXCHANNELINFO_H_
+
+#include <qmap.h>
+#include <qstring.h>
+
+class FreeboxChannelInfo
+{
+  public:
+    FreeboxChannelInfo() : m_name(QString::null), m_url(QString::null) {}
+
+    FreeboxChannelInfo(const QString &name, const QString &url) :
+        m_name(name), m_url(url) {}
+
+    bool isValid(void) const
+    {
+        return !m_name.isEmpty() && !m_url.isEmpty();
+    }
+
+  public:
+    QString m_name;
+    QString m_url;
+};
+
+typedef QMap<QString,FreeboxChannelInfo> fbox_chan_map_t;
+
+#endif //_FREEBOXCHANNELINFO_H_
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/freeboxchannelfetcher.cpp
===================================================================
--- libs/libmythtv/freeboxchannelfetcher.cpp	(revision 0)
+++ libs/libmythtv/freeboxchannelfetcher.cpp	(revision 0)
@@ -0,0 +1,98 @@
+#include "freeboxchannelfetcher.h"
+
+#include "libmyth/httpcomms.h"
+#include "libmyth/mythcontext.h"
+
+#define LOC QString("FBChanFetch: ")
+#define LOC_ERR QString("FBChanFetch, Error: ")
+
+QString FreeboxChannelFetcher::DownloadPlaylist(const QString& url)
+{
+    QString rwUrl(url);
+    return QString::fromUtf8(HttpComms::getHttp(rwUrl));
+}
+
+static bool parse_chan_info(const QString &line1,
+                            QString &channum, QString &name)
+{
+    // each line contains ://
+    // header:extension,channelNum - channelName rtsp://channelUrl
+    //#EXTINF:0,2 - France 2 rtsp://mafreebox.freebox.fr/freeboxtv/201
+
+    QString msg = LOC_ERR +
+        QString("Invalid header in channel list line \n\t\t\t%1").arg(line1);
+
+    channum = name = QString::null;
+
+    // Verify Line Header
+    int pos = line1.find(":", 0);
+    if ((pos < 0) || (line1.mid(0, pos) != "#EXTINF"))
+    {
+        VERBOSE(VB_IMPORTANT, msg);
+        return false;
+    }
+
+    // Parse extension portion
+    pos = line1.find(",", pos + 1);
+    if (pos < 0)
+    {
+        VERBOSE(VB_IMPORTANT, msg);
+        return false;
+    }
+    //list.push_back(line1.mid(oldPos, pos - oldPos));
+
+    // Parse freebox channel number
+    int oldpos = pos + 1;
+    pos = line1.find(" ", pos + 1);
+    if (pos < 0)
+    {
+        VERBOSE(VB_IMPORTANT, msg);
+        return false;
+    }
+    channum = line1.mid(oldpos, pos - oldpos);
+
+    // Parse freebox channel name
+    pos = line1.find("- ", pos + 1);
+    if (pos < 0)
+    {
+        VERBOSE(VB_IMPORTANT, msg);
+        return false;
+    }
+    name = line1.mid(pos + 2, line1.length());
+
+    return true;
+}
+
+fbox_chan_map_t FreeboxChannelFetcher::ParsePlaylist(const QString& rawdata)
+{
+    fbox_chan_map_t chanmap;
+
+    // Verify header is ok
+    QString header = rawdata.section("\n", 0, 0);
+    if (header != "#EXTM3U")
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                QString("Invalid channel list header (%1)").arg(header));
+
+        return chanmap;
+    }
+
+    // Parse each channel
+    for (int i = 1; true; i += 2)
+    {
+        QString tmp = rawdata.section("\n", i+0, i+0);
+        QString url = rawdata.section("\n", i+1, i+1);
+        if (tmp.isEmpty() || url.isEmpty())
+            break;
+
+        QString channum, name;
+        if (parse_chan_info(tmp, channum, name))
+        {
+            chanmap[channum] = FreeboxChannelInfo(name, url);
+        }
+    }
+
+    return chanmap;
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/freeboxrecorder.cpp
===================================================================
--- libs/libmythtv/freeboxrecorder.cpp	(revision 0)
+++ libs/libmythtv/freeboxrecorder.cpp	(revision 0)
@@ -0,0 +1,523 @@
+/**
+ *  FreeboxRecorder
+ *  Copyright (c) 2006 by Laurent Arnal, Benjamin Lerman & Mickaël Remars
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#include "freeboxrecorder.h"
+
+#include <BasicUsageEnvironment.hh>
+#include <MediaSession.hh>
+#include <RTSPClient.hh>
+#include <qwaitcondition.h>
+#include <qmutex.h>
+
+#include "mpeg/mpegstreamdata.h"
+#include "mpeg/streamlisteners.h"
+#include "mpeg/tspacket.h"
+#include "freeboxchannel.h"
+#include "freeboxmediasink.h"
+
+
+
+// ============================================================================
+// FreeboxData : Helper class use for static Callback handler
+// ============================================================================
+class FreeboxData
+{
+  public:
+    FreeboxData(FreeboxRecorder *pFreeboxRecorder,
+                MediaSubsession *pMediaSubSession) :
+        freeboxRecorder(pFreeboxRecorder),
+        mediaSubSession(pMediaSubSession)
+    {
+    }
+    
+    static void subsessionAfterPlayingCallback(void *clientData);
+    static void subsessionByeHandlerCallback(void *clientData);
+    void SubsessionAfterPlaying();
+    void SubsessionByeHandler();
+
+  private:
+    FreeboxRecorder *freeboxRecorder;
+    MediaSubsession *mediaSubSession;
+};
+
+void FreeboxData::subsessionAfterPlayingCallback(void *clientData)
+{
+    ((FreeboxData*)clientData)->SubsessionAfterPlaying();
+}
+
+void FreeboxData::subsessionByeHandlerCallback(void *clientData)
+{
+    ((FreeboxData*)clientData)->SubsessionByeHandler();
+}
+
+void FreeboxData::SubsessionAfterPlaying()
+{
+    MediaSubsession* subsession = mediaSubSession;
+    Medium::close(subsession->sink);
+    subsession->sink = NULL;
+
+    MediaSession& session = subsession->parentSession();
+    MediaSubsessionIterator iter(session);
+    while ((subsession = iter.next()) != NULL)
+    {
+        if (subsession->sink != NULL) return;
+    }
+}
+
+void FreeboxData::SubsessionByeHandler()
+{
+    SubsessionAfterPlaying();
+}
+
+
+
+class FreeboxRecorderImpl : public MPEGSingleProgramStreamListener
+{
+  public:
+    FreeboxRecorderImpl(FreeboxRecorder& thisRecorder,
+                        FreeboxChannel&  channel) :
+        _this(thisRecorder),
+        _streamData(1,true),
+        _curChanInfo(channel.GetCurrentChanInfo()),
+        env(NULL),
+        rtspClient(NULL),
+        session(NULL),
+        _channel(channel),
+        _abort_rtsp(0),
+        _abort_recording(false)
+    {
+        _streamData.AddMPEGSPListener(this);
+    }
+
+    MPEGStreamData& StreamData(void)
+        { return _streamData; }
+    const FreeboxChannelInfo& CurrentChanInfo(void) const
+        { return _curChanInfo; }
+    void SetCurrentChanInfo(const FreeboxChannelInfo& info)
+        { _curChanInfo = info; }
+
+  public: //MPEGSingleProgramStreamListener
+    void HandleSingleProgramPAT(ProgramAssociationTable *pat);
+    void HandleSingleProgramPMT(ProgramMapTable *pmt);
+
+  private:
+    FreeboxRecorder& _this;
+    MPEGStreamData   _streamData;
+
+    FreeboxChannelInfo _curChanInfo;
+
+  public: //TODO all members should be private
+    // livemedia
+    UsageEnvironment* env;
+    RTSPClient* rtspClient;
+    MediaSession* session;
+
+    // Current channel
+    FreeboxChannel& _channel;
+
+    // condition  used to coordinate threads
+    QWaitCondition _cond;
+    
+    // lock  used to coordinate threads
+    QMutex _lock;
+
+    // var to check if we need to abort current rtsp session
+    char _abort_rtsp;
+
+    // request abort for StartRecording method
+    bool _abort_recording;
+};
+
+void FreeboxRecorderImpl::HandleSingleProgramPAT(ProgramAssociationTable *pat)
+{
+    if (!pat)
+        return;
+
+    int next = (pat->tsheader()->ContinuityCounter()+1)&0xf;
+    pat->tsheader()->SetContinuityCounter(next);
+    _this.BufferedWrite(*(reinterpret_cast<const TSPacket*>(pat->tsheader())));
+}
+
+void FreeboxRecorderImpl::HandleSingleProgramPMT(ProgramMapTable *pmt)
+{
+    if (!pmt)
+        return;
+
+    int next = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
+    pmt->tsheader()->SetContinuityCounter(next);
+    _this.BufferedWrite(*(reinterpret_cast<const TSPacket*>(pmt->tsheader())));
+}
+
+
+
+FreeboxRecorder::FreeboxRecorder(TVRec *rec, FreeboxChannel& channel) :
+    DTVRecorder(rec),
+    _impl(new FreeboxRecorderImpl(*this, channel))
+{
+    channel.SetRecorder(this);
+}
+
+
+
+FreeboxRecorder::~FreeboxRecorder()
+{
+    _impl->_channel.SetRecorder(NULL);
+    delete _impl;
+}
+
+
+
+bool FreeboxRecorder::Open()
+{
+    bool result = StartRtsp();
+    _error = !result;
+    return result;
+}
+
+
+
+void FreeboxRecorder::Close()
+{
+    if (_impl->session == NULL) return;
+
+    // Ensure RTSP cleanup, remove old RTSP session
+    MediaSubsessionIterator iter(*_impl->session);
+    MediaSubsession* subsession;
+    while ((subsession = iter.next()) != NULL)
+    {
+        Medium::close(subsession->sink);
+        subsession->sink = NULL;
+    }
+
+    if (_impl->session == NULL) return;
+
+    _impl->rtspClient->teardownMediaSession(*_impl->session);
+
+    // Close all RTSP descriptor
+    Medium::close(_impl->session);
+    Medium::close(_impl->rtspClient);
+}
+
+
+
+void FreeboxRecorder::ChannelChanged(const FreeboxChannelInfo& chaninfo)
+{
+    _impl->SetCurrentChanInfo(chaninfo);
+    // Channel change, we need to close current RTSP flow, and open a new one
+    ResetEventLoop();
+}
+
+
+
+void FreeboxRecorder::SetOptionsFromProfile(RecordingProfile *profile,
+                                            const QString    &videodev,
+                                            const QString    &audiodev,
+                                            const QString    &vbidev)
+{
+    (void)videodev;
+    (void)audiodev;
+    (void)vbidev;
+    (void)profile;
+}
+
+
+
+void FreeboxRecorder::StartRecording()
+{
+    _impl->_lock.lock();
+    _recording = true;
+    while(!_impl->_abort_recording && Open())
+    {
+        _impl->_lock.unlock();
+        // Go into main RTSP loop, feeding data to mythtv
+        _impl->env->taskScheduler().doEventLoop(&_impl->_abort_rtsp);
+
+        _impl->_lock.lock();
+        FinishRecording();
+        Close();
+
+        // Reset _abort_rtsp before unlocking ResetEventLoop()
+        // to avoid race condition
+        _impl->_abort_rtsp = 0;
+        _impl->_cond.wakeAll();
+    }
+    _recording = false;
+    _impl->_lock.unlock();
+}
+
+void FreeboxRecorder::StopRecording(void)
+{
+    _impl->_abort_recording = true; // No lock needed
+    ResetEventLoop();
+}
+
+void FreeboxRecorder::ResetEventLoop()
+{
+    _impl->_lock.lock();
+    _impl->_abort_rtsp = ~0;
+    while(_recording && _impl->_abort_rtsp)
+        _impl->_cond.wait(&_impl->_lock, 1000);
+    _impl->_lock.unlock();
+}
+
+// ======================================================================
+// StartRtsp : start a new RTSP session for the current channel
+// ======================================================================
+bool FreeboxRecorder::StartRtsp()
+{
+    // Retrieve the RTSP channel URL
+    FreeboxChannelInfo chaninfo = _impl->CurrentChanInfo();
+    if (!chaninfo.isValid())
+        return false;
+    QString url = chaninfo.m_url;
+
+    // Begin by setting up our usage environment:
+    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
+    _impl->env = BasicUsageEnvironment::createNew(*scheduler);
+
+
+    // Create our client object:
+    _impl->rtspClient = RTSPClient::createNew(*_impl->env, 0, "myRTSP", 0);
+    if (_impl->rtspClient == NULL)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("Freebox # Failed to create RTSP client: %1")
+                    .arg(_impl->env->getResultMsg()));
+        return false;
+    }
+
+    // Setup URL for the current session
+    char* sdpDescription = _impl->rtspClient->describeURL(url);
+    _impl->rtspClient->describeStatus();
+    if (sdpDescription == NULL)
+    {
+        VERBOSE(VB_IMPORTANT, QString(
+            "Freebox # Failed to get a SDP description from URL: %1 %2")
+                .arg(url)
+                .arg(_impl->env->getResultMsg()));
+        return false;
+    }
+
+    // Create a media session object from this SDP description:
+    _impl->session = MediaSession::createNew(*_impl->env, sdpDescription);
+    delete[] sdpDescription;
+    if (_impl->session == NULL)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("Freebox # Failed to create MediaSession: %1")
+                    .arg(_impl->env->getResultMsg()));
+        return false;
+    }
+    else if (!_impl->session->hasSubsessions())
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("Freebox # This session has no media subsessions"));
+        return false;
+    }
+
+    // Then, setup the "RTPSource"s for the session:
+    MediaSubsessionIterator iter(*_impl->session);
+    MediaSubsession *subsession;
+    Boolean madeProgress = False;
+    while ((subsession = iter.next()) != NULL)
+    {
+        if (!subsession->initiate(-1))
+        {
+            VERBOSE(VB_IMPORTANT, QString(
+                "Freebox # Can't create receiver for: %1 / %2 subsession: %3")
+                    .arg(subsession->mediumName())
+                    .arg(subsession->codecName())
+                    .arg(_impl->env->getResultMsg()));
+        }
+        else
+        {
+            madeProgress = True;
+
+            if (subsession->rtpSource() != NULL)
+            {
+                unsigned const thresh = 1000000; // 1 second
+                subsession->rtpSource()
+                    ->setPacketReorderingThresholdTime(thresh);
+            }
+        }
+    }
+
+    if (!madeProgress) return false;
+
+    // Perform additional 'setup' on each subsession, before playing them:
+    madeProgress = false;
+    iter.reset();
+    while ((subsession = iter.next()) != NULL)
+    {
+        if (subsession->clientPortNum() == 0) continue; // port # was not set
+
+        if (_impl->rtspClient->setupMediaSubsession(*subsession, False, false))
+        {
+            madeProgress = True;
+        }
+        else
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("Freebox # Failed to setup: %1 %2 : %3")
+                        .arg(subsession->mediumName())
+                        .arg(subsession->codecName())
+                        .arg(_impl->env->getResultMsg()));
+        }
+    }
+
+    if (!madeProgress) return false;
+
+   // Create and start "FileSink"s for each subsession:
+   // FileSink while receive Mpeg2 TS Data & will feed them to mythtv
+    madeProgress = False;
+    iter.reset();
+    while ((subsession = iter.next()) != NULL)
+    {
+        if (subsession->readSource() == NULL) continue; // was not initiated
+
+        FreeboxMediaSink* freeboxMediaSink = FreeboxMediaSink::createNew(
+                *_impl->env, *this, TSPacket::SIZE*128);
+
+        subsession->sink = freeboxMediaSink;
+        if (subsession->sink == NULL)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("Freebox # Failed to create sink: %1")
+                        .arg(_impl->env->getResultMsg()));
+        }
+
+        subsession->sink->startPlaying(
+                *(subsession->readSource()),
+                FreeboxData::subsessionAfterPlayingCallback,
+                new FreeboxData(this, subsession));
+
+        if (subsession->rtcpInstance() != NULL)
+        {
+            subsession->rtcpInstance()->setByeHandler(
+                    FreeboxData::subsessionByeHandlerCallback,
+                    new FreeboxData(this, subsession));
+        }
+
+        madeProgress = True;
+    }
+
+    if (!madeProgress) return false;
+
+    // Setup player
+    if (!(_impl->rtspClient->playMediaSession(*_impl->session)))
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("Freebox # Failed to start playing session: %1")
+                    .arg(_impl->env->getResultMsg()));
+        return false;
+    }
+
+    return true;
+}
+
+// ===================================================
+// findTSHeader : find a TS Header in flow
+// ===================================================
+static int FreeboxRecorder_findTSHeader(const unsigned char *data,
+                                        unsigned            dataSize)
+{
+    unsigned int pos = 0;
+    while (pos < dataSize)
+    {
+        if (data[pos] == 0x47)
+            return pos;
+        pos++;
+    }
+    return -1;
+}
+
+// ===================================================
+// addData : feed date from RTSP flow to mythtv
+// ===================================================
+void FreeboxRecorder::addData(unsigned char* data,
+                              unsigned       dataSize,
+                              struct timeval)
+{
+    unsigned int readIndex = 0;
+
+    // data may be compose from more than one packet, loop to consume all data
+    while (readIndex < dataSize)
+    {
+        // If recorder is pause, stop there
+        if (PauseAndWait())
+        {
+            return;
+        }
+
+        // Find the next TS Header in data
+        int tsPos = FreeboxRecorder_findTSHeader(data + readIndex, dataSize);
+
+        // if no TS, something bad happens
+        if (tsPos == -1)
+        {
+            VERBOSE(VB_IMPORTANT, QString("FREEBOX: No TS header."));
+            break;
+        }
+
+        // if TS Header not at start of data, we receive out of sync data
+        if (tsPos > 0)
+        {
+            VERBOSE(VB_IMPORTANT,
+                    QString("FREEBOX: TS header at %1, not in sync.")
+                        .arg(tsPos));
+        }
+
+        // Check if the next packet in buffer is complete :
+        // packet size is 188 bytes long
+        if ((dataSize - tsPos) < TSPacket::SIZE)
+        {
+            VERBOSE(VB_IMPORTANT, QString(
+                "FREEBOX: TS header at %1 but packet not yet complete."
+                ).arg(tsPos));
+            break;
+        }
+
+        // Cast current found TS Packet to TSPacket structure
+        const void *newData = data + tsPos + readIndex;
+        ProcessTSPacket(*reinterpret_cast<const TSPacket*>(newData));
+
+        // follow to next packet
+        readIndex += tsPos + TSPacket::SIZE;
+    }
+}
+
+void FreeboxRecorder::ProcessTSPacket(const TSPacket& tspacket)
+{
+    if (tspacket.TransportError())
+        return;
+
+    if (tspacket.ScramplingControl())
+        return;
+
+    MPEGStreamData& sd = _impl->StreamData();
+    if (tspacket.HasAdaptationField())
+        sd.HandleAdaptationFieldControl(&tspacket);
+
+    if (tspacket.HasPayload())
+    {
+        const unsigned int lpid = tspacket.PID();
+
+        // Pass or reject packets based on PID, and parse info from them
+        if (lpid == sd.VideoPIDSingleProgram())
+        {
+            _buffer_packets = !FindMPEG2Keyframes(&tspacket);
+            BufferedWrite(tspacket);
+        }
+        else if (sd.IsAudioPID(lpid))
+            BufferedWrite(tspacket);
+        else if (sd.IsListeningPID(lpid))
+            sd.HandleTSTables(&tspacket);
+        else if (sd.IsWritingPID(lpid))
+            BufferedWrite(tspacket);
+    }
+}
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/DeviceReadBuffer.h
===================================================================
--- libs/libmythtv/DeviceReadBuffer.h	(revision 10247)
+++ libs/libmythtv/DeviceReadBuffer.h	(working copy)
@@ -17,6 +17,7 @@
 class ReaderPausedCB
 {
   public:
+	virtual ~ReaderPausedCB() {}
     virtual void ReaderPaused(int fd) = 0;
 };
 
Index: libs/libmythtv/mpeg/streamlisteners.h
===================================================================
--- libs/libmythtv/mpeg/streamlisteners.h	(revision 10247)
+++ libs/libmythtv/mpeg/streamlisteners.h	(working copy)
@@ -36,6 +36,7 @@
 class MPEGStreamListener
 {
   public:
+	virtual ~MPEGStreamListener() {}
     virtual void HandlePAT(const ProgramAssociationTable*) = 0;
     virtual void HandleCAT(const ConditionalAccessTable*) = 0;
     virtual void HandlePMT(uint program_num, const ProgramMapTable*) = 0;
@@ -44,6 +45,7 @@
 class MPEGSingleProgramStreamListener
 {
   public:
+	virtual ~MPEGSingleProgramStreamListener() {}
     virtual void HandleSingleProgramPAT(ProgramAssociationTable*) = 0;
     virtual void HandleSingleProgramPMT(ProgramMapTable*) = 0;
 };
@@ -51,6 +53,7 @@
 class ATSCMainStreamListener
 {
   public:
+	virtual ~ATSCMainStreamListener() {}
     virtual void HandleSTT(const SystemTimeTable*) = 0;
     virtual void HandleMGT(const MasterGuideTable*) = 0;
     virtual void HandleVCT(uint pid, const VirtualChannelTable*) = 0;
@@ -59,6 +62,7 @@
 class ATSCAuxStreamListener
 {
   public:
+	virtual ~ATSCAuxStreamListener() {}
     virtual void HandleTVCT(uint pid,const TerrestrialVirtualChannelTable*)=0;
     virtual void HandleCVCT(uint pid, const CableVirtualChannelTable*) = 0;
     virtual void HandleRRT(const RatingRegionTable*) = 0;
@@ -70,6 +74,7 @@
 class ATSCEITStreamListener
 {
   public:
+	virtual ~ATSCEITStreamListener() {}
     virtual void HandleEIT( uint pid, const EventInformationTable*) = 0;
     virtual void HandleETT( uint pid, const ExtendedTextTable*) = 0;
 };
@@ -77,6 +82,7 @@
 class DVBMainStreamListener
 {
   public:
+	virtual ~DVBMainStreamListener() {}
     virtual void HandleNIT(const NetworkInformationTable*) = 0;
     virtual void HandleSDT(uint tsid, const ServiceDescriptionTable*) = 0;
 };
@@ -84,6 +90,7 @@
 class DVBOtherStreamListener
 {
   public:
+	virtual ~DVBOtherStreamListener() {}
     virtual void HandleNITo(const NetworkInformationTable*) = 0;
     virtual void HandleSDTo(uint tsid, const ServiceDescriptionTable*) = 0;
 };
@@ -91,6 +98,7 @@
 class DVBEITStreamListener
 {
   public:
+	virtual ~DVBEITStreamListener() {}
     virtual void HandleEIT(const DVBEventInformationTable*) = 0;
 };
 
Index: libs/libmythtv/cardutil.cpp
===================================================================
--- libs/libmythtv/cardutil.cpp	(revision 10247)
+++ libs/libmythtv/cardutil.cpp	(working copy)
@@ -571,6 +571,7 @@
 
     if (("FIREWIRE"  == cardtype) ||
         ("DBOX2"     == cardtype) ||
+        ("FREEBOX"   == cardtype) ||
         ("HDHOMERUN" == cardtype) ||
         ("CRC_IP"    == cardtype)) 
     {
@@ -804,6 +805,7 @@
 
     if (("FIREWIRE"  == cardtype) ||
         ("DBOX2"     == cardtype) ||
+        ("FREEBOX"   == cardtype) ||
         ("HDHOMERUN" == cardtype) ||
         ("CRC_IP"    == cardtype))
     {
Index: libs/libmythtv/freeboxmediasink.h
===================================================================
--- libs/libmythtv/freeboxmediasink.h	(revision 0)
+++ libs/libmythtv/freeboxmediasink.h	(revision 0)
@@ -0,0 +1,63 @@
+/**
+ *  FreeboxMediaSink
+ *  Copyright (c) 2006 by Laurent Arnal, Benjamin Lerman & Mickaël Remars
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#ifndef _FREEBOXMEDIASINK_H_
+#define _FREEBOXMEDIASINK_H_
+
+#include <MediaSink.hh>
+
+class FreeboxRecorder;
+
+
+
+// ============================================================================
+// FreeboxMediaSink : Helper class use to receive RTSP data from socket.
+// ============================================================================
+class FreeboxMediaSink: public MediaSink
+{
+  public:
+    static FreeboxMediaSink* createNew(UsageEnvironment& env,
+                                       FreeboxRecorder&  pRecorder,
+                                       unsigned          bufferSize);
+
+    /// Callback function called when rtsp data are ready
+    void addData(unsigned char* data,
+                 unsigned       dataSize,
+                 struct timeval presentationTime);
+
+  protected:
+    FreeboxMediaSink(UsageEnvironment& env,
+                     FreeboxRecorder&  pRecorder,
+                     unsigned          bufferSize);
+    virtual ~FreeboxMediaSink();
+
+    static void afterGettingFrame(void*          clientData,
+                                  unsigned       frameSize,
+                                  unsigned       numTruncatedBytes,
+                                  struct timeval presentationTime,
+                                  unsigned       durationInMicroseconds);
+    virtual void afterGettingFrame1(unsigned       frameSize,
+                                    struct timeval presentationTime);
+
+  private:
+    virtual Boolean continuePlaying(void);
+
+  private:
+    unsigned char*    fBuffer;
+    unsigned          fBufferSize;
+    UsageEnvironment& env;
+    FreeboxRecorder&  recorder;
+
+  private:
+    // avoid default contructors & operator=
+    FreeboxMediaSink();
+    FreeboxMediaSink(const FreeboxMediaSink&);
+    FreeboxMediaSink& operator=(const FreeboxMediaSink&);
+};
+
+#endif //_FREEBOXMEDIASINK_H_
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/videosource.cpp
===================================================================
--- libs/libmythtv/videosource.cpp	(revision 10247)
+++ libs/libmythtv/videosource.cpp	(working copy)
@@ -996,6 +996,17 @@
     }
 };
 
+class FreeboxHost : public LineEditSetting, public CCSetting {
+  public:
+    FreeboxHost(const CaptureCard &parent):
+        CCSetting(parent, "videodevice")
+    {
+        setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u");
+        setLabel(QObject::tr("Freebox MRL"));
+        setHelpText(QObject::tr("The freebox Media Resource Locator (MRL)."));
+    }
+};
+
 class HDHomeRunTunerIndex: public ComboBoxSetting, public CCSetting
 {
   public:
@@ -1008,6 +1019,25 @@
     }
 };
 
+class FreeboxConfigurationGroup: public VerticalConfigurationGroup {
+  public:
+    FreeboxConfigurationGroup(CaptureCard& a_parent):
+       ConfigurationGroup(false, true, false, false),
+       VerticalConfigurationGroup(false, true, false, false),
+       parent(a_parent)
+    {
+        setUseLabel(false);
+        addChild(new FreeboxHost(parent));
+
+        //FIXME Create a generic class to replace duplicates
+        HDHRCardInput *defaultinput = new HDHRCardInput(parent);
+        addChild(defaultinput);
+        defaultinput->setVisible(false);
+    };
+  private:
+    CaptureCard& parent;
+};
+
 void HDHRCardInput::fillSelections(const QString&)
 {
     clearSelections();
@@ -1232,6 +1262,10 @@
     addTarget("DBOX2",     new DBOX2ConfigurationGroup(parent));
 #endif // USING_DBOX2
 
+#ifdef USING_FREEBOX
+    addTarget("FREEBOX",   new FreeboxConfigurationGroup(parent));
+#endif // USING_FREEBOX
+
 #ifdef USING_HDHOMERUN
     addTarget("HDHOMERUN", new HDHomeRunConfigurationGroup(parent));
 #endif // USING_HDHOMERUN
@@ -1353,6 +1387,11 @@
         QObject::tr("DBox2 TCP/IP cable box"), "DBOX2");
 #endif // USING_DBOX2
 
+#ifdef USING_FREEBOX
+    setting->addSelection(
+        QObject::tr("Freebox"), "FREEBOX");
+#endif // USING_FREEBOX
+
 #ifdef USING_HDHOMERUN
     setting->addSelection(
         QObject::tr("HDHomeRun DTV tuner box"), "HDHOMERUN");
Index: libs/libmythtv/freeboxrecorder.h
===================================================================
--- libs/libmythtv/freeboxrecorder.h	(revision 0)
+++ libs/libmythtv/freeboxrecorder.h	(revision 0)
@@ -0,0 +1,65 @@
+/**
+ *  FreeboxRecorder
+ *  Copyright (c) 2006 by Laurent Arnal, Benjamin Lerman & Mickaël Remars
+ *  Distributed as part of MythTV under GPL v2 and later.
+ */
+
+#ifndef FREEBOXRECORDER_H_
+#define FREEBOXRECORDER_H_
+
+#include "dtvrecorder.h"
+
+class FreeboxChannel;
+class FreeboxChannelInfo;
+class FreeboxRecorderImpl;
+
+
+
+/**
+ *  Constructs a FreeboxRecorder
+ */
+class FreeboxRecorder : public DTVRecorder
+{
+    friend class FreeboxChannel;
+    friend class FreeboxMediaSink;
+    friend class FreeboxRecorderImpl;
+  public:
+    FreeboxRecorder(TVRec *rec, FreeboxChannel& channel);
+    ~FreeboxRecorder();
+
+    void StartRecording(void);
+    void StopRecording(void);
+
+    void SetOptionsFromProfile(RecordingProfile *profile,
+                               const QString    &videodev,
+                               const QString    &audiodev,
+                               const QString    &vbidev);
+
+  private:
+    bool Open(void);
+    void Close(void);
+
+    void ChannelChanged(const FreeboxChannelInfo& chaninfo);
+
+    /// Callback function to add MPEG2 TS data
+    void addData(unsigned char* data,
+                 unsigned       dataSize,
+                 struct timeval presentationTime);
+
+    void ProcessTSPacket(const TSPacket& tspacket);
+
+    bool StartRtsp(void);
+    void ResetEventLoop(void);
+
+  private:
+    FreeboxRecorderImpl* _impl;
+
+  private:
+    FreeboxRecorder& operator=(const FreeboxRecorder&); //< avoid default impl
+    FreeboxRecorder(const FreeboxRecorder&);            //< avoid default impl
+    FreeboxRecorder();                                  //< avoid default impl
+};
+
+#endif //FREEBOXRECORDER_H_
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/eitscanner.h
===================================================================
--- libs/libmythtv/eitscanner.h	(revision 10247)
+++ libs/libmythtv/eitscanner.h	(working copy)
@@ -21,6 +21,7 @@
 class EITSource
 {
   public:
+	virtual ~EITSource() {}
     virtual void SetEITHelper(EITHelper*) = 0;
     virtual void SetEITRate(float rate) = 0;
 };
Index: libs/libmythtv/tv_rec.cpp
===================================================================
--- libs/libmythtv/tv_rec.cpp	(revision 10247)
+++ libs/libmythtv/tv_rec.cpp	(working copy)
@@ -45,6 +45,7 @@
 #include "dvbchannel.h"
 #include "dbox2channel.h"
 #include "hdhrchannel.h"
+#include "freeboxchannel.h"
 
 #include "recorderbase.h"
 #include "NuppelVideoRecorder.h"
@@ -53,6 +54,7 @@
 #include "dvbrecorder.h"
 #include "dbox2recorder.h"
 #include "hdhrrecorder.h"
+#include "freeboxrecorder.h"
 
 #ifdef USING_CRC_IP_NETWORK_REC
 #include "crcipnetworkrecorder.h"
@@ -178,6 +180,16 @@
         init_run = true;
 #endif
     }
+    else if (genOpt.cardtype == "FREEBOX")
+    {
+#ifdef USING_FREEBOX
+        channel = new FreeboxChannel(this, genOpt.videodev, cardid);
+        if (!channel->Open())
+            return false;
+        InitChannel(genOpt.defaultinput, startchannel);
+        init_run = true;
+#endif
+    }    
     else if (genOpt.cardtype == "HDHOMERUN")
     {
 #ifdef USING_HDHOMERUN
@@ -867,6 +879,14 @@
         recorder->SetOption("httpport", dboxOpt.httpport);
 #endif // USING_DBOX2
     }
+    else if (genOpt.cardtype == "FREEBOX")
+    {
+#ifdef USING_FREEBOX
+        FreeboxChannel *chan = dynamic_cast<FreeboxChannel*>(channel);
+        recorder = new FreeboxRecorder(this, *chan);
+        recorder->SetOption("mrl", genOpt.videodev);
+#endif // USING_FREEBOX
+    }
     else if (genOpt.cardtype == "HDHOMERUN")
     {
 #ifdef USING_HDHOMERUN
@@ -1362,7 +1382,7 @@
     return ok;
 }
 
-bool TVRec::GetDevices(int cardid,
+bool TVRec::GetDevices(int                cardid,
                        GeneralDBOptions   &gen_opts,
                        DVBDBOptions       &dvb_opts,
                        FireWireDBOptions  &firewire_opts,
Index: libs/libmythtv/tv_rec.h
===================================================================
--- libs/libmythtv/tv_rec.h	(revision 10247)
+++ libs/libmythtv/tv_rec.h	(working copy)
@@ -104,6 +104,17 @@
     QString host;
 };
 
+ 
+class FreeboxDBOptions
+{
+  public:
+    FreeboxDBOptions() :
+        mrl("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u") {}
+
+   QString mrl;
+};
+
+
 class TuningRequest
 {
   public:
@@ -236,7 +247,7 @@
     void SetPseudoLiveTVRecording(ProgramInfo*);
     void TeardownAll(void);
 
-    static bool GetDevices(int cardid,
+    static bool GetDevices(int                cardid,
                            GeneralDBOptions   &general_opts,
                            DVBDBOptions       &dvb_opts,
                            FireWireDBOptions  &firewire_opts,
@@ -334,6 +345,7 @@
     DVBDBOptions       dvbOpt;
     FireWireDBOptions  fwOpt;
     DBox2DBOptions     dboxOpt;
+    FreeboxDBOptions   freeboxOpt;
 
     // State variables
     QMutex         stateChangeLock;
Index: settings.pro
===================================================================
--- settings.pro	(revision 10247)
+++ settings.pro	(working copy)
@@ -79,6 +79,7 @@
 EXTRA_LIBS += $$CONFIG_AUDIO_JACK_LIBS
 EXTRA_LIBS += $$CONFIG_FIREWIRE_LIBS
 EXTRA_LIBS += $$CONFIG_DIRECTFB_LIBS
+EXTRA_LIBS += $$CONFIG_LIVE_LIBS
 
 EXTRA_LIBS += $$LOCAL_LIBDIR_OGL
 EXTRA_LIBS += $$LOCAL_LIBDIR_X11
