From: Andreas Monzner Date: Mon, 23 Oct 2006 10:52:20 +0000 (+0000) Subject: prepare for dvb subtitle support (not finished yet) X-Git-Tag: 2.6.0~2857 X-Git-Url: https://git.cweiske.de/enigma2.git/commitdiff_plain/0adf3ed39b6fe254154e3b4c38feea616e5aece1 prepare for dvb subtitle support (not finished yet) cache query of eDVBServicePMThandler::getProgram --- diff --git a/lib/dvb/Makefile.am b/lib/dvb/Makefile.am index 98f4030a..bac03751 100644 --- a/lib/dvb/Makefile.am +++ b/lib/dvb/Makefile.am @@ -6,5 +6,5 @@ noinst_LIBRARIES = libenigma_dvb.a libenigma_dvb_a_SOURCES = dvb.cpp demux.cpp frontend.cpp esection.cpp db.cpp \ sec.cpp scan.cpp crc32.cpp pmt.cpp decoder.cpp eit.cpp rotor_calc.cpp \ epgcache.cpp dvbtime.cpp metaparser.cpp volume.cpp tstools.cpp pvrparse.cpp \ - pesparse.cpp teletext.cpp radiotext.cpp + pesparse.cpp teletext.cpp radiotext.cpp subtitle.cpp diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index d6f002e6..ccc77960 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -12,6 +12,7 @@ #include #include #include +#include eDVBServicePMTHandler::eDVBServicePMTHandler() :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF) @@ -83,6 +84,7 @@ void eDVBServicePMTHandler::PMTready(int error) serviceEvent(eventNoPMT); else { + m_have_cached_program = false; serviceEvent(eventNewProgramInfo); eEPGCache::getInstance()->PMTready(this); if (!m_pvr_channel) // don't send campmt to camd.socket for playbacked services @@ -164,6 +166,7 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) int cached_apid_mpeg = -1; int cached_vpid = -1; int cached_tpid = -1; + int ret = -1; program.videoStreams.clear(); program.audioStreams.clear(); @@ -181,136 +184,151 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) if ( ((m_service && m_service->usePMT()) || !m_service) && !m_PMT.getCurrent(ptr)) { - eDVBTableSpec table_spec; - ptr->getSpec(table_spec); - program.pmtPid = table_spec.pid < 0x1fff ? table_spec.pid : -1; - std::vector::const_iterator i; - for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + if (m_have_cached_program) { - const ProgramMapSection &pmt = **i; - program.pcrPid = pmt.getPcrPid(); - - ElementaryStreamInfoConstIterator es; - for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) + program = m_cached_program; + ret = 0; + } + else + { + eDVBTableSpec table_spec; + ptr->getSpec(table_spec); + program.pmtPid = table_spec.pid < 0x1fff ? table_spec.pid : -1; + std::vector::const_iterator i; + for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) { - int isaudio = 0, isvideo = 0; - videoStream video; - audioStream audio; - audio.component_tag=-1; - video.component_tag=-1; - - video.pid = (*es)->getPid(); - audio.pid = (*es)->getPid(); - video.type = videoStream::vtMPEG2; - - switch ((*es)->getType()) + const ProgramMapSection &pmt = **i; + program.pcrPid = pmt.getPcrPid(); + + ElementaryStreamInfoConstIterator es; + for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) { - case 0x1b: // AVC Video Stream (MPEG4 H264) - video.type = videoStream::vtMPEG4_H264; - case 0x01: // MPEG 1 video - case 0x02: // MPEG 2 video - isvideo = 1; - //break; fall through !!! - case 0x03: // MPEG 1 audio - case 0x04: // MPEG 2 audio: - if (!isvideo) - { - isaudio = 1; - audio.type = audioStream::atMPEG; - } - //break; fall through !!! - case 0x06: // PES Private - /* PES private can contain AC-3, DTS or lots of other stuff. - check descriptors to get the exact type. */ - for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); - desc != (*es)->getDescriptors()->end(); ++desc) + int isaudio = 0, isvideo = 0; + videoStream video; + audioStream audio; + audio.component_tag=video.component_tag=-1; + video.type = videoStream::vtMPEG2; + + switch ((*es)->getType()) { - switch ((*desc)->getTag()) + case 0x1b: // AVC Video Stream (MPEG4 H264) + video.type = videoStream::vtMPEG4_H264; + case 0x01: // MPEG 1 video + case 0x02: // MPEG 2 video + isvideo = 1; + //break; fall through !!! + case 0x03: // MPEG 1 audio + case 0x04: // MPEG 2 audio: + if (!isvideo) + { + isaudio = 1; + audio.type = audioStream::atMPEG; + } + //break; fall through !!! + case 0x06: // PES Private + /* PES private can contain AC-3, DTS or lots of other stuff. + check descriptors to get the exact type. */ + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + desc != (*es)->getDescriptors()->end(); ++desc) { - case TELETEXT_DESCRIPTOR: - if ( program.textPid == -1 || (*es)->getPid() == cached_tpid ) - program.textPid = (*es)->getPid(); - break; - case DTS_DESCRIPTOR: - if (!isvideo) + switch ((*desc)->getTag()) { + case SUBTITLING_DESCRIPTOR: + { + SubtitlingDescriptor *d = (SubtitlingDescriptor*)(*desc); + const SubtitlingList *list = d->getSubtitlings(); + subtitleStream s; + s.pid = (*es)->getPid(); + for (SubtitlingConstIterator it(list->begin()); it != list->end(); ++it) + { + s.subtitling_type = (*it)->getSubtitlingType(); + s.composition_page_id = (*it)->getCompositionPageId(); + s.ancillary_page_id = (*it)->getAncillaryPageId(); + s.language_code = (*it)->getIso639LanguageCode(); + program.subtitleStreams.push_back(s); + } + break; + } + case TELETEXT_DESCRIPTOR: + if ( program.textPid == -1 || (*es)->getPid() == cached_tpid ) + program.textPid = (*es)->getPid(); + break; + case DTS_DESCRIPTOR: isaudio = 1; audio.type = audioStream::atDTS; - } - break; - case AAC_DESCRIPTOR: - if (!isvideo) - { + break; + case AAC_DESCRIPTOR: isaudio = 1; audio.type = audioStream::atAAC; - } - break; - case AC3_DESCRIPTOR: - if (!isvideo) - { + break; + case AC3_DESCRIPTOR: isaudio = 1; audio.type = audioStream::atAC3; - } - break; - case ISO_639_LANGUAGE_DESCRIPTOR: - if (!isvideo) + break; + case ISO_639_LANGUAGE_DESCRIPTOR: + if (!isvideo) + { + const Iso639LanguageList *languages = ((Iso639LanguageDescriptor*)*desc)->getIso639Languages(); + /* use last language code */ + for (Iso639LanguageConstIterator i(languages->begin()); i != languages->end(); ++i) + audio.language_code = (*i)->getIso639LanguageCode(); + } + break; + case STREAM_IDENTIFIER_DESCRIPTOR: + audio.component_tag = + video.component_tag = + ((StreamIdentifierDescriptor*)*desc)->getComponentTag(); + break; + case CA_DESCRIPTOR: { - const Iso639LanguageList *languages = ((Iso639LanguageDescriptor*)*desc)->getIso639Languages(); - /* use last language code */ - for (Iso639LanguageConstIterator i(languages->begin()); i != languages->end(); ++i) - audio.language_code = (*i)->getIso639LanguageCode(); + CaDescriptor *descr = (CaDescriptor*)(*desc); + program.caids.insert(descr->getCaSystemId()); + break; + } } - break; - case STREAM_IDENTIFIER_DESCRIPTOR: - audio.component_tag = - video.component_tag = - ((StreamIdentifierDescriptor*)*desc)->getComponentTag(); - break; - case CA_DESCRIPTOR: - { - CaDescriptor *descr = (CaDescriptor*)(*desc); - program.caids.insert(descr->getCaSystemId()); - break; } + break; + } + if (isaudio) + { + audio.pid = (*es)->getPid(); + if ( !program.audioStreams.empty() && + ( audio.pid == cached_apid_ac3 || audio.pid == cached_apid_mpeg) ) + { + program.audioStreams.push_back(program.audioStreams[0]); + program.audioStreams[0] = audio; } + else + program.audioStreams.push_back(audio); } - break; - } - if (isaudio) - { - if ( !program.audioStreams.empty() && - ( audio.pid == cached_apid_ac3 || audio.pid == cached_apid_mpeg) ) + else if (isvideo) { - program.audioStreams.push_back(program.audioStreams[0]); - program.audioStreams[0] = audio; + video.pid = (*es)->getPid(); + if ( !program.videoStreams.empty() && video.pid == cached_vpid ) + { + program.videoStreams.push_back(program.videoStreams[0]); + program.videoStreams[0] = video; + } + else + program.videoStreams.push_back(video); } else - program.audioStreams.push_back(audio); + continue; } - else if (isvideo) + for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); + desc != pmt.getDescriptors()->end(); ++desc) { - if ( !program.videoStreams.empty() && video.pid == cached_vpid ) + if ((*desc)->getTag() == CA_DESCRIPTOR) { - program.videoStreams.push_back(program.videoStreams[0]); - program.videoStreams[0] = video; + CaDescriptor *descr = (CaDescriptor*)(*desc); + program.caids.insert(descr->getCaSystemId()); } - else - program.videoStreams.push_back(video); - } - else - continue; - } - for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); - desc != pmt.getDescriptors()->end(); ++desc) - { - if ((*desc)->getTag() == CA_DESCRIPTOR) - { - CaDescriptor *descr = (CaDescriptor*)(*desc); - program.caids.insert(descr->getCaSystemId()); } } + ret = 0; + m_cached_program = program; + m_have_cached_program = true; } - return 0; } else if ( m_service && !m_service->cacheEmpty() ) { int cached_pcrpid = m_service->getCacheEntry(eDVBService::cPCRPID), @@ -356,9 +374,9 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) for (CAID_LIST::iterator it(caids.begin()); it != caids.end(); ++it) program.caids.insert(*it); if ( cnt ) - return 0; + ret = 0; } - return -1; + return ret; } int eDVBServicePMTHandler::getChannel(eUsePtr &channel) @@ -517,7 +535,6 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, void eDVBServicePMTHandler::free() { - eDVBScan *tmp = m_dvb_scan; // do a copy on stack (recursive call of free()) !!! m_dvb_scan = 0; delete m_dvb_scan; diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index fc8b15dd..701930dc 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -72,9 +72,10 @@ class eDVBServicePMTHandler: public Object void PMTready(int error); void PATready(int error); + int m_pmt_pid; + int m_use_decode_demux; uint8_t m_decode_demux_num; - public: eDVBServicePMTHandler(); ~eDVBServicePMTHandler(); @@ -112,11 +113,21 @@ public: int component_tag; std::string language_code; /* iso-639, if available. */ }; - + + struct subtitleStream + { + int pid; + int subtitling_type; + int composition_page_id; + int ancillary_page_id; + std::string language_code; + }; + struct program { std::vector videoStreams; std::vector audioStreams; + std::vector subtitleStreams; std::set caids; int pcrPid; int pmtPid; @@ -137,8 +148,9 @@ public: int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0); void free(); - - int m_pmt_pid; +private: + bool m_have_cached_program; + program m_cached_program; }; #endif diff --git a/lib/dvb/subtitle.cpp b/lib/dvb/subtitle.cpp new file mode 100644 index 00000000..5510997b --- /dev/null +++ b/lib/dvb/subtitle.cpp @@ -0,0 +1,996 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void bitstream_init(struct bitstream *bit, const void *buffer, int size) +{ + bit->data = (__u8*) buffer; + bit->size = size; + bit->avail = 8; + bit->consumed = 0; +} + +int bitstream_get(struct bitstream *bit) +{ + int val; + bit->avail -= bit->size; + val = ((*bit->data) >> bit->avail) & ((1<size) - 1); + if (!bit->avail) + { + bit->data++; + bit->consumed++; + bit->avail = 8; + } + return val; +} + +static int extract_pts(pts_t &pts, const __u8 *pkt) +{ + pkt += 7; + int flags = *pkt++; + + pkt++; // header length + + if (flags & 0x80) /* PTS present? */ + { + /* damn gcc bug */ + pts = ((unsigned long long)(((pkt[0] >> 1) & 7))) << 30; + pts |= pkt[1] << 22; + pts |= (pkt[2]>>1) << 15; + pts |= pkt[3] << 7; + pts |= (pkt[5]>>1); + + return 0; + } else + return -1; +} + +void eDVBSubtitleParser::subtitle_process_line(struct subtitle_page *page, int object_id, int line, const __u8 *data, int len) +{ + struct subtitle_region *region = page->regions; +// eDebug("line for %d:%d", page->page_id, object_id); + while (region) + { + struct subtitle_region_object *object = region->region_objects; + while (object) + { + if (object->object_id == object_id) + { + int x = object->object_horizontal_position; + int y = object->object_vertical_position + line; + if (x + len > region->region_width) + { + //eDebug("[SUB] !!!! XCLIP %d + %d > %d", x, len, region->region_width); + len = region->region_width - x; + } + if (len < 0) + break; + if (y >= region->region_height) + { + //eDebug("[SUB] !!!! YCLIP %d >= %d", y, region->region_height); + break; + } +// //eDebug("inserting %d bytes (into region %d)", len, region->region_id); + memcpy((__u8*)region->region_buffer->surface->data + region->region_width * y + x, data, len); + } + object = object->next; + } + region = region->next; + } +} + +int eDVBSubtitleParser::subtitle_process_pixel_data(struct subtitle_page *page, int object_id, int *linenr, int *linep, const __u8 *data) +{ + int data_type = *data++; + static __u8 line[720]; + + struct bitstream bit; + bit.size=0; + switch (data_type) + { + case 0x10: // 2bit pixel data + bitstream_init(&bit, data, 2); + while (1) + { + int len=0, col=0; + int code = bitstream_get(&bit); + if (code) + { + col = code; + len = 1; + } else + { + code = bitstream_get(&bit); + if (!code) + { + code = bitstream_get(&bit); + if (code == 1) + { + col = 0; + len = 2; + } else if (code == 2) + { + len = bitstream_get(&bit) << 2; + len |= bitstream_get(&bit); + len += 12; + col = bitstream_get(&bit); + } else if (code == 3) + { + len = bitstream_get(&bit) << 6; + len |= bitstream_get(&bit) << 4; + len |= bitstream_get(&bit) << 2; + len |= bitstream_get(&bit); + len += 29; + col = bitstream_get(&bit); + } else + break; + } else if (code==1) + { + col = 0; + len = 1; + } else if (code&2) + { + if (code&1) + len = 3 + 4 + bitstream_get(&bit); + else + len = 3 + bitstream_get(&bit); + col = bitstream_get(&bit); + } + } + while (len && ((*linep) < 720)) + { + line[(*linep)++] = col; + len--; + } + } + while (bit.avail != 8) + bitstream_get(&bit); + return bit.consumed + 1; + case 0x11: // 4bit pixel data + bitstream_init(&bit, data, 4); + while (1) + { + int len=0, col=0; + int code = bitstream_get(&bit); + if (code) + { + col = code; + len = 1; + } else + { + code = bitstream_get(&bit); + if (!code) + break; + else if (code == 0xC) + { + col = 0; + len = 1; + } else if (code == 0xD) + { + col = 0; + len = 2; + } else if (code < 8) + { + col = 0; + len = (code & 7) + 2; + } else if ((code & 0xC) == 0x8) + { + col = bitstream_get(&bit); + len = (code & 3) + 4; + } else if (code == 0xE) + { + len = bitstream_get(&bit) + 9; + col = bitstream_get(&bit); + } else if (code == 0xF) + { + len = bitstream_get(&bit) << 4; + len |= bitstream_get(&bit); + len += 25; + col = bitstream_get(&bit); + } + } + while (len && ((*linep) < 720)) + { + line[(*linep)++] = col; + len--; + } + } + while (bit.avail != 8) + bitstream_get(&bit); + return bit.consumed + 1; + case 0x12: // 8bit pixel data + bitstream_init(&bit, data, 8); + while(1) + { + int len=0, col=0; + int code = bitstream_get(&bit); + if (code) + { + col = code; + len = 1; + } else + { + code = bitstream_get(&bit); + if ((code & 0x80) == 0x80) + { + len = code&0x7F; + col = bitstream_get(&bit); + } else if (code&0x7F) + { + len = code&0x7F; + col = 0; + } else + break; + } + while (len && ((*linep) < 720)) + { + line[(*linep)++] = col; + len--; + } + } + return bit.consumed + 1; + case 0x20: // ignore 2 -> 4bit map table + bitstream_init(&bit, data, 4); + for ( int i=0; i < 4; ++i ) + bitstream_get(&bit); + break; + case 0x21: // ignore 2 -> 8bit map table + bitstream_init(&bit, data, 8); + for ( int i=0; i < 4; ++i ) + bitstream_get(&bit); + break; + case 0x22: // ignore 4 -> 8bit map table + bitstream_init(&bit, data, 8); + for ( int i=0; i < 16; ++i ) + bitstream_get(&bit); + break; + case 0xF0: + subtitle_process_line(page, object_id, *linenr, line, *linep); +/* { + int i; + for (i=0; i<720; ++i) + //eDebugNoNewLine("%d ", line[i]); + //eDebug(""); + } */ + (*linenr)+=2; // interlaced + *linep = 0; +// //eDebug("[SUB] EOL"); + return 1; + default: + eDebug("subtitle_process_pixel_data: invalid data_type %02x", data_type); + return -1; + } + return 0; +} + +int eDVBSubtitleParser::subtitle_process_segment(const __u8 *segment) +{ + int segment_type, page_id, segment_length, processed_length; + if (*segment++ != 0x0F) + { + eDebug("out of sync."); + return -1; + } + segment_type = *segment++; + page_id = *segment++ << 8; + page_id |= *segment++; + segment_length = *segment++ << 8; + segment_length |= *segment++; + if (segment_type == 0xFF) + return segment_length + 6; +// //eDebug("have %d bytes of segment data", segment_length); + +// //eDebug("page_id %d, segtype %02x", page_id, segment_type); + + struct subtitle_page *page, **ppage; + + page = this->pages; ppage = &this->pages; + + while (page) + { + if (page->page_id == page_id) + break; + ppage = &page->next; + page = page->next; + } + + processed_length = 0; + + switch (segment_type) + { + case 0x10: // page composition segment + { + int page_time_out = *segment++; processed_length++; + int page_version_number = *segment >> 4; + int page_state = (*segment >> 2) & 0x3; + //eDebug("pcs with %d bytes data (%d:%d:%d)", segment_length, page_id, page_version_number, page_state); + segment++; + processed_length++; + + //eDebug("page time out: %d", page_time_out); + //eDebug("page_version_number: %d" ,page_version_number); + //eDebug("page_state: %d", page_state); + + if (!page) + { + //eDebug("page not found"); + page = new subtitle_page; + page->page_regions = 0; + page->regions = 0; + page->page_id = page_id; + page->cluts = 0; + page->next = 0; + *ppage = page; + } else + { + if (page->pcs_size != segment_length) + page->page_version_number = -1; + // if no update, just skip this data. + if (page->page_version_number == page_version_number) + { + eDebug("skip data... "); + break; + } + } + +// eDebug("page updated: old: %d, new: %d", page->page_version_number, page_version_number); + // when acquisition point or mode change: remove all displayed pages. + if ((page_state == 1) || (page_state == 2)) + { + while (page->page_regions) + { + struct subtitle_page_region *p = page->page_regions->next; + delete page->page_regions; + page->page_regions = p; + } + } + +// eDebug("new page.. (%d)", page_state); + + page->page_time_out = page_time_out; + + page->page_version_number = page_version_number; + + struct subtitle_page_region **r = &page->page_regions; + + //eDebug("%d / %d data left", processed_length, segment_length); + + // go to last entry + while (*r) + r = &(*r)->next; + + while (processed_length < segment_length) + { + struct subtitle_page_region *pr; + + // append new entry to list + pr = new subtitle_page_region; + pr->next = 0; + *r = pr; + r = &pr->next; + + pr->region_id = *segment++; processed_length++; + segment++; processed_length++; + + pr->region_horizontal_address = *segment++ << 8; + pr->region_horizontal_address |= *segment++; + processed_length += 2; + + pr->region_vertical_address = *segment++ << 8; + pr->region_vertical_address |= *segment++; + processed_length += 2; + + //eDebug("appended active region"); + } + + if (processed_length != segment_length) + eDebug("%d != %d", processed_length, segment_length); + break; + } + case 0x11: // region composition segment + { + int region_id = *segment++; processed_length++; + int region_version_number = *segment >> 4; + int region_fill_flag = (*segment >> 3) & 1; + segment++; processed_length++; + + // if we didn't yet received the pcs for this page, drop the region + if (!page) + { + eDebug("ignoring region %x, since page %02x doesn't yet exist.", region_id, page_id); + break; + } + + struct subtitle_region *region, **pregion; + + region = page->regions; pregion = &page->regions; + + while (region) + { + fflush(stdout); + if (region->region_id == region_id) + break; + pregion = ®ion->next; + region = region->next; + } + + if (!region) + { + *pregion = region = new subtitle_region; + region->next = 0; + } + else if (region->region_version_number != region_version_number) + { + struct subtitle_region_object *objects = region->region_objects; + while (objects) + { + struct subtitle_region_object *n = objects->next; + delete objects; + objects = n; + } + if (region->region_buffer) + { + if (region->region_buffer->surface) + delete region->region_buffer->surface; + region->region_buffer=0; + } + } + else + break; + + //eDebug("region %d:%d update", page_id, region_id); + + region->region_id = region_id; + region->region_version_number = region_version_number; + + region->region_width = *segment++ << 8; + region->region_width |= *segment++; + processed_length += 2; + + region->region_height = *segment++ << 8; + region->region_height |= *segment++; + processed_length += 2; + + region->region_buffer = new gPixmap(eSize(region->region_width, region->region_height), 8); + + int region_level_of_compatibility, region_depth; + + region_level_of_compatibility = (*segment >> 5) & 7; + region_depth = (*segment++ >> 2) & 7; + region->region_depth = (subtitle_region::depth) region_depth; + processed_length++; + + int CLUT_id = *segment++; processed_length++; + + region->clut_id = CLUT_id; + + int region_8bit_pixel_code, region_4bit_pixel_code, region_2bit_pixel_code; + region_8bit_pixel_code = *segment++; processed_length++; + region_4bit_pixel_code = *segment >> 4; + region_2bit_pixel_code = (*segment++ >> 2) & 3; + processed_length++; + + if (!region_fill_flag) + { + region_2bit_pixel_code = region_4bit_pixel_code = region_8bit_pixel_code = 0; + region_fill_flag = 1; + } + + if (region_fill_flag) + { + if (region_depth == 1) + memset(region->region_buffer->surface->data, region_2bit_pixel_code, region->region_height * region->region_width); + else if (region_depth == 2) + memset(region->region_buffer->surface->data, region_4bit_pixel_code, region->region_height * region->region_width); + else if (region_depth == 3) + memset(region->region_buffer->surface->data, region_8bit_pixel_code, region->region_height * region->region_width); + else + eDebug("!!!! invalid depth"); + } + + //eDebug("region %02x, version %d, %dx%d", region->region_id, region->region_version_number, region->region_width, region->region_height); + + region->region_objects = 0; + struct subtitle_region_object **pobject = ®ion->region_objects; + + while (processed_length < segment_length) + { + + struct subtitle_region_object *object; + + object = new subtitle_region_object; + + *pobject = object; + object->next = 0; + pobject = &object->next; + + object->object_id = *segment++ << 8; + object->object_id |= *segment++; processed_length += 2; + + object->object_type = *segment >> 6; + object->object_provider_flag = (*segment >> 4) & 3; + object->object_horizontal_position = (*segment++ & 0xF) << 8; + object->object_horizontal_position |= *segment++; + processed_length += 2; + + object->object_vertical_position = *segment++ << 8; + object->object_vertical_position |= *segment++ ; + processed_length += 2; + + if ((object->object_type == 1) || (object->object_type == 2)) + { + object->foreground_pixel_value = *segment++; + object->background_pixel_value = *segment++; + processed_length += 2; + } + } + + if (processed_length != segment_length) + eDebug("too less data! (%d < %d)", segment_length, processed_length); + + break; + } + case 0x12: // CLUT definition segment + { + int CLUT_id, CLUT_version_number; + struct subtitle_clut *clut, **pclut; + + if (!page) + break; + + //eDebug("CLUT: %02x", *segment); + CLUT_id = *segment++; + + CLUT_version_number = *segment++ >> 4; + processed_length += 2; + + //eDebug("page %d, CLUT %02x, version %d", page->page_id, CLUT_id, CLUT_version_number); + + clut = page->cluts; pclut = &page->cluts; + + while (clut) + { + if (clut->clut_id == CLUT_id) + break; + pclut = &clut->next; + clut = clut->next; + } + + if (!clut) + { + *pclut = clut = new subtitle_clut; + clut->next = 0; + clut->clut_id = CLUT_id; + } + else if (clut->CLUT_version_number == CLUT_version_number) + break; + + clut->CLUT_version_number=CLUT_version_number; + clut->size_2 = clut->size_4 = clut->size_8 = 0; + + /* invalidate CLUT if updated. */ + if ((this->current_clut_page_id == page_id) && (this->current_clut_id == CLUT_id)) + this->current_clut_id = -1; + + //eDebug("new clut"); + while (processed_length < segment_length) + { + int CLUT_entry_id, entry_CLUT_flag, full_range; + int v_Y, v_Cr, v_Cb, v_T; + + CLUT_entry_id = *segment++; + full_range = *segment & 1; + entry_CLUT_flag = (*segment++ & 0xE0) >> 5; + processed_length += 2; + + if (full_range) + { + v_Y = *segment++; + v_Cr = *segment++; + v_Cb = *segment++; + v_T = *segment++; + processed_length += 4; + } else + { + v_Y = *segment & 0xFC; + v_Cr = (*segment++ & 3) << 6; + v_Cr |= (*segment & 0xC0) >> 2; + v_Cb = (*segment & 0x3C) << 2; + v_T = (*segment++ & 3) << 6; + processed_length += 2; + } + + if (entry_CLUT_flag & 1) // 8bit + { + ASSERT(CLUT_entry_id < 256); + ++clut->size_8; + clut->entries_8bit[CLUT_entry_id].Y = v_Y; + clut->entries_8bit[CLUT_entry_id].Cr = v_Cr; + clut->entries_8bit[CLUT_entry_id].Cb = v_Cb; + clut->entries_8bit[CLUT_entry_id].T = v_T; + } + if (entry_CLUT_flag & 2) // 4bit + { + ASSERT(CLUT_entry_id < 16); + ++clut->size_4; + clut->entries_4bit[CLUT_entry_id].Y = v_Y; + clut->entries_4bit[CLUT_entry_id].Cr = v_Cr; + clut->entries_4bit[CLUT_entry_id].Cb = v_Cb; + clut->entries_4bit[CLUT_entry_id].T = v_T; + } + if (entry_CLUT_flag & 4) // 2bit + { + ASSERT(CLUT_entry_id < 4); + ++clut->size_2; + clut->entries_2bit[CLUT_entry_id].Y = v_Y; + clut->entries_2bit[CLUT_entry_id].Cr = v_Cr; + clut->entries_2bit[CLUT_entry_id].Cb = v_Cb; + clut->entries_2bit[CLUT_entry_id].T = v_T; + } + //eDebug(" %04x %02x %02x %02x %02x", CLUT_entry_id, v_Y, v_Cb, v_Cr, v_T); + } + break; + } + case 0x13: // object data segment + { + int object_id, object_version_number, object_coding_method, non_modifying_color_flag; + + object_id = *segment++ << 8; + object_id |= *segment++; + processed_length += 2; + + object_version_number = *segment >> 4; + object_coding_method = (*segment >> 2) & 3; + non_modifying_color_flag = (*segment++ >> 1) & 1; + processed_length++; + + //eDebug("object id %04x, version %d, object_coding_method %d (page_id %d)", object_id, object_version_number, object_coding_method, page_id); + + if (object_coding_method == 0) + { + int top_field_data_blocklength, bottom_field_data_blocklength; + int i, line, linep; + + top_field_data_blocklength = *segment++ << 8; + top_field_data_blocklength |= *segment++; + + bottom_field_data_blocklength = *segment++ << 8; + bottom_field_data_blocklength |= *segment++; + //eDebug("%d / %d bytes", top_field_data_blocklength, bottom_field_data_blocklength); + processed_length += 4; + + i = 0; + line = 0; + linep = 0; + while (i < top_field_data_blocklength) + { + int len; + len = subtitle_process_pixel_data(page, object_id, &line, &linep, segment); + if (len < 0) + return -1; + segment += len; + processed_length += len; + i += len; + } + + line = 1; + linep = 0; + + if (bottom_field_data_blocklength) + { + i = 0; + while (i < bottom_field_data_blocklength) + { + int len; + len = subtitle_process_pixel_data(page, object_id, &line, &linep, segment); + if (len < 0) + return -1; + segment += len; + processed_length += len; + i += len; + } + } + else if (top_field_data_blocklength) + eDebug("!!!! unimplemented: no bottom field! (%d : %d)", top_field_data_blocklength, bottom_field_data_blocklength); + + if ((top_field_data_blocklength + bottom_field_data_blocklength) & 1) + { + segment++; processed_length++; + } + } + else if (object_coding_method == 1) + eDebug("---- object_coding_method 1 unsupported!"); + + break; + } + case 0x80: // end of display set segment + { +// eDebug("end of display set segment"); + subtitle_redraw_all(); + } + case 0xFF: // stuffing + break; + default: + eDebug("unhandled segment type %02x", segment_type); + } + + return segment_length + 6; +} + +void eDVBSubtitleParser::subtitle_process_pes(const __u8 *pkt, int len) +{ + if (!extract_pts(show_time, pkt)) + { + pkt += 6; len -= 6; + // skip PES header + pkt++; len--; + pkt++; len--; + + int hdr_len = *pkt++; len--; + + pkt+=hdr_len; len-=hdr_len; + + if (*pkt != 0x20) + { + //eDebug("data identifier is 0x%02x, but not 0x20", *pkt); + return; + } + pkt++; len--; // data identifier + *pkt++; len--; // stream id; + + if (len <= 0) + { + //eDebug("no data left (%d)", len); + return; + } + + while (len && *pkt == 0x0F) + { + int l = subtitle_process_segment(pkt); + if (l < 0) + break; + pkt += l; + len -= l; + } + // if (len && *pkt != 0xFF) + // eDebug("strange data at the end"); + } + else + eDebug("dvb subtitle packet without PTS.. ignore!!"); +} + +void eDVBSubtitleParser::subtitle_clear_screen() +{ + /* clear bbox */ + int y; + + //eDebug("BBOX clear %d:%d -> %d:%d", this->bbox_left, this->bbox_top, this->bbox_right, this->bbox_bottom); + + // do not draw when anyone has locked the + // framebuffer ( non enigma plugins... ) + this->bbox_right = 720; + if (this->bbox_right > this->bbox_left) + for (y=this->bbox_top; y < this->bbox_bottom; ++y) + ; // TODO fixmee clear subtitle screen + + this->bbox_right = 0; + this->bbox_left = this->screen_width; + this->bbox_top = this->screen_height; + this->bbox_bottom = 0; +} + +void eDVBSubtitleParser::subtitle_redraw_all() +{ +#if 1 + struct subtitle_page *page = this->pages; + if ( page ) + { + struct subtitle_page_region *region = page->page_regions; + if ( region ) + subtitle_clear_screen(); + } + while(page) + { + subtitle_redraw(page->page_id); + page = page->next; + } +#else + subtitle_clear_screen(); + + struct subtitle_page *page = this->pages; + //eDebug("----------- end of display set"); + //eDebug("active pages:"); + while (page) + { + //eDebug(" page_id %02x", page->page_id); + //eDebug(" page_version_number: %d", page->page_version_number); + //eDebug(" active regions:"); + { + struct subtitle_page_region *region = page->page_regions; + while (region) + { + //eDebug(" region_id: %04x", region->region_id); + //eDebug(" region_horizontal_address: %d", region->region_horizontal_address); + //eDebug(" region_vertical_address: %d", region->region_vertical_address); + + region = region->next; + } + } + + subtitle_redraw(page->page_id); + //eDebug("defined regions:"); + struct subtitle_region *region = page->regions; + while (region) + { + //eDebug(" region_id %04x, version %d, %dx%d", region->region_id, region->region_version_number, region->region_width, region->region_height); + + struct subtitle_region_object *object = region->region_objects; + while (object) + { + //eDebug(" object %02x, type %d, %d:%d", object->object_id, object->object_type, object->object_horizontal_position, object->object_vertical_position); + object = object->next; + } + region = region->next; + } + page = page->next; + } +#endif +} + +void eDVBSubtitleParser::subtitle_reset() +{ + while (struct subtitle_page *page = this->pages) + { + /* free page regions */ + while (page->page_regions) + { + struct subtitle_page_region *p = page->page_regions->next; + delete page->page_regions; + page->page_regions = p; + } + /* free regions */ + while (page->regions) + { + struct subtitle_region *region = page->regions; + + while (region->region_objects) + { + struct subtitle_region_object *obj = region->region_objects; + region->region_objects = obj->next; + delete obj; + } + + if (region->region_buffer) + { + if (region->region_buffer->surface) + delete region->region_buffer->surface; + region->region_buffer=0; + } + + page->regions = region->next; + delete region; + } + + /* free CLUTs */ + while (page->cluts) + { + struct subtitle_clut *clut = page->cluts; + page->cluts = clut->next; + delete clut; + } + + this->pages = page->next; + delete page; + } +} + +void eDVBSubtitleParser::subtitle_redraw(int page_id) +{ + struct subtitle_page *page = this->pages; + + //eDebug("displaying page id %d", page_id); + + while (page) + { + if (page->page_id == page_id) + break; + page = page->next; + } + if (!page) + { + //eDebug("page not found"); + return; + } + + + //eDebug("iterating regions.."); + /* iterate all regions in this pcs */ + struct subtitle_page_region *region = page->page_regions; + while (region) + { + //eDebug("region %d", region->region_id); + /* find corresponding region */ + struct subtitle_region *reg = page->regions; + while (reg) + { + if (reg->region_id == region->region_id) + break; + reg = reg->next; + } + if (reg) + { + int y; + //eDebug("copy region %d to %d, %d", region->region_id, region->region_horizontal_address, region->region_vertical_address); + + int x0 = region->region_horizontal_address; + int y0 = region->region_vertical_address; + int x1 = x0 + reg->region_width; + int y1 = y0 + reg->region_height; + + if ((x0 < 0) || (y0 < 0) || (x0 > this->screen_width) || (x0 > this->screen_height)) + continue; + + /* adjust bbox */ + if (x0 < this->bbox_left) + this->bbox_left = x0; + if (y0 < this->bbox_top) + this->bbox_top = y0; + if (x1 > this->bbox_right) + this->bbox_right = x1; + if (y1 > this->bbox_bottom) + this->bbox_bottom = y1; + + int timeout = page->page_time_out; + + /* find corresponding clut */ + struct subtitle_clut *clut = page->cluts; + while (clut) + { + //eDebug("have %d, want %d", clut->clut_id, main_clut_id); + if (clut->clut_id == reg->clut_id) + break; + clut = clut->next; + } + if (clut) + { + // TODO fill region->surface->clut !!!!! + } + else + { + // apply default clut depending on region->region_depth + // TODO fill region->surface->clut !!!!! + } + // TODO Blit Region Pixmap !!! + } + else + eDebug("region not found"); + region = region->next; + } + //eDebug("schon gut."); +} + +DEFINE_REF(eDVBSubtitleParser); + +eDVBSubtitleParser::eDVBSubtitleParser(iDVBDemux *demux) +{ + setStreamID(0xBD); + + if (demux->createPESReader(eApp, m_pes_reader)) + eDebug("failed to create dvb subtitle PES reader!"); + else + m_pes_reader->connectRead(slot(*this, &eDVBSubtitleParser::subtitle_process_pes), m_read_connection); +} + +eDVBSubtitleParser::~eDVBSubtitleParser() +{ + subtitle_reset(); +} diff --git a/lib/dvb/subtitle.h b/lib/dvb/subtitle.h new file mode 100644 index 00000000..0d364f8a --- /dev/null +++ b/lib/dvb/subtitle.h @@ -0,0 +1,122 @@ +#ifndef __lib_dvb_subtitle_h +#define __lib_dvb_subtitle_h + +#include +#include +#include +#include + +typedef unsigned char __u8; + +struct subtitle_clut_entry +{ + __u8 Y, Cr, Cb, T; +}; + +struct subtitle_clut +{ + unsigned char clut_id; + unsigned char size_2, size_4, size_8; + unsigned char CLUT_version_number; + struct subtitle_clut_entry entries_2bit[4]; + struct subtitle_clut_entry entries_4bit[16]; + struct subtitle_clut_entry entries_8bit[256]; + struct subtitle_clut *next; +}; + +struct subtitle_page_region +{ + int region_id; + int region_horizontal_address; + int region_vertical_address; + struct subtitle_page_region *next; +}; + +struct subtitle_region_object +{ + int object_id; + int object_type; + int object_provider_flag; + + int object_horizontal_position; + int object_vertical_position; + + // not supported right now... + int foreground_pixel_value; + int background_pixel_value; + + struct subtitle_region_object *next; +}; + +struct subtitle_region +{ + int region_id; + int region_version_number; + int region_height, region_width; + enum depth { bpp2=1, bpp4=2, bpp8=3 } region_depth; + ePtr region_buffer; + + int clut_id; + + struct subtitle_region_object *region_objects; + + struct subtitle_region *next; +}; + +struct subtitle_page +{ + int page_id; + time_t page_time_out; + int page_version_number; + int pcs_size; + struct subtitle_page_region *page_regions; + + struct subtitle_region *regions; + + struct subtitle_clut *cluts; + + struct subtitle_page *next; +}; + +struct bitstream +{ + __u8 *data; + int size; + int avail; + int consumed; +}; + +struct eDVBSubtitleRegion +{ + pts_t show_time; + int timeout; + ePtr region; +}; + +class eDVBSubtitleParser + :public iObject, public ePESParser, public Object +{ + DECLARE_REF(eDVBSubtitleParser); + struct subtitle_page *pages; + int current_clut_id, current_clut_page_id; + int screen_width, screen_height; + int bbox_left, bbox_top, bbox_right, bbox_bottom; + ePtr m_pes_reader; + ePtr m_read_connection; + pts_t show_time; +public: + eDVBSubtitleParser(iDVBDemux *demux); + virtual ~eDVBSubtitleParser(); + int start(int pid); +private: + void subtitle_process_line(struct subtitle_page *page, int object_id, int line, const __u8 *data, int len); + int subtitle_process_pixel_data(struct subtitle_page *page, int object_id, int *linenr, int *linep, const __u8 *data); + int subtitle_process_segment(const __u8 *segment); + void subtitle_process_pes(const __u8 *buffer, int len); + void subtitle_clear_screen(); + void subtitle_redraw_all(); + void subtitle_reset(); + void subtitle_redraw(int page_id); +}; + +#endif diff --git a/lib/dvb/teletext.cpp b/lib/dvb/teletext.cpp index a8c9bdae..2a44f4f9 100644 --- a/lib/dvb/teletext.cpp +++ b/lib/dvb/teletext.cpp @@ -73,7 +73,7 @@ eDVBTeletextParser::eDVBTeletextParser(iDVBDemux *demux) setPage(-1); if (demux->createPESReader(eApp, m_pes_reader)) - eDebug("failed to create PES reader!"); + eDebug("failed to create teletext subtitle PES reader!"); else m_pes_reader->connectRead(slot(*this, &eDVBTeletextParser::processData), m_read_connection); } diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 2cf5b6ea..b63420ae 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -2075,6 +2075,14 @@ void eDVBServicePlay::checkSubtitleTiming() } } +void eDVBServicePlay::newDVBSubtitleRegion(const eDVBSubtitleRegion &p) +{ +} + +void eDVBServicePlay::checkDvbSubtitleTiming() +{ +} + int eDVBServicePlay::getAC3Delay() { if (m_dvb_service) diff --git a/lib/service/servicedvb.h b/lib/service/servicedvb.h index b2306e10..3f133d69 100644 --- a/lib/service/servicedvb.h +++ b/lib/service/servicedvb.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -238,19 +239,25 @@ private: void cutlistToCuesheet(); + eSubtitleWidget *m_subtitle_widget; + /* teletext subtitles */ + ePtr m_teletext_parser; void newSubtitlePage(const eDVBTeletextSubtitlePage &p); ePtr m_new_subtitle_page_connection; - - ePtr m_teletext_parser; - ePtr m_radiotext_parser; - eSubtitleWidget *m_subtitle_widget; eTimer m_subtitle_sync_timer; std::list m_subtitle_pages; - void checkSubtitleTiming(); + + /* dvb subtitles */ + void newDVBSubtitleRegion(const eDVBSubtitleRegion &p); + ePtr m_new_dvb_subtitle_region_connection; + eTimer m_dvb_subtitle_sync_timer; + std::list m_dvb_subtitle_regions; + void checkDvbSubtitleTiming(); /* radiotext */ + ePtr m_radiotext_parser; ePtr m_radiotext_updated_connection; void radioTextUpdated(); };