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() |
90 | 90 | { |
91 | 91 | startObj.m_GroupId.Copy(MHOctetString("~//startup")); |
92 | 92 | if (! Launch(startObj)) |
| 93 | { |
| 94 | MHLOG(MHLogWarning, "MHEG engine auto-boot failed"); |
93 | 95 | return -1; |
| 96 | } |
94 | 97 | } |
95 | 98 | m_fBooting = false; |
96 | 99 | } |
… |
… |
int MHEngine::RunAll() |
110 | 113 | CheckContentRequests(); |
111 | 114 | |
112 | 115 | // 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; |
117 | 117 | if (CurrentApp()) { |
118 | 118 | // The UK MHEG profile allows applications to have timers. |
119 | 119 | int nAppTime = CurrentApp()->CheckTimers(this); |
… |
… |
void MHEngine::GenerateUserAction(int nCode) |
651 | 651 | else EventTriggered(pScene, EventUserInput, nCode); |
652 | 652 | } |
653 | 653 | |
| 654 | void MHEngine::EngineEvent(int nCode) |
| 655 | { |
| 656 | EventTriggered(CurrentApp(), EventEngineEvent, nCode); |
| 657 | } |
| 658 | |
654 | 659 | // Called by an ingredient wanting external content. |
655 | 660 | void MHEngine::RequestExternalContent(MHIngredient *pRequester) |
656 | 661 | { |
… |
… |
void MHEngine::RequestExternalContent(MHIngredient *pRequester) |
663 | 668 | // Is this actually a carousel object? It could be a stream. We should deal |
664 | 669 | // with that separately. |
665 | 670 | if (csPath.isEmpty()) |
| 671 | { |
| 672 | MHLOG(MHLogWarning, "RequestExternalContent empty path"); |
666 | 673 | return; |
| 674 | } |
667 | 675 | QByteArray text; |
668 | 676 | if (m_Context->CheckCarouselObject(csPath) && m_Context->GetCarouselData(csPath, text)) { |
669 | 677 | // Available now - pass it to the ingredient. |
… |
… |
void MHEngine::RequestExternalContent(MHIngredient *pRequester) |
671 | 679 | } |
672 | 680 | else { |
673 | 681 | // Need to record this and check later. |
| 682 | MHLOG(MHLogLinks, QString("RequestExternalContent %1 pending").arg(csPath)); |
674 | 683 | MHExternContent *pContent = new MHExternContent; |
675 | 684 | pContent->m_FileName = csPath; |
676 | 685 | pContent->m_pRequester = pRequester; |
… |
… |
void MHEngine::CheckContentRequests() |
710 | 719 | { |
711 | 720 | // If the content is not recognized catch the exception and continue |
712 | 721 | try { |
| 722 | MHLOG(MHLogLinks, QString("CheckContentRequests %1 arrived") |
| 723 | .arg(pContent->m_FileName)); |
713 | 724 | pContent->m_pRequester->ContentArrived((const unsigned char *)text.data(), |
714 | 725 | text.size(), this); |
715 | 726 | } |
… |
… |
bool MHEngine::GetEngineSupport(const MHOctetString &feature) |
824 | 835 | else return false; |
825 | 836 | } |
826 | 837 | |
827 | | if (strings[0] == "UKEngineProfile" || strings[0] == "UEP") { |
| 838 | if (strings[0] == "UKEngineProfile" || strings[0] == "UniversalEngineProfile" || strings[0] == "UEP") { |
828 | 839 | if (strings.count() < 2) return false; |
829 | 840 | if (strings[1] == MHEGEngineProviderIdString) |
830 | 841 | return true; |
diff --git a/mythtv/libs/libmythfreemheg/Engine.h b/mythtv/libs/libmythfreemheg/Engine.h
index 5c847b9..4612aa8 100644
a
|
b
|
public: |
114 | 114 | void RunActions(); |
115 | 115 | // Generate a UserAction event i.e. a key press. |
116 | 116 | virtual void GenerateUserAction(int nCode); |
| 117 | virtual void EngineEvent(int nCode); |
117 | 118 | |
118 | 119 | // Called from an ingredient to request a load of external content. |
119 | 120 | void RequestExternalContent(MHIngredient *pRequester); |
… |
… |
public: |
155 | 156 | MHInteractible *GetInteraction(void) { return m_Interacting; } |
156 | 157 | void SetInteraction(MHInteractible *p) { m_Interacting = p; } |
157 | 158 | |
| 159 | int GetTuneInfo() { return CurrentApp() ? CurrentApp()->m_tuneinfo : 0; } |
| 160 | void SetTuneInfo(int tuneinfo) { if (CurrentApp()) CurrentApp()->m_tuneinfo = tuneinfo; } |
| 161 | |
158 | 162 | protected: |
159 | 163 | void CheckLinks(const MHObjectRef &sourceRef, enum EventType ev, const MHUnion &un); |
160 | 164 | MHGroup *ParseProgram(QByteArray &text); |
diff --git a/mythtv/libs/libmythfreemheg/Groups.cpp b/mythtv/libs/libmythfreemheg/Groups.cpp
index 8b9a42c..a4d6266 100644
a
|
b
|
MHApplication::MHApplication() |
275 | 275 | m_nStrCHook = 0; |
276 | 276 | m_nBitmapCHook = 0; |
277 | 277 | m_nLineArtCHook = 0; |
| 278 | m_tuneinfo = 0; |
278 | 279 | |
279 | 280 | m_pCurrentScene = NULL; |
280 | 281 | m_nLockCount = 0; |
diff --git a/mythtv/libs/libmythfreemheg/Groups.h b/mythtv/libs/libmythfreemheg/Groups.h
index 447a4b8..d767f4b 100644
a
|
b
|
protected: |
122 | 122 | int m_nTextCHook, m_nIPCHook, m_nStrCHook, m_nBitmapCHook, m_nLineArtCHook; |
123 | 123 | MHFontBody m_Font; |
124 | 124 | MHOctetString m_FontAttrs; |
| 125 | int m_tuneinfo; |
125 | 126 | |
126 | 127 | // Internal attributes and additional state |
127 | 128 | int m_nLockCount; // Count for locking the screen |
diff --git a/mythtv/libs/libmythfreemheg/Presentable.h b/mythtv/libs/libmythfreemheg/Presentable.h
index 5198266..c08e4ed 100644
a
|
b
|
public: |
43 | 43 | virtual void Stop(MHEngine *engine); |
44 | 44 | |
45 | 45 | // Additional actions for stream components. |
46 | | virtual void SetStreamRef(const MHContentRef &) {} |
| 46 | virtual void SetStreamRef(MHEngine *, const MHContentRef &) {} |
47 | 47 | virtual void BeginPlaying(MHEngine *) {} |
48 | 48 | virtual void StopPlaying(MHEngine *) {} |
49 | 49 | }; |
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 |
374 | 374 | QString str = QString::fromUtf8((const char *)string.Bytes(), string.Size()); |
375 | 375 | int nResult = engine->GetContext()->GetChannelIndex(str); |
376 | 376 | 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); |
379 | 378 | } |
380 | 379 | else SetSuccessFlag(success, false, engine); |
381 | 380 | } |
… |
… |
void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, co |
384 | 383 | // Tunes to an index returned by GSI |
385 | 384 | if (args.Size() == 1) { |
386 | 385 | 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; |
388 | 388 | SetSuccessFlag(success, res, engine); |
389 | 389 | } |
390 | 390 | else SetSuccessFlag(success, false, engine); |
391 | 391 | } |
392 | 392 | else if (m_Name.Equal("TII")) { // SI_TuneIndexInfo |
393 | 393 | // 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); |
395 | 400 | } |
396 | 401 | else if (m_Name.Equal("BSI")) { // SI_GetBasicSI |
397 | 402 | // Returns basic SI information about the service indicated by an index |
… |
… |
void MHResidentProgram::CallProgram(bool fIsFork, const MHObjectRef &success, co |
517 | 522 | MHLOG(MHLogNotifications, message); |
518 | 523 | } |
519 | 524 | |
| 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 | |
520 | 536 | else { |
521 | 537 | MHERROR(QString("Unknown ResidentProgram %1").arg(m_Name.Printable())); |
522 | 538 | } |
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) |
121 | 121 | |
122 | 122 | // The MHEG corrigendum allows SetData to be targeted to a stream so |
123 | 123 | // the content ref could change while the stream is playing. |
124 | | // Not currently handled. |
125 | 124 | void MHStream::ContentPreparation(MHEngine *engine) |
126 | 125 | { |
127 | 126 | engine->EventTriggered(this, EventContentAvailable); // Perhaps test for the streams being available? |
128 | 127 | 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); |
130 | 129 | } |
131 | 130 | |
132 | 131 | // TODO: Generate StreamPlaying and StreamStopped events. These are supposed |
… |
… |
void MHAudio::Deactivation(MHEngine *engine) |
198 | 197 | MHPresentable::Deactivation(engine); |
199 | 198 | } |
200 | 199 | |
201 | | void MHAudio::SetStreamRef(const MHContentRef &cr) |
| 200 | void MHAudio::SetStreamRef(MHEngine *engine, const MHContentRef &cr) |
202 | 201 | { |
203 | 202 | m_streamContentRef.Copy(cr); |
| 203 | if (m_fStreamPlaying) BeginPlaying(engine); |
204 | 204 | } |
205 | 205 | |
206 | 206 | void MHAudio::BeginPlaying(MHEngine *engine) |
… |
… |
void MHVideo::Deactivation(MHEngine *engine) |
340 | 340 | if (m_fStreamPlaying) engine->GetContext()->StopVideo(); |
341 | 341 | } |
342 | 342 | |
343 | | void MHVideo::SetStreamRef(const MHContentRef &cr) |
| 343 | void MHVideo::SetStreamRef(MHEngine *engine, const MHContentRef &cr) |
344 | 344 | { |
345 | 345 | m_streamContentRef.Copy(cr); |
| 346 | if (m_fStreamPlaying) BeginPlaying(engine); |
346 | 347 | } |
347 | 348 | |
348 | 349 | void MHVideo::BeginPlaying(MHEngine *engine) |
diff --git a/mythtv/libs/libmythfreemheg/Stream.h b/mythtv/libs/libmythfreemheg/Stream.h
index 752c3ae..16f36dd 100644
a
|
b
|
public: |
62 | 62 | virtual void Activation(MHEngine *engine); |
63 | 63 | virtual void Deactivation(MHEngine *engine); |
64 | 64 | |
65 | | virtual void SetStreamRef(const MHContentRef &); |
| 65 | virtual void SetStreamRef(MHEngine *, const MHContentRef &); |
66 | 66 | virtual void BeginPlaying(MHEngine *engine); |
67 | 67 | virtual void StopPlaying(MHEngine *engine); |
68 | 68 | |
… |
… |
public: |
97 | 97 | virtual void SetVideoDecodeOffset(int newXOffset, int newYOffset, MHEngine *); |
98 | 98 | virtual void GetVideoDecodeOffset(MHRoot *pXOffset, MHRoot *pYOffset, MHEngine *); |
99 | 99 | |
100 | | virtual void SetStreamRef(const MHContentRef &); |
| 100 | virtual void SetStreamRef(MHEngine *, const MHContentRef &); |
101 | 101 | virtual void BeginPlaying(MHEngine *engine); |
102 | 102 | virtual void StopPlaying(MHEngine *engine); |
103 | 103 | |
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) |
246 | 246 | m_Value.Copy(value.m_StrVal); |
247 | 247 | } |
248 | 248 | // Debug |
249 | | MHOctetString sample(m_Value, 0, 10); |
| 249 | MHOctetString sample(m_Value, 0, 60); |
250 | 250 | MHLOG(MHLogDetail, QString("Update %1 := %2").arg(m_ObjectReference.Printable()) |
251 | 251 | .arg(sample.Printable())); |
252 | 252 | } |
diff --git a/mythtv/libs/libmythfreemheg/freemheg.h b/mythtv/libs/libmythfreemheg/freemheg.h
index 86d4edc..6421c22 100644
a
|
b
|
public: |
50 | 50 | virtual int RunAll(void) = 0; |
51 | 51 | // Generate a UserAction event i.e. a key press. |
52 | 52 | virtual void GenerateUserAction(int nCode) = 0; |
| 53 | virtual void EngineEvent(int) = 0; |
53 | 54 | }; |
54 | 55 | |
55 | 56 | // Logging control |
… |
… |
public: |
121 | 122 | virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId, |
122 | 123 | int &transportId, int &serviceId) = 0; |
123 | 124 | // Tune to an index returned by GetChannelIndex |
124 | | virtual bool TuneTo(int channel) = 0; |
| 125 | virtual bool TuneTo(int channel, int tuneinfo) = 0; |
125 | 126 | |
126 | 127 | // Check whether we have requested a stop. Returns true and signals |
127 | 128 | // the m_stopped condition if we have. |
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) |
85 | 85 | for (it = car->m_Tags.begin(); it != car->m_Tags.end(); ++it) |
86 | 86 | { |
87 | 87 | if (*it == componentTag) |
88 | | break; |
| 88 | return car; |
89 | 89 | } |
90 | 90 | |
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)); |
98 | 96 | |
99 | 97 | return car; |
100 | 98 | } |
… |
… |
bool Dsmcc::ProcessSectionHeader(DsmccSectionHeader *header, |
112 | 110 | * else skip packet */ |
113 | 111 | if (((header->flags[0] & 0x80) == 0) || (header->flags[0] & 0x40) != 0) |
114 | 112 | { |
115 | | VERBOSE(VB_DSMCC, "[dsmcc] Invalid section\n"); |
| 113 | VERBOSE(VB_DSMCC, "[dsmcc] WARN Invalid section header\n"); |
116 | 114 | return false; |
117 | 115 | } |
118 | 116 | |
… |
… |
bool Dsmcc::ProcessSectionHeader(DsmccSectionHeader *header, |
144 | 142 | void Dsmcc::ProcessDownloadServerInitiate(const unsigned char *data, |
145 | 143 | int length) |
146 | 144 | { |
147 | | (void) length; |
148 | | |
149 | | int off = 0, ret; |
150 | | |
151 | 145 | /* 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 | } |
152 | 156 | |
153 | 157 | /* 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 | } |
154 | 163 | |
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]; |
158 | 166 | off += 2; |
| 167 | if (data_len + off > length) |
| 168 | { |
| 169 | VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI ServiceGatewayInfo too big"); |
| 170 | return; |
| 171 | } |
159 | 172 | |
| 173 | // 24.. IOP::IOR |
160 | 174 | 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")) |
163 | 179 | { |
164 | | off += ret; |
| 180 | VERBOSE(VB_DSMCC, QString("[dsmcc] WARN IOR unexpected type_id: '%1'") |
| 181 | .arg(gatewayProfile.type_id)); |
| 182 | return; /* error */ |
165 | 183 | } |
166 | | else |
| 184 | if (ret + 4 > data_len) |
167 | 185 | { |
168 | | return; /* TODO error */ |
| 186 | VERBOSE(VB_DSMCC, "[dsmcc] WARN DSI IOP:IOR too big"); |
| 187 | return; /* error */ |
169 | 188 | } |
170 | 189 | |
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; |
175 | 191 | |
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); |
178 | 194 | |
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); |
184 | 198 | |
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())); |
188 | 207 | |
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); |
190 | 222 | |
191 | 223 | // The UK profile says that we can have the file to boot in |
192 | 224 | // the serviceContextList but in practice this seems not to |
193 | | |
194 | 225 | // be used and all these counts are zero. |
195 | 226 | unsigned short downloadTapsCount = data[off]; |
196 | 227 | 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 | |
198 | 234 | unsigned short serviceContextListCount = data[off]; |
199 | 235 | 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 | |
201 | 242 | unsigned short userInfoLength = (data[off] << 8) | data[off+1]; |
202 | 243 | 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 | } |
208 | 249 | } |
209 | 250 | |
210 | 251 | void Dsmcc::ProcessDownloadInfoIndication(const unsigned char *data, |
… |
… |
void Dsmcc::ProcessDownloadInfoIndication(const unsigned char *data, |
253 | 294 | dii.modules[i].module_version = data[off++]; |
254 | 295 | dii.modules[i].module_info_len = data[off++]; |
255 | 296 | |
256 | | VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 -> " |
| 297 | VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Module %1 -> " |
257 | 298 | "Size = %2 Version = %3") |
258 | 299 | .arg(dii.modules[i].module_id) |
259 | 300 | .arg(dii.modules[i].module_size) |
… |
… |
void Dsmcc::ProcessSectionIndication(const unsigned char *data, |
289 | 330 | |
290 | 331 | unsigned char protocol = hdrData[0]; |
291 | 332 | if (protocol != 0x11) |
| 333 | { |
| 334 | VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Server/Info invalid protocol %1") |
| 335 | .arg(protocol)); |
292 | 336 | return; |
| 337 | } |
293 | 338 | |
294 | 339 | unsigned char header_type = hdrData[1]; |
295 | 340 | if (header_type != 0x03) |
| 341 | { |
| 342 | VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Server/Info invalid header type %1") |
| 343 | .arg(header_type)); |
296 | 344 | return; |
| 345 | } |
297 | 346 | |
298 | | unsigned short message_id = (hdrData[2] << 8) | hdrData[3]; |
| 347 | unsigned message_id = (hdrData[2] << 8) | hdrData[3]; |
299 | 348 | |
300 | 349 | // unsigned long transaction_id = (hdrData[4] << 24) | (hdrData[5] << 16) | |
301 | 350 | // (hdrData[6] << 8) | hdrData[7]; |
… |
… |
void Dsmcc::ProcessSectionIndication(const unsigned char *data, |
303 | 352 | /* Data[8] - reserved */ |
304 | 353 | /* Data[9] - adapationLength 0x00 */ |
305 | 354 | |
306 | | unsigned short message_len = (hdrData[10] << 8) | hdrData[11]; |
| 355 | unsigned message_len = (hdrData[10] << 8) | hdrData[11]; |
307 | 356 | if (message_len > 4076) // Beyond valid length |
| 357 | { |
| 358 | VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Server/Info invalid length %1") |
| 359 | .arg(message_len)); |
308 | 360 | return; |
| 361 | } |
309 | 362 | |
310 | 363 | if (message_id == DSMCC_MESSAGE_DSI) |
311 | 364 | { |
312 | | VERBOSE(VB_DSMCC, "[dsmcc] Server Gateway"); |
| 365 | VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Server Gateway"); |
313 | 366 | // We only process DSI messages if they are received on the initial |
314 | 367 | // stream. Because we add taps eagerly we could see a DSI on a |
315 | 368 | // different stream before we see the one we actually want. |
… |
… |
void Dsmcc::ProcessSectionIndication(const unsigned char *data, |
320 | 373 | } |
321 | 374 | else |
322 | 375 | { |
323 | | VERBOSE(VB_DSMCC, QString("[dsmcc] Discarding DSI from tag %1") |
| 376 | VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Discarding DSI from tag %1") |
324 | 377 | .arg(streamTag)); |
325 | 378 | } |
326 | 379 | // Otherwise discard it. |
327 | 380 | } |
328 | 381 | else if (message_id == DSMCC_MESSAGE_DII) |
329 | 382 | { |
330 | | VERBOSE(VB_DSMCC, "[dsmcc] Module Info"); |
| 383 | VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Module Info"); |
331 | 384 | ProcessDownloadInfoIndication(data + DSMCC_DII_OFFSET, streamTag); |
332 | 385 | } |
333 | 386 | else |
334 | 387 | { |
335 | | VERBOSE(VB_DSMCC, "[dsmcc] Unknown section"); |
| 388 | VERBOSE(VB_DSMCC, "[dsmcc] WARN Unknown section"); |
336 | 389 | /* Error */ |
337 | 390 | } |
338 | 391 | |
… |
… |
void Dsmcc::ProcessSectionData(const unsigned char *data, int length) |
347 | 400 | |
348 | 401 | const unsigned char *hdrData = data + DSMCC_DATAHDR_OFFSET; |
349 | 402 | |
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 | |
353 | 426 | unsigned long download_id = ((hdrData[4] << 24) | (hdrData[5] << 16) | |
354 | 427 | (hdrData[6] << 8) | (hdrData[7])); |
355 | 428 | /* skip reserved byte */ |
356 | 429 | // char adaptation_len = hdrData[9]; |
357 | | unsigned short message_len = (hdrData[10] << 8) | hdrData[11]; |
| 430 | unsigned message_len = (hdrData[10] << 8) | hdrData[11]; |
358 | 431 | |
359 | 432 | const unsigned char *blockData = data + DSMCC_DDB_OFFSET; |
360 | 433 | DsmccDb ddb; |
… |
… |
void Dsmcc::ProcessSectionData(const unsigned char *data, int length) |
365 | 438 | ddb.block_number = (blockData[4] << 8) | blockData[5]; |
366 | 439 | ddb.len = message_len - 6; |
367 | 440 | |
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") |
369 | 442 | .arg(ddb.module_id).arg(ddb.block_number).arg(ddb.module_version)); |
370 | 443 | |
371 | 444 | ObjCarousel *car = GetCarouselById(download_id); |
372 | 445 | 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)); |
374 | 451 | |
375 | 452 | return; |
376 | 453 | } |
… |
… |
void Dsmcc::ProcessSection(const unsigned char *data, int length, |
390 | 467 | { |
391 | 468 | // Does this component tag match one of our carousels? |
392 | 469 | QLinkedList<ObjCarousel*>::iterator it = carousels.begin(); |
393 | | ObjCarousel *car = NULL; |
394 | 470 | |
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") |
397 | 473 | .arg(length).arg(componentTag) |
398 | | .arg(carouselId).arg(dataBroadcastId)); |
| 474 | .arg(carouselId).arg(dataBroadcastId,0,16)); |
399 | 475 | |
400 | 476 | bool found = false; |
401 | 477 | for (; it != carousels.end(); ++it) |
402 | 478 | { |
403 | | car = *it; |
| 479 | ObjCarousel *car = *it; |
404 | 480 | // Is the component tag one of the ones we know? |
405 | 481 | vector<unsigned short>::iterator it2; |
406 | 482 | for (it2 = car->m_Tags.begin(); it2 != car->m_Tags.end(); ++it2) |
… |
… |
void Dsmcc::ProcessSection(const unsigned char *data, int length, |
419 | 495 | // We haven't seen this stream before but it has the correct |
420 | 496 | // data_broadcast_id. Create a carousel for it. |
421 | 497 | // 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 | } |
425 | 503 | } |
426 | 504 | |
427 | | (void) car; // <- we don't currently use this but may in the future. |
428 | | |
429 | 505 | if (!found) |
430 | 506 | { |
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)); |
433 | 511 | |
434 | 512 | return; // Ignore this stream. |
435 | 513 | } |
… |
… |
void Dsmcc::ProcessSection(const unsigned char *data, int length, |
442 | 520 | if (crc32_decode != 0) |
443 | 521 | { |
444 | 522 | VERBOSE(VB_DSMCC, |
445 | | QString("[dsmcc] Dropping corrupt section (Got %1)") |
| 523 | QString("[dsmcc] WARN Dropping corrupt section (Got %1)") |
446 | 524 | .arg(crc32_decode)); |
447 | 525 | return; |
448 | 526 | } |
… |
… |
void Dsmcc::ProcessSection(const unsigned char *data, int length, |
450 | 528 | switch (data[0]) |
451 | 529 | { |
452 | 530 | case DSMCC_SECTION_INDICATION: |
453 | | VERBOSE(VB_DSMCC, "[dsmcc] Server/Info Section"); |
| 531 | VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Server/Info Section"); |
454 | 532 | ProcessSectionIndication(data, length, componentTag); |
455 | 533 | break; |
456 | 534 | case DSMCC_SECTION_DATA: |
457 | | VERBOSE(VB_DSMCC, "[dsmcc] Data Section"); |
| 535 | VERBOSE(VB_DSMCC|VB_EXTRA, "[dsmcc] Data Section"); |
458 | 536 | ProcessSectionData(data, length); |
459 | 537 | break; |
460 | 538 | case DSMCC_SECTION_DESCR: |
… |
… |
void Dsmcc::ProcessSection(const unsigned char *data, int length, |
462 | 540 | ProcessSectionDesc(data, length); |
463 | 541 | break; |
464 | 542 | default: |
465 | | VERBOSE(VB_DSMCC, QString("[dsmcc] Unknown Section %1") |
| 543 | VERBOSE(VB_DSMCC, QString("[dsmcc] WARN Unknown Section %1") |
466 | 544 | .arg(data[0])); |
467 | 545 | break; |
468 | 546 | } |
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) |
55 | 55 | { |
56 | 56 | int off = 0; |
57 | 57 | m_comp_count = data[0]; |
| 58 | if (m_comp_count != 1) |
| 59 | VERBOSE(VB_DSMCC,"[biop] WARN Expected one name"); |
58 | 60 | off++; |
59 | 61 | m_comps = new BiopNameComp[m_comp_count]; |
60 | 62 | |
… |
… |
int BiopName::Process(const unsigned char *data) |
64 | 66 | if (ret > 0) |
65 | 67 | off += ret; |
66 | 68 | else |
67 | | return off; // Error |
| 69 | return ret; // Error |
68 | 70 | } |
69 | 71 | |
70 | 72 | return off; |
… |
… |
int BiopBinding::Process(const unsigned char *data) |
78 | 80 | if (ret > 0) |
79 | 81 | off += ret; |
80 | 82 | else |
81 | | return off; // Error |
| 83 | return ret; // Error |
82 | 84 | |
83 | 85 | m_binding_type = data[off++]; |
84 | 86 | ret = m_ior.Process(data + off); |
… |
… |
int BiopBinding::Process(const unsigned char *data) |
86 | 88 | if (ret > 0) |
87 | 89 | off += ret; |
88 | 90 | else |
89 | | return off; // Error |
| 91 | return ret; // Error |
90 | 92 | |
91 | 93 | m_objinfo_len = (data[off] << 8) | data[off + 1]; |
92 | 94 | off += 2; |
… |
… |
bool BiopMessage::Process(DSMCCCacheModuleData *cachep, DSMCCCache *filecache, |
115 | 117 | // Parse header |
116 | 118 | if (! ProcessMsgHdr(data, curp)) |
117 | 119 | { |
118 | | VERBOSE(VB_DSMCC,"[biop] Invalid biop header, " |
| 120 | VERBOSE(VB_DSMCC,"[biop] WARN Invalid biop header, " |
119 | 121 | "dropping rest of module"); |
120 | 122 | |
121 | 123 | /* not valid, skip rest of data */ |
… |
… |
bool BiopMessage::Process(DSMCCCacheModuleData *cachep, DSMCCCache *filecache, |
125 | 127 | // Handle each message type |
126 | 128 | if (strcmp(m_objkind, "fil") == 0) |
127 | 129 | { |
128 | | VERBOSE(VB_DSMCC,"[biop] Processing file"); |
| 130 | VERBOSE(VB_DSMCC|VB_EXTRA,"[biop] Processing file"); |
129 | 131 | return ProcessFile(cachep, filecache, data, curp); |
130 | 132 | } |
131 | 133 | else if (strcmp(m_objkind, "dir") == 0) |
132 | 134 | { |
133 | | VERBOSE(VB_DSMCC,"[biop] Processing directory"); |
| 135 | VERBOSE(VB_DSMCC|VB_EXTRA,"[biop] Processing directory"); |
134 | 136 | return ProcessDir(false, cachep, filecache, data, curp); |
135 | 137 | } |
136 | 138 | else if (strcmp(m_objkind, "srg") == 0) |
137 | 139 | { |
138 | | VERBOSE(VB_DSMCC,"[biop] Processing gateway"); |
| 140 | VERBOSE(VB_DSMCC|VB_EXTRA,"[biop] Processing gateway"); |
139 | 141 | return ProcessDir(true, cachep, filecache, data, curp); |
140 | 142 | } |
141 | 143 | else |
142 | 144 | { |
143 | 145 | /* 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") |
145 | 147 | .arg(m_objkind[0]).arg(m_objkind[1]) |
146 | 148 | .arg(m_objkind[2]).arg(m_objkind[3])); |
147 | 149 | return false; |
… |
… |
bool BiopMessage::ProcessMsgHdr(unsigned char *data, unsigned long *curp) |
159 | 161 | const unsigned char *buf = data + (*curp); |
160 | 162 | int off = 0; |
161 | 163 | |
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') |
163 | 165 | { |
164 | | VERBOSE(VB_DSMCC, "BiopMessage - invalid header"); |
| 166 | VERBOSE(VB_DSMCC, "BiopMessage WARN invalid header"); |
165 | 167 | return false; |
166 | 168 | } |
| 169 | off += 4; |
167 | 170 | |
168 | | off += 4;/* skip magic */ |
169 | 171 | m_version_major = buf[off++]; |
170 | 172 | 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 | |
172 | 190 | m_message_size = ((buf[off + 0] << 24) | (buf[off+1] << 16) | |
173 | 191 | (buf[off + 2] << 8) | (buf[off + 3])); |
174 | 192 | off += 4; |
| 193 | |
175 | 194 | uint nObjLen = buf[off++]; |
176 | 195 | m_objkey = DSMCCCacheKey((const char*)buf + off, nObjLen); |
177 | 196 | off += nObjLen; |
| 197 | |
178 | 198 | m_objkind_len = ((buf[off + 0] << 24) | (buf[off + 1] << 16) | |
179 | 199 | (buf[off + 2] << 8) | (buf[off + 3])); |
180 | | |
181 | 200 | off += 4; |
182 | 201 | m_objkind = (char*) malloc(m_objkind_len); |
183 | 202 | memcpy(m_objkind, buf + off, m_objkind_len); |
184 | 203 | off += m_objkind_len; |
| 204 | |
185 | 205 | m_objinfo_len = buf[off] << 8 | buf[off + 1]; |
186 | 206 | off += 2; |
187 | 207 | m_objinfo = (char*) malloc(m_objinfo_len); |
188 | 208 | memcpy(m_objinfo, buf + off, m_objinfo_len); |
189 | 209 | off += m_objinfo_len; |
| 210 | |
190 | 211 | (*curp) += off; |
191 | 212 | |
192 | 213 | return true; |
… |
… |
bool BiopMessage::ProcessDir( |
206 | 227 | unsigned char *data, unsigned long *curp) |
207 | 228 | { |
208 | 229 | 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 | } |
211 | 244 | |
212 | 245 | unsigned long msgbody_len = ((buf[off + 0] << 24) | (buf[off + 1] << 16) | |
213 | 246 | (buf[off + 2] << 8) | (buf[off + 3])); |
214 | | (void) msgbody_len; |
215 | 247 | off += 4; |
| 248 | int const start = off; |
216 | 249 | |
217 | 250 | unsigned int bindings_count = buf[off] << 8 | buf[off + 1]; |
218 | 251 | off += 2; |
219 | 252 | |
220 | 253 | DSMCCCacheReference ref(cachep->CarouselId(), cachep->ModuleId(), |
221 | 254 | 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); |
230 | 256 | |
231 | 257 | for (uint i = 0; i < bindings_count; i++) |
232 | 258 | { |
… |
… |
bool BiopMessage::ProcessDir( |
237 | 263 | else |
238 | 264 | return false; // Error |
239 | 265 | |
| 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 | |
240 | 272 | // Process any taps in this binding. |
241 | 273 | binding.m_ior.AddTap(filecache->m_Dsmcc); |
242 | 274 | |
243 | | if (pDir) |
| 275 | if (pDir && binding.m_name.m_comp_count >= 1) |
244 | 276 | { |
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) |
248 | 278 | 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)); |
249 | 284 | } |
250 | 285 | } |
251 | 286 | |
| 287 | if ((unsigned)(off - start) != msgbody_len) |
| 288 | VERBOSE(VB_DSMCC,"[biop] WARN ProcessDir incorrect msgbody_len"); |
| 289 | |
252 | 290 | (*curp) += off; |
253 | 291 | |
254 | 292 | return true; |
… |
… |
bool BiopMessage::ProcessFile(DSMCCCacheModuleData *cachep, DSMCCCache *filecach |
262 | 300 | unsigned long msgbody_len; |
263 | 301 | unsigned long content_len; |
264 | 302 | |
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 | } |
266 | 315 | |
267 | | off++; |
268 | 316 | msgbody_len = ((buf[off ] << 24) | (buf[off + 1] << 16) | |
269 | 317 | (buf[off + 2] << 8) | (buf[off + 3])); |
270 | 318 | off += 4; |
271 | 319 | content_len = ((buf[off ] << 24) | (buf[off + 1] << 16) | |
272 | 320 | (buf[off + 2] << 8) | (buf[off + 3])); |
273 | 321 | off += 4; |
| 322 | if (content_len + 4 != msgbody_len) |
| 323 | VERBOSE(VB_DSMCC,"[biop] WARN ProcessFile incorrect msgbody_len"); |
274 | 324 | |
275 | 325 | (*curp) += off; |
276 | 326 | |
… |
… |
int BiopModuleInfo::Process(const unsigned char *data) |
336 | 386 | taps_count = data[12]; |
337 | 387 | off = 13; |
338 | 388 | |
| 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 | |
339 | 394 | if (taps_count > 0) |
340 | 395 | { |
341 | 396 | /* only 1 allowed TODO - may not be first though ? */ |
342 | 397 | 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; |
346 | 401 | } |
347 | 402 | |
348 | 403 | unsigned userinfo_len = data[off++]; |
… |
… |
int BiopTap::Process(const unsigned char *data) |
360 | 415 | { |
361 | 416 | int off=0; |
362 | 417 | |
363 | | id = (data[0] << 8) | data[1]; |
| 418 | id = (data[off] << 8) | data[off + 1]; // Ignored |
364 | 419 | off += 2; |
365 | 420 | use = (data[off] << 8) | data[off + 1]; |
366 | 421 | off += 2; |
… |
… |
int BiopTap::Process(const unsigned char *data) |
369 | 424 | selector_len = data[off++]; |
370 | 425 | selector_data = (char*) malloc(selector_len); |
371 | 426 | 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 | } |
373 | 445 | |
| 446 | off += selector_len; |
374 | 447 | return off; |
375 | 448 | } |
376 | 449 | |
… |
… |
int BiopConnbinder::Process(const unsigned char *data) |
380 | 453 | |
381 | 454 | component_tag = ((data[0] << 24) | (data[1] << 16) | |
382 | 455 | (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 | } |
384 | 461 | off += 4; |
385 | 462 | component_data_len = data[off++]; |
386 | 463 | taps_count = data[off++]; |
… |
… |
int BiopConnbinder::Process(const unsigned char *data) |
389 | 466 | /* UKProfile - only first tap read */ |
390 | 467 | ret = tap.Process(data + off); |
391 | 468 | //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; |
395 | 472 | } |
396 | 473 | |
397 | 474 | return off; |
… |
… |
int BiopObjLocation::Process(const unsigned char *data) |
403 | 480 | |
404 | 481 | component_tag = ((data[0] << 24) | (data[1] << 16) | |
405 | 482 | (data[2] << 8) | (data[3])); |
| 483 | if (0x49534F50 != component_tag) |
| 484 | { |
| 485 | VERBOSE(VB_DSMCC, "[biop] WARN Invalid ObjectLocation tag"); |
| 486 | return 0; |
| 487 | } |
406 | 488 | off += 4; |
| 489 | |
407 | 490 | component_data_len = data[off++]; |
408 | 491 | m_Reference.m_nCarouselId = |
409 | 492 | ((data[off ] << 24) | (data[off + 1] << 16) | |
410 | 493 | (data[off + 2] << 8) | (data[off + 3])); |
411 | 494 | |
412 | 495 | off += 4; |
| 496 | |
413 | 497 | m_Reference.m_nModuleId = (data[off] << 8) | data[off + 1]; |
414 | 498 | off += 2; |
| 499 | |
415 | 500 | version_major = data[off++]; |
416 | 501 | 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 | |
417 | 508 | uint objKeyLen = data[off++]; /* <= 4 */ |
418 | 509 | m_Reference.m_Key = DSMCCCacheKey((char*)data + off, objKeyLen); |
419 | 510 | off += objKeyLen; |
… |
… |
int BiopObjLocation::Process(const unsigned char *data) |
424 | 515 | // a different PMT, We don't support that, at least at the moment. |
425 | 516 | int ProfileBodyLite::Process(const unsigned char */*data*/) |
426 | 517 | { |
427 | | VERBOSE(VB_DSMCC, "Found LiteProfileBody - Not Implemented Yet"); |
| 518 | VERBOSE(VB_DSMCC, "[biop] WARN LiteProfileBody Not Implemented"); |
428 | 519 | return 0; |
429 | 520 | } |
430 | 521 | |
… |
… |
int ProfileBodyFull::Process(const unsigned char *data) |
435 | 526 | data_len = ((data[off ] << 24) | (data[off + 1] << 16) | |
436 | 527 | (data[off + 2] << 8) | (data[off + 3])); |
437 | 528 | 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 | |
440 | 537 | 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 | } |
441 | 543 | |
442 | 544 | 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; |
446 | 548 | |
447 | 549 | 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; |
451 | 553 | |
452 | 554 | obj_loc.m_Reference.m_nStreamTag = dsm_conn.tap.assoc_tag; |
453 | 555 | |
… |
… |
int BiopIor::Process(const unsigned char *data) |
465 | 567 | off += 4; |
466 | 568 | memcpy(type_id, data + off, type_id_len); |
467 | 569 | off += type_id_len; |
| 570 | |
468 | 571 | tagged_profiles_count = ((data[off ] << 24) | (data[off + 1] << 16) | |
469 | 572 | (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 | } |
470 | 578 | off += 4; |
| 579 | |
471 | 580 | profile_id_tag = ((data[off ] << 24) | (data[off + 1] << 16) | |
472 | 581 | (data[off + 2] << 8) | (data[off + 3])); |
473 | 582 | off += 4; |
474 | 583 | |
475 | | if ((profile_id_tag & 0xFF) == 0x06) // profile_id_tag == 0x49534F06 |
| 584 | if (profile_id_tag == 0x49534F06) // profile_id_tag == 0x49534F06 |
476 | 585 | { |
477 | 586 | m_profile_body = new ProfileBodyFull; |
478 | 587 | 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; |
482 | 591 | } |
483 | | else if((profile_id_tag & 0xFF) == 0x05) // profile_id_tag == 0x49534F05 |
| 592 | else if(profile_id_tag == 0x49534F05) // profile_id_tag == 0x49534F05 |
484 | 593 | { |
485 | 594 | m_profile_body = new ProfileBodyLite; |
486 | 595 | 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; |
490 | 606 | } |
491 | | |
492 | | /* UKProfile - receiver may ignore other profiles */ |
493 | 607 | |
494 | 608 | return off; |
495 | 609 | } |
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) |
140 | 140 | { |
141 | 141 | VERBOSE(VB_DSMCC, QString("[DSMCCCache] Already seen gateway %1") |
142 | 142 | .arg(ref.toString())); |
143 | | return NULL; |
| 143 | return *dir; |
144 | 144 | } |
145 | 145 | |
| 146 | VERBOSE(VB_DSMCC, QString("[DSMCCCache] New gateway reference %1") |
| 147 | .arg(ref.toString())); |
| 148 | |
146 | 149 | DSMCCCacheDir *pSrg = new DSMCCCacheDir(ref); |
147 | 150 | m_Gateways.insert(ref, pSrg); |
148 | 151 | |
… |
… |
DSMCCCacheDir *DSMCCCache::Directory(const DSMCCCacheReference &ref) |
160 | 163 | { |
161 | 164 | VERBOSE(VB_DSMCC, QString("[DSMCCCache] Already seen directory %1") |
162 | 165 | .arg(ref.toString())); |
163 | | return NULL; |
| 166 | return *dir; |
164 | 167 | } |
165 | 168 | |
| 169 | VERBOSE(VB_DSMCC, QString("[DSMCCCache] New directory reference %1") |
| 170 | .arg(ref.toString())); |
| 171 | |
166 | 172 | DSMCCCacheDir *pDir = new DSMCCCacheDir(ref); |
167 | 173 | m_Directories.insert(ref, pDir); |
168 | 174 | |
… |
… |
void DSMCCCache::AddFileInfo(DSMCCCacheDir *pDir, const BiopBinding *pBB) |
209 | 215 | pDir->m_Files.insert(name, *entry); |
210 | 216 | |
211 | 217 | 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())); |
214 | 220 | } |
215 | 221 | |
216 | 222 | // Add a sub-directory to the directory. |
… |
… |
void DSMCCCache::AddDirInfo(DSMCCCacheDir *pDir, const BiopBinding *pBB) |
226 | 232 | pDir->m_SubDirectories.insert(name, *entry); |
227 | 233 | |
228 | 234 | 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())); |
231 | 237 | } |
232 | 238 | |
233 | 239 | // Find File, Directory or Gateway by reference. |
… |
… |
int DSMCCCache::GetDSMObject(QStringList &objectPath, QByteArray &result) |
321 | 327 | // Set the gateway reference from a DSI message. |
322 | 328 | void DSMCCCache::SetGateway(const DSMCCCacheReference &ref) |
323 | 329 | { |
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 | } |
328 | 336 | } |
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, |
46 | 46 | const unsigned char *data) |
47 | 47 | { |
48 | 48 | 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)); |
49 | 52 | return NULL; // Wrong version |
| 53 | } |
50 | 54 | |
51 | 55 | if (m_completed) |
52 | 56 | return NULL; // Already got it. |
53 | 57 | |
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 | | |
58 | 58 | if (ddb->block_number >= m_blocks.size()) |
59 | 59 | { |
60 | | VERBOSE(VB_DSMCC, QString("[dsmcc] Module %1 block number %2 " |
| 60 | VERBOSE(VB_IMPORTANT, QString("[dsmcc] WARN Module %1 block number %2 " |
61 | 61 | "is larger than %3") |
62 | 62 | .arg(ddb->module_id).arg(ddb->block_number) |
63 | 63 | .arg(m_blocks.size())); |
64 | | |
65 | 64 | return NULL; |
66 | 65 | } |
67 | 66 | |
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. |
74 | 76 | } |
75 | 77 | |
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)); |
79 | 87 | |
80 | 88 | if (m_receivedData < m_moduleSize) |
81 | 89 | return NULL; // Not yet complete |
… |
… |
unsigned char *DSMCCCacheModuleData::AddModuleData(DsmccDb *ddb, |
92 | 100 | for (uint i = 0; i < m_blocks.size(); i++) |
93 | 101 | { |
94 | 102 | QByteArray *block = m_blocks[i]; |
| 103 | m_blocks[i] = NULL; |
95 | 104 | uint size = block->size(); |
96 | 105 | memcpy(tmp_data + curp, block->data(), size); |
97 | 106 | curp += size; |
98 | 107 | delete block; |
99 | 108 | } |
100 | | m_blocks.clear(); // No longer required: free the space. |
101 | 109 | |
102 | 110 | /* Uncompress.... */ |
103 | 111 | if (m_descriptorData.isCompressed) |
104 | 112 | { |
105 | | unsigned long dataLen = m_descriptorData.originalSize + 1; |
| 113 | unsigned long dataLen = m_descriptorData.originalSize; |
106 | 114 | VERBOSE(VB_DSMCC, QString("[dsmcc] uncompressing: " |
107 | 115 | "compressed size %1, final size %2") |
108 | 116 | .arg(m_moduleSize).arg(dataLen)); |
109 | 117 | |
110 | | unsigned char *uncompressed = (unsigned char*) malloc(dataLen + 1); |
| 118 | unsigned char *uncompressed = (unsigned char*) malloc(dataLen); |
111 | 119 | int ret = uncompress(uncompressed, &dataLen, tmp_data, m_moduleSize); |
112 | 120 | if (ret != Z_OK) |
113 | 121 | { |
114 | | VERBOSE(VB_DSMCC,"[dsmcc] compression error, skipping"); |
| 122 | VERBOSE(VB_DSMCC,"[dsmcc] WARN compression error, skipping"); |
115 | 123 | free(tmp_data); |
116 | 124 | free(uncompressed); |
117 | 125 | return NULL; |
118 | 126 | } |
119 | 127 | |
120 | 128 | 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; |
128 | 130 | } |
| 131 | |
| 132 | m_completed = true; |
| 133 | m_blocks.clear(); // No longer required: free the space. |
| 134 | return tmp_data; |
129 | 135 | } |
130 | 136 | |
131 | 137 | |
… |
… |
void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, |
148 | 154 | for (int i = 0; i < dii->number_modules; i++) |
149 | 155 | { |
150 | 156 | DsmccModuleInfo *info = &(dii->modules[i]); |
| 157 | bool bFound = false; |
151 | 158 | // Do we already know this module? |
152 | 159 | // If so and it is the same version we don't need to do anything. |
153 | 160 | // If the version has changed we have to replace it. |
… |
… |
void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, |
161 | 168 | /* already known */ |
162 | 169 | if (cachep->Version() == info->module_version) |
163 | 170 | { |
164 | | VERBOSE(VB_DSMCC, QString("[dsmcc] Already Know " |
| 171 | VERBOSE(VB_DSMCC|VB_EXTRA, QString("[dsmcc] Already Know " |
165 | 172 | "Module %1") |
166 | 173 | .arg(info->module_id)); |
167 | 174 | if (cachep->ModuleSize() == info->module_size) |
168 | | return; |
| 175 | { |
| 176 | bFound = true; |
| 177 | break; |
| 178 | } |
169 | 179 | // It seems that when ITV4 starts broadcasting it |
170 | 180 | // updates the contents of a file but doesn't |
171 | 181 | // update the version. This is a work-around. |
… |
… |
void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, |
188 | 198 | } |
189 | 199 | } |
190 | 200 | |
| 201 | if (bFound) |
| 202 | continue; |
| 203 | |
191 | 204 | VERBOSE(VB_DSMCC, QString("[dsmcc] Saving info for module %1") |
192 | 205 | .arg(dii->modules[i].module_id)); |
193 | 206 | |
… |
… |
void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, |
195 | 208 | DSMCCCacheModuleData *cachep = new DSMCCCacheModuleData(dii, info, streamTag); |
196 | 209 | |
197 | 210 | 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") |
200 | 213 | .arg(tag).arg(cachep->CarouselId())); |
201 | 214 | |
202 | 215 | // If a carousel with this id does not exist create it. |
… |
… |
void ObjCarousel::AddModuleInfo(DsmccDii *dii, Dsmcc *status, |
212 | 225 | * |
213 | 226 | * Add it to the module and process the module if it's now complete. |
214 | 227 | */ |
215 | | void ObjCarousel::AddModuleData(unsigned long carousel, DsmccDb *ddb, |
216 | | const unsigned char *data) |
| 228 | void ObjCarousel::AddModuleData(DsmccDb *ddb, const unsigned char *data) |
217 | 229 | { |
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)); |
219 | 231 | |
220 | 232 | // Search the saved module info for this module |
221 | 233 | QLinkedList<DSMCCCacheModuleData*>::iterator it = m_Cache.begin(); |
222 | | DSMCCCacheModuleData *cachep = NULL; |
223 | 234 | for (; it != m_Cache.end(); ++it) |
224 | 235 | { |
225 | | cachep = *it; |
226 | | if (cachep->CarouselId() == carousel && |
| 236 | DSMCCCacheModuleData *cachep = *it; |
| 237 | if (cachep->CarouselId() == m_id && |
227 | 238 | (cachep->ModuleId() == ddb->module_id)) |
228 | 239 | { |
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; |
255 | 262 | } |
256 | | free(tmp_data); |
257 | 263 | } |
| 264 | VERBOSE(VB_DSMCC, QString("[dsmcc] Data block module %1 not on carousel %2") |
| 265 | .arg(ddb->module_id).arg(m_id)); |
258 | 266 | } |
diff --git a/mythtv/libs/libmythtv/dsmccobjcarousel.h b/mythtv/libs/libmythtv/dsmccobjcarousel.h
index 106bef3..e906f56 100644
a
|
b
|
class ObjCarousel |
67 | 67 | ObjCarousel(Dsmcc*); |
68 | 68 | ~ObjCarousel(); |
69 | 69 | 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); |
72 | 71 | |
73 | 72 | DSMCCCache filecache; |
74 | 73 | QLinkedList<DSMCCCacheModuleData*> m_Cache; |
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; |
24 | 24 | #define SCALED_X(arg1) (int)(((float)arg1 * m_xScale) + 0.5f) |
25 | 25 | #define SCALED_Y(arg1) (int)(((float)arg1 * m_yScale) + 0.5f) |
26 | 26 | |
| 27 | // LifecycleExtension tuneinfo: |
| 28 | const unsigned kTuneQuietly = 1U<<0; // b0 tune quietly |
| 29 | const unsigned kTuneKeepApp = 1U<<1; // b1 keep app running |
| 30 | const unsigned kTuneCarId = 1U<<2; // b2 carousel id in bits 8..16 |
| 31 | const unsigned kTuneCarReset = 1U<<3; // b3 get carousel id from gateway info |
| 32 | const unsigned kTuneBcastDisa = 1U<<4; // b4 broadcaster_interrupt disable |
| 33 | // b5..7 reserverd |
| 34 | // b8..15 carousel id |
| 35 | // b16..31 reserved |
| 36 | const unsigned kTuneKeepChnl = 1U<<16; // Keep current channel |
| 37 | |
27 | 38 | /** \class MHIImageData |
28 | 39 | * \brief Data for items in the interactive television display stack. |
29 | 40 | */ |
… |
… |
MHIContext::MHIContext(InteractiveTV *parent) |
44 | 55 | m_engine(NULL), m_stop(false), |
45 | 56 | m_stopped(false), m_updated(false), |
46 | 57 | 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), |
48 | 59 | m_isLive(false), m_currentCard(0), |
49 | 60 | m_audioTag(-1), m_videoTag(-1), |
50 | | m_tuningTo(-1), m_lastNbiVersion(NBI_VERSION_UNSET), |
| 61 | m_lastNbiVersion(NBI_VERSION_UNSET), |
51 | 62 | m_videoRect(0, 0, StdDisplayWidth, StdDisplayHeight), |
52 | 63 | m_displayRect(0, 0, StdDisplayWidth, StdDisplayHeight) |
53 | 64 | { |
… |
… |
void MHIContext::StopEngine() |
146 | 157 | // Start or restart the MHEG engine. |
147 | 158 | void MHIContext::Restart(uint chanid, uint cardid, bool isLive) |
148 | 159 | { |
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 | |
150 | 165 | m_currentCard = cardid; |
| 166 | m_currentStream = (chanid) ? (int)chanid : -1; |
| 167 | if (!(tuneinfo & kTuneKeepChnl)) |
| 168 | m_currentChannel = m_currentStream; |
151 | 169 | |
152 | | if (m_currentChannel == m_tuningTo && m_currentChannel != -1) |
| 170 | if (tuneinfo & kTuneKeepApp) |
153 | 171 | { |
154 | 172 | // We have tuned to the channel in order to find the streams. |
155 | 173 | // Leave the MHEG engine running but restart the DSMCC carousel. |
… |
… |
void MHIContext::Restart(uint chanid, uint cardid, bool isLive) |
159 | 177 | m_dsmcc = new Dsmcc(); |
160 | 178 | { |
161 | 179 | QMutexLocker locker(&m_dsmccLock); |
162 | | m_dsmcc->Reset(); |
| 180 | if (tuneinfo & kTuneCarReset) |
| 181 | m_dsmcc->Reset(); |
163 | 182 | ClearQueue(); |
164 | 183 | } |
| 184 | |
| 185 | if (tuneinfo & (kTuneCarReset|kTuneCarId)) |
| 186 | m_engine->EngineEvent(10); // NonDestructiveTuneOK |
165 | 187 | } |
166 | 188 | else |
167 | 189 | { |
168 | 190 | StopEngine(); |
169 | 191 | |
| 192 | m_audioTag = -1; |
| 193 | m_videoTag = -1; |
| 194 | |
170 | 195 | if (!m_dsmcc) |
171 | 196 | m_dsmcc = new Dsmcc(); |
172 | 197 | |
… |
… |
void MHIContext::Restart(uint chanid, uint cardid, bool isLive) |
193 | 218 | // after the PMT is processed. |
194 | 219 | m_stopped = pthread_create(&m_engineThread, NULL, |
195 | 220 | StartMHEGEngine, this) != 0; |
196 | | m_audioTag = -1; |
197 | | m_videoTag = -1; |
198 | | m_tuningTo = -1; |
199 | 221 | } |
200 | 222 | } |
201 | 223 | |
… |
… |
void *MHIContext::StartMHEGEngine(void *param) |
206 | 228 | MHIContext *context = (MHIContext*) param; |
207 | 229 | context->RunMHEGEngine(); |
208 | 230 | context->m_stopped = true; |
| 231 | VERBOSE(VB_MHEG, "[mhi] Engine stopped"); |
209 | 232 | return NULL; |
210 | 233 | } |
211 | 234 | |
… |
… |
void MHIContext::RunMHEGEngine(void) |
236 | 259 | // Run the engine and find out how long to pause. |
237 | 260 | toWait = m_engine->RunAll(); |
238 | 261 | if (toWait < 0) |
| 262 | { |
| 263 | VERBOSE(VB_GENERAL, "[mhi] Engine requested exit"); |
239 | 264 | return; |
| 265 | } |
240 | 266 | } while (key != 0); |
241 | 267 | |
242 | | if (toWait > 1000 || toWait == 0) |
| 268 | if (toWait > 1000 || toWait <= 0) |
243 | 269 | toWait = 1000; |
244 | 270 | |
245 | 271 | m_engine_wait.wait(&mutex, toWait); |
… |
… |
void MHIContext::QueueDSMCCPacket( |
273 | 299 | unsigned char *data, int length, int componentTag, |
274 | 300 | unsigned carouselId, int dataBroadcastId) |
275 | 301 | { |
| 302 | if (m_stopped) |
| 303 | return; |
| 304 | |
276 | 305 | unsigned char *dataCopy = |
277 | 306 | (unsigned char*) malloc(length * sizeof(unsigned char)); |
278 | 307 | |
… |
… |
void MHIContext::SetNetBootInfo(const unsigned char *data, uint length) |
292 | 321 | { |
293 | 322 | if (length < 2) // A valid descriptor should always have at least 2 bytes. |
294 | 323 | return; |
| 324 | VERBOSE(VB_MHEG, QString("[mhi] SetNetBootInfo version %1 mode %2 len %3") |
| 325 | .arg(data[0]).arg(data[1]).arg(length)); |
295 | 326 | QMutexLocker locker(&m_dsmccLock); |
296 | 327 | // Save the data from the descriptor. |
297 | 328 | m_nbiData.resize(0); |
… |
… |
void MHIContext::NetworkBootRequested(void) |
311 | 342 | if (m_nbiData.size() >= 2 && m_nbiData[0] != m_lastNbiVersion) |
312 | 343 | { |
313 | 344 | m_lastNbiVersion = m_nbiData[0]; // Update the saved version |
314 | | if (m_nbiData[1] == 1) |
| 345 | switch (m_nbiData[1]) |
315 | 346 | { |
| 347 | case 1: |
316 | 348 | m_dsmcc->Reset(); |
317 | 349 | m_engine->SetBooting(); |
318 | 350 | ClearDisplay(); |
319 | 351 | 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; |
320 | 360 | } |
321 | | // TODO: else if it is 2 generate an EngineEvent. |
322 | 361 | } |
323 | 362 | } |
324 | 363 | |
… |
… |
bool MHIContext::GetCarouselData(QString objectPath, QByteArray &result) |
368 | 407 | // and return true otherwise we return false. |
369 | 408 | bool MHIContext::OfferKey(QString key) |
370 | 409 | { |
| 410 | if (m_stopped) |
| 411 | return false; |
| 412 | |
371 | 413 | int action = 0; |
372 | 414 | QMutexLocker locker(&m_keyLock); |
373 | 415 | |
… |
… |
void MHIContext::DrawVideo(const QRect &videoRect, const QRect &dispRect) |
594 | 636 | // Returns -1 if it cannot find it. |
595 | 637 | int MHIContext::GetChannelIndex(const QString &str) |
596 | 638 | { |
597 | | if (str.startsWith("dvb://")) |
| 639 | int nResult = -1; |
| 640 | |
| 641 | do if (str.startsWith("dvb://")) |
598 | 642 | { |
599 | 643 | QStringList list = str.mid(6).split('.'); |
600 | 644 | MSqlQuery query(MSqlQuery::InitCon()); |
601 | | if (list.size() != 3) return -1; // Malformed. |
| 645 | if (list.size() != 3) |
| 646 | break; // Malformed. |
602 | 647 | // The various fields are expressed in hexadecimal. |
603 | 648 | // Convert them to decimal for the DB. |
604 | 649 | bool ok; |
605 | 650 | int netID = list[0].toInt(&ok, 16); |
606 | 651 | if (!ok) |
607 | | return -1; |
| 652 | break; |
608 | 653 | int serviceID = list[2].toInt(&ok, 16); |
609 | 654 | if (!ok) |
610 | | return -1; |
| 655 | break; |
611 | 656 | // We only return channels that match the current capture card. |
612 | 657 | if (list[1].isEmpty()) // TransportID is not specified |
613 | 658 | { |
… |
… |
int MHIContext::GetChannelIndex(const QString &str) |
625 | 670 | { |
626 | 671 | int transportID = list[1].toInt(&ok, 16); |
627 | 672 | if (!ok) |
628 | | return -1; |
| 673 | break; |
629 | 674 | query.prepare( |
630 | 675 | "SELECT chanid " |
631 | 676 | "FROM channel, dtv_multiplex, cardinput, capturecard " |
… |
… |
int MHIContext::GetChannelIndex(const QString &str) |
642 | 687 | query.bindValue(":SERVICEID", serviceID); |
643 | 688 | query.bindValue(":CARDID", m_currentCard); |
644 | 689 | 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(); |
649 | 691 | } |
650 | 692 | else if (str.startsWith("rec://svc/lcn/")) |
651 | 693 | { |
652 | 694 | // I haven't seen this yet so this is untested. |
653 | 695 | bool ok; |
654 | 696 | int channelNo = str.mid(14).toInt(&ok); // Decimal integer |
655 | | if (!ok) return -1; |
| 697 | if (!ok) |
| 698 | break; |
656 | 699 | MSqlQuery query(MSqlQuery::InitCon()); |
657 | 700 | query.prepare("SELECT chanid " |
658 | 701 | "FROM channel, cardinput, capturecard " |
… |
… |
int MHIContext::GetChannelIndex(const QString &str) |
663 | 706 | query.bindValue(":CHAN", channelNo); |
664 | 707 | query.bindValue(":CARDID", m_currentCard); |
665 | 708 | if (query.exec() && query.isActive() && query.next()) |
666 | | return query.value(0).toInt(); |
| 709 | nResult = query.value(0).toInt(); |
667 | 710 | } |
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; |
670 | 715 | 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; |
674 | 722 | } |
675 | 723 | |
676 | 724 | // Get netId etc from the channel index. This is the inverse of GetChannelIndex. |
677 | 725 | bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId, |
678 | 726 | int &transportId, int &serviceId) |
679 | 727 | { |
| 728 | VERBOSE(VB_MHEG, QString("[mhi] GetServiceInfo %1").arg(channelId)); |
680 | 729 | MSqlQuery query(MSqlQuery::InitCon()); |
681 | 730 | query.prepare("SELECT networkid, transportid, serviceid " |
682 | 731 | "FROM channel, dtv_multiplex " |
… |
… |
bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId, |
694 | 743 | else return false; |
695 | 744 | } |
696 | 745 | |
697 | | bool MHIContext::TuneTo(int channel) |
| 746 | bool MHIContext::TuneTo(int channel, int tuneinfo) |
698 | 747 | { |
| 748 | VERBOSE(VB_MHEG, QString("[mhi] TuneTo %1 0x%2") |
| 749 | .arg(channel).arg(tuneinfo,0,16)); |
699 | 750 | if (!m_isLive) |
700 | 751 | return false; // Can't tune if this is a recording. |
701 | 752 | |
| 753 | m_tuneinfo.append(tuneinfo); |
| 754 | |
702 | 755 | // Post an event requesting a channel change. |
703 | 756 | MythEvent me(QString("NETWORK_CONTROL CHANID %1").arg(channel)); |
704 | 757 | gCoreContext->dispatch(me); |
… |
… |
bool MHIContext::TuneTo(int channel) |
712 | 765 | // Begin playing audio from the specified stream |
713 | 766 | bool MHIContext::BeginAudio(const QString &stream, int tag) |
714 | 767 | { |
| 768 | VERBOSE(VB_MHEG, QString("[mhi] BeginAudio %1 %2").arg(stream).arg(tag)); |
715 | 769 | int chan = GetChannelIndex(stream); |
| 770 | if (chan < 0) |
| 771 | return false; |
716 | 772 | |
717 | | if (chan != m_currentChannel) |
| 773 | if (chan != m_currentStream) |
718 | 774 | { |
719 | 775 | // We have to tune to the channel where the audio is to be found. |
720 | 776 | // Because the audio and video are both components of an MHEG stream |
721 | 777 | // they will both be on the same channel. |
722 | | m_tuningTo = chan; |
| 778 | m_currentStream = chan; |
723 | 779 | m_audioTag = tag; |
724 | | return TuneTo(chan); |
| 780 | return TuneTo(chan, kTuneKeepChnl|kTuneQuietly|kTuneKeepApp); |
725 | 781 | } |
726 | 782 | |
727 | 783 | if (tag < 0) |
… |
… |
void MHIContext::StopAudio(void) |
741 | 797 | // Begin displaying video from the specified stream |
742 | 798 | bool MHIContext::BeginVideo(const QString &stream, int tag) |
743 | 799 | { |
| 800 | VERBOSE(VB_MHEG, QString("[mhi] BeginVideo %1 %2").arg(stream).arg(tag)); |
744 | 801 | int chan = GetChannelIndex(stream); |
745 | | if (chan != m_currentChannel) |
| 802 | if (chan < 0) |
| 803 | return false; |
| 804 | if (chan != m_currentStream) |
746 | 805 | { |
747 | 806 | // We have to tune to the channel where the video is to be found. |
748 | | m_tuningTo = chan; |
| 807 | m_currentStream = chan; |
749 | 808 | m_videoTag = tag; |
750 | | return TuneTo(chan); |
| 809 | return TuneTo(chan, kTuneKeepChnl|kTuneQuietly|kTuneKeepApp); |
751 | 810 | } |
752 | 811 | if (tag < 0) |
753 | 812 | return true; // Leave it at the default. |
diff --git a/mythtv/libs/libmythtv/mhi.h b/mythtv/libs/libmythtv/mhi.h
index 52ffa2e..63bae67 100644
a
|
b
|
using namespace std; |
18 | 18 | #include <QString> |
19 | 19 | #include <QWaitCondition> |
20 | 20 | #include <QImage> |
| 21 | #include <QList> |
21 | 22 | |
22 | 23 | // MythTV headers |
23 | 24 | #include "../libmythfreemheg/freemheg.h" |
… |
… |
class MHIContext : public MHContext |
105 | 106 | /// Get netId etc from the channel index. |
106 | 107 | virtual bool GetServiceInfo(int channelId, int &netId, int &origNetId, |
107 | 108 | int &transportId, int &serviceId); |
108 | | virtual bool TuneTo(int channel); |
| 109 | virtual bool TuneTo(int channel, int tuneinfo); |
109 | 110 | |
110 | 111 | /// Begin playing audio from the specified stream |
111 | 112 | virtual bool BeginAudio(const QString &stream, int tag); |
… |
… |
class MHIContext : public MHContext |
175 | 176 | pthread_t m_engineThread; |
176 | 177 | |
177 | 178 | int m_currentChannel; |
| 179 | int m_currentStream; |
178 | 180 | bool m_isLive; |
179 | 181 | int m_currentCard; |
180 | 182 | |
181 | 183 | int m_audioTag; |
182 | 184 | int m_videoTag; |
183 | | int m_tuningTo; |
| 185 | QList<int> m_tuneinfo; |
184 | 186 | |
185 | 187 | uint m_lastNbiVersion; |
186 | 188 | vector<unsigned char> m_nbiData; |