Ticket #9867: 0044-freemheg-Support-BBCi-streams-eg-sports-multiscreen-.patch

File 0044-freemheg-Support-BBCi-streams-eg-sports-multiscreen-.patch, 70.2 KB (added by Lawrence Rust <lvr@…>, 9 years ago)
  • mythtv/libs/libmythfreemheg/Engine.cpp

    From 58bd22f7f82cb8c02f844367789a3cc5e6f81f47 Mon Sep 17 00:00:00 2001
    From: Lawrence Rust <lvr@softsystem.co.uk>
    Date: Fri, 24 Jun 2011 11:32:10 +0200
    Subject: [PATCH 44/47] freemheg: Support BBCi streams (eg sports multiscreen) using LifecycleExtension
    
    - Add support for MHEG LifecycleExtension as described in ETSI ES 202 184
      V2.1.1.  This requires interpreting the tuneinfo from the SI_TuneIndexInfo
      resident program, preserving the running application across the re-tune and
      generating a NonDestructiveTuneOK EngineEvent when the re-tune completes.
      LifecycleExtension is required to run the interactive content from the BBC
      sports multiscreen stream.
    
    - Support SetData of running stream elements.  This allows the stream source
      to be changed whilst it is live.  Required for some BBCi MHEG e.g. returning
      from a fullscreen sports feed to the sports multiscreen.
    
    - Additional logging in freemheg around critical events.
    
    - Fix a bug in dsmcc that mis-matched file data blocks.
    
    - Drop dsmcc packets and ignore key presses if the MHEG engine terminates.
      This prevents a potential memory exhaustion bug.
    
    - Many more tests in dsmcc for correctly formatted blocks and detection of
      unexpected structures.  Improved error handling in may functions.
    
    - Move some dsmcc logging to extra and increase the detail in important events.
      This quietens the constant stream of logging when dsmcc is enabled but shows
      the important events in better detail.
    
    Signed-off-by: Lawrence Rust <lvr@softsystem.co.uk>
    ---
     mythtv/libs/libmythfreemheg/Engine.cpp     |   21 ++-
     mythtv/libs/libmythfreemheg/Engine.h       |    4 +
     mythtv/libs/libmythfreemheg/Groups.cpp     |    1 +
     mythtv/libs/libmythfreemheg/Groups.h       |    1 +
     mythtv/libs/libmythfreemheg/Presentable.h  |    2 +-
     mythtv/libs/libmythfreemheg/Programs.cpp   |   24 +++-
     mythtv/libs/libmythfreemheg/Stream.cpp     |    9 +-
     mythtv/libs/libmythfreemheg/Stream.h       |    4 +-
     mythtv/libs/libmythfreemheg/Variables.cpp  |    2 +-
     mythtv/libs/libmythfreemheg/freemheg.h     |    3 +-
     mythtv/libs/libmythtv/dsmcc.cpp            |  224 ++++++++++++++++++---------
     mythtv/libs/libmythtv/dsmccbiop.cpp        |  230 +++++++++++++++++++++-------
     mythtv/libs/libmythtv/dsmcccache.cpp       |   28 +++--
     mythtv/libs/libmythtv/dsmccobjcarousel.cpp |  134 +++++++++--------
     mythtv/libs/libmythtv/dsmccobjcarousel.h   |    3 +-
     mythtv/libs/libmythtv/mhi.cpp              |  127 +++++++++++----
     mythtv/libs/libmythtv/mhi.h                |    6 +-
     17 files changed, 563 insertions(+), 260 deletions(-)
    
    diff --git a/mythtv/libs/libmythfreemheg/Engine.cpp b/mythtv/libs/libmythfreemheg/Engine.cpp
    index fce1ea9..1284d2d 100644
    a b int MHEngine::RunAll() 
    9090        {
    9191             startObj.m_GroupId.Copy(MHOctetString("~//startup"));
    9292             if (! Launch(startObj))
     93             {
     94                 MHLOG(MHLogWarning, "MHEG engine auto-boot failed");
    9395                 return -1;
     96             }
    9497        }
    9598        m_fBooting = false;
    9699    }
    int MHEngine::RunAll() 
    110113        CheckContentRequests();
    111114
    112115        // Check the timers.  This may result in timer events being raised.
    113         if (CurrentScene()) {
    114             int next = CurrentScene()->CheckTimers(this);
    115             if (nNextTime == 0 || nNextTime > next) nNextTime = next;
    116         }
     116        nNextTime = CurrentScene() ? CurrentScene()->CheckTimers(this) : 0;
    117117        if (CurrentApp()) {
    118118            // The UK MHEG profile allows applications to have timers.
    119119            int nAppTime = CurrentApp()->CheckTimers(this);
    void MHEngine::GenerateUserAction(int nCode) 
    651651    else EventTriggered(pScene, EventUserInput, nCode);
    652652}
    653653
     654void MHEngine::EngineEvent(int nCode)
     655{
     656    EventTriggered(CurrentApp(), EventEngineEvent, nCode);
     657}
     658
    654659// Called by an ingredient wanting external content.
    655660void MHEngine::RequestExternalContent(MHIngredient *pRequester)
    656661{
    void MHEngine::RequestExternalContent(MHIngredient *pRequester) 
    663668    // Is this actually a carousel object?  It could be a stream.  We should deal
    664669    // with that separately.
    665670    if (csPath.isEmpty())
     671    {
     672        MHLOG(MHLogWarning, "RequestExternalContent empty path");
    666673        return;
     674    }
    667675    QByteArray text;
    668676    if (m_Context->CheckCarouselObject(csPath) && m_Context->GetCarouselData(csPath, text)) {
    669677        // Available now - pass it to the ingredient.
    void MHEngine::RequestExternalContent(MHIngredient *pRequester) 
    671679    }
    672680    else {
    673681        // Need to record this and check later.
     682        MHLOG(MHLogLinks, QString("RequestExternalContent %1 pending").arg(csPath));
    674683        MHExternContent *pContent = new MHExternContent;
    675684        pContent->m_FileName = csPath;
    676685        pContent->m_pRequester = pRequester;
    void MHEngine::CheckContentRequests() 
    710719        {
    711720            // If the content is not recognized catch the exception and continue
    712721            try {
     722                MHLOG(MHLogLinks, QString("CheckContentRequests %1 arrived")
     723                    .arg(pContent->m_FileName));
    713724                pContent->m_pRequester->ContentArrived((const unsigned char *)text.data(),
    714725                                                       text.size(), this);
    715726            }
    bool MHEngine::GetEngineSupport(const MHOctetString &feature) 
    824835        else return false;
    825836    }
    826837
    827     if (strings[0] == "UKEngineProfile" || strings[0] == "UEP") {
     838    if (strings[0] == "UKEngineProfile" || strings[0] == "UniversalEngineProfile" || strings[0] == "UEP") {
    828839        if (strings.count() < 2) return false;
    829840        if (strings[1] == MHEGEngineProviderIdString)
    830841            return true;
  • mythtv/libs/libmythfreemheg/Engine.h

    diff --git a/mythtv/libs/libmythfreemheg/Engine.h b/mythtv/libs/libmythfreemheg/Engine.h
    index 5c847b9..4612aa8 100644
    a b public: 
    114114    void RunActions();
    115115    // Generate a UserAction event i.e. a key press.
    116116    virtual void GenerateUserAction(int nCode);
     117    virtual void EngineEvent(int nCode);
    117118
    118119    // Called from an ingredient to request a load of external content.
    119120    void RequestExternalContent(MHIngredient *pRequester);
    public: 
    155156    MHInteractible *GetInteraction(void) { return m_Interacting; }
    156157    void SetInteraction(MHInteractible *p) { m_Interacting = p; }
    157158
     159    int GetTuneInfo() { return CurrentApp() ? CurrentApp()->m_tuneinfo : 0; }
     160    void SetTuneInfo(int tuneinfo) { if (CurrentApp()) CurrentApp()->m_tuneinfo = tuneinfo; }
     161
    158162protected:
    159163    void CheckLinks(const MHObjectRef &sourceRef, enum EventType ev, const MHUnion &un);
    160164    MHGroup *ParseProgram(QByteArray &text);
  • mythtv/libs/libmythfreemheg/Groups.cpp

    diff --git a/mythtv/libs/libmythfreemheg/Groups.cpp b/mythtv/libs/libmythfreemheg/Groups.cpp
    index 8b9a42c..a4d6266 100644
    a b MHApplication::MHApplication() 
    275275    m_nStrCHook = 0;
    276276    m_nBitmapCHook = 0;
    277277    m_nLineArtCHook = 0;
     278    m_tuneinfo = 0;
    278279
    279280    m_pCurrentScene = NULL;
    280281    m_nLockCount = 0;
  • mythtv/libs/libmythfreemheg/Groups.h

    diff --git a/mythtv/libs/libmythfreemheg/Groups.h b/mythtv/libs/libmythfreemheg/Groups.h
    index 447a4b8..d767f4b 100644
    a b protected: 
    122122    int         m_nTextCHook, m_nIPCHook, m_nStrCHook, m_nBitmapCHook, m_nLineArtCHook;
    123123    MHFontBody  m_Font;
    124124    MHOctetString   m_FontAttrs;
     125    int         m_tuneinfo;
    125126
    126127    // Internal attributes and additional state
    127128    int  m_nLockCount; // Count for locking the screen
  • mythtv/libs/libmythfreemheg/Presentable.h

    diff --git a/mythtv/libs/libmythfreemheg/Presentable.h b/mythtv/libs/libmythfreemheg/Presentable.h
    index 5198266..c08e4ed 100644
    a b public: 
    4343    virtual void Stop(MHEngine *engine);
    4444
    4545    // Additional actions for stream components.
    46     virtual void SetStreamRef(const MHContentRef &) {}
     46    virtual void SetStreamRef(MHEngine *, const MHContentRef &) {}
    4747    virtual void BeginPlaying(MHEngine *) {}
    4848    virtual void StopPlaying(MHEngine *) {}
    4949};
  • mythtv/libs/libmythfreemheg/Programs.cpp

    diff --git a/mythtv/libs/libmythfreemheg/Programs.cpp b/mythtv/libs/libmythfreemheg/Programs.cpp
    index b0117f2..e059ec2 100644
    a b void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, co 
    374374                QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size());
    375375                int nResult = engine->GetContext()->GetChannelIndex(str);
    376376                engine->FindObject(*(pResInt->GetReference()))->SetVariableValue(nResult);
    377                 MHLOG(MHLogDetail, QString("Get service index for %1 - result %2").arg(string.Printable()).arg(nResult));
    378                 SetSuccessFlag(success, true, engine);
     377                SetSuccessFlag(success, nResult >= 0, engine);
    379378            }
    380379            else SetSuccessFlag(success, false, engine);
    381380        }
    void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, co 
    384383            // Tunes to an index returned by GSI
    385384            if (args.Size() == 1) {
    386385                int nChannel = GetInt(args.GetAt(0), engine);
    387                 bool res = engine->GetContext()->TuneTo(nChannel);
     386                bool res = nChannel >= 0 ? engine->GetContext()->TuneTo(
     387                    nChannel, engine->GetTuneInfo()) : false;
    388388                SetSuccessFlag(success, res, engine);
    389389            }
    390390            else SetSuccessFlag(success, false, engine);
    391391        }
    392392        else if (m_Name.Equal("TII")) { // SI_TuneIndexInfo
    393393            // Indicates whether to perform a subsequent TIn quietly or normally.
    394             MHERROR("SI_TuneIndexInfo ResidentProgram is not implemented");
     394            if (args.Size() == 1) {
     395                int tuneinfo = GetInt(args.GetAt(0), engine);
     396                engine->SetTuneInfo(tuneinfo);
     397                SetSuccessFlag(success, true, engine);
     398            }
     399            else SetSuccessFlag(success, false, engine);
    395400        }
    396401        else if (m_Name.Equal("BSI")) { // SI_GetBasicSI
    397402            // Returns basic SI information about the service indicated by an index
    void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, co 
    517522            MHLOG(MHLogNotifications, message);
    518523        }
    519524
     525        else if (m_Name.Equal("SBI")) { // SetBroadcastInterrupt
     526            // Required for InteractionChannelExtension
     527            // En/dis/able program interruptions e.g. green button
     528            MHERROR("SetBroadcastInterrupt ResidentProgram is not implemented");
     529        }
     530
     531        else if (m_Name.Equal("GIS")) { // GetICStatus
     532            // Required for NativeApplicationExtension
     533            MHERROR("GetICStatus ResidentProgram is not implemented");
     534        }
     535
    520536        else {
    521537            MHERROR(QString("Unknown ResidentProgram %1").arg(m_Name.Printable()));
    522538        }
  • mythtv/libs/libmythfreemheg/Stream.cpp

    diff --git a/mythtv/libs/libmythfreemheg/Stream.cpp b/mythtv/libs/libmythfreemheg/Stream.cpp
    index a25bee7..5c0fdf0 100644
    a b void MHStream::Deactivation(MHEngine *engine) 
    121121
    122122// The MHEG corrigendum allows SetData to be targeted to a stream so
    123123// the content ref could change while the stream is playing.
    124 // Not currently handled.
    125124void MHStream::ContentPreparation(MHEngine *engine)
    126125{
    127126    engine->EventTriggered(this, EventContentAvailable); // Perhaps test for the streams being available?
    128127    for (int i = 0; i < m_Multiplex.Size(); i++)
    129         m_Multiplex.GetAt(i)->SetStreamRef(m_ContentRef);
     128        m_Multiplex.GetAt(i)->SetStreamRef(engine, m_ContentRef);
    130129}
    131130
    132131// TODO: Generate StreamPlaying and StreamStopped events.  These are supposed
    void MHAudio::Deactivation(MHEngine *engine) 
    198197    MHPresentable::Deactivation(engine);
    199198}
    200199
    201 void MHAudio::SetStreamRef(const MHContentRef &cr)
     200void MHAudio::SetStreamRef(MHEngine *engine, const MHContentRef &cr)
    202201{
    203202    m_streamContentRef.Copy(cr);
     203    if (m_fStreamPlaying) BeginPlaying(engine);
    204204}
    205205
    206206void MHAudio::BeginPlaying(MHEngine *engine)
    void MHVideo::Deactivation(MHEngine *engine) 
    340340    if (m_fStreamPlaying) engine->GetContext()->StopVideo();
    341341}
    342342
    343 void MHVideo::SetStreamRef(const MHContentRef &cr)
     343void MHVideo::SetStreamRef(MHEngine *engine, const MHContentRef &cr)
    344344{
    345345    m_streamContentRef.Copy(cr);
     346    if (m_fStreamPlaying) BeginPlaying(engine);
    346347}
    347348
    348349void MHVideo::BeginPlaying(MHEngine *engine)
  • mythtv/libs/libmythfreemheg/Stream.h

    diff --git a/mythtv/libs/libmythfreemheg/Stream.h b/mythtv/libs/libmythfreemheg/Stream.h
    index 752c3ae..16f36dd 100644
    a b public: 
    6262    virtual void Activation(MHEngine *engine);
    6363    virtual void Deactivation(MHEngine *engine);
    6464
    65     virtual void SetStreamRef(const MHContentRef &);
     65    virtual void SetStreamRef(MHEngine *, const MHContentRef &);
    6666    virtual void BeginPlaying(MHEngine *engine);
    6767    virtual void StopPlaying(MHEngine *engine);
    6868
    public: 
    9797    virtual void SetVideoDecodeOffset(int newXOffset, int newYOffset, MHEngine *);
    9898    virtual void GetVideoDecodeOffset(MHRoot *pXOffset, MHRoot *pYOffset, MHEngine *);
    9999
    100     virtual void SetStreamRef(const MHContentRef &);
     100    virtual void SetStreamRef(MHEngine *, const MHContentRef &);
    101101    virtual void BeginPlaying(MHEngine *engine);
    102102    virtual void StopPlaying(MHEngine *engine);
    103103
  • mythtv/libs/libmythfreemheg/Variables.cpp

    diff --git a/mythtv/libs/libmythfreemheg/Variables.cpp b/mythtv/libs/libmythfreemheg/Variables.cpp
    index da813c5..b6eac43 100644
    a b void MHOctetStrVar::SetVariableValue(const MHUnion &value) 
    246246        m_Value.Copy(value.m_StrVal);
    247247    }
    248248    // Debug
    249     MHOctetString sample(m_Value, 0, 10);
     249    MHOctetString sample(m_Value, 0, 60);
    250250    MHLOG(MHLogDetail, QString("Update %1 := %2").arg(m_ObjectReference.Printable())
    251251        .arg(sample.Printable()));
    252252}
  • mythtv/libs/libmythfreemheg/freemheg.h

    diff --git a/mythtv/libs/libmythfreemheg/freemheg.h b/mythtv/libs/libmythfreemheg/freemheg.h
    index 86d4edc..6421c22 100644
    a b public: 
    5050    virtual int RunAll(void) = 0;
    5151    // Generate a UserAction event i.e. a key press.
    5252    virtual void GenerateUserAction(int nCode) = 0;
     53    virtual void EngineEvent(int) = 0;
    5354};
    5455
    5556// Logging control
    public: 
    121122    virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId,
    122123                                int &transportId, int &serviceId) = 0;
    123124    // Tune to an index returned by GetChannelIndex
    124     virtual bool TuneTo(int channel) = 0;
     125    virtual bool TuneTo(int channel, int tuneinfo) = 0;
    125126
    126127    // Check whether we have requested a stop.  Returns true and signals
    127128    // the m_stopped condition if we have.
  • mythtv/libs/libmythtv/dsmcc.cpp

    diff --git a/mythtv/libs/libmythtv/dsmcc.cpp b/mythtv/libs/libmythtv/dsmcc.cpp
    index 7db2150..9009f50 100644
    a b ObjCarousel *Dsmcc::AddTap(unsigned short componentTag, unsigned carouselId) 
    8585    for (it = car->m_Tags.begin(); it != car->m_Tags.end(); ++it)
    8686    {
    8787        if (*it == componentTag)
    88             break;
     88            return car;
    8989    }
    9090
    91     if (it == car->m_Tags.end())
    92     { // Not there.
    93         car->m_Tags.push_back(componentTag);
    94         VERBOSE(VB_DSMCC, QString("[dsmcc] Adding tap for stream "
    95                                   "tag %1 with carousel %2")
    96                 .arg(componentTag).arg(carouselId));
    97     }
     91    // Not there.
     92    car->m_Tags.push_back(componentTag);
     93    VERBOSE(VB_DSMCC, QString("[dsmcc] Adding tap for stream "
     94                              "tag %1 with carousel %2")
     95            .arg(componentTag).arg(carouselId));
    9896
    9997    return car;
    10098}
    bool Dsmcc::ProcessSectionHeader(DsmccSectionHeader *header, 
    112110     * else skip packet */
    113111    if (((header->flags[0] & 0x80) == 0) || (header->flags[0] & 0x40) != 0)
    114112    {
    115         VERBOSE(VB_DSMCC, "[dsmcc] Invalid section\n");
     113        VERBOSE(VB_DSMCC, "[dsmcc] WARN Invalid section header\n");
    116114        return false;
    117115    }
    118116
    bool Dsmcc::ProcessSectionHeader(DsmccSectionHeader *header, 
    144142void Dsmcc::ProcessDownloadServerInitiate(const unsigned char *data,
    145143                                          int length)
    146144{
    147     (void) length;
    148 
    149     int off = 0, ret;
    150 
    151145    /* 0-19 Server id = 20 * 0xFF */
     146    int off;
     147    for (off = 0; off < 20; ++off)
     148    {
     149        if (data[off] != 0xff)
     150        {
     151            VERBOSE(VB_DSMCC, QString("[dsmcc] WARN DSI invalid serverID"
     152                    " index %1: 0x%2").arg(off).arg(data[off],0,16));
     153            return;
     154        }
     155    }
    152156
    153157    /* 20,21 compatibilitydescriptorlength = 0x0000 */
     158    if (data[off++] != 0 || data[off++] != 0)
     159    {
     160        VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI non zero compatibilityDescriptorLen");
     161        return;
     162    }
    154163
    155     off = 22;
    156     //unsigned short data_len = (data[off] << 8) | data[off+1];
    157 
     164    // 22,23 privateData length
     165    int data_len = (data[off] << 8) | data[off+1];
    158166    off += 2;
     167    if (data_len + off > length)
     168    {
     169        VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI ServiceGatewayInfo too big");
     170        return;
     171    }
    159172
     173    // 24.. IOP::IOR
    160174    BiopIor gatewayProfile;
    161     ret = gatewayProfile.Process(data+DSMCC_BIOP_OFFSET);
    162     if (ret > 0)
     175    int ret = gatewayProfile.Process(data+DSMCC_BIOP_OFFSET);
     176    if (ret <= 0)
     177        return; /* error */
     178    if (strcmp(gatewayProfile.type_id, "srg"))
    163179    {
    164         off += ret;
     180        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN IOR unexpected type_id: '%1'")
     181            .arg(gatewayProfile.type_id));
     182        return; /* error */
    165183    }
    166     else
     184    if (ret + 4 > data_len)
    167185    {
    168         return; /* TODO error */
     186        VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI IOP:IOR too big");
     187        return; /* error */
    169188    }
    170189
    171     VERBOSE(VB_DSMCC, QString("[dsmcc] Gateway Module %1 on carousel %2")
    172             .arg(gatewayProfile.m_profile_body->GetReference()->m_nModuleId)
    173             .arg(gatewayProfile.m_profile_body->
    174                  GetReference()->m_nCarouselId));
     190    off += ret;
    175191
    176     // This provides us with a map from component tag to carousel ID.
    177     ProfileBodyFull *full = (ProfileBodyFull*)gatewayProfile.m_profile_body;
     192    // Process any new taps
     193    gatewayProfile.AddTap(this);
    178194
    179     VERBOSE(VB_DSMCC, QString("[dsmcc] DSI tap identifies "
    180                               "tag %1 with carousel %2")
    181             .arg(full->dsm_conn.tap.assoc_tag)
    182             .arg(gatewayProfile.m_profile_body->
    183                  GetReference()->m_nCarouselId));
     195    DSMCCCacheReference *ref = gatewayProfile.m_profile_body->GetReference();
     196    unsigned carouselId = ref->m_nCarouselId;
     197    ObjCarousel *car = GetCarouselById(carouselId);
    184198
    185     // Add the tap to the map and create a new carousel if necessary.
    186     unsigned int carouselId =
    187         gatewayProfile.m_profile_body->GetReference()->m_nCarouselId;
     199    // This provides us with a map from component tag to carousel ID.
     200    ProfileBodyFull *full = dynamic_cast<ProfileBodyFull*>(gatewayProfile.m_profile_body);
     201    if (full)
     202    {
     203        VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] DSI ServiceGateway"
     204                    " carousel %1 tag %2 module %3 key %4")
     205                .arg(carouselId).arg(full->dsm_conn.tap.assoc_tag)
     206                .arg(ref->m_nModuleId).arg(ref->m_Key.toString()));
    188207
    189     ObjCarousel *car = AddTap(full->dsm_conn.tap.assoc_tag, carouselId);
     208        // Add the tap to the map and create a new carousel if necessary.
     209        car = AddTap(full->dsm_conn.tap.assoc_tag, carouselId);
     210    }
     211    else
     212    {
     213        VERBOSE(VB_DSMCC, QString("[dsmcc] DSI ServiceGateway"
     214                    " carousel %1 module %2 key %3")
     215                .arg(carouselId).arg(ref->m_nModuleId)
     216                .arg(ref->m_Key.toString()));
     217    }
     218
     219    // Set the gateway (if it isn't already set).
     220    if (car)
     221        car->filecache.SetGateway(*ref);
    190222
    191223    // The UK profile says that we can have the file to boot in
    192224    // the serviceContextList but in practice this seems not to
    193 
    194225    // be used and all these counts are zero.
    195226    unsigned short downloadTapsCount = data[off];
    196227    off++;
    197     off += downloadTapsCount;
     228    if (downloadTapsCount)
     229    {
     230        VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI unexpected downloadTap");
     231        // TODO off += downloadTapsCount * sizeof(DSM::Tap);
     232    }
     233
    198234    unsigned short serviceContextListCount = data[off];
    199235    off++;
    200     off += serviceContextListCount;
     236    if (serviceContextListCount)
     237    {
     238        VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI unexpected serviceContextList");
     239        // TODO off += serviceContextListCount * sizeof serviceContextList;
     240    }
     241
    201242    unsigned short userInfoLength = (data[off] << 8) | data[off+1];
    202243    off += 2;
    203     off += userInfoLength;
    204 
    205     // Set the gateway (if it isn't already set).
    206     car->filecache.SetGateway(
    207         *(gatewayProfile.m_profile_body->GetReference()));
     244    if (userInfoLength)
     245    {
     246        VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI unexpected userInfo");
     247        off += userInfoLength;
     248    }
    208249}
    209250
    210251void Dsmcc::ProcessDownloadInfoIndication(const unsigned char *data,
    void Dsmcc::ProcessDownloadInfoIndication(const unsigned char *data, 
    253294        dii.modules[i].module_version  = data[off++];
    254295        dii.modules[i].module_info_len = data[off++];
    255296
    256         VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 -> "
     297        VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Module %1 -> "
    257298                                  "Size = %2 Version = %3")
    258299                .arg(dii.modules[i].module_id)
    259300                .arg(dii.modules[i].module_size)
    void Dsmcc::ProcessSectionIndication(const unsigned char *data, 
    289330
    290331    unsigned char protocol = hdrData[0];
    291332    if (protocol != 0x11)
     333    {
     334        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Server/Info invalid protocol %1")
     335                .arg(protocol));
    292336        return;
     337    }
    293338
    294339    unsigned char header_type = hdrData[1];
    295340    if (header_type != 0x03)
     341    {
     342        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Server/Info invalid header type %1")
     343                .arg(header_type));
    296344        return;
     345    }
    297346
    298     unsigned short message_id = (hdrData[2] << 8) | hdrData[3];
     347    unsigned message_id = (hdrData[2] << 8) | hdrData[3];
    299348
    300349//    unsigned long transaction_id = (hdrData[4] << 24) | (hdrData[5] << 16) |
    301350//                             (hdrData[6] << 8) | hdrData[7];
    void Dsmcc::ProcessSectionIndication(const unsigned char *data, 
    303352    /* Data[8] - reserved */
    304353    /* Data[9] - adapationLength 0x00 */
    305354
    306     unsigned short message_len = (hdrData[10] << 8) | hdrData[11];
     355    unsigned message_len = (hdrData[10] << 8) | hdrData[11];
    307356    if (message_len > 4076) // Beyond valid length
     357    {
     358        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Server/Info invalid length %1")
     359                .arg(message_len));
    308360        return;
     361    }
    309362
    310363    if (message_id == DSMCC_MESSAGE_DSI)
    311364    {
    312         VERBOSE(VB_DSMCC, "[dsmcc] Server Gateway");
     365        VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Server Gateway");
    313366        // We only process DSI messages if they are received on the initial
    314367        // stream. Because we add taps eagerly we could see a DSI on a
    315368        // different stream before we see the one we actually want.
    void Dsmcc::ProcessSectionIndication(const unsigned char *data, 
    320373        }
    321374        else
    322375        {
    323             VERBOSE(VB_DSMCC, QString("[dsmcc] Discarding DSI from tag %1")
     376            VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Discarding DSI from tag %1")
    324377                    .arg(streamTag));
    325378        }
    326379        // Otherwise discard it.
    327380    }
    328381    else if (message_id == DSMCC_MESSAGE_DII)
    329382    {
    330         VERBOSE(VB_DSMCC, "[dsmcc] Module Info");
     383        VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Module Info");
    331384        ProcessDownloadInfoIndication(data + DSMCC_DII_OFFSET, streamTag);
    332385    }
    333386    else
    334387    {
    335         VERBOSE(VB_DSMCC, "[dsmcc] Unknown section");
     388        VERBOSE(VB_DSMCC, "[dsmcc] WARN Unknown section");
    336389        /* Error */
    337390    }
    338391
    void Dsmcc::ProcessSectionData(const unsigned char *data, int length) 
    347400
    348401    const unsigned char *hdrData = data + DSMCC_DATAHDR_OFFSET;
    349402
    350 //    char protocol = hdrData[0];
    351 //    char header_type = hdrData[1];
    352 //    unsigned short message_id = (hdrData[2] << 8) | hdrData[3];
     403    unsigned char protocol = hdrData[0];
     404    if (protocol != 0x11)
     405    {
     406        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Data invalid protocol %1")
     407                .arg(protocol));
     408        return;
     409    }
     410
     411    unsigned char header_type = hdrData[1];
     412    if (header_type != 0x03)
     413    {
     414        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Data invalid header type %1")
     415                .arg(header_type));
     416        return;
     417    }
     418
     419    unsigned message_id = (hdrData[2] << 8) | hdrData[3];
     420    if (message_id != DSMCC_MESSAGE_DDB)
     421    {
     422        VERBOSE(VB_DSMCC, "[dsmcc] WARN Data unknown section");
     423        return;
     424    }
     425
    353426    unsigned long download_id = ((hdrData[4] << 24) | (hdrData[5] << 16) |
    354427                                 (hdrData[6] <<  8) | (hdrData[7]));
    355428    /* skip reserved byte */
    356429//    char adaptation_len = hdrData[9];
    357     unsigned short message_len = (hdrData[10] << 8) | hdrData[11];
     430    unsigned message_len = (hdrData[10] << 8) | hdrData[11];
    358431
    359432    const unsigned char *blockData = data + DSMCC_DDB_OFFSET;
    360433    DsmccDb ddb;
    void Dsmcc::ProcessSectionData(const unsigned char *data, int length) 
    365438    ddb.block_number   = (blockData[4] << 8) | blockData[5];
    366439    ddb.len = message_len - 6;
    367440
    368     VERBOSE(VB_DSMCC, QString("[dsmcc] Data Block ModID %1 Pos %2 Version %3")
     441    VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Data Block ModID %1 Pos %2 Version %3")
    369442            .arg(ddb.module_id).arg(ddb.block_number).arg(ddb.module_version));
    370443
    371444    ObjCarousel *car = GetCarouselById(download_id);
    372445    if (car != NULL)
    373         car->AddModuleData(download_id, &ddb, blockData + 6);
     446        car->AddModuleData(&ddb, blockData + 6);
     447    else
     448        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Data Block ModID %1 Pos %2"
     449                " unknown carousel %3")
     450                .arg(ddb.module_id).arg(ddb.block_number).arg(download_id));
    374451
    375452    return;
    376453}
    void Dsmcc::ProcessSection(const unsigned char *data, int length, 
    390467{
    391468    // Does this component tag match one of our carousels?
    392469    QLinkedList<ObjCarousel*>::iterator it = carousels.begin();
    393     ObjCarousel *car = NULL;
    394470
    395     VERBOSE(VB_DSMCC, QString("[dsmcc] Read block size %1 from tag %2 "
    396                               "carousel id %3 data broadcast Id %4")
     471    VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Read block size %1 from tag %2 "
     472                              "carouselID %3 dataBroadcastID 0x%4")
    397473            .arg(length).arg(componentTag)
    398             .arg(carouselId).arg(dataBroadcastId));
     474            .arg(carouselId).arg(dataBroadcastId,0,16));
    399475
    400476    bool found = false;
    401477    for (; it != carousels.end(); ++it)
    402478    {
    403         car = *it;
     479        ObjCarousel *car = *it;
    404480        // Is the component tag one of the ones we know?
    405481        vector<unsigned short>::iterator it2;
    406482        for (it2 = car->m_Tags.begin(); it2 != car->m_Tags.end(); ++it2)
    void Dsmcc::ProcessSection(const unsigned char *data, int length, 
    419495        // We haven't seen this stream before but it has the correct
    420496        // data_broadcast_id. Create a carousel for it.
    421497        // This will only happen at start-up
    422         car = AddTap(componentTag, carouselId);
    423         m_startTag = componentTag;
    424         found = true;
     498        if (AddTap(componentTag, carouselId))
     499        {
     500            m_startTag = componentTag;
     501            found = true;
     502        }
    425503    }
    426504
    427     (void) car; // <- we don't currently use this but may in the future.
    428 
    429505    if (!found)
    430506    {
    431         VERBOSE(VB_DSMCC, QString("[dsmcc] Dropping block from tag %1")
    432                 .arg(componentTag));
     507        VERBOSE(VB_DSMCC, QString("[dsmcc] Dropping block size %1 with tag %2"
     508                                  ", carouselID %3, dataBroadcastID 0x%4")
     509                .arg(length).arg(componentTag).arg(carouselId)
     510                .arg(dataBroadcastId,0,16));
    433511
    434512        return; // Ignore this stream.
    435513    }
    void Dsmcc::ProcessSection(const unsigned char *data, int length, 
    442520    if (crc32_decode != 0)
    443521    {
    444522        VERBOSE(VB_DSMCC,
    445                 QString("[dsmcc] Dropping corrupt section (Got %1)")
     523                QString("[dsmcc] WARN Dropping corrupt section (Got %1)")
    446524                .arg(crc32_decode));
    447525        return;
    448526    }
    void Dsmcc::ProcessSection(const unsigned char *data, int length, 
    450528    switch (data[0])
    451529    {
    452530        case DSMCC_SECTION_INDICATION:
    453             VERBOSE(VB_DSMCC, "[dsmcc] Server/Info Section");
     531            VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Server/Info Section");
    454532            ProcessSectionIndication(data, length, componentTag);
    455533            break;
    456534        case DSMCC_SECTION_DATA:
    457             VERBOSE(VB_DSMCC, "[dsmcc] Data Section");
     535            VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Data Section");
    458536            ProcessSectionData(data, length);
    459537            break;
    460538        case DSMCC_SECTION_DESCR:
    void Dsmcc::ProcessSection(const unsigned char *data, int length, 
    462540            ProcessSectionDesc(data, length);
    463541            break;
    464542        default:
    465             VERBOSE(VB_DSMCC, QString("[dsmcc] Unknown Section %1")
     543            VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Unknown Section %1")
    466544                    .arg(data[0]));
    467545            break;
    468546    }
  • mythtv/libs/libmythtv/dsmccbiop.cpp

    diff --git a/mythtv/libs/libmythtv/dsmccbiop.cpp b/mythtv/libs/libmythtv/dsmccbiop.cpp
    index f15071f..5c55dbe 100644
    a b int BiopName::Process(const unsigned char *data) 
    5555{
    5656    int off = 0;
    5757    m_comp_count = data[0];
     58    if (m_comp_count != 1)
     59        VERBOSE(VB_DSMCC,"[biop] WARN Expected one name");
    5860    off++;
    5961    m_comps = new BiopNameComp[m_comp_count];
    6062
    int BiopName::Process(const unsigned char *data) 
    6466        if (ret > 0)
    6567            off += ret;
    6668        else
    67             return off; // Error
     69            return ret; // Error
    6870    }
    6971
    7072    return off;
    int BiopBinding::Process(const unsigned char *data) 
    7880    if (ret > 0)
    7981        off += ret;
    8082    else
    81         return off; // Error
     83        return ret; // Error
    8284
    8385    m_binding_type = data[off++];
    8486    ret = m_ior.Process(data + off);
    int BiopBinding::Process(const unsigned char *data) 
    8688    if (ret > 0)
    8789        off += ret;
    8890    else
    89         return off; // Error
     91        return ret; // Error
    9092
    9193    m_objinfo_len = (data[off] << 8) | data[off + 1];
    9294    off += 2;
    bool BiopMessage::Process(DSMCCCacheModuleData *cachep, DSMCCCache *filecache, 
    115117    // Parse header
    116118    if (! ProcessMsgHdr(data, curp))
    117119    {
    118         VERBOSE(VB_DSMCC,"[biop] Invalid biop header, "
     120        VERBOSE(VB_DSMCC,"[biop] WARN Invalid biop header, "
    119121                "dropping rest of module");
    120122
    121123        /* not valid, skip rest of data */
    bool BiopMessage::Process(DSMCCCacheModuleData *cachep, DSMCCCache *filecache, 
    125127    // Handle each message type
    126128    if (strcmp(m_objkind, "fil") == 0)
    127129    {
    128         VERBOSE(VB_DSMCC,"[biop] Processing file");
     130        VERBOSE(VB_DSMCC|VB_EXTRA,"[biop] Processing file");
    129131        return ProcessFile(cachep, filecache, data, curp);
    130132    }
    131133    else if (strcmp(m_objkind, "dir") == 0)
    132134    {
    133         VERBOSE(VB_DSMCC,"[biop] Processing directory");
     135        VERBOSE(VB_DSMCC|VB_EXTRA,"[biop] Processing directory");
    134136        return ProcessDir(false, cachep, filecache, data, curp);
    135137    }
    136138    else if (strcmp(m_objkind, "srg") == 0)
    137139    {
    138         VERBOSE(VB_DSMCC,"[biop] Processing gateway");
     140        VERBOSE(VB_DSMCC|VB_EXTRA,"[biop] Processing gateway");
    139141        return ProcessDir(true, cachep, filecache, data, curp);
    140142    }
    141143    else
    142144    {
    143145        /* Error */
    144         VERBOSE(VB_DSMCC, QString("Unknown or unsupported format %1%2%3%4")
     146        VERBOSE(VB_DSMCC, QString("[biop] WARN Unknown or unsupported format %1%2%3%4")
    145147                .arg(m_objkind[0]).arg(m_objkind[1])
    146148                .arg(m_objkind[2]).arg(m_objkind[3]));
    147149        return false;
    bool BiopMessage::ProcessMsgHdr(unsigned char *data, unsigned long *curp) 
    159161    const unsigned char *buf = data + (*curp);
    160162    int off = 0;
    161163
    162     if (buf[0] !='B' || buf[1] !='I' || buf[2] !='O' || buf[3] !='P')
     164    if (buf[off] !='B' || buf[off +1] !='I' || buf[off +2] !='O' || buf[off +3] !='P')
    163165    {
    164         VERBOSE(VB_DSMCC, "BiopMessage - invalid header");
     166        VERBOSE(VB_DSMCC, "BiopMessage WARN invalid header");
    165167        return false;
    166168    }
     169    off += 4;
    167170
    168     off += 4;/* skip magic */
    169171    m_version_major = buf[off++];
    170172    m_version_minor = buf[off++];
    171     off += 2; /* skip byte order & message type */
     173    if (m_version_major != 1 || m_version_minor != 0)
     174    {
     175        VERBOSE(VB_DSMCC, "BiopMessage WARN invalid version");
     176        return false;
     177    }
     178
     179    if (buf[off++] != 0)
     180    {
     181        VERBOSE(VB_DSMCC, "BiopMessage WARN invalid byte order");
     182        return false;
     183    }
     184    if (buf[off++] != 0)
     185    {
     186        VERBOSE(VB_DSMCC, "BiopMessage WARN invalid message type");
     187        return false;
     188    }
     189
    172190    m_message_size  = ((buf[off + 0] << 24) | (buf[off+1] << 16) |
    173191                       (buf[off + 2] << 8)  | (buf[off + 3]));
    174192    off += 4;
     193
    175194    uint nObjLen = buf[off++];
    176195    m_objkey = DSMCCCacheKey((const char*)buf + off, nObjLen);
    177196    off += nObjLen;
     197
    178198    m_objkind_len = ((buf[off + 0] << 24) | (buf[off + 1] << 16) |
    179199                     (buf[off + 2] << 8)  | (buf[off + 3]));
    180 
    181200    off += 4;
    182201    m_objkind = (char*) malloc(m_objkind_len);
    183202    memcpy(m_objkind, buf + off, m_objkind_len);
    184203    off += m_objkind_len;
     204
    185205    m_objinfo_len = buf[off] << 8 | buf[off + 1];
    186206    off += 2;
    187207    m_objinfo = (char*) malloc(m_objinfo_len);
    188208    memcpy(m_objinfo, buf + off, m_objinfo_len);
    189209    off += m_objinfo_len;
     210
    190211    (*curp) += off;
    191212
    192213    return true;
    bool BiopMessage::ProcessDir( 
    206227    unsigned char *data, unsigned long *curp)
    207228{
    208229    int off = 0;
    209     const unsigned char *buf = data + (*curp);
    210     off++; // skip service context count
     230    const unsigned char * const buf = data + (*curp);
     231
     232    if (m_objinfo_len)
     233        VERBOSE(VB_DSMCC,"[biop] WARN ProcessDir non-zero objectInfo_length");
     234
     235    const unsigned serviceContextList_count = buf[off++];
     236    if (serviceContextList_count)
     237    {
     238        // TODO Handle serviceContextList for service gateway
     239        VERBOSE(VB_DSMCC,
     240            QString("[biop] WARN ProcessDir serviceContextList count %1")
     241            .arg(serviceContextList_count));
     242        return false; // Error
     243    }
    211244
    212245    unsigned long msgbody_len = ((buf[off + 0] << 24) | (buf[off + 1] << 16) |
    213246                                 (buf[off + 2] <<  8) | (buf[off + 3]));
    214     (void) msgbody_len;
    215247    off += 4;
     248    int const start = off;
    216249
    217250    unsigned int bindings_count = buf[off] << 8 | buf[off + 1];
    218251    off += 2;
    219252
    220253    DSMCCCacheReference ref(cachep->CarouselId(), cachep->ModuleId(),
    221254                            cachep->StreamId(), m_objkey);
    222     DSMCCCacheDir *pDir;
    223     if (isSrg)
    224         pDir = filecache->Srg(ref);
    225     else
    226         pDir = filecache->Directory(ref);
    227 
    228     VERBOSE(VB_DSMCC, QString("[Biop] Processing %1 reference %2")
    229             .arg(isSrg ? "gateway" : "directory").arg(ref.toString()));
     255    DSMCCCacheDir *pDir = isSrg ? filecache->Srg(ref) : filecache->Directory(ref);
    230256
    231257    for (uint i = 0; i < bindings_count; i++)
    232258    {
    bool BiopMessage::ProcessDir( 
    237263        else
    238264            return false; // Error
    239265
     266        if (binding.m_name.m_comp_count != 1)
     267            VERBOSE(VB_DSMCC,"[biop] WARN ProcessDir nameComponents != 1");
     268
     269        if (binding.m_binding_type != 1 && binding.m_binding_type != 2)
     270            VERBOSE(VB_DSMCC,"[biop] WARN ProcessDir invalid BindingType");
     271
    240272        // Process any taps in this binding.
    241273        binding.m_ior.AddTap(filecache->m_Dsmcc);
    242274
    243         if (pDir)
     275        if (pDir && binding.m_name.m_comp_count >= 1)
    244276        {
    245             if (strcmp("dir", binding.m_name.m_comps[0].m_kind) == 0)
    246                 filecache->AddDirInfo(pDir, &binding);
    247             else if (strcmp("fil", binding.m_name.m_comps[0].m_kind) == 0)
     277            if (strcmp("fil", binding.m_name.m_comps[0].m_kind) == 0)
    248278                filecache->AddFileInfo(pDir, &binding);
     279            else if (strcmp("dir", binding.m_name.m_comps[0].m_kind) == 0)
     280                filecache->AddDirInfo(pDir, &binding);
     281            else
     282                VERBOSE(VB_DSMCC,QString("[biop] WARN ProcessDir unknown kind %1")
     283                    .arg(binding.m_name.m_comps[0].m_kind));
    249284        }
    250285    }
    251286
     287    if ((unsigned)(off - start) != msgbody_len)
     288        VERBOSE(VB_DSMCC,"[biop] WARN ProcessDir incorrect msgbody_len");
     289
    252290    (*curp) += off;
    253291
    254292    return true;
    bool BiopMessage::ProcessFile(DSMCCCacheModuleData *cachep, DSMCCCache *filecach 
    262300    unsigned long msgbody_len;
    263301    unsigned long content_len;
    264302
    265     /* skip service contect count */
     303    if (m_objinfo_len != 8)
     304        VERBOSE(VB_DSMCC,QString("[biop] WARN ProcessFile objectInfo_length = %1")
     305                .arg(m_objinfo_len));
     306
     307    const unsigned serviceContextList_count = buf[off++];
     308    if (serviceContextList_count)
     309    {
     310        VERBOSE(VB_DSMCC,
     311            QString("[biop] WARN ProcessFile Unexpected serviceContextList_count %1")
     312            .arg(serviceContextList_count));
     313        return false; // Error
     314    }
    266315
    267     off++;
    268316    msgbody_len = ((buf[off    ] << 24) | (buf[off + 1] << 16) |
    269317                   (buf[off + 2] <<  8) | (buf[off + 3]));
    270318    off += 4;
    271319    content_len = ((buf[off    ] << 24) | (buf[off + 1] << 16) |
    272320                   (buf[off + 2] <<  8) | (buf[off + 3]));
    273321    off += 4;
     322    if (content_len + 4 != msgbody_len)
     323        VERBOSE(VB_DSMCC,"[biop] WARN ProcessFile incorrect msgbody_len");
    274324
    275325    (*curp) += off;
    276326
    int BiopModuleInfo::Process(const unsigned char *data) 
    336386    taps_count = data[12];
    337387    off = 13;
    338388
     389    VERBOSE(VB_DSMCC|VB_EXTRA, QString("[Biop] "
     390            "ModuleTimeout %1 BlockTimeout %2 MinBlockTime %3 Taps %4")
     391            .arg(mod_timeout).arg(block_timeout).arg(min_blocktime)
     392            .arg(taps_count));
     393
    339394    if (taps_count > 0)
    340395    {
    341396        /* only 1 allowed TODO - may not be first though ? */
    342397        ret = tap.Process(data + off);
    343         if (ret > 0)
    344             off += ret;
    345         /* else TODO error */
     398        if (ret <= 0)
     399            return ret;
     400        off += ret;
    346401    }
    347402
    348403    unsigned userinfo_len = data[off++];
    int BiopTap::Process(const unsigned char *data) 
    360415{
    361416    int off=0;
    362417
    363     id = (data[0] << 8) | data[1];
     418    id = (data[off] << 8) | data[off + 1]; // Ignored
    364419    off += 2;
    365420    use = (data[off] << 8) | data[off + 1];
    366421    off += 2;
    int BiopTap::Process(const unsigned char *data) 
    369424    selector_len = data[off++];
    370425    selector_data = (char*) malloc(selector_len);
    371426    memcpy(selector_data, data + off, selector_len);
    372     off += selector_len;
     427    if (use == 0x0016) // BIOP_DELIVERY_PARA_USE
     428    {
     429        unsigned selector_type = (data[off] << 8) | data[off + 1];
     430        if (selector_len >= 10 && selector_type == 0x0001)
     431        {
     432            off += 2;
     433            unsigned long transactionId = ((data[off] << 24) | (data[off + 1] << 16) |
     434                         (data[off + 2] << 8)  | (data[off + 3]));
     435            off += 4;
     436            unsigned long timeout = ((data[off] << 24) | (data[off + 1] << 16) |
     437                         (data[off + 2] << 8)  | (data[off + 3]));
     438            VERBOSE(VB_DSMCC|VB_EXTRA, QString(
     439                    "[biop] BIOP_DELIVERY_PARA_USE tag %1 id 0x%2 timeout %3uS")
     440                .arg(assoc_tag).arg(transactionId,0,16).arg(timeout));
     441            off += 4;
     442            selector_len -= 10;
     443        }
     444    }
    373445
     446    off += selector_len;
    374447    return off;
    375448}
    376449
    int BiopConnbinder::Process(const unsigned char *data) 
    380453
    381454    component_tag = ((data[0] << 24) | (data[1] << 16) |
    382455                     (data[2] << 8)  | (data[3]));
    383 
     456    if (0x49534F40 != component_tag)
     457    {
     458        VERBOSE(VB_DSMCC, "[biop] WARN Invalid Connbinder tag");
     459        return 0;
     460    }
    384461    off += 4;
    385462    component_data_len = data[off++];
    386463    taps_count = data[off++];
    int BiopConnbinder::Process(const unsigned char *data) 
    389466        /* UKProfile - only first tap read */
    390467        ret = tap.Process(data + off);
    391468        //printf("Binder - assoc_tag %u\n", tap.assoc_tag);
    392         if (ret > 0)
    393             off += ret;
    394         /* else TODO error */
     469        if (ret <= 0)
     470            return ret;
     471        off += ret;
    395472    }
    396473
    397474    return off;
    int BiopObjLocation::Process(const unsigned char *data) 
    403480
    404481    component_tag = ((data[0] << 24) | (data[1] << 16) |
    405482                     (data[2] <<  8) | (data[3]));
     483    if (0x49534F50 != component_tag)
     484    {
     485        VERBOSE(VB_DSMCC, "[biop] WARN Invalid ObjectLocation tag");
     486        return 0;
     487    }
    406488    off += 4;
     489
    407490    component_data_len = data[off++];
    408491    m_Reference.m_nCarouselId =
    409492        ((data[off    ] << 24) | (data[off + 1] << 16) |
    410493         (data[off + 2] <<  8) | (data[off + 3]));
    411494
    412495    off += 4;
     496
    413497    m_Reference.m_nModuleId = (data[off] << 8) | data[off + 1];
    414498    off += 2;
     499
    415500    version_major = data[off++];
    416501    version_minor = data[off++];
     502    if (1 != version_major || 0 != version_minor)
     503    {
     504        VERBOSE(VB_DSMCC, "[biop] WARN Invalid ObjectLocation version");
     505        return 0;
     506    }
     507
    417508    uint objKeyLen = data[off++]; /* <= 4 */
    418509    m_Reference.m_Key = DSMCCCacheKey((char*)data + off, objKeyLen);
    419510    off += objKeyLen;
    int BiopObjLocation::Process(const unsigned char *data) 
    424515// a different PMT, We don't support that, at least at the moment.
    425516int ProfileBodyLite::Process(const unsigned char */*data*/)
    426517{
    427     VERBOSE(VB_DSMCC, "Found LiteProfileBody - Not Implemented Yet");
     518    VERBOSE(VB_DSMCC, "[biop] WARN LiteProfileBody Not Implemented");
    428519    return 0;
    429520}
    430521
    int ProfileBodyFull::Process(const unsigned char *data) 
    435526    data_len = ((data[off    ] << 24) | (data[off + 1] << 16) |
    436527                (data[off + 2] <<  8) | (data[off + 3]));
    437528    off += 4;
    438     /* skip bit order */
    439     off += 1;
     529
     530    /* bit order */
     531    if (data[off++] != 0)
     532    {
     533        VERBOSE(VB_DSMCC, "[biop] WARN ProfileBody invalid byte order");
     534        return 0;
     535    }
     536
    440537    lite_components_count = data[off++];
     538    if (lite_components_count < 2)
     539    {
     540        VERBOSE(VB_DSMCC, "[biop] WARN ProfileBody invalid components_count");
     541        return 0;
     542    }
    441543
    442544    ret = obj_loc.Process(data + off);
    443     if (ret > 0)
    444         off += ret;
    445     /* else TODO error */
     545    if (ret <= 0)
     546        return ret;
     547    off += ret;
    446548
    447549    ret = dsm_conn.Process(data + off);
    448     if (ret > 0)
    449         off += ret;
    450     /* else TODO error */
     550    if (ret <= 0)
     551        return ret;
     552    off += ret;
    451553
    452554    obj_loc.m_Reference.m_nStreamTag = dsm_conn.tap.assoc_tag;
    453555
    int BiopIor::Process(const unsigned char *data) 
    465567    off += 4;
    466568    memcpy(type_id, data + off, type_id_len);
    467569    off += type_id_len;
     570
    468571    tagged_profiles_count = ((data[off    ] << 24) | (data[off + 1] << 16) |
    469572                             (data[off + 2] <<  8) | (data[off + 3]));
     573    if (tagged_profiles_count < 1)
     574    {
     575        VERBOSE(VB_DSMCC, "[biop] WARN IOR missing taggedProfile");
     576        return 0;
     577    }
    470578    off += 4;
     579
    471580    profile_id_tag = ((data[off    ] << 24) | (data[off + 1] << 16) |
    472581                      (data[off + 2] <<  8) | (data[off + 3]));
    473582    off += 4;
    474583
    475     if ((profile_id_tag & 0xFF) == 0x06) // profile_id_tag == 0x49534F06
     584    if (profile_id_tag == 0x49534F06) // profile_id_tag == 0x49534F06
    476585    {
    477586        m_profile_body = new ProfileBodyFull;
    478587        ret = m_profile_body->Process(data + off);
    479         if (ret > 0)
    480             off += ret;
    481         /* else TODO error */
     588        if (ret <= 0)
     589            return ret;
     590        off += ret;
    482591    }
    483     else if((profile_id_tag & 0xFF) == 0x05) // profile_id_tag == 0x49534F05
     592    else if(profile_id_tag == 0x49534F05) // profile_id_tag == 0x49534F05
    484593    {
    485594        m_profile_body = new ProfileBodyLite;
    486595        ret = m_profile_body->Process(data + off);
    487         if (ret > 0)
    488             off += ret;
    489         /* else TODO error */
     596        if (ret <= 0)
     597            return ret;
     598        off += ret;
     599    }
     600    else
     601    {
     602        /* UKProfile - receiver may ignore other profiles */
     603        VERBOSE(VB_DSMCC, QString("[biop] WARN Unknown Ior profile 0x%1")
     604                .arg(profile_id_tag, 0, 16));
     605        return 0;
    490606    }
    491 
    492     /* UKProfile - receiver may ignore other profiles */
    493607
    494608    return off;
    495609}
  • mythtv/libs/libmythtv/dsmcccache.cpp

    diff --git a/mythtv/libs/libmythtv/dsmcccache.cpp b/mythtv/libs/libmythtv/dsmcccache.cpp
    index 9bb441c..2e50f4d 100644
    a b DSMCCCacheDir *DSMCCCache::Srg(const DSMCCCacheReference &ref) 
    140140    {
    141141        VERBOSE(VB_DSMCC, QString("[DSMCCCache] Already seen gateway %1")
    142142                .arg(ref.toString()));
    143         return NULL;
     143        return *dir;
    144144    }
    145145
     146    VERBOSE(VB_DSMCC, QString("[DSMCCCache] New gateway reference %1")
     147            .arg(ref.toString()));
     148
    146149    DSMCCCacheDir *pSrg = new DSMCCCacheDir(ref);
    147150    m_Gateways.insert(ref, pSrg);
    148151
    DSMCCCacheDir *DSMCCCache::Directory(const DSMCCCacheReference &ref) 
    160163    {
    161164        VERBOSE(VB_DSMCC, QString("[DSMCCCache] Already seen directory %1")
    162165                .arg(ref.toString()));
    163         return NULL;
     166        return *dir;
    164167    }
    165168
     169    VERBOSE(VB_DSMCC, QString("[DSMCCCache] New directory reference %1")
     170            .arg(ref.toString()));
     171
    166172    DSMCCCacheDir *pDir = new DSMCCCacheDir(ref);
    167173    m_Directories.insert(ref, pDir);
    168174
    void DSMCCCache::AddFileInfo(DSMCCCacheDir *pDir, const BiopBinding *pBB) 
    209215    pDir->m_Files.insert(name, *entry);
    210216
    211217    VERBOSE(VB_DSMCC,
    212             QString("[DSMCCCache] Adding file with name %1 reference %2")
    213             .arg(name).arg(entry->toString()));
     218            QString("[DSMCCCache] Added file name %1 reference %2 parent %3")
     219            .arg(name).arg(entry->toString()).arg(pDir->m_Reference.toString()));
    214220}
    215221
    216222// Add a sub-directory to the directory.
    void DSMCCCache::AddDirInfo(DSMCCCacheDir *pDir, const BiopBinding *pBB) 
    226232    pDir->m_SubDirectories.insert(name, *entry);
    227233
    228234    VERBOSE(VB_DSMCC,
    229             QString("[DSMCCCache] Adding directory with name %1 reference %2")
    230             .arg(name).arg(entry->toString()));
     235            QString("[DSMCCCache] added subdirectory name %1 reference %2 parent %3")
     236            .arg(name).arg(entry->toString()).arg(pDir->m_Reference.toString()));
    231237}
    232238
    233239// Find File, Directory or Gateway by reference.
    int DSMCCCache::GetDSMObject(QStringList &objectPath, QByteArray &result) 
    321327// Set the gateway reference from a DSI message.
    322328void DSMCCCache::SetGateway(const DSMCCCacheReference &ref)
    323329{
    324     VERBOSE(VB_DSMCC, QString("[DSMCCCache] Setting gateway to reference %1")
    325             .arg(ref.toString()));
    326 
    327     m_GatewayRef = ref;
     330    if (!m_GatewayRef.Equal(ref))
     331    {
     332        VERBOSE(VB_DSMCC, QString("[DSMCCCache] Setting gateway to reference %1")
     333                .arg(ref.toString()));
     334        m_GatewayRef = ref;
     335    }
    328336}
  • mythtv/libs/libmythtv/dsmccobjcarousel.cpp

    diff --git a/mythtv/libs/libmythtv/dsmccobjcarousel.cpp b/mythtv/libs/libmythtv/dsmccobjcarousel.cpp
    index cea0818..905975a 100644
    a b unsigned char *DSMCCCacheModuleData::AddModuleData(DsmccDb *ddb, 
    4646                                                   const unsigned char *data)
    4747{
    4848    if (m_version != ddb->module_version)
     49    {
     50        VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Module %1 my version %2 != %3")
     51                .arg(ddb->module_id).arg(m_version).arg(ddb->module_version));
    4952        return NULL; // Wrong version
     53    }
    5054
    5155    if (m_completed)
    5256        return NULL; // Already got it.
    5357
    54     // Check if we have this block already or not. If not append to list
    55     VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 block number %2 length %3")
    56             .arg(ddb->module_id).arg(ddb->block_number).arg(ddb->len));
    57 
    5858    if (ddb->block_number >= m_blocks.size())
    5959    {
    60         VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 block number %2 "
     60        VERBOSE(VB_IMPORTANT, QString("[dsmcc] WARN Module %1 block number %2 "
    6161                                  "is larger than %3")
    6262                .arg(ddb->module_id).arg(ddb->block_number)
    6363                .arg(m_blocks.size()));
    64 
    6564        return NULL;
    6665    }
    6766
    68     if (m_blocks[ddb->block_number] == NULL)
    69     {   // We haven't seen this block before.
    70         QByteArray *block = new QByteArray((char*) data, ddb->len);
    71         // Add this to our set of blocks.
    72         m_blocks[ddb->block_number] = block;
    73         m_receivedData += ddb->len;
     67    // Check if we have this block already or not. If not append to list
     68    if (m_blocks[ddb->block_number])
     69    {
     70        QString s;
     71        for (unsigned i = 0; i < m_blocks.size(); ++i)
     72            s += m_blocks[i] ? '+' : 'X';
     73        VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 block %2 dup: %3")
     74                .arg(ddb->module_id).arg(ddb->block_number +1).arg(s));
     75        return NULL; // We have seen this block before.
    7476    }
    7577
    76     VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 Current Size %2 "
    77                               "Total Size %3")
    78             .arg(m_module_id).arg(m_receivedData).arg(m_moduleSize));
     78    // Add this to our set of blocks.
     79    m_blocks[ddb->block_number] = new QByteArray((char*) data, ddb->len);
     80    if (m_blocks[ddb->block_number])
     81        m_receivedData += ddb->len;
     82
     83    VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 block %2/%3 bytes %4/%5")
     84            .arg(ddb->module_id)
     85            .arg(ddb->block_number +1).arg(m_blocks.size())
     86            .arg(m_receivedData).arg(m_moduleSize));
    7987
    8088    if (m_receivedData < m_moduleSize)
    8189        return NULL; // Not yet complete
    unsigned char *DSMCCCacheModuleData::AddModuleData(DsmccDb *ddb, 
    92100    for (uint i = 0; i < m_blocks.size(); i++)
    93101    {
    94102        QByteArray *block = m_blocks[i];
     103        m_blocks[i] = NULL;
    95104        uint size = block->size();
    96105        memcpy(tmp_data + curp, block->data(), size);
    97106        curp += size;
    98107        delete block;
    99108    }
    100     m_blocks.clear(); // No longer required: free the space.
    101109
    102110    /* Uncompress....  */
    103111    if (m_descriptorData.isCompressed)
    104112    {
    105         unsigned long dataLen = m_descriptorData.originalSize + 1;
     113        unsigned long dataLen = m_descriptorData.originalSize;
    106114        VERBOSE(VB_DSMCC, QString("[dsmcc] uncompressing: "
    107115                                  "compressed size %1, final size %2")
    108116                .arg(m_moduleSize).arg(dataLen));
    109117
    110         unsigned char *uncompressed = (unsigned char*) malloc(dataLen + 1);
     118        unsigned char *uncompressed = (unsigned char*) malloc(dataLen);
    111119        int ret = uncompress(uncompressed, &dataLen, tmp_data, m_moduleSize);
    112120        if (ret != Z_OK)
    113121        {
    114             VERBOSE(VB_DSMCC,"[dsmcc] compression error, skipping");
     122            VERBOSE(VB_DSMCC,"[dsmcc] WARN compression error, skipping");
    115123            free(tmp_data);
    116124            free(uncompressed);
    117125            return NULL;
    118126        }
    119127
    120128        free(tmp_data);
    121         m_completed = true;
    122         return uncompressed;
    123     }
    124     else
    125     {
    126         m_completed = true;
    127         return tmp_data;
     129        tmp_data = uncompressed;
    128130    }
     131
     132    m_completed = true;
     133    m_blocks.clear(); // No longer required: free the space.
     134    return tmp_data;
    129135}
    130136
    131137
    void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, 
    148154    for (int i = 0; i < dii->number_modules; i++)
    149155    {
    150156        DsmccModuleInfo *info = &(dii->modules[i]);
     157        bool bFound = false;
    151158        // Do we already know this module?
    152159        // If so and it is the same version we don't need to do anything.
    153160        // If the version has changed we have to replace it.
    void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, 
    161168                /* already known */
    162169                if (cachep->Version() == info->module_version)
    163170                {
    164                     VERBOSE(VB_DSMCC, QString("[dsmcc] Already Know "
     171                    VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Already Know "
    165172                                              "Module %1")
    166173                            .arg(info->module_id));
    167174                    if (cachep->ModuleSize() == info->module_size)
    168                         return;
     175                    {
     176                        bFound = true;
     177                        break;
     178                    }
    169179                    // It seems that when ITV4 starts broadcasting it
    170180                    // updates the contents of a file but doesn't
    171181                    // update the version.  This is a work-around.
    void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, 
    188198            }
    189199        }
    190200
     201        if (bFound)
     202            continue;
     203
    191204        VERBOSE(VB_DSMCC, QString("[dsmcc] Saving info for module %1")
    192205                .arg(dii->modules[i].module_id));
    193206
    void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, 
    195208        DSMCCCacheModuleData *cachep = new DSMCCCacheModuleData(dii, info, streamTag);
    196209
    197210        int tag = info->modinfo.tap.assoc_tag;
    198         VERBOSE(VB_DSMCC, QString("[dsmcc] Module info tap "
    199                                   "identifies tag %1 with carousel %2\n")
     211        VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Module info tap "
     212                                  "identifies tag %1 with carousel %2")
    200213                .arg(tag).arg(cachep->CarouselId()));
    201214
    202215        // If a carousel with this id does not exist create it.
    void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, 
    212225 *
    213226 *   Add it to the module and process the module if it's now complete.
    214227 */
    215 void ObjCarousel::AddModuleData(unsigned long carousel, DsmccDb *ddb,
    216                                 const unsigned char *data)
     228void ObjCarousel::AddModuleData(DsmccDb *ddb, const unsigned char *data)
    217229{
    218     VERBOSE(VB_DSMCC, QString("[dsmcc] Data block on carousel %1").arg(m_id));
     230    VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Data block on carousel %1").arg(m_id));
    219231
    220232    // Search the saved module info for this module
    221233    QLinkedList<DSMCCCacheModuleData*>::iterator it = m_Cache.begin();
    222     DSMCCCacheModuleData *cachep = NULL;
    223234    for (; it != m_Cache.end(); ++it)
    224235    {
    225         cachep = *it;
    226         if (cachep->CarouselId() == carousel &&
     236        DSMCCCacheModuleData *cachep = *it;
     237        if (cachep->CarouselId() == m_id &&
    227238            (cachep->ModuleId() == ddb->module_id))
    228239        {
    229             break;
    230         }
    231     }
    232 
    233     if (cachep == NULL)
    234         return; // Not found module info.
    235 
    236     // Add the block to the module
    237     unsigned char *tmp_data = cachep->AddModuleData(ddb, data);
    238 
    239     if (tmp_data)
    240     {
    241         // It is complete and we have the data
    242         unsigned int len   = cachep->DataSize();
    243         unsigned long curp = 0;
    244         VERBOSE(VB_DSMCC, QString("[biop] Module size (uncompressed) = %1")
    245                 .arg(len));
    246 
    247         // Now process the BIOP tables in this module.
    248         // Tables may be file contents or the descriptions of
    249         // directories or service gateways (root directories).
    250         while (curp < len)
    251         {
    252             BiopMessage bm;
    253             if (!bm.Process(cachep, &filecache, tmp_data, &curp))
    254                 break;
     240            // Add the block to the module
     241            unsigned char *tmp_data = cachep->AddModuleData(ddb, data);
     242            if (tmp_data)
     243            {
     244                // It is complete and we have the data
     245                unsigned int len   = cachep->DataSize();
     246                unsigned long curp = 0;
     247                VERBOSE(VB_DSMCC|VB_EXTRA,
     248                    QString("[biop] Module size (uncompressed) = %1").arg(len));
     249
     250                // Now process the BIOP tables in this module.
     251                // Tables may be file contents or the descriptions of
     252                // directories or service gateways (root directories).
     253                while (curp < len)
     254                {
     255                    BiopMessage bm;
     256                    if (!bm.Process(cachep, &filecache, tmp_data, &curp))
     257                        break;
     258                }
     259                free(tmp_data);
     260            }
     261            return;
    255262        }
    256         free(tmp_data);
    257263    }
     264    VERBOSE(VB_DSMCC, QString("[dsmcc] Data block module %1 not on carousel %2")
     265        .arg(ddb->module_id).arg(m_id));
    258266}
  • mythtv/libs/libmythtv/dsmccobjcarousel.h

    diff --git a/mythtv/libs/libmythtv/dsmccobjcarousel.h b/mythtv/libs/libmythtv/dsmccobjcarousel.h
    index 106bef3..e906f56 100644
    a b class ObjCarousel 
    6767    ObjCarousel(Dsmcc*);
    6868    ~ObjCarousel();
    6969    void AddModuleInfo(DsmccDii *dii, Dsmcc *status, unsigned short streamTag);
    70     void AddModuleData(unsigned long carousel, DsmccDb *ddb,
    71                        const unsigned char *data);
     70    void AddModuleData(DsmccDb *ddb, const unsigned char *data);
    7271
    7372    DSMCCCache                     filecache;
    7473    QLinkedList<DSMCCCacheModuleData*> m_Cache;
  • mythtv/libs/libmythtv/mhi.cpp

    diff --git a/mythtv/libs/libmythtv/mhi.cpp b/mythtv/libs/libmythtv/mhi.cpp
    index 8da5985..81163ae 100644
    a b static FT_Library ft_library; 
    2424#define SCALED_X(arg1) (int)(((float)arg1 * m_xScale) + 0.5f)
    2525#define SCALED_Y(arg1) (int)(((float)arg1 * m_yScale) + 0.5f)
    2626
     27// LifecycleExtension tuneinfo:
     28const unsigned kTuneQuietly   = 1U<<0; // b0 tune quietly
     29const unsigned kTuneKeepApp   = 1U<<1; // b1 keep app running
     30const unsigned kTuneCarId     = 1U<<2; // b2 carousel id in bits 8..16
     31const unsigned kTuneCarReset  = 1U<<3; // b3 get carousel id from gateway info
     32const unsigned kTuneBcastDisa = 1U<<4; // b4 broadcaster_interrupt disable
     33// b5..7 reserverd
     34// b8..15 carousel id
     35// b16..31 reserved
     36const unsigned kTuneKeepChnl  = 1U<<16; // Keep current channel
     37
    2738/** \class MHIImageData
    2839 *  \brief Data for items in the interactive television display stack.
    2940 */
    MHIContext::MHIContext(InteractiveTV *parent) 
    4455      m_engine(NULL),       m_stop(false),
    4556      m_stopped(false),     m_updated(false),
    4657      m_displayWidth(StdDisplayWidth), m_displayHeight(StdDisplayHeight),
    47       m_face_loaded(false), m_currentChannel(-1),
     58      m_face_loaded(false), m_currentChannel(-1), m_currentStream(-1),
    4859      m_isLive(false),      m_currentCard(0),
    4960      m_audioTag(-1),       m_videoTag(-1),
    50       m_tuningTo(-1),       m_lastNbiVersion(NBI_VERSION_UNSET),
     61      m_lastNbiVersion(NBI_VERSION_UNSET),
    5162      m_videoRect(0, 0, StdDisplayWidth, StdDisplayHeight),
    5263      m_displayRect(0, 0, StdDisplayWidth, StdDisplayHeight)
    5364{
    void MHIContext::StopEngine() 
    146157// Start or restart the MHEG engine.
    147158void MHIContext::Restart(uint chanid, uint cardid, bool isLive)
    148159{
    149     m_currentChannel = (chanid) ? (int)chanid : -1;
     160    int tuneinfo = m_tuneinfo.isEmpty() ? 0 : m_tuneinfo.takeFirst();
     161   
     162    VERBOSE(VB_MHEG, QString("[mhi] Restart ch=%1 card=%2 live=%3 tuneinfo=0x%4")
     163        .arg((int)chanid).arg((int)cardid).arg(isLive).arg(tuneinfo,0,16));
     164
    150165    m_currentCard = cardid;
     166    m_currentStream = (chanid) ? (int)chanid : -1;
     167    if (!(tuneinfo & kTuneKeepChnl))
     168        m_currentChannel = m_currentStream;
    151169
    152     if (m_currentChannel == m_tuningTo && m_currentChannel != -1)
     170    if (tuneinfo & kTuneKeepApp)
    153171    {
    154172        // We have tuned to the channel in order to find the streams.
    155173        // Leave the MHEG engine running but restart the DSMCC carousel.
    void MHIContext::Restart(uint chanid, uint cardid, bool isLive) 
    159177            m_dsmcc = new Dsmcc();
    160178        {
    161179            QMutexLocker locker(&m_dsmccLock);
    162             m_dsmcc->Reset();
     180            if (tuneinfo & kTuneCarReset)
     181                m_dsmcc->Reset();
    163182            ClearQueue();
    164183        }
     184
     185        if (tuneinfo & (kTuneCarReset|kTuneCarId))
     186            m_engine->EngineEvent(10); // NonDestructiveTuneOK
    165187    }
    166188    else
    167189    {
    168190        StopEngine();
    169191
     192        m_audioTag = -1;
     193        m_videoTag = -1;
     194
    170195        if (!m_dsmcc)
    171196            m_dsmcc = new Dsmcc();
    172197
    void MHIContext::Restart(uint chanid, uint cardid, bool isLive) 
    193218        // after the PMT is processed.
    194219        m_stopped = pthread_create(&m_engineThread, NULL,
    195220                                   StartMHEGEngine, this) != 0;
    196         m_audioTag = -1;
    197         m_videoTag = -1;
    198         m_tuningTo = -1;
    199221    }
    200222}
    201223
    void *MHIContext::StartMHEGEngine(void *param) 
    206228    MHIContext *context = (MHIContext*) param;
    207229    context->RunMHEGEngine();
    208230    context->m_stopped = true;
     231    VERBOSE(VB_MHEG, "[mhi] Engine stopped");
    209232    return NULL;
    210233}
    211234
    void MHIContext::RunMHEGEngine(void) 
    236259            // Run the engine and find out how long to pause.
    237260            toWait = m_engine->RunAll();
    238261            if (toWait < 0)
     262            {
     263                VERBOSE(VB_GENERAL, "[mhi] Engine requested exit");
    239264                return;
     265            }
    240266        } while (key != 0);
    241267
    242         if (toWait > 1000 || toWait == 0)
     268        if (toWait > 1000 || toWait <= 0)
    243269            toWait = 1000;
    244270
    245271        m_engine_wait.wait(&mutex, toWait);
    void MHIContext::QueueDSMCCPacket( 
    273299    unsigned char *data, int length, int componentTag,
    274300    unsigned carouselId, int dataBroadcastId)
    275301{
     302    if (m_stopped)
     303        return;
     304
    276305    unsigned char *dataCopy =
    277306        (unsigned char*) malloc(length * sizeof(unsigned char));
    278307
    void MHIContext::SetNetBootInfo(const unsigned char *data, uint length) 
    292321{
    293322    if (length < 2) // A valid descriptor should always have at least 2 bytes.
    294323        return;
     324    VERBOSE(VB_MHEG, QString("[mhi] SetNetBootInfo version %1 mode %2 len %3")
     325        .arg(data[0]).arg(data[1]).arg(length));
    295326    QMutexLocker locker(&m_dsmccLock);
    296327    // Save the data from the descriptor.
    297328    m_nbiData.resize(0);
    void MHIContext::NetworkBootRequested(void) 
    311342    if (m_nbiData.size() >= 2 && m_nbiData[0] != m_lastNbiVersion)
    312343    {
    313344        m_lastNbiVersion = m_nbiData[0]; // Update the saved version
    314         if (m_nbiData[1] == 1)
     345        switch (m_nbiData[1])
    315346        {
     347        case 1:
    316348            m_dsmcc->Reset();
    317349            m_engine->SetBooting();
    318350            ClearDisplay();
    319351            m_updated = true;
     352            break;
     353        case 2:
     354            m_engine->EngineEvent(9); // NetworkBootInfo EngineEvent
     355            break;
     356        default:
     357            VERBOSE(VB_GENERAL, QString("[mhi] Unknown NetworkBoot type %1")
     358                    .arg(m_nbiData[1]));
     359            break;
    320360        }
    321         // TODO: else if it is 2 generate an EngineEvent.
    322361    }
    323362}
    324363
    bool MHIContext::GetCarouselData(QString objectPath, QByteArray &result) 
    368407// and return true otherwise we return false.
    369408bool MHIContext::OfferKey(QString key)
    370409{
     410    if (m_stopped)
     411        return false;
     412
    371413    int action = 0;
    372414    QMutexLocker locker(&m_keyLock);
    373415
    void MHIContext::DrawVideo(const QRect &videoRect, const QRect &dispRect) 
    594636// Returns -1 if it cannot find it.
    595637int MHIContext::GetChannelIndex(const QString &str)
    596638{
    597     if (str.startsWith("dvb://"))
     639    int nResult = -1;
     640
     641    do if (str.startsWith("dvb://"))
    598642    {
    599643        QStringList list = str.mid(6).split('.');
    600644        MSqlQuery query(MSqlQuery::InitCon());
    601         if (list.size() != 3) return -1; // Malformed.
     645        if (list.size() != 3)
     646            break; // Malformed.
    602647        // The various fields are expressed in hexadecimal.
    603648        // Convert them to decimal for the DB.
    604649        bool ok;
    605650        int netID = list[0].toInt(&ok, 16);
    606651        if (!ok)
    607             return -1;
     652            break;
    608653        int serviceID = list[2].toInt(&ok, 16);
    609654        if (!ok)
    610             return -1;
     655            break;
    611656        // We only return channels that match the current capture card.
    612657        if (list[1].isEmpty()) // TransportID is not specified
    613658        {
    int MHIContext::GetChannelIndex(const QString &str) 
    625670        {
    626671            int transportID = list[1].toInt(&ok, 16);
    627672            if (!ok)
    628                 return -1;
     673                break;
    629674            query.prepare(
    630675                "SELECT chanid "
    631676                "FROM channel, dtv_multiplex, cardinput, capturecard "
    int MHIContext::GetChannelIndex(const QString &str) 
    642687        query.bindValue(":SERVICEID", serviceID);
    643688        query.bindValue(":CARDID", m_currentCard);
    644689        if (query.exec() && query.isActive() && query.next())
    645         {
    646             int nResult = query.value(0).toInt();
    647             return nResult;
    648         }
     690            nResult = query.value(0).toInt();
    649691    }
    650692    else if (str.startsWith("rec://svc/lcn/"))
    651693    {
    652694        // I haven't seen this yet so this is untested.
    653695        bool ok;
    654696        int channelNo = str.mid(14).toInt(&ok); // Decimal integer
    655         if (!ok) return -1;
     697        if (!ok)
     698            break;
    656699        MSqlQuery query(MSqlQuery::InitCon());
    657700        query.prepare("SELECT chanid "
    658701                      "FROM channel, cardinput, capturecard "
    int MHIContext::GetChannelIndex(const QString &str) 
    663706        query.bindValue(":CHAN", channelNo);
    664707        query.bindValue(":CARDID", m_currentCard);
    665708        if (query.exec() && query.isActive() && query.next())
    666             return query.value(0).toInt();
     709            nResult = query.value(0).toInt();
    667710    }
    668     else if (str == "rec://svc/cur" || str == "rec://svc/def")
    669         return m_currentChannel;
     711    else if (str == "rec://svc/cur")
     712        nResult = m_currentStream;
     713    else if (str == "rec://svc/def")
     714        nResult = m_currentChannel;
    670715    else if (str.startsWith("rec://"))
    671     {
    672     }
    673     return -1;
     716        ;
     717    while(0);
     718
     719    VERBOSE(VB_MHEG, QString("[mhi] GetChannelIndex %1 => %2")
     720        .arg(str).arg(nResult));
     721    return nResult;
    674722}
    675723
    676724// Get netId etc from the channel index.  This is the inverse of GetChannelIndex.
    677725bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId,
    678726                                int &transportId, int &serviceId)
    679727{
     728    VERBOSE(VB_MHEG, QString("[mhi] GetServiceInfo %1").arg(channelId));
    680729    MSqlQuery query(MSqlQuery::InitCon());
    681730    query.prepare("SELECT networkid, transportid, serviceid "
    682731                  "FROM channel, dtv_multiplex "
    bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId, 
    694743    else return false;
    695744}
    696745
    697 bool MHIContext::TuneTo(int channel)
     746bool MHIContext::TuneTo(int channel, int tuneinfo)
    698747{
     748    VERBOSE(VB_MHEG, QString("[mhi] TuneTo %1 0x%2")
     749        .arg(channel).arg(tuneinfo,0,16));
    699750    if (!m_isLive)
    700751        return false; // Can't tune if this is a recording.
    701752
     753    m_tuneinfo.append(tuneinfo);
     754
    702755    // Post an event requesting a channel change.
    703756    MythEvent me(QString("NETWORK_CONTROL CHANID %1").arg(channel));
    704757    gCoreContext->dispatch(me);
    bool MHIContext::TuneTo(int channel) 
    712765// Begin playing audio from the specified stream
    713766bool MHIContext::BeginAudio(const QString &stream, int tag)
    714767{
     768    VERBOSE(VB_MHEG, QString("[mhi] BeginAudio %1 %2").arg(stream).arg(tag));
    715769    int chan = GetChannelIndex(stream);
     770    if (chan < 0)
     771        return false;
    716772
    717     if (chan != m_currentChannel)
     773    if (chan != m_currentStream)
    718774    {
    719775        // We have to tune to the channel where the audio is to be found.
    720776        // Because the audio and video are both components of an MHEG stream
    721777        // they will both be on the same channel.
    722         m_tuningTo = chan;
     778        m_currentStream = chan;
    723779        m_audioTag = tag;
    724         return TuneTo(chan);
     780        return TuneTo(chan, kTuneKeepChnl|kTuneQuietly|kTuneKeepApp);
    725781    }
    726782
    727783    if (tag < 0)
    void MHIContext::StopAudio(void) 
    741797// Begin displaying video from the specified stream
    742798bool MHIContext::BeginVideo(const QString &stream, int tag)
    743799{
     800    VERBOSE(VB_MHEG, QString("[mhi] BeginVideo %1 %2").arg(stream).arg(tag));
    744801    int chan = GetChannelIndex(stream);
    745     if (chan != m_currentChannel)
     802    if (chan < 0)
     803        return false;
     804    if (chan != m_currentStream)
    746805    {
    747806        // We have to tune to the channel where the video is to be found.
    748         m_tuningTo = chan;
     807        m_currentStream = chan;
    749808        m_videoTag = tag;
    750         return TuneTo(chan);
     809        return TuneTo(chan, kTuneKeepChnl|kTuneQuietly|kTuneKeepApp);
    751810    }
    752811    if (tag < 0)
    753812        return true; // Leave it at the default.
  • mythtv/libs/libmythtv/mhi.h

    diff --git a/mythtv/libs/libmythtv/mhi.h b/mythtv/libs/libmythtv/mhi.h
    index 52ffa2e..63bae67 100644
    a b using namespace std; 
    1818#include <QString>
    1919#include <QWaitCondition>
    2020#include <QImage>
     21#include <QList>
    2122
    2223// MythTV headers
    2324#include "../libmythfreemheg/freemheg.h"
    class MHIContext : public MHContext 
    105106    /// Get netId etc from the channel index.
    106107    virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId,
    107108                                int &transportId, int &serviceId);
    108     virtual bool TuneTo(int channel);
     109    virtual bool TuneTo(int channel, int tuneinfo);
    109110
    110111    /// Begin playing audio from the specified stream
    111112    virtual bool BeginAudio(const QString &stream, int tag);
    class MHIContext : public MHContext 
    175176    pthread_t        m_engineThread;
    176177
    177178    int              m_currentChannel;
     179    int              m_currentStream;
    178180    bool             m_isLive;
    179181    int              m_currentCard;
    180182
    181183    int              m_audioTag;
    182184    int              m_videoTag;
    183     int              m_tuningTo;
     185    QList<int>       m_tuneinfo;
    184186
    185187    uint             m_lastNbiVersion;
    186188    vector<unsigned char> m_nbiData;