00001 extern "C" {
00002 #include "vbitext/vbi.h"
00003 }
00004
00005 #include <algorithm>
00006 using namespace std;
00007
00008 #include "mythplayer.h"
00009 #include "cc608reader.h"
00010
00011 CC608Reader::CC608Reader(MythPlayer *parent)
00012 : m_parent(parent), m_enabled(false), m_readPosition(0),
00013 m_writePosition(0), m_maxTextSize(0), m_ccMode(CC_CC1),
00014 m_ccPageNum(0x888)
00015 {
00016 memset(&m_inputBuffers, 0, sizeof(m_inputBuffers));
00017 m_maxTextSize = 8 * (sizeof(teletextsubtitle) + VT_WIDTH);
00018 for (int i = 0; i < MAXTBUFFER; i++)
00019 m_inputBuffers[i].buffer = new unsigned char[m_maxTextSize + 1];
00020 }
00021
00022 CC608Reader::~CC608Reader()
00023 {
00024 ClearBuffers(true, true);
00025 for (int i = 0; i < MAXTBUFFER; i++)
00026 {
00027 if (m_inputBuffers[i].buffer)
00028 {
00029 delete [] m_inputBuffers[i].buffer;
00030 m_inputBuffers[i].buffer = NULL;
00031 }
00032 }
00033 }
00034
00035 void CC608Reader::FlushTxtBuffers(void)
00036 {
00037 QMutexLocker locker(&m_inputBufLock);
00038 m_readPosition = m_writePosition;
00039 }
00040
00041 CC608Buffer *CC608Reader::GetOutputText(bool &changed)
00042 {
00043 bool last_changed = true;
00044 while (last_changed)
00045 {
00046 last_changed = false;
00047 int streamIdx = -1;
00048 CC608Buffer *tmp = GetOutputText(last_changed, streamIdx);
00049 if (last_changed && (streamIdx == m_ccMode))
00050 {
00051 changed = true;
00052 return tmp;
00053 }
00054 }
00055
00056 return NULL;
00057 }
00058
00059 CC608Buffer *CC608Reader::GetOutputText(bool &changed, int &streamIdx)
00060 {
00061 streamIdx = -1;
00062
00063 if (!m_enabled)
00064 return NULL;
00065
00066 if (!m_parent)
00067 {
00068 if (NumInputBuffers())
00069 {
00070 streamIdx = Update(m_inputBuffers[m_writePosition].buffer);
00071 changed = true;
00072
00073 QMutexLocker locker(&m_inputBufLock);
00074 if (m_writePosition != m_readPosition)
00075 m_writePosition = (m_writePosition + 1) % MAXTBUFFER;
00076 }
00077
00078 if (streamIdx >= 0)
00079 {
00080 m_state[streamIdx].m_changed = false;
00081 return &m_state[streamIdx].m_output;
00082 }
00083 else
00084 return &m_state[MAXOUTBUFFERS - 1].m_output;
00085 }
00086
00087 VideoFrame *last = NULL;
00088 if (m_parent->GetVideoOutput())
00089 last = m_parent->GetVideoOutput()->GetLastShownFrame();
00090
00091 if (NumInputBuffers() && m_inputBuffers[m_writePosition].timecode &&
00092 (last && m_inputBuffers[m_writePosition].timecode <= last->timecode))
00093 {
00094 if (m_inputBuffers[m_writePosition].type == 'T')
00095 {
00096 streamIdx = MAXOUTBUFFERS - 1;
00097
00098
00099
00100
00101
00102
00103
00104
00105 unsigned char *inpos = m_inputBuffers[m_writePosition].buffer;
00106 int pagenr;
00107 memcpy(&pagenr, inpos, sizeof(int));
00108 inpos += sizeof(int);
00109
00110 if (pagenr == (m_ccPageNum<<16))
00111 {
00112
00113 ClearBuffers(false, true, streamIdx);
00114 (*inpos)++;
00115 while (*inpos)
00116 {
00117 struct teletextsubtitle st;
00118 memcpy(&st, inpos, sizeof(st));
00119 inpos += sizeof(st);
00120
00121 CC608Text *cc = new CC608Text(
00122 QString((const char*) inpos), st.row, st.col, true);
00123
00124 m_state[streamIdx].m_output.lock.lock();
00125 m_state[streamIdx].m_output.buffers.push_back(cc);
00126 m_state[streamIdx].m_output.lock.unlock();
00127
00128 inpos += st.len;
00129 }
00130 changed = true;
00131 }
00132 }
00133 else if (m_inputBuffers[m_writePosition].type == 'C')
00134 {
00135 streamIdx = Update(m_inputBuffers[m_writePosition].buffer);
00136 changed = true;
00137 }
00138
00139 QMutexLocker locker(&m_inputBufLock);
00140 if (m_writePosition != m_readPosition)
00141 m_writePosition = (m_writePosition + 1) % MAXTBUFFER;
00142 }
00143
00144 if (streamIdx >= 0)
00145 {
00146 m_state[streamIdx].m_changed = false;
00147 return &m_state[streamIdx].m_output;
00148 }
00149 else
00150 {
00151 return &m_state[MAXOUTBUFFERS - 1].m_output;
00152 }
00153 }
00154
00155 void CC608Reader::SetMode(int mode)
00156 {
00157
00158
00159 m_ccMode = (mode <= 2) ? ((mode == 1) ? CC_CC1 : CC_CC2) :
00160 ((mode == 3) ? CC_CC3 : CC_CC4);
00161
00162
00163 }
00164
00165 int CC608Reader::Update(unsigned char *inpos)
00166 {
00167 struct ccsubtitle subtitle;
00168
00169 memcpy(&subtitle, inpos, sizeof(subtitle));
00170 inpos += sizeof(ccsubtitle);
00171
00172 const int streamIdx = (subtitle.resumetext & CC_MODE_MASK) >> 4;
00173
00174 if (subtitle.row == 0)
00175 subtitle.row = 1;
00176
00177 if (subtitle.clr)
00178 {
00179 #if 0
00180 LOG(VB_VBI, LOG_DEBUG, "erase displayed memory");
00181 #endif
00182 ClearBuffers(false, true, streamIdx);
00183 if (!subtitle.len)
00184 return streamIdx;
00185 }
00186
00187
00188 {
00189 unsigned char *end = inpos + subtitle.len;
00190 int row = 0;
00191 int linecont = (subtitle.resumetext & CC_LINE_CONT);
00192
00193 vector<CC608Text*> *ccbuf = new vector<CC608Text*>;
00194 vector<CC608Text*>::iterator ccp;
00195 CC608Text *tmpcc = NULL;
00196 int replace = linecont;
00197 int scroll = 0;
00198 bool scroll_prsv = false;
00199 int scroll_yoff = 0;
00200 int scroll_ymax = 15;
00201
00202 do
00203 {
00204 if (linecont)
00205 {
00206
00207 replace = 1;
00208
00209 int bscnt = 0;
00210 while ((inpos < end) && *inpos != 0 && (char)*inpos == '\b')
00211 {
00212 bscnt++;
00213 inpos++;
00214 }
00215 if (bscnt)
00216 {
00217 m_state[streamIdx].m_outputText.remove(
00218 m_state[streamIdx].m_outputText.length() - bscnt,
00219 bscnt);
00220 }
00221 }
00222 else
00223 {
00224
00225 row++;
00226 m_state[streamIdx].m_outputCol = 0;
00227 m_state[streamIdx].m_outputText = "";
00228 while ((inpos < end) && *inpos != 0 && (char)*inpos == ' ')
00229 {
00230 inpos++;
00231 m_state[streamIdx].m_outputCol++;
00232 }
00233 }
00234
00235 m_state[streamIdx].m_outputRow = subtitle.row;
00236 unsigned char *cur = inpos;
00237
00238
00239 while (cur < end && *cur != '\n' && *cur != 0)
00240 cur++;
00241 *cur = 0;
00242
00243 if (*inpos != 0 || linecont)
00244 {
00245 if (linecont)
00246 {
00247 m_state[streamIdx].m_outputText +=
00248 QString::fromUtf8((const char *)inpos, -1);
00249 }
00250 else
00251 {
00252 m_state[streamIdx].m_outputText =
00253 QString::fromUtf8((const char *)inpos, -1);
00254 }
00255 tmpcc = new CC608Text(
00256 m_state[streamIdx].m_outputText,
00257 m_state[streamIdx].m_outputCol,
00258 m_state[streamIdx].m_outputRow, false);
00259 ccbuf->push_back(tmpcc);
00260 #if 0
00261 if (ccbuf->size() > 4)
00262 LOG(VB_VBI, LOG_DEBUG, QString("CC overflow: %1 %2 %3")
00263 .arg(m_outputCol) .arg(m_outputRow)
00264 .arg(m_outputText));
00265 #endif
00266 }
00267 subtitle.row++;
00268 inpos = cur + 1;
00269 linecont = 0;
00270 } while (inpos < end);
00271
00272
00273 if (subtitle.resumetext & CC_TXT_MASK)
00274 {
00275
00276
00277
00278 if (m_state[streamIdx].m_outputRow > 15)
00279 {
00280 if (row)
00281 scroll = m_state[streamIdx].m_outputRow - 15;
00282 if (tmpcc)
00283 tmpcc->y = 15;
00284 }
00285 }
00286 else if (subtitle.rowcount == 0 || row > 1)
00287 {
00288
00289
00290 if (m_state[streamIdx].m_outputRow > 15)
00291 {
00292 ccp = ccbuf->begin();
00293 for (; ccp != ccbuf->end(); ++ccp)
00294 {
00295 tmpcc = *ccp;
00296 tmpcc->y -= (m_state[streamIdx].m_outputRow - 15);
00297 }
00298 }
00299 }
00300 else
00301 {
00302
00303
00304
00305
00306
00307 if (subtitle.rowcount > 4)
00308 subtitle.rowcount = 4;
00309 if (m_state[streamIdx].m_outputRow < subtitle.rowcount)
00310 {
00311 m_state[streamIdx].m_outputRow = subtitle.rowcount;
00312 if (tmpcc)
00313 tmpcc->y = m_state[streamIdx].m_outputRow;
00314 }
00315 if (row)
00316 {
00317 scroll = row;
00318 scroll_prsv = true;
00319 scroll_yoff =
00320 m_state[streamIdx].m_outputRow - subtitle.rowcount;
00321 scroll_ymax = m_state[streamIdx].m_outputRow;
00322 }
00323 }
00324
00325 Update608Text(ccbuf, replace, scroll,
00326 scroll_prsv, scroll_yoff, scroll_ymax, streamIdx);
00327 delete ccbuf;
00328 }
00329
00330 return streamIdx;
00331 }
00332
00333 void CC608Reader::TranscodeWriteText(void (*func)
00334 (void *, unsigned char *, int, int, int),
00335 void * ptr)
00336 {
00337 QMutexLocker locker(&m_inputBufLock);
00338 while (NumInputBuffers(false))
00339 {
00340 locker.unlock();
00341 int pagenr = 0;
00342 unsigned char *inpos = m_inputBuffers[m_readPosition].buffer;
00343 if (m_inputBuffers[m_readPosition].type == 'T')
00344 {
00345 memcpy(&pagenr, inpos, sizeof(int));
00346 inpos += sizeof(int);
00347 m_inputBuffers[m_readPosition].len -= sizeof(int);
00348 }
00349 func(ptr, inpos, m_inputBuffers[m_readPosition].len,
00350 m_inputBuffers[m_readPosition].timecode, pagenr);
00351
00352 locker.relock();
00353 m_readPosition = (m_readPosition + 1) % MAXTBUFFER;
00354 }
00355 }
00356
00357 void CC608Reader::Update608Text(
00358 vector<CC608Text*> *ccbuf, int replace, int scroll, bool scroll_prsv,
00359 int scroll_yoff, int scroll_ymax, int streamIdx)
00360
00361
00362
00363
00364
00365
00366 {
00367 vector<CC608Text*>::iterator i;
00368 int visible = 0;
00369
00370 m_state[streamIdx].m_output.lock.lock();
00371 if (m_state[streamIdx].m_output.buffers.size() && (scroll || replace))
00372 {
00373 CC608Text *cc;
00374
00375
00376 int ylast = 0;
00377 i = m_state[streamIdx].m_output.buffers.end() - 1;
00378 cc = *i;
00379 if (cc)
00380 ylast = cc->y;
00381
00382
00383 int ydel = scroll_yoff + scroll;
00384 int ykeep = scroll_ymax;
00385 int ymove = 0;
00386 if (scroll_prsv && ylast)
00387 {
00388 ymove = ylast - scroll_ymax;
00389 ydel += ymove;
00390 ykeep += ymove;
00391 }
00392
00393 i = m_state[streamIdx].m_output.buffers.begin();
00394 while (i < m_state[streamIdx].m_output.buffers.end())
00395 {
00396 cc = (*i);
00397 if (!cc)
00398 {
00399 i = m_state[streamIdx].m_output.buffers.erase(i);
00400 continue;
00401 }
00402
00403 if (cc->y > (ylast - replace))
00404 {
00405
00406 delete cc;
00407 i = m_state[streamIdx].m_output.buffers.erase(i);
00408 }
00409 else if (scroll)
00410 {
00411 if (cc->y > ydel && cc->y <= ykeep)
00412 {
00413
00414 cc->y -= (scroll + ymove);
00415 ++i;
00416 }
00417 else
00418 {
00419
00420 i = m_state[streamIdx].m_output.buffers.erase(i);
00421 delete cc;
00422 }
00423 }
00424 else
00425 {
00426 ++i;
00427 }
00428 }
00429 }
00430
00431 visible += m_state[streamIdx].m_output.buffers.size();
00432
00433 if (ccbuf)
00434 {
00435
00436 for (i = ccbuf->begin(); i != ccbuf->end(); ++i)
00437 {
00438 if (*i)
00439 {
00440 visible++;
00441 m_state[streamIdx].m_output.buffers.push_back(*i);
00442 }
00443 }
00444 }
00445 m_state[streamIdx].m_changed = visible;
00446 m_state[streamIdx].m_output.lock.unlock();
00447 }
00448
00449 void CC608Reader::ClearBuffers(bool input, bool output, int outputStreamIdx)
00450 {
00451 if (input)
00452 {
00453 for (int i = 0; i < MAXTBUFFER; i++)
00454 {
00455 m_inputBuffers[i].timecode = 0;
00456 if (m_inputBuffers[i].buffer)
00457 memset(m_inputBuffers[i].buffer, 0, m_maxTextSize);
00458 }
00459
00460 QMutexLocker locker(&m_inputBufLock);
00461 m_readPosition = 0;
00462 m_writePosition = 0;
00463 }
00464
00465 if (output && outputStreamIdx < 0)
00466 {
00467 for (int i = 0; i < MAXOUTBUFFERS; ++i)
00468 m_state[i].Clear();
00469 }
00470
00471 if (output && outputStreamIdx >= 0)
00472 {
00473 outputStreamIdx = min(outputStreamIdx, MAXOUTBUFFERS - 1);
00474 m_state[outputStreamIdx].Clear();
00475 }
00476 }
00477
00478 int CC608Reader::NumInputBuffers(bool need_to_lock)
00479 {
00480 int ret;
00481
00482 if (need_to_lock)
00483 m_inputBufLock.lock();
00484
00485 if (m_readPosition >= m_writePosition)
00486 ret = m_readPosition - m_writePosition;
00487 else
00488 ret = MAXTBUFFER - (m_writePosition - m_readPosition);
00489
00490 if (need_to_lock)
00491 m_inputBufLock.unlock();
00492
00493 return ret;
00494 }
00495
00496 void CC608Reader::AddTextData(unsigned char *buffer, int len,
00497 int64_t timecode, char type)
00498 {
00499 if (m_parent)
00500 m_parent->WrapTimecode(timecode, TC_CC);
00501
00502 if (!m_enabled)
00503 return;
00504
00505 if (NumInputBuffers() >= MAXTBUFFER - 1)
00506 {
00507 LOG(VB_VBI, LOG_ERR, "AddTextData(): Text buffer overflow");
00508 return;
00509 }
00510
00511 if (len > m_maxTextSize)
00512 len = m_maxTextSize;
00513
00514 QMutexLocker locker(&m_inputBufLock);
00515 int prev_readpos = (m_readPosition - 1 + MAXTBUFFER) % MAXTBUFFER;
00516
00517
00518
00519
00520
00521
00522
00523 if (NumInputBuffers(false) > 0 &&
00524 m_inputBuffers[m_readPosition].timecode > timecode &&
00525 timecode > m_inputBuffers[prev_readpos].timecode)
00526 {
00527
00528
00529
00530
00531
00532
00533 LOG(VB_VBI, LOG_INFO,
00534 QString("Writing caption timecode %1 but waiting on %2")
00535 .arg(timecode).arg(m_inputBuffers[m_readPosition].timecode));
00536 m_inputBuffers[m_readPosition].timecode =
00537 m_inputBuffers[prev_readpos].timecode + 500;
00538 }
00539
00540 m_inputBuffers[m_readPosition].timecode = timecode;
00541 m_inputBuffers[m_readPosition].type = type;
00542 m_inputBuffers[m_readPosition].len = len;
00543 memset(m_inputBuffers[m_readPosition].buffer, 0, m_maxTextSize);
00544 memcpy(m_inputBuffers[m_readPosition].buffer, buffer, len);
00545
00546 m_readPosition = (m_readPosition+1) % MAXTBUFFER;
00547 }