#include "ttxtsubsdisplayer.h" #include "ttxtsubsdisplay.h" #include "utils.h" #include "ttxtsubs.h" #include "siinfo.h" // ----- class cTtxtSubsDisplayer ----- cTtxtSubsDisplayer::cTtxtSubsDisplayer(int textpage) : mDisp(NULL), mGetMutex(), mGetCond(), mRingBuf(18800, true), mRun(0) { mDisp = new cTtxtSubsDisplay(); mDisp->SetPage(textpage); ShowDisplay(); mRun = 1; this->Start(); // start thread } cTtxtSubsDisplayer::~cTtxtSubsDisplayer(void) { // stop thread mRun = false; mGetCond.Broadcast(); this->Cancel(5); if(mDisp) { delete mDisp; mDisp = NULL; } } // handled messages in ring buffer - runs in a thread of its own void cTtxtSubsDisplayer::Action(void) { while(mRun) { cFrame *f; f = mRingBuf.Get(); if(f) { mDisp->TtxtData(f->Data()); mRingBuf.Drop(f); } else { // wait for more data mGetMutex.Lock(); mGetCond.TimedWait(mGetMutex, 500); mDisp->TtxtData(NULL); // give display some idle time mGetMutex.Unlock(); } } } void cTtxtSubsDisplayer::ShowDisplay(void) { mDisp->Show(); } void cTtxtSubsDisplayer::HideDisplay(void) { mDisp->Hide(); } // ----- class cTtxtSubsLiveReceiver ----- cTtxtSubsLiveReceiver::cTtxtSubsLiveReceiver(int Pid, int textpage) : cReceiver(0, -1, 1, Pid), cTtxtSubsDisplayer(textpage) { } void cTtxtSubsLiveReceiver::Activate(bool On) { //dprint("cTtxtSubsLiveReceiver::Activate: On:%d\n", On); } // Take TS packets and break out the teletext data // Buffer the data for processing in a separate thread // XXX We should do some filtering here to avoid unneccessary load! void cTtxtSubsLiveReceiver::Receive(uchar *Data, int Length) { int i; if(Length != 188) // should never happen return; if(Data[1] & 0x80) // transport_error_indicator return; // payload_unit_start_indicator for(i = (Data[1] & 0x40) ? 1 : 0; i < 4; i++) { if(0xff == Data[4 + i*46]) // stuffing data continue; cFrame *f = new cFrame(Data + 4 + i*46, 46); mRingBuf.Put(f); mGetCond.Broadcast(); } } // ----- class cTtxtSubsPlayer ----- cTtxtSubsPlayer::cTtxtSubsPlayer(char *lang, int HI, int backup_textpage) : cTtxtSubsDisplayer(backup_textpage), mHearingImpaired(HI), mHasFilteredStream(0), mFoundLangPage(0), mLangInfoState(0) { memcpy(mLanguage, lang, 3); mLanguage[3] = '\0'; } // Take PES packets and break out the teletext data // Buffer the data for processing in a separate thread // XXX We should do some filtering here to avoid unneccessary load! void cTtxtSubsPlayer::PES_data(uchar *p, int Length) { int i; //printf("cTtxtSubsPlayer: len: %d\n", Length); // XXX if(Length < 46 || p[0] != 0 || p[1] != 0 || p[2] != 1 || p[3] != 0xbd || p[8] != 0x24 || p[45] < 0x10 || p[45] >= 0x20) { fprintf(stderr, "cTtxtSubsPlayer::PES_data: bad indata!\n"); } // Recorded teletext typically has payload type 0x10. // We use the payload type 0x1f to indicate that we have our own // filtered stream, if we find that we only use that. if(p[45] == 0x1f && !mHasFilteredStream) mHasFilteredStream = 1; if(mHasFilteredStream && p[45] != 0x1f) return; // other teletext data, ignore if(mHasFilteredStream && !mFoundLangPage) SearchLanguagePage(p, Length); // payload_unit_start_indicator for(i = 1; (i*46) < Length ; i++) { if(0xff == p[i*46]) // stuffing data continue; cFrame *f = new cFrame(p + i*46, 46); mRingBuf.Put(f); mGetCond.Broadcast(); } } static void copy_inv_strip_par(uint8_t *dest, uint8_t *src, size_t len) { size_t i; for(i = 0; i < len; i++) dest[i] = invtab[src[i]] & 0x7f; } void cTtxtSubsPlayer::SearchLanguagePage(uint8_t *p, int len) { char *infoline = "Subtitles Index Page"; int foundlines = 0; unsigned int foundNonHIPage = 0; int foundNonHI = 0; // 1 = found non hearing impaired if(len < (3*46)) return; struct ttxt_data_field *d; for(d = ((struct ttxt_data_field *) p) + 1; ((uint8_t *) d) < (p + len + 45); d++) { int mp = UNHAM_INV(d->mag_addr_ham[0], d->mag_addr_ham[1]); //int mag = mp & 0x7; int packet = (mp >> 3) & 0x1f; uint8_t buf[40]; if(d->data_unit_id != 0x02) break; // a subtitle line switch(foundlines) { case 0: if(packet != 0) // need a Y0 first return; foundlines++; break; case 1: if(packet != 1) // need a Y1 return; copy_inv_strip_par(buf, d->data, sizeof(buf)); if(memcmp((char *) buf, infoline, strlen(infoline))) return; foundlines++; break; case 2: mLangInfoState++; if(mLangInfoState == 3) fprintf(stderr, "ttxtsubs: Language \"%c%c%c\" not found in recording, available languages:\n", mLanguage[0], mLanguage[1], mLanguage[2]); if(packet < 2) // need a Y2 or more return; copy_inv_strip_par(buf, d->data, sizeof(buf)); for(size_t i = 0; i < 40; i += 8) { if(mLangInfoState == 3 && buf[i] >= 'a' && buf[i] <= 'z') fprintf(stderr, " %c%c%c: %c%c%c %s\n", buf[i+4], buf[i+5], buf[i+6], buf[i], buf[i+1], buf[i+2], buf[i+3] == ' ' ? "" : buf[i+3] == 'h' ? "(Hearing Impaired)" : "(Unknown type)"); if(buf[i] == mLanguage[0] && buf[i+1] == mLanguage[1] && buf[i+2] == mLanguage[2] && ((buf[i+3] == ' ') || (buf[i+3] == 'h')) && buf[i+4] >= '1' && buf[i+4] <= '8' && buf[i+5] >= '0' && buf[i+5] <= '9' && buf[i+6] >= '0' && buf[i+6] <= '9' && buf[i+7] == ' ') { unsigned int page = ((buf[i+4] - '0') << 8) + ((buf[i+5] - '0') << 4) + (buf[i+6] - '0'); if(page >= 0x100 && page < 0x900) { if(page >= 0x800) page -= 0x800; if(((!mHearingImpaired) && (buf[3+i] == ' ')) || (mHearingImpaired && (buf[3+i] == 'h'))) { mDisp->SetPage(page); mFoundLangPage = 1; fprintf(stderr, "FOUND subtitle page: %03x\n", page); // XXX return; } if(mHearingImpaired && (buf[3+i] == ' ')) { foundNonHIPage = page; foundNonHI = 1; fprintf(stderr, "FOUND non hi subtitle page, remembering: %03x\n", page); // XXX } } } } break; } } if(foundNonHI) { mDisp->SetPage(foundNonHIPage); mFoundLangPage = 1; fprintf(stderr, "Didn't find HI page, but found right language: %03x\n", foundNonHIPage); // XXX return; } }