From: Fraxinas Date: Wed, 26 Jan 2011 14:31:09 +0000 (+0100) Subject: Merge branch 'bug_241_EOFrestart' into experimental X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=commitdiff_plain;h=69c8b669b814af168ed5bbedd60bdf2909274f12;hp=7fb088ec04db7eb68ff7294393f53ba33f533655 Merge branch 'bug_241_EOFrestart' into experimental --- diff --git a/README b/README index f745e9c..0018e58 100644 --- a/README +++ b/README @@ -17,6 +17,7 @@ autoconf automake build-essential gettext +libdvdnav-dev libfreetype6-dev libfribidi-dev libgif-dev @@ -48,7 +49,15 @@ dpkg-buildpackage -uc -us cd .. sudo dpkg -i libxmlccwrap*.deb -4.) Build and install enigma2: +4.) Build and install libdreamdvd: + +git clone git://schwerkraft.elitedvb.net/libdreamdvd/libdreamdvd.git +cd libdreamdvd +dpkg-buildpackage -uc -us +cd .. +sudo dpkg -i libdreamdvd*.deb + +5.) Build and install enigma2: git clone git://git.opendreambox.org/git/enigma2.git cd enigma2 diff --git a/RecordTimer.py b/RecordTimer.py index 4ece9c5..1cb7eb3 100755 --- a/RecordTimer.py +++ b/RecordTimer.py @@ -517,7 +517,7 @@ class RecordTimer(timer.Timer): checkit = True for timer in root.findall("timer"): newTimer = createTimer(timer) - if (self.record(newTimer, True, True) is not None) and (checkit == True): + if (self.record(newTimer, True, dosave=False) is not None) and (checkit == True): from Tools.Notifications import AddPopup from Screens.MessageBox import MessageBox AddPopup(_("Timer overlap in timers.xml detected!\nPlease recheck it!"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed") diff --git a/data/keymap.xml b/data/keymap.xml index 9461d50..f986b62 100755 --- a/data/keymap.xml +++ b/data/keymap.xml @@ -174,10 +174,12 @@ + + @@ -370,6 +372,7 @@ + @@ -463,6 +466,7 @@ + @@ -643,6 +647,7 @@ + diff --git a/data/setup.xml b/data/setup.xml index f5dea73..c5eb07f 100755 --- a/data/setup.xml +++ b/data/setup.xml @@ -47,27 +47,10 @@ config.seek.speeds_forward config.seek.speeds_backward config.seek.speeds_slowmotion - - config.seek.enter_forward - config.seek.enter_backward - - config.seek.stepwise_minspeed - config.seek.stepwise_repeat + + config.seek.enter_forward + config.seek.enter_backward config.seek.on_pause config.usage.pip_zero_button config.usage.alternatives_priority diff --git a/lib/driver/rcsdl.cpp b/lib/driver/rcsdl.cpp index a907b80..145b23c 100644 --- a/lib/driver/rcsdl.cpp +++ b/lib/driver/rcsdl.cpp @@ -33,6 +33,10 @@ void eSDLInputDevice::handleCode(long arg) if (km == eRCInput::kmNone) { code = translateKey(key->sym); } else { + // ASCII keys should only generate key press events + if (flags == eRCKey::flagBreak) + return; + eDebug("unicode=%04x scancode=%02x", m_unicode, key->scancode); if (m_unicode & 0xff80) { eDebug("SDL: skipping unicode character"); diff --git a/lib/dvb/decoder.cpp b/lib/dvb/decoder.cpp index 88cd3ee..8ed9f43 100644 --- a/lib/dvb/decoder.cpp +++ b/lib/dvb/decoder.cpp @@ -203,6 +203,9 @@ int eDVBAudio::startPid(int pid, int type) case aLPCM: bypass = 6; break; + case aDTSHD: + bypass = 0x10; + break; } eDebugNoNewLine("AUDIO_SET_BYPASS(%d) - ", bypass); diff --git a/lib/dvb/decoder.h b/lib/dvb/decoder.h index 3a0fbac..7610b65 100644 --- a/lib/dvb/decoder.h +++ b/lib/dvb/decoder.h @@ -13,7 +13,7 @@ private: ePtr m_demux; int m_fd, m_fd_demux, m_dev, m_is_freezed; public: - enum { aMPEG, aAC3, aDTS, aAAC, aAACHE, aLPCM }; + enum { aMPEG, aAC3, aDTS, aAAC, aAACHE, aLPCM, aDTSHD }; eDVBAudio(eDVBDemux *demux, int dev); enum { aMonoLeft, aStereo, aMonoRight }; void setChannel(int channel); diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 414ab8d..cd6c232 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -108,7 +108,7 @@ eDVBResourceManager::eDVBResourceManager() m_boxtype = DM8000; } - eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d", + eDebug("found %zd adapter, %zd frontends(%zd sim) and %zd demux, boxtype %d", m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype); eDVBCAService::registerChannelCallback(this); @@ -334,7 +334,7 @@ PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list) } if (assigned != m_frontend.size()) { char blasel[256]; - sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations .. assigned %d socket informations, but %d registered frontends!", + sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations .. assigned %zd socket informations, but %d registered frontends!", m_frontend.size(), assigned); PyErr_SetString(PyExc_StandardError, blasel); return NULL; @@ -1488,7 +1488,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off size = max; else size = aligned_end - current_offset; - eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size); + eDebug("HIT, %lld < %lld < %lld, size: %zd", i->first, current_offset, i->second, size); return; } if (current_offset < aligned_start) @@ -1529,10 +1529,10 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off len = aligned_end - aligned_start; start = aligned_end - len; - eDebug("skipping to %llx, %d", start, len); + eDebug("skipping to %llx, %zd", start, len); } - eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end); + eDebug("result: %llx, %zx (%llx %llx)", start, size, aligned_start, aligned_end); return; } } @@ -1548,7 +1548,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off { start = current_offset; size = max; - eDebug("NO CUESHEET. (%08llx, %d)", start, size); + eDebug("NO CUESHEET. (%08llx, %zd)", start, size); } else { start = current_offset; diff --git a/lib/dvb/epgcache.cpp b/lib/dvb/epgcache.cpp index ed31903..4d32474 100644 --- a/lib/dvb/epgcache.cpp +++ b/lib/dvb/epgcache.cpp @@ -366,6 +366,8 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) messages.send(Message(Message::startChannel, chan)); // -> gotMessage -> changedService } + else + data.state=-1; } } } @@ -1187,7 +1189,7 @@ void eEPGCache::save() eEPGCache::channel_data::channel_data(eEPGCache *ml) :cache(ml) - ,abortTimer(eTimer::create(ml)), zapTimer(eTimer::create(ml)), state(-1) + ,abortTimer(eTimer::create(ml)), zapTimer(eTimer::create(ml)), state(-2) ,isRunning(0), haveData(0) #ifdef ENABLE_PRIVATE_EPG ,startPrivateTimer(eTimer::create(ml)) diff --git a/lib/dvb/esection.h b/lib/dvb/esection.h index 2bb17a9..3e097cc 100644 --- a/lib/dvb/esection.h +++ b/lib/dvb/esection.h @@ -63,7 +63,7 @@ protected: else TABLE_eDebugNoNewLine("-"); - TABLE_eDebug(" %d/%d TID %02x", avail.size(), max, data[0]); + TABLE_eDebug(" %zd/%d TID %02x", avail.size(), max, data[0]); if (avail.size() == max) { @@ -100,6 +100,10 @@ class eAUTable: public eAUGTable int first; ePtr m_demux; eMainloop *ml; + + /* needed to detect broken table version handling (seen on some m2ts files) */ + struct timespec m_prev_table_update; + int m_table_cnt; public: eAUTable() @@ -119,6 +123,7 @@ public: int begin(eMainloop *m, const eDVBTableSpec &spec, ePtr demux) { + m_table_cnt = 0; ml = m; m_demux = demux; first= 1; @@ -197,6 +202,24 @@ public: if (current && (!current->getSpec(spec))) { + /* detect broken table version handling (seen on some m2ts files) */ + if (m_table_cnt) + { + if (abs(timeout_usec(m_prev_table_update)) > 500000) + m_table_cnt = -1; + else if (m_table_cnt > 1) // two pmt update within one second + { + eDebug("Seen two consecutive table version changes within 500ms. " + "This seems broken, so auto update for pid %04x, table %02x is now disabled!!", + spec.pid, spec.tid); + m_table_cnt = 0; + return; + } + } + + ++m_table_cnt; + clock_gettime(CLOCK_MONOTONIC, &m_prev_table_update); + next = new Table(); CONNECT(next->tableReady, eAUTable::slotTableReady); spec.flags &= ~(eDVBTableSpec::tfAnyVersion|eDVBTableSpec::tfThisVersion|eDVBTableSpec::tfHaveTimeout); diff --git a/lib/dvb/idvb.h b/lib/dvb/idvb.h index f15cd04..e56a2c7 100644 --- a/lib/dvb/idvb.h +++ b/lib/dvb/idvb.h @@ -650,7 +650,7 @@ public: /** Set Displayed Video PID and type */ virtual RESULT setVideoPID(int vpid, int type)=0; - enum { af_MPEG, af_AC3, af_DTS, af_AAC }; + enum { af_MPEG, af_AC3, af_DTS, af_AAC, af_DTSHD }; /** Set Displayed Audio PID and type */ virtual RESULT setAudioPID(int apid, int type)=0; diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index ca56141..4ad4e76 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -20,13 +20,14 @@ #include eDVBServicePMTHandler::eDVBServicePMTHandler() - :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF) + :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF), m_no_pat_entry_delay(eTimer::create()) { m_use_decode_demux = 0; m_pmt_pid = -1; eDVBResourceManager::getInstance(m_resourceManager); CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); + CONNECT(m_no_pat_entry_delay->timeout, eDVBServicePMTHandler::sendEventNoPatEntry); } eDVBServicePMTHandler::~eDVBServicePMTHandler() @@ -126,25 +127,55 @@ void eDVBServicePMTHandler::PMTready(int error) } } +void eDVBServicePMTHandler::sendEventNoPatEntry() +{ + serviceEvent(eventNoPATEntry); +} + void eDVBServicePMTHandler::PATready(int) { + eDebug("PATready"); ePtr > ptr; if (!m_PAT.getCurrent(ptr)) { + int service_id_single = -1; + int pmtpid_single = -1; int pmtpid = -1; + int cnt=0; std::vector::const_iterator i; for (i = ptr->getSections().begin(); pmtpid == -1 && i != ptr->getSections().end(); ++i) { const ProgramAssociationSection &pat = **i; ProgramAssociationConstIterator program; for (program = pat.getPrograms()->begin(); pmtpid == -1 && program != pat.getPrograms()->end(); ++program) + { + ++cnt; if (eServiceID((*program)->getProgramNumber()) == m_reference.getServiceID()) pmtpid = (*program)->getProgramMapPid(); + if (++cnt == 1 && pmtpid_single == -1 && pmtpid == -1) + { + pmtpid_single = (*program)->getProgramMapPid(); + service_id_single = (*program)->getProgramNumber(); + } + else + pmtpid_single = service_id_single = -1; + } } - if (pmtpid == -1) - serviceEvent(eventNoPATEntry); - else + if (pmtpid_single != -1) // only one PAT entry .. and not valid pmtpid found + { + eDebug("use single pat entry!"); + m_reference.setServiceID(eServiceID(service_id_single)); + pmtpid = pmtpid_single; + } + if (pmtpid == -1) { + eDebug("no PAT entry found.. start delay"); + m_no_pat_entry_delay->start(1000, true); + } + else { + eDebug("use pmtpid %04x for service_id %04x", pmtpid, m_reference.getServiceID().get()); + m_no_pat_entry_delay->stop(); m_PMT.begin(eApp, eDVBPMTSpec(pmtpid, m_reference.getServiceID().get()), m_demux); + } } else serviceEvent(eventNoPAT); } @@ -230,8 +261,29 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) { const ProgramMapSection &pmt = **i; + int is_hdmv = 0; + program.pcrPid = pmt.getPcrPid(); + for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); + desc != pmt.getDescriptors()->end(); ++desc) + { + if ((*desc)->getTag() == CA_DESCRIPTOR) + { + CaDescriptor *descr = (CaDescriptor*)(*desc); + program::capid_pair pair; + pair.caid = descr->getCaSystemId(); + pair.capid = descr->getCaPid(); + program.caids.push_back(pair); + } + else if ((*desc)->getTag() == REGISTRATION_DESCRIPTOR) + { + RegistrationDescriptor *d = (RegistrationDescriptor*)(*desc); + if (d->getFormatIdentifier() == 0x48444d56) // HDMV + is_hdmv = 1; + } + } + ElementaryStreamInfoConstIterator es; for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) { @@ -287,25 +339,34 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) audio.type = audioStream::atAACHE; forced_audio = 1; } - case 0x80: // user private ... but blueray LPCM - if (!isvideo && !isaudio) + case 0x80: // user private ... but bluray LPCM + case 0xA0: // bluray secondary LPCM + if (!isvideo && !isaudio && is_hdmv) { isaudio = 1; audio.type = audioStream::atLPCM; } - case 0x81: // user private ... but blueray AC3 - if (!isvideo && !isaudio) + case 0x81: // user private ... but bluray AC3 + case 0xA1: // bluray secondary AC3 + if (!isvideo && !isaudio && is_hdmv) { isaudio = 1; audio.type = audioStream::atAC3; } - case 0x82: // Blueray DTS (dvb user private...) - case 0xA2: // Blueray secondary DTS - if (!isvideo && !isaudio) + case 0x82: // bluray DTS (dvb user private...) + case 0xA2: // bluray secondary DTS + if (!isvideo && !isaudio && is_hdmv) { isaudio = 1; audio.type = audioStream::atDTS; } + case 0x86: // bluray DTS-HD (dvb user private...) + case 0xA6: // bluray secondary DTS-HD + if (!isvideo && !isaudio && is_hdmv) + { + isaudio = 1; + audio.type = audioStream::atDTSHD; + } case 0x06: // PES Private case 0xEA: // TS_PSI_ST_SMPTE_VC1 { @@ -497,9 +558,9 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) default: break; } - if (isteletext && (isaudio || isvideo)) + if (isteletext && (isaudio || isvideo)) { - eDebug("ambiguous streamtype for PID %04x detected.. forced as teletext!", (*es)->getPid()); + eDebug("ambiguous streamtype for PID %04x detected.. forced as teletext!", (*es)->getPid()); continue; // continue with next PID } else if (issubtitle && (isaudio || isvideo)) @@ -537,18 +598,6 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) else continue; } - for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); - desc != pmt.getDescriptors()->end(); ++desc) - { - if ((*desc)->getTag() == CA_DESCRIPTOR) - { - CaDescriptor *descr = (CaDescriptor*)(*desc); - program::capid_pair pair; - pair.caid = descr->getCaSystemId(); - pair.capid = descr->getCaPid(); - program.caids.push_back(pair); - } - } } ret = 0; @@ -710,8 +759,8 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem { RESULT res=0; m_reference = ref; - m_use_decode_demux = use_decode_demux; + m_no_pat_entry_delay->stop(); /* use given service as backup. This is used for timeshift where we want to clone the live stream using the cache, but in fact have a PVR channel */ m_service = service; @@ -735,13 +784,12 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem { if (!ref.getServiceID().get() /* incorrect sid in meta file or recordings.epl*/ ) { - eWarning("no .meta file found, trying to find PMT pid"); eDVBTSTools tstools; + bool b = source || !tstools.openFile(ref.path.c_str(), 1); + eWarning("no .meta file found, trying to find PMT pid"); if (source) - tstools.setSource(source, streaminfo_file ? streaminfo_file : ref.path.c_str()); - else if (tstools.openFile(ref.path.c_str())) - eWarning("failed to open file"); - else + tstools.setSource(source, NULL); + if (b) { int service_id, pmt_pid; if (!tstools.findPMT(pmt_pid, service_id)) @@ -751,6 +799,8 @@ int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_dem m_pmt_pid = pmt_pid; } } + else + eWarning("no valid source to find PMT pid!"); } eDebug("alloc PVR"); /* allocate PVR */ diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index 86b634f..1888e05 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -102,6 +102,7 @@ class eDVBServicePMTHandler: public Object int m_use_decode_demux; uint8_t m_decode_demux_num; + ePtr m_no_pat_entry_delay; public: eDVBServicePMTHandler(); ~eDVBServicePMTHandler(); @@ -144,7 +145,7 @@ public: { int pid, rdsPid; // hack for some radio services which transmit radiotext on different pid (i.e. harmony fm, HIT RADIO FFH, ...) - enum { atMPEG, atAC3, atDTS, atAAC, atAACHE, atLPCM }; + enum { atMPEG, atAC3, atDTS, atAAC, atAACHE, atLPCM, atDTSHD }; int type; // mpeg2, ac3, dts, ... int component_tag; @@ -210,6 +211,7 @@ public: int getPMT(ePtr > &ptr) { return m_PMT.getCurrent(ptr); } int getChannel(eUsePtr &channel); void resetCachedProgram() { m_have_cached_program = false; } + void sendEventNoPatEntry(); /* deprecated interface */ int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); diff --git a/lib/dvb/pvrparse.cpp b/lib/dvb/pvrparse.cpp index 5cdecbd..e19dd1e 100644 --- a/lib/dvb/pvrparse.cpp +++ b/lib/dvb/pvrparse.cpp @@ -123,7 +123,7 @@ void eMPEGStreamInformation::fixupDiscontinuties() pts_t current = i->second - currentDelta; pts_t diff = current - lastpts_t; - if (llabs(diff) > (90000*5)) // 5sec diff + if (llabs(diff) > (90000*10)) // 10sec diff { // eDebug("%llx < %llx, have discont. new timestamp is %llx (diff is %llx)!", current, lastpts_t, i->second, diff); currentDelta = i->second - lastpts_t; /* FIXME: should be the extrapolated new timestamp, based on the current rate */ diff --git a/lib/dvb/scan.cpp b/lib/dvb/scan.cpp index d559614..fb6f204 100644 --- a/lib/dvb/scan.cpp +++ b/lib/dvb/scan.cpp @@ -193,9 +193,9 @@ RESULT eDVBScan::nextChannel() if (m_ch_toScan.empty()) { SCAN_eDebug("no channels left to scan."); - SCAN_eDebug("%d channels scanned, %d were unavailable.", + SCAN_eDebug("%zd channels scanned, %zd were unavailable.", m_ch_scanned.size(), m_ch_unavailable.size()); - SCAN_eDebug("%d channels in database.", m_new_channels.size()); + SCAN_eDebug("%zd channels in database.", m_new_channels.size()); m_event(evtFinish); return -ENOENT; } diff --git a/lib/dvb/tstools.cpp b/lib/dvb/tstools.cpp index 1403059..6cd855c 100644 --- a/lib/dvb/tstools.cpp +++ b/lib/dvb/tstools.cpp @@ -212,6 +212,8 @@ int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed) break; case 0x71: // AC3 / DTS break; + case 0x72: // DTS - HD + break; default: eDebug("skip unknwn stream_id_extension %02x\n", payload[9+offs]); continue; @@ -700,9 +702,26 @@ int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int fram else if (direction == +1) direction = 0; } - /* let's find the next frame after the given offset */ off_t start = offset; +#if 0 + /* backtrack to find the previous sequence start, in case of MPEG2 */ + if ((data & 0xFF) == 0x00) { + do { + --start; + if (m_streaminfo.getStructureEntry(start, data, 0)) + { + eDebug("get previous failed"); + return -1; + } + } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00) && ((data & 0xFF) != 0xB3)); /* sequence start or previous frame */ + if ((data & 0xFF) != 0xB3) + start = offset; /* Failed to find corresponding sequence start, so never mind */ + } + +#endif + + /* let's find the next frame after the given offset */ do { if (m_streaminfo.getStructureEntry(offset, data, 1)) { @@ -717,9 +736,11 @@ int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int fram // eDebug("%08llx@%llx (next)", data, offset); } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */ +#if 0 /* align to TS pkt start */ -// start = start - (start % 188); -// offset = offset - (offset % 188); + start = start - (start % 188); + offset = offset - (offset % 188); +#endif len = offset - start; _offset = start; diff --git a/lib/dvb_ci/dvbci.cpp b/lib/dvb_ci/dvbci.cpp index 8a43e5b..374672a 100644 --- a/lib/dvb_ci/dvbci.cpp +++ b/lib/dvb_ci/dvbci.cpp @@ -739,7 +739,7 @@ PyObject *eDVBCIInterfaces::getDescrambleRules(int slotid) if (!slot) { char tmp[255]; - snprintf(tmp, 255, "eDVBCIInterfaces::getDescrambleRules try to get rules for CI Slot %d... but just %d slots are available", slotid, m_slots.size()); + snprintf(tmp, 255, "eDVBCIInterfaces::getDescrambleRules try to get rules for CI Slot %d... but just %zd slots are available", slotid, m_slots.size()); PyErr_SetString(PyExc_StandardError, tmp); return 0; } @@ -791,7 +791,7 @@ RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject) if (!slot) { char tmp[255]; - snprintf(tmp, 255, "eDVBCIInterfaces::setDescrambleRules try to set rules for CI Slot %d... but just %d slots are available", slotid, m_slots.size()); + snprintf(tmp, 255, "eDVBCIInterfaces::setDescrambleRules try to set rules for CI Slot %d... but just %zd slots are available", slotid, m_slots.size()); PyErr_SetString(PyExc_StandardError, tmp); return -1; } @@ -862,7 +862,7 @@ RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject) if (PyTuple_Size(tuple) != 2) { char buf[255]; - snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules provider tuple has %d instead of 2 entries!!", PyTuple_Size(tuple)); + snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules provider tuple has %zd instead of 2 entries!!", PyTuple_Size(tuple)); PyErr_SetString(PyExc_StandardError, buf); return -1; } @@ -914,7 +914,7 @@ PyObject *eDVBCIInterfaces::readCICaIds(int slotid) if (!slot) { char tmp[255]; - snprintf(tmp, 255, "eDVBCIInterfaces::readCICaIds try to get CAIds for CI Slot %d... but just %d slots are available", slotid, m_slots.size()); + snprintf(tmp, 255, "eDVBCIInterfaces::readCICaIds try to get CAIds for CI Slot %d... but just %zd slots are available", slotid, m_slots.size()); PyErr_SetString(PyExc_StandardError, tmp); } else diff --git a/lib/gdi/accel.cpp b/lib/gdi/accel.cpp index 9450ecc..bd1439f 100644 --- a/lib/gdi/accel.cpp +++ b/lib/gdi/accel.cpp @@ -112,7 +112,7 @@ int gAccel::blit(gSurface *dst, const gSurface *src, const eRect &p, const eRect pal_addr = src->stride * src->y; unsigned long *pal = (unsigned long*)(((unsigned char*)src->data) + pal_addr); pal_addr += src->data_phys; - for (i = 0; i < 256; ++i) + for (i = 0; i < src->clut.colors; ++i) *pal++ = src->clut.data[i].argb() ^ 0xFF000000; } else return -1; /* unsupported source format */ @@ -138,12 +138,14 @@ int gAccel::fill(gSurface *dst, const eRect &area, unsigned long col) col); return 0; #endif -#if 0 // def BCM_ACCEL - bcm_accel_fill( - dst->data_phys, dst->x, dst->y, dst->stride, - area.left(), area.top(), area.width(), area.height(), - col); - return 0; +#ifdef BCM_ACCEL + if (!m_bcm_accel_state) { + bcm_accel_fill( + dst->data_phys, dst->x, dst->y, dst->stride, + area.left(), area.top(), area.width(), area.height(), + col); + return 0; + } #endif return -1; } diff --git a/lib/gdi/bcm.cpp b/lib/gdi/bcm.cpp index 12b5f22..1021994 100644 --- a/lib/gdi/bcm.cpp +++ b/lib/gdi/bcm.cpp @@ -123,6 +123,51 @@ void bcm_accel_fill( int x, int y, int width, int height, unsigned long color) { -// printf("unimplemented bcm_accel_fill\n"); + C(0x43); // reset source + C(0x53); // reset dest + C(0x5b); // reset pattern + C(0x67); // reset blend + C(0x75); // reset output + + // clear dest surface + P(0x0, 0); + P(0x1, 0); + P(0x2, 0); + P(0x3, 0); + P(0x4, 0); + C(0x45); + + // clear src surface + P(0x0, 0); + P(0x1, 0); + P(0x2, 0); + P(0x3, 0); + P(0x4, 0); + C(0x5); + + P(0x2d, color); + + P(0x2e, x); // prepare output rect + P(0x2f, y); + P(0x30, width); + P(0x31, height); + C(0x6e); // set this rect as output rect + + P(0x0, dst_addr); // prepare output surface + P(0x1, dst_stride); + P(0x2, dst_width); + P(0x3, dst_height); + P(0x4, 0x7e48888); + C(0x69); // set output surface + + P(0x6f, 0); + P(0x70, 0); + P(0x71, 2); + P(0x72, 2); + C(0x73); // select color keying + + C(0x77); // do it + + exec_list(); } diff --git a/lib/gdi/picexif.cpp b/lib/gdi/picexif.cpp index f9e8055..2daeeff 100644 --- a/lib/gdi/picexif.cpp +++ b/lib/gdi/picexif.cpp @@ -428,7 +428,7 @@ bool Cexif::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, case 6: strcpy(m_exifinfo->Orientation,"Right-Top"); break; case 7: strcpy(m_exifinfo->Orientation,"Right-Bottom"); break; case 8: strcpy(m_exifinfo->Orientation,"Left-Bottom"); break; - default: strcpy(m_exifinfo->Orientation,"Undefined rotation value"); + default: strcpy(m_exifinfo->Orientation,"Undefined"); break; } break; case TAG_EXIF_IMAGELENGTH: diff --git a/lib/gui/elistboxcontent.cpp b/lib/gui/elistboxcontent.cpp index a19e11a..ba0794e 100644 --- a/lib/gui/elistboxcontent.cpp +++ b/lib/gui/elistboxcontent.cpp @@ -641,7 +641,8 @@ static ePyObject lookupColor(ePyObject color, ePyObject data) if ((icolor & 0xFF000000) == 0xFF000000) { int index = icolor & 0xFFFFFF; - eDebug("[eListboxPythonMultiContent] template color index: %d", index); + if (PyTuple_GetItem(data, index) == Py_None) + return ePyObject(); return PyTuple_GetItem(data, index); } diff --git a/lib/gui/epositiongauge.cpp b/lib/gui/epositiongauge.cpp index ff98c08..e45d4a6 100644 --- a/lib/gui/epositiongauge.cpp +++ b/lib/gui/epositiongauge.cpp @@ -112,6 +112,7 @@ int ePositionGauge::event(int event, void *data, void *data2) // painter.fill(eRect(0, 10, s.width(), s.height()-20)); pts_t in = 0, out = 0; + int xm, xm_last = -1; std::multiset::iterator i(m_cue_entries.begin()); @@ -126,17 +127,22 @@ int ePositionGauge::event(int event, void *data, void *data2) continue; } else if (i->what == 1) /* out */ out = i++->where; - else if (i->what == 2) /* mark */ + else /* mark or last */ { - int xm = scale(i->where); - painter.setForegroundColor(gRGB(0xFF8080)); - painter.fill(eRect(xm - 2, 0, 4, s.height())); + xm = scale(i->where); + if (i->what == 2) { + painter.setForegroundColor(gRGB(0xFF8080)); + if (xm - 2 < xm_last) /* Make sure last is not overdrawn */ + painter.fill(eRect(xm_last, 0, 2 + xm - xm_last, s.height())); + else + painter.fill(eRect(xm - 2, 0, 4, s.height())); + } else if (i->what == 3) { + painter.setForegroundColor(gRGB(0x80FF80)); + painter.fill(eRect(xm - 1, 0, 3, s.height())); + xm_last = xm + 2; + } i++; continue; - } else /* other marker, like last position */ - { - ++i; - continue; } } diff --git a/lib/gui/esubtitle.h b/lib/gui/esubtitle.h index 45345db..cdad728 100644 --- a/lib/gui/esubtitle.h +++ b/lib/gui/esubtitle.h @@ -23,15 +23,21 @@ struct ePangoSubtitlePageElement struct ePangoSubtitlePage { - pts_t show_pts; + pts_t m_show_pts; int m_timeout; /* in milliseconds */ std::vector m_elements; void clear() { m_elements.clear(); } }; +struct eVobSubtitlePage +{ + pts_t m_show_pts; + int m_timeout; /* in milliseconds */ + ePtr m_pixmap; +}; + class eDVBTeletextSubtitlePage; -class eDVBPangoSubtitlePage; -class ePangoSubtitlePage; +class eDVBSubtitlePage; class eSubtitleWidget: public eWidget, public Object { diff --git a/lib/python/Components/FileList.py b/lib/python/Components/FileList.py index 1d71514..1b7e81f 100755 --- a/lib/python/Components/FileList.py +++ b/lib/python/Components/FileList.py @@ -28,7 +28,8 @@ EXTENSIONS = { "mpeg": "movie", "mkv": "movie", "mp4": "movie", - "mov": "movie" + "mov": "movie", + "m2ts": "movie", } def FileEntryComponent(name, absolute = None, isDir = False): diff --git a/lib/python/Components/TimerSanityCheck.py b/lib/python/Components/TimerSanityCheck.py index b472a19..b9dda6a 100644 --- a/lib/python/Components/TimerSanityCheck.py +++ b/lib/python/Components/TimerSanityCheck.py @@ -2,6 +2,7 @@ import NavigationInstance from time import localtime, mktime, gmtime from ServiceReference import ServiceReference from enigma import iServiceInformation, eServiceCenter, eServiceReference +from timer import TimerEntry class TimerSanityCheck: def __init__(self, timerlist, newtimer=None): @@ -107,7 +108,7 @@ class TimerSanityCheck: self.rep_eventlist.append((begin, idx)) begin += 86400 rflags >>= 1 - else: + elif timer.state < TimerEntry.StateEnded: self.nrep_eventlist.extend([(timer.begin,self.bflag,idx),(timer.end,self.eflag,idx)]) idx += 1 diff --git a/lib/python/Components/UsageConfig.py b/lib/python/Components/UsageConfig.py index 8ea9aa6..a265a16 100644 --- a/lib/python/Components/UsageConfig.py +++ b/lib/python/Components/UsageConfig.py @@ -102,13 +102,11 @@ def InitUsageConfig(): config.seek.selfdefined_79 = ConfigNumber(default=300) config.seek.speeds_forward = ConfigSet(default=[2, 4, 8, 16, 32, 64, 128], choices=[2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]) - config.seek.speeds_backward = ConfigSet(default=[8, 16, 32, 64, 128], choices=[1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]) + config.seek.speeds_backward = ConfigSet(default=[2, 4, 8, 16, 32, 64, 128], choices=[1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]) config.seek.speeds_slowmotion = ConfigSet(default=[2, 4, 8], choices=[2, 4, 6, 8, 12, 16, 25]) config.seek.enter_forward = ConfigSelection(default = "2", choices = ["2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"]) config.seek.enter_backward = ConfigSelection(default = "1", choices = ["1", "2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"]) - config.seek.stepwise_minspeed = ConfigSelection(default = "16", choices = ["Never", "2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"]) - config.seek.stepwise_repeat = ConfigSelection(default = "3", choices = ["2", "3", "4", "5", "6"]) config.seek.on_pause = ConfigSelection(default = "play", choices = [ ("play", _("Play")), diff --git a/lib/python/Plugins/Extensions/DVDPlayer/plugin.py b/lib/python/Plugins/Extensions/DVDPlayer/plugin.py index e1ab3ef..7f330eb 100755 --- a/lib/python/Plugins/Extensions/DVDPlayer/plugin.py +++ b/lib/python/Plugins/Extensions/DVDPlayer/plugin.py @@ -244,8 +244,6 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP self.saved_config_speeds_backward = config.seek.speeds_backward.value self.saved_config_enter_forward = config.seek.enter_forward.value self.saved_config_enter_backward = config.seek.enter_backward.value - self.saved_config_seek_stepwise_minspeed = config.seek.stepwise_minspeed.value - self.saved_config_seek_stepwise_repeat = config.seek.stepwise_repeat.value self.saved_config_seek_on_pause = config.seek.on_pause.value self.saved_config_seek_speeds_slowmotion = config.seek.speeds_slowmotion.value @@ -255,8 +253,6 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP config.seek.speeds_slowmotion.value = [ ] config.seek.enter_forward.value = "2" config.seek.enter_backward.value = "2" - config.seek.stepwise_minspeed.value = "Never" - config.seek.stepwise_repeat.value = "3" config.seek.on_pause.value = "play" def restore_infobar_seek_config(self): @@ -265,8 +261,6 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP config.seek.speeds_slowmotion.value = self.saved_config_seek_speeds_slowmotion config.seek.enter_forward.value = self.saved_config_enter_forward config.seek.enter_backward.value = self.saved_config_enter_backward - config.seek.stepwise_minspeed.value = self.saved_config_seek_stepwise_minspeed - config.seek.stepwise_repeat.value = self.saved_config_seek_stepwise_repeat config.seek.on_pause.value = self.saved_config_seek_on_pause def __init__(self, session, dvd_device = None, dvd_filelist = [ ], args = None): @@ -278,7 +272,7 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP HelpableScreen.__init__(self) self.save_infobar_seek_config() self.change_infobar_seek_config() - InfoBarSeek.__init__(self, useSeekBackHack=False) + InfoBarSeek.__init__(self) InfoBarPVRState.__init__(self) self.dvdScreen = self.session.instantiateDialog(DVDOverlay) diff --git a/lib/python/Plugins/Extensions/MediaPlayer/plugin.py b/lib/python/Plugins/Extensions/MediaPlayer/plugin.py index 9ae886f..d61dbc7 100755 --- a/lib/python/Plugins/Extensions/MediaPlayer/plugin.py +++ b/lib/python/Plugins/Extensions/MediaPlayer/plugin.py @@ -110,7 +110,7 @@ class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoB # 'None' is magic to start at the list of mountpoints defaultDir = config.mediaplayer.defaultDir.getValue() - self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls") + self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov|m2ts)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls") self["filelist"] = self.filelist self.playlist = MyPlayList() diff --git a/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp b/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp index 673b525..9a69de3 100644 --- a/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp +++ b/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp @@ -118,11 +118,11 @@ eAutoInitP0 init_socketui(eAutoInitNumbers::rc, "Socket MMI"); int eSocketMMIHandler::send_to_mmisock( void* buf, size_t len) { - int ret = write(connfd, buf, len); + ssize_t ret = write(connfd, buf, len); if ( ret < 0 ) eDebug("[eSocketMMIHandler] write (%m)"); - else if ( (uint)ret != len ) - eDebug("[eSocketMMIHandler] only %d bytes sent.. %d bytes should be sent", ret, len ); + else if ( (size_t)ret != len ) + eDebug("[eSocketMMIHandler] only %zd bytes sent.. %zu bytes should be sent", ret, len ); else return 0; return ret; diff --git a/lib/python/Screens/AudioSelection.py b/lib/python/Screens/AudioSelection.py index a0bfcab..068da87 100644 --- a/lib/python/Screens/AudioSelection.py +++ b/lib/python/Screens/AudioSelection.py @@ -46,7 +46,7 @@ class AudioSelection(Screen, ConfigListScreen): "cancel": self.cancel, "up": self.keyUp, "down": self.keyDown, - }, -3) + }, -2) self.settings = ConfigSubsection() choicelist = [(PAGE_AUDIO,_("audio tracks")), (PAGE_SUBTITLES,_("Subtitles"))] @@ -62,13 +62,12 @@ class AudioSelection(Screen, ConfigListScreen): streams = [] conflist = [] selectedidx = 0 - - service = self.session.nav.getCurrentService() - self.audioTracks = audio = service and service.audioTracks() - n = audio and audio.getNumberOfTracks() or 0 - + if self.settings.menupage.getValue() == PAGE_AUDIO: self.setTitle(_("Select audio track")) + service = self.session.nav.getCurrentService() + self.audioTracks = audio = service and service.audioTracks() + n = audio and audio.getNumberOfTracks() or 0 if SystemInfo["CanDownmixAC3"]: self.settings.downmix = ConfigOnOff(default=config.av.downmix_ac3.value) self.settings.downmix.addNotifier(self.changeAC3Downmix, initial_call = False) @@ -156,7 +155,7 @@ class AudioSelection(Screen, ConfigListScreen): number = "%x%02x" % (x[3],x[2]) elif x[0] == 2: - types = ("UTF-8 text","SSA / AAS",".SRT file") + types = (_(""), "UTF-8 text", "SSA", "AAS", ".SRT file", "VOB", "PGS (unsupported)") description = types[x[2]] streams.append((x, "", number, description, language, selected)) @@ -185,7 +184,7 @@ class AudioSelection(Screen, ConfigListScreen): conflist.append(getConfigListEntry(Plugins[0][0], ConfigNothing())) self.plugincallfunc = Plugins[0][1] if len(Plugins) > 1: - print "these plugins are installed but not displayed in the dialog box:", Plugins[1:] + print "plugin(s) installed but not displayed in the dialog box:", Plugins[1:] self["config"].list = conflist self["config"].l.setList(conflist) diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py index 6fa8911..4f6eafc 100644 --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -717,7 +717,7 @@ class InfoBarSeek: SEEK_STATE_PAUSE = (1, 0, 0, "||") SEEK_STATE_EOF = (1, 0, 0, "END") - def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True): + def __init__(self, actionmap = "InfobarSeekActions"): self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged, @@ -774,20 +774,10 @@ class InfoBarSeek: self.__seekableStatusChanged() def makeStateForward(self, n): -# minspeed = config.seek.stepwise_minspeed.value -# repeat = int(config.seek.stepwise_repeat.value) -# if minspeed != "Never" and n >= int(minspeed) and repeat > 1: -# return (0, n * repeat, repeat, ">> %dx" % n) -# else: - return (0, n, 0, ">> %dx" % n) + return (0, n, 0, ">> %dx" % n) def makeStateBackward(self, n): -# minspeed = config.seek.stepwise_minspeed.value -# repeat = int(config.seek.stepwise_repeat.value) -# if minspeed != "Never" and n >= int(minspeed) and repeat > 1: -# return (0, -n * repeat, repeat, "<< %dx" % n) -# else: - return (0, -n, 0, "<< %dx" % n) + return (0, -n, 0, "<< %dx" % n) def makeStateSlowMotion(self, n): return (0, 0, n, "/%d" % n) @@ -1970,20 +1960,21 @@ class InfoBarCueSheetSupport: return True def jumpPreviousMark(self): - # we add 2 seconds, so if the play position is <2s after + # we add 5 seconds, so if the play position is <5s after # the mark, the mark before will be used self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True) def jumpNextMark(self): - if not self.jumpPreviousNextMark(lambda x: x): + if not self.jumpPreviousNextMark(lambda x: x-90000): self.doSeek(-1) def getNearestCutPoint(self, pts, cmp=abs, start=False): # can be optimized - beforecut = False + beforecut = True nearest = None + bestdiff = -1 + instate = True if start: - beforecut = True bestdiff = cmp(0 - pts) if bestdiff >= 0: nearest = [0, False] @@ -1992,14 +1983,19 @@ class InfoBarCueSheetSupport: beforecut = False if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks diff = cmp(cp[0] - pts) - if diff >= 0: + if start and diff >= 0: nearest = cp bestdiff = diff else: nearest = None - if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST): + bestdiff = -1 + if cp[1] == self.CUT_TYPE_IN: + instate = True + elif cp[1] == self.CUT_TYPE_OUT: + instate = False + elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST): diff = cmp(cp[0] - pts) - if diff >= 0 and (nearest is None or bestdiff > diff): + if instate and diff >= 0 and (nearest is None or bestdiff > diff): nearest = cp bestdiff = diff return nearest diff --git a/lib/service/Makefile.am b/lib/service/Makefile.am index edafd1a..9f956b6 100644 --- a/lib/service/Makefile.am +++ b/lib/service/Makefile.am @@ -16,7 +16,8 @@ libenigma_service_a_SOURCES = \ servicedvb.cpp \ servicedvbrecord.cpp \ servicefs.cpp \ - servicemp3.cpp + servicemp3.cpp \ + servicem2ts.cpp serviceincludedir = $(pkgincludedir)/lib/service serviceinclude_HEADERS = \ @@ -27,7 +28,8 @@ serviceinclude_HEADERS = \ servicedvb.h \ servicedvbrecord.h \ servicefs.h \ - servicemp3.h + servicemp3.h \ + servicem2ts.h if HAVE_LIBXINE libenigma_service_a_SOURCES += \ diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 3e58091..8650989 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -1801,6 +1801,8 @@ RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int info.m_description = "AAC-HE"; else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS) info.m_description = "DTS"; + else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTSHD) + info.m_description = "DTS-HD"; else info.m_description = "???"; @@ -2403,7 +2405,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged) eDebug("getting program info failed."); else { - eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size()); + eDebugNoNewLine("have %zd video stream(s)", program.videoStreams.size()); if (!program.videoStreams.empty()) { eDebugNoNewLine(" ("); @@ -2422,7 +2424,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged) } eDebugNoNewLine(")"); } - eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size()); + eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size()); if (!program.audioStreams.empty()) { eDebugNoNewLine(" ("); @@ -2599,7 +2601,7 @@ void eDVBServicePlay::loadCuesheet() m_cue_entries.insert(cueEntry(where, what)); } fclose(f); - eDebug("%d entries", m_cue_entries.size()); + eDebug("%zd entries", m_cue_entries.size()); } else eDebug("cutfile not found!"); diff --git a/lib/service/servicedvbrecord.cpp b/lib/service/servicedvbrecord.cpp index 419c26b..08cd247 100644 --- a/lib/service/servicedvbrecord.cpp +++ b/lib/service/servicedvbrecord.cpp @@ -313,7 +313,7 @@ int eDVBServiceRecord::doRecord() int timing_pid = -1, timing_pid_type = -1; - eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size()); + eDebugNoNewLine("RECORD: have %zd video stream(s)", program.videoStreams.size()); if (!program.videoStreams.empty()) { eDebugNoNewLine(" ("); @@ -335,7 +335,7 @@ int eDVBServiceRecord::doRecord() } eDebugNoNewLine(")"); } - eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size()); + eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size()); if (!program.audioStreams.empty()) { eDebugNoNewLine(" ("); diff --git a/lib/service/servicem2ts.cpp b/lib/service/servicem2ts.cpp new file mode 100644 index 0000000..e79907d --- /dev/null +++ b/lib/service/servicem2ts.cpp @@ -0,0 +1,380 @@ +#include +#include +#include +#include + +DEFINE_REF(eServiceFactoryM2TS) + +class eM2TSFile: public iTsSource +{ + DECLARE_REF(eM2TSFile); + eSingleLock m_lock; +public: + eM2TSFile(const char *filename, bool cached=false); + ~eM2TSFile(); + + // iTsSource + off_t lseek(off_t offset, int whence); + ssize_t read(off_t offset, void *buf, size_t count); + off_t length(); + int valid(); +private: + int m_sync_offset; + int m_fd; /* for uncached */ + FILE *m_file; /* for cached */ + off_t m_current_offset, m_length; + bool m_cached; + off_t lseek_internal(off_t offset, int whence); +}; + +class eStaticServiceM2TSInformation: public iStaticServiceInformation +{ + DECLARE_REF(eStaticServiceM2TSInformation); + eServiceReference m_ref; + eDVBMetaParser m_parser; +public: + eStaticServiceM2TSInformation(const eServiceReference &ref); + RESULT getName(const eServiceReference &ref, std::string &name); + int getLength(const eServiceReference &ref); + RESULT getEvent(const eServiceReference &ref, ePtr &SWIG_OUTPUT, time_t start_time); + int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; } + int getInfo(const eServiceReference &ref, int w); + std::string getInfoString(const eServiceReference &ref,int w); + PyObject *getInfoObject(const eServiceReference &r, int what); +}; + +DEFINE_REF(eStaticServiceM2TSInformation); + +eStaticServiceM2TSInformation::eStaticServiceM2TSInformation(const eServiceReference &ref) +{ + m_ref = ref; + m_parser.parseFile(ref.path); +} + +RESULT eStaticServiceM2TSInformation::getName(const eServiceReference &ref, std::string &name) +{ + ASSERT(ref == m_ref); + if (m_parser.m_name.size()) + name = m_parser.m_name; + else + { + name = ref.path; + size_t n = name.rfind('/'); + if (n != std::string::npos) + name = name.substr(n + 1); + } + return 0; +} + +int eStaticServiceM2TSInformation::getLength(const eServiceReference &ref) +{ + ASSERT(ref == m_ref); + + eDVBTSTools tstools; + + struct stat s; + stat(ref.path.c_str(), &s); + + eM2TSFile *file = new eM2TSFile(ref.path.c_str()); + ePtr source = file; + + if (!source->valid()) + return 0; + + tstools.setSource(source); + + /* check if cached data is still valid */ + if (m_parser.m_data_ok && (s.st_size == m_parser.m_filesize) && (m_parser.m_length)) + return m_parser.m_length / 90000; + + /* open again, this time with stream info */ + tstools.setSource(source, ref.path.c_str()); + + /* otherwise, re-calc length and update meta file */ + pts_t len; + if (tstools.calcLen(len)) + return 0; + + m_parser.m_length = len; + m_parser.m_filesize = s.st_size; + m_parser.updateMeta(ref.path); + return m_parser.m_length / 90000; +} + +int eStaticServiceM2TSInformation::getInfo(const eServiceReference &ref, int w) +{ + switch (w) + { + case iServiceInformation::sDescription: + return iServiceInformation::resIsString; + case iServiceInformation::sServiceref: + return iServiceInformation::resIsString; + case iServiceInformation::sFileSize: + return m_parser.m_filesize; + case iServiceInformation::sTimeCreate: + if (m_parser.m_time_create) + return m_parser.m_time_create; + else + return iServiceInformation::resNA; + default: + return iServiceInformation::resNA; + } +} + +std::string eStaticServiceM2TSInformation::getInfoString(const eServiceReference &ref,int w) +{ + switch (w) + { + case iServiceInformation::sDescription: + return m_parser.m_description; + case iServiceInformation::sServiceref: + return m_parser.m_ref.toString(); + case iServiceInformation::sTags: + return m_parser.m_tags; + default: + return ""; + } +} + +PyObject *eStaticServiceM2TSInformation::getInfoObject(const eServiceReference &r, int what) +{ + switch (what) + { + case iServiceInformation::sFileSize: + return PyLong_FromLongLong(m_parser.m_filesize); + default: + Py_RETURN_NONE; + } +} + +RESULT eStaticServiceM2TSInformation::getEvent(const eServiceReference &ref, ePtr &evt, time_t start_time) +{ + if (!ref.path.empty()) + { + ePtr event = new eServiceEvent; + std::string filename = ref.path; + filename.erase(filename.length()-4, 2); + filename+="eit"; + if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get())) + { + evt = event; + return 0; + } + } + evt = 0; + return -1; +} + +DEFINE_REF(eM2TSFile); + +eM2TSFile::eM2TSFile(const char *filename, bool cached) + :m_lock(false), m_sync_offset(0), m_fd(-1), m_file(NULL), m_current_offset(0), m_length(0), m_cached(cached) +{ + if (!m_cached) + m_fd = ::open(filename, O_RDONLY | O_LARGEFILE); + else + m_file = ::fopen64(filename, "rb"); + if (valid()) + m_current_offset = m_length = lseek_internal(0, SEEK_END); +} + +eM2TSFile::~eM2TSFile() +{ + if (m_cached) + { + if (m_file) + { + ::fclose(m_file); + m_file = 0; + } + } + else + { + if (m_fd >= 0) + ::close(m_fd); + m_fd = -1; + } +} + +off_t eM2TSFile::lseek(off_t offset, int whence) +{ + eSingleLocker l(m_lock); + + offset = (offset % 188) + (offset * 192) / 188; + + if (offset != m_current_offset) + m_current_offset = lseek_internal(offset, whence); + + return m_current_offset; +} + +off_t eM2TSFile::lseek_internal(off_t offset, int whence) +{ + off_t ret; + + if (!m_cached) + ret = ::lseek(m_fd, offset, whence); + else + { + if (::fseeko(m_file, offset, whence) < 0) + perror("fseeko"); + ret = ::ftello(m_file); + } + return ret <= 0 ? ret : (ret % 192) + (ret*188) / 192; +} + +ssize_t eM2TSFile::read(off_t offset, void *b, size_t count) +{ + eSingleLocker l(m_lock); + unsigned char tmp[192*3]; + unsigned char *buf = (unsigned char*)b; + + size_t rd=0; + offset = (offset % 188) + (offset * 192) / 188; + +sync: + if ((offset+m_sync_offset) != m_current_offset) + { +// eDebug("seekTo %lld", offset+m_sync_offset); + m_current_offset = lseek_internal(offset+m_sync_offset, SEEK_SET); + if (m_current_offset < 0) + return m_current_offset; + } + + while (rd < count) { + size_t ret; + if (!m_cached) + ret = ::read(m_fd, tmp, 192); + else + ret = ::fread(tmp, 1, 192, m_file); + if (ret < 0 || ret < 192) + return rd ? rd : ret; + + if (tmp[4] != 0x47) + { + if (rd > 0) { + eDebug("short read at pos %lld async!!", m_current_offset); + return rd; + } + else { + int x=0; + if (!m_cached) + ret = ::read(m_fd, tmp+192, 384); + else + ret = ::fread(tmp+192, 1, 384, m_file); + +#if 0 + eDebugNoNewLine("m2ts out of sync at pos %lld, real %lld:", offset + m_sync_offset, m_current_offset); + for (; x < 192; ++x) + eDebugNoNewLine(" %02x", tmp[x]); + eDebug(""); + x=0; +#else + eDebug("m2ts out of sync at pos %lld, real %lld", offset + m_sync_offset, m_current_offset); +#endif + for (; x < 192; ++x) + { + if (tmp[x] == 0x47 && tmp[x+192] == 0x47) + { + int add_offs = (x - 4); + eDebug("sync found at pos %d, sync_offset is now %d, old was %d", x, add_offs + m_sync_offset, m_sync_offset); + m_sync_offset += add_offs; + goto sync; + } + } + } + } + + memcpy(buf+rd, tmp+4, 188); + + rd += 188; + m_current_offset += 188; + } + + m_sync_offset %= 188; + + return rd; +} + +int eM2TSFile::valid() +{ + if (!m_cached) + return m_fd != -1; + else + return !!m_file; +} + +off_t eM2TSFile::length() +{ + return m_length; +} + +eServiceFactoryM2TS::eServiceFactoryM2TS() +{ + ePtr sc; + eServiceCenter::getPrivInstance(sc); + if (sc) + { + std::list extensions; + extensions.push_back("m2ts"); + extensions.push_back("mts"); + sc->addServiceFactory(eServiceFactoryM2TS::id, this, extensions); + } +} + +eServiceFactoryM2TS::~eServiceFactoryM2TS() +{ + ePtr sc; + + eServiceCenter::getPrivInstance(sc); + if (sc) + sc->removeServiceFactory(eServiceFactoryM2TS::id); +} + +RESULT eServiceFactoryM2TS::play(const eServiceReference &ref, ePtr &ptr) +{ + ptr = new eServiceM2TS(ref); + return 0; +} + +RESULT eServiceFactoryM2TS::record(const eServiceReference &ref, ePtr &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryM2TS::list(const eServiceReference &ref, ePtr &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryM2TS::info(const eServiceReference &ref, ePtr &ptr) +{ + ptr=new eStaticServiceM2TSInformation(ref); + return 0; +} + +RESULT eServiceFactoryM2TS::offlineOperations(const eServiceReference &ref, ePtr &ptr) +{ + ptr = 0; + return -1; +} + +eServiceM2TS::eServiceM2TS(const eServiceReference &ref) + :eDVBServicePlay(ref, NULL) +{ +} + +ePtr eServiceM2TS::createTsSource(eServiceReferenceDVB &ref) +{ + ePtr source = new eM2TSFile(ref.path.c_str()); + return source; +} + +RESULT eServiceM2TS::isCurrentlySeekable() +{ + return 1; // for fast winding we need index files... so only skip forward/backward yet +} + +eAutoInitPtr init_eServiceFactoryM2TS(eAutoInitNumbers::service+1, "eServiceFactoryM2TS"); diff --git a/lib/service/servicem2ts.h b/lib/service/servicem2ts.h new file mode 100644 index 0000000..bfa4f7d --- /dev/null +++ b/lib/service/servicem2ts.h @@ -0,0 +1,33 @@ +#ifndef __servicem2ts_h +#define __servicem2ts_h + +#include + +class eServiceFactoryM2TS: public iServiceHandler +{ + DECLARE_REF(eServiceFactoryM2TS); +public: + eServiceFactoryM2TS(); + virtual ~eServiceFactoryM2TS(); + enum { id = 0x3 }; + + // iServiceHandler + RESULT play(const eServiceReference &, ePtr &ptr); + RESULT record(const eServiceReference &, ePtr &ptr); + RESULT list(const eServiceReference &, ePtr &ptr); + RESULT info(const eServiceReference &, ePtr &ptr); + RESULT offlineOperations(const eServiceReference &, ePtr &ptr); +}; + +class eServiceM2TS: public eDVBServicePlay +{ + friend class eServiceFactoryM2TS; +protected: + eServiceM2TS(const eServiceReference &ref); + ePtr createTsSource(eServiceReferenceDVB &ref); + + // iSeekableService + RESULT isCurrentlySeekable(); +}; + +#endif diff --git a/lib/service/servicemp3.cpp b/lib/service/servicemp3.cpp index e453a2b..f96b8c4 100644 --- a/lib/service/servicemp3.cpp +++ b/lib/service/servicemp3.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -19,6 +20,9 @@ #include #define HTTP_TIMEOUT 10 +static GstStaticPadTemplate subsinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("text/plain; text/x-pango-markup; video/x-dvd-subpicture; subpicture/x-pgs")); +// int ret = gst_pad_set_caps (ghostpad, caps2); +// gst_caps_unref(caps2);)); // eServiceFactoryMP3 @@ -218,6 +222,7 @@ eServiceMP3::eServiceMP3(eServiceReference ref) m_seekTimeout = eTimer::create(eApp); m_subtitle_sync_timer = eTimer::create(eApp); m_streamingsrc_timeout = 0; + m_subtitle_hide_timer = eTimer::create(eApp); m_stream_tags = 0; m_currentAudioStream = -1; m_currentSubtitleStream = 0; @@ -225,8 +230,12 @@ eServiceMP3::eServiceMP3(eServiceReference ref) m_currentTrickRatio = 0; m_subs_to_pull = 0; m_buffer_size = 1*1024*1024; + m_prev_decoder_time = -1; + m_decoder_time_valid_state = 0; + CONNECT(m_seekTimeout->timeout, eServiceMP3::seekTimeoutCB); CONNECT(m_subtitle_sync_timer->timeout, eServiceMP3::pushSubtitles); + CONNECT(m_subtitle_hide_timer->timeout, eServiceMP3::hideSubtitles); CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll); m_aspect = m_width = m_height = m_framerate = m_progressive = -1; @@ -327,23 +336,62 @@ eServiceMP3::eServiceMP3(eServiceReference ref) g_object_set (G_OBJECT (m_gst_playbin), "uri", uri, NULL); - int flags = 0x47; // ( == GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT ) + int flags = 0x47; // ( GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT ); g_object_set (G_OBJECT (m_gst_playbin), "flags", flags, NULL); g_free(uri); - GstElement *subsink = gst_element_factory_make("appsink", "subtitle_sink"); - if (!subsink) - eDebug("eServiceMP3::sorry, can't play: missing gst-plugin-appsink"); - else - { - m_subs_to_pull_handler_id = g_signal_connect (subsink, "new-buffer", G_CALLBACK (gstCBsubtitleAvail), this); - g_object_set (G_OBJECT (subsink), "caps", gst_caps_from_string("text/plain; text/x-plain; text/x-pango-markup"), NULL); - g_object_set (G_OBJECT (m_gst_playbin), "text-sink", subsink, NULL); - } - + m_gst_subtitlebin = gst_bin_new("subtitle_bin"); + if ( m_gst_playbin ) { + GstElement *appsink = gst_element_factory_make("appsink", "subtitle_sink"); + + if (!appsink) + eDebug("eServiceMP3::sorry, can't play: missing gst-plugin-appsink"); + + GstElement *dvdsubdec = gst_element_factory_make("dvdsubdec", "vobsubtitle_decoder"); + if ( dvdsubdec ) + { + gst_bin_add_many(GST_BIN(m_gst_subtitlebin), dvdsubdec, appsink, NULL); + g_object_set (G_OBJECT (dvdsubdec), "singlebuffer", TRUE, NULL); + } + else + { + eDebug("eServiceMP3::missing gst-plugin-dvdsub, no vob subtitle support!"); + gst_bin_add(GST_BIN(m_gst_subtitlebin), appsink); + } + + GstPadTemplate *templ; + templ = gst_static_pad_template_get (&subsinktemplate); + + GstPad *ghostpad = gst_ghost_pad_new_no_target_from_template("sink", templ); + gst_element_add_pad (m_gst_subtitlebin, ghostpad); + + GstCaps* caps = gst_caps_from_string("text/plain; text/x-pango-markup; video/x-raw-rgb; subpicture/x-pgs"); + g_object_set (G_OBJECT (appsink), "caps", caps, NULL); + gst_caps_unref(caps); + + g_object_set (G_OBJECT (appsink), "async", FALSE, NULL); + g_object_set (G_OBJECT (appsink), "sync", TRUE, NULL); + g_object_set (G_OBJECT (appsink), "emit-signals", TRUE, NULL); + g_object_set (G_OBJECT (appsink), "ts-offset", 0 * GST_SECOND, NULL); + + g_object_set_data (G_OBJECT (ghostpad), "application-instance", this); + g_signal_connect (G_OBJECT (ghostpad), "notify::caps", G_CALLBACK (gstGhostpadHasCAPS), this); + gst_pad_set_getcaps_function (ghostpad, gstGhostpadGetCAPS); + gst_pad_set_acceptcaps_function (ghostpad, gstGhostpadAcceptCAPS); + m_ghost_pad_buffer_alloc = GST_PAD_BUFFERALLOCFUNC(ghostpad); + m_ghost_pad_chain_function = GST_PAD_CHAINFUNC(ghostpad); + m_ghost_pad_subtitle_sink_event = GST_PAD_EVENTFUNC(ghostpad); + gst_pad_set_bufferalloc_function (ghostpad, GST_DEBUG_FUNCPTR(gstGhostpadBufferAlloc)); + gst_pad_set_event_function (ghostpad, GST_DEBUG_FUNCPTR(gstGhostpadSinkEvent)); + gst_pad_set_chain_function (ghostpad, GST_DEBUG_FUNCPTR(gstGhostpadChainFunction)); + m_gst_prev_subtitle_caps = gst_caps_new_empty(); + + g_object_set (G_OBJECT (m_gst_playbin), "text-sink", m_gst_subtitlebin, NULL); + m_subs_to_pull_handler_id = g_signal_connect (appsink, "new-buffer", G_CALLBACK (gstCBsubtitleAvail), this); + gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin)), gstBusSyncHandler, this); char srt_filename[strlen(filename)+1]; strncpy(srt_filename,filename,strlen(filename)-3); @@ -380,15 +428,17 @@ eServiceMP3::eServiceMP3(eServiceReference ref) eServiceMP3::~eServiceMP3() { // disconnect subtitle callback - GstElement *sink; - g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL); - if (sink) + GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_subtitlebin), "subtitle_sink"); +// GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); + + if (appsink) { - g_signal_handler_disconnect (sink, m_subs_to_pull_handler_id); - gst_object_unref(sink); + g_signal_handler_disconnect (appsink, m_subs_to_pull_handler_id); + gst_object_unref(appsink); } delete m_subtitle_widget; + gst_caps_unref(this->m_gst_prev_subtitle_caps); // disconnect sync handler callback gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin)), NULL, NULL); @@ -442,6 +492,8 @@ RESULT eServiceMP3::stop() if (m_state == stStopped) return -1; + + //GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(m_gst_playbin),GST_DEBUG_GRAPH_SHOW_ALL,"e2-playbin"); eDebug("eServiceMP3::stop %s", m_ref.path.c_str()); gst_element_set_state(m_gst_playbin, GST_STATE_NULL); @@ -568,6 +620,8 @@ RESULT eServiceMP3::seekTo(pts_t to) if (!(ret = seekToImpl(to))) { m_subtitle_pages.clear(); + m_prev_decoder_time = -1; + m_decoder_time_valid_state = 0; m_subs_to_pull = 0; } } @@ -669,6 +723,7 @@ RESULT eServiceMP3::getPlayPosition(pts_t &pts) /* pos is in nanoseconds. we have 90 000 pts per second. */ pts = pos / 11111; +// eDebug("gst_element_query_position %lld pts (%lld ms)", pts, pos/1000000); return 0; } @@ -1111,6 +1166,48 @@ RESULT eServiceMP3::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i) return 0; } +subtype_t getSubtitleType(GstPad* pad, gchar *g_codec=NULL) +{ + subtype_t type = stUnknown; + GstCaps* caps = gst_pad_get_negotiated_caps(pad); + + if ( caps ) + { + GstStructure* str = gst_caps_get_structure(caps, 0); + const gchar *g_type = gst_structure_get_name(str); + eDebug("getSubtitleType::subtitle probe caps type=%s", g_type); + + if ( !strcmp(g_type, "video/x-dvd-subpicture") ) + type = stVOB; + else if ( !strcmp(g_type, "text/x-pango-markup") ) + type = stSSA; + else if ( !strcmp(g_type, "text/plain") ) + type = stPlainText; + else if ( !strcmp(g_type, "subpicture/x-pgs") ) + type = stPGS; + else + eDebug("getSubtitleType::unsupported subtitle caps %s (%s)", g_type, g_codec); + } + else if ( g_codec ) + { + eDebug("getSubtitleType::subtitle probe codec tag=%s", g_codec); + if ( !strcmp(g_codec, "VOB") ) + type = stVOB; + else if ( !strcmp(g_codec, "SubStation Alpha") || !strcmp(g_codec, "SSA") ) + type = stSSA; + else if ( !strcmp(g_codec, "ASS") ) + type = stASS; + else if ( !strcmp(g_codec, "UTF-8 plain text") ) + type = stPlainText; + else + eDebug("getSubtitleType::unsupported subtitle codec %s", g_codec); + } + else + eDebug("getSubtitleType::unidentifiable subtitle stream!"); + + return type; +} + void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) { if (!msg) @@ -1157,16 +1254,16 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) } break; case GST_STATE_CHANGE_READY_TO_PAUSED: { - GstElement *sink; - g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL); - if (sink) - { - g_object_set (G_OBJECT (sink), "max-buffers", 2, NULL); - g_object_set (G_OBJECT (sink), "sync", FALSE, NULL); - g_object_set (G_OBJECT (sink), "async", FALSE, NULL); - g_object_set (G_OBJECT (sink), "emit-signals", TRUE, NULL); - gst_object_unref(sink); - } + GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_subtitlebin), "subtitle_sink"); +// GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); + if (appsink) + { + g_object_set (G_OBJECT (appsink), "max-buffers", 2, NULL); + g_object_set (G_OBJECT (appsink), "sync", FALSE, NULL); + g_object_set (G_OBJECT (appsink), "emit-signals", TRUE, NULL); + eDebug("eServiceMP3::appsink properties set!"); + gst_object_unref(appsink); + } setAC3Delay(ac3_delay); setPCMDelay(pcm_delay); } break; @@ -1283,7 +1380,6 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) continue; GstStructure* str = gst_caps_get_structure(caps, 0); const gchar *g_type = gst_structure_get_name(str); - eDebug("AUDIO STRUCT=%s", g_type); audio.type = gstCheckAudioPad(str); g_codec = g_strdup(g_type); g_lang = g_strdup_printf ("und"); @@ -1304,25 +1400,30 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) } for (i = 0; i < n_text; i++) - { - gchar *g_lang; -// gchar *g_type; -// GstPad* pad = 0; -// g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad); -// GstCaps* caps = gst_pad_get_negotiated_caps(pad); -// GstStructure* str = gst_caps_get_structure(caps, 0); -// g_type = gst_structure_get_name(str); -// g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags); + { + gchar *g_codec = NULL, *g_lang = NULL; + g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags); subtitleStream subs; - subs.type = stPlainText; +// int ret; + g_lang = g_strdup_printf ("und"); if ( tags && gst_is_tag_list(tags) ) + { gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); + gst_tag_list_get_string(tags, GST_TAG_SUBTITLE_CODEC, &g_codec); + gst_tag_list_free(tags); + } + subs.language_code = std::string(g_lang); - eDebug("eServiceMP3::subtitle stream=%i language=%s"/* type=%s*/, i, g_lang/*, g_type*/); + eDebug("eServiceMP3::subtitle stream=%i language=%s codec=%s", i, g_lang, g_codec); + + GstPad* pad = 0; + g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad); + if ( subs.type != stSRT ) + subs.type = getSubtitleType(pad, g_codec); + m_subtitleStreams.push_back(subs); g_free (g_lang); -// g_free (g_type); } m_event((iPlayableService*)this, evUpdatedEventInfo); break; @@ -1415,7 +1516,7 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) GstBusSyncReply eServiceMP3::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data) { eServiceMP3 *_this = (eServiceMP3*)user_data; - _this->m_pump.send(1); + _this->m_pump.send(Message(1)); /* wake */ return GST_BUS_PASS; } @@ -1469,44 +1570,377 @@ audiotype_t eServiceMP3::gstCheckAudioPad(GstStructure* structure) return atUnknown; } -void eServiceMP3::gstPoll(const int &msg) +void eServiceMP3::gstPoll(const Message &msg) { - /* ok, we have a serious problem here. gstBusSyncHandler sends - us the wakup signal, but likely before it was posted. - the usleep, an EVIL HACK (DON'T DO THAT!!!) works around this. - - I need to understand the API a bit more to make this work - proplerly. */ - if (msg == 1) + if (msg.type == 1) { GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin)); GstMessage *message; - usleep(1); - while ((message = gst_bus_pop (bus))) + while (message = gst_bus_pop(bus)) { gstBusCall(bus, message); gst_message_unref (message); } } - else + else if (msg.type == 2) pullSubtitle(); + else if (msg.type == 3) + gstGhostpadHasCAPS_synced(msg.d.pad); + else + eDebug("gstPoll unhandled Message %d\n", msg.type); } eAutoInitPtr init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3"); void eServiceMP3::gstCBsubtitleAvail(GstElement *appsink, gpointer user_data) { - eServiceMP3 *_this = (eServiceMP3*)user_data; + eServiceMP3 *_this = (eServiceMP3*)user_data; eSingleLocker l(_this->m_subs_to_pull_lock); ++_this->m_subs_to_pull; - _this->m_pump.send(2); + _this->m_pump.send(Message(2)); +} + +gboolean eServiceMP3::gstGhostpadSinkEvent(GstPad * pad, GstEvent * event) +{ +// eDebug("eServiceMP3::gstGhostpadSinkEvent %s", gst_structure_get_name (event->structure)); + +// eServiceMP3 *_this = (eServiceMP3*) (gst_pad_get_parent (pad)); + eServiceMP3 *_this = (eServiceMP3*) g_object_get_data (G_OBJECT (pad), "application-instance"); + gboolean ret; + GstFormat format; + + if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && event->structure && strcmp (gst_structure_get_name (event->structure), "subtitleoverlay-flush-subtitle") == 0) + { + eDebug ("Custom subtitle flush event"); +// GST_SUBTITLE_OVERLAY_LOCK (self); +// self->subtitle_flush = TRUE; +// self->subtitle_error = FALSE; +// if (self->subtitle_block_pad) +// gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE, +// _pad_blocked_cb, gst_object_ref (self), +// (GDestroyNotify) gst_object_unref); +// if (self->video_block_pad) +// gst_pad_set_blocked_async_full (self->video_block_pad, TRUE, +// _pad_blocked_cb, gst_object_ref (self), +// (GDestroyNotify) gst_object_unref); +// GST_SUBTITLE_OVERLAY_UNLOCK (self); +// + gst_event_unref (event); + event = NULL; + ret = TRUE; + goto out; + } else if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) + { + gst_event_parse_new_segment_full (event, NULL, NULL, NULL, &format, NULL, NULL, NULL); + if (_this->m_gst_subtitle_segment.format != GST_FORMAT_UNDEFINED && _this->m_gst_subtitle_segment.format != format) + { + eDebug("Subtitle segment format changed: %s -> %s", gst_format_get_name(_this->m_gst_subtitle_segment.format), gst_format_get_name(format)); + gst_segment_init (&_this->m_gst_subtitle_segment, GST_FORMAT_UNDEFINED); + } + } + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + eDebug("Resetting subtitle segment because of flush-stop"); + gst_segment_init (&_this->m_gst_subtitle_segment, GST_FORMAT_UNDEFINED); + /* fall through */ + case GST_EVENT_FLUSH_START: + case GST_EVENT_NEWSEGMENT: + case GST_EVENT_EOS: +// eDebug("GST_EVENT_FLUSH_START GST_EVENT_NEWSEGMENT GST_EVENT_EOS"); + /* Add our event marker to make sure no events from here go ever outside + * the element, they're only interesting for our internal elements */ +// event = +// GST_EVENT_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST +// (event))); +// if (!event->structure) { +// event->structure = +// gst_structure_id_empty_new (_subtitle_overlay_event_marker_id); +// gst_structure_set_parent_refcount (event->structure, +// &event->mini_object.refcount); +// } +// gst_structure_id_set (event->structure, _subtitle_overlay_event_marker_id, +// G_TYPE_BOOLEAN, TRUE, NULL); + break; + default: + eDebug("GST_EVENT_TYPE other: %i", GST_EVENT_TYPE (event)); + break; + } + + ret = _this->m_ghost_pad_subtitle_sink_event (pad, gst_event_ref (event)); +// eDebug("original EVENTFUNC returned %i", ret); + + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { + gboolean update; + gdouble rate, applied_rate; + gint64 start, stop, position; + + GST_DEBUG_OBJECT (pad, "Newsegment event: %" GST_PTR_FORMAT, + event->structure); + gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, + &format, &start, &stop, &position); + + GST_DEBUG_OBJECT (pad, "Old subtitle segment: %" GST_SEGMENT_FORMAT, + &_this->m_gst_subtitle_segment); + if (_this->m_gst_subtitle_segment.format != format) { + GST_DEBUG_OBJECT (pad, "Subtitle segment format changed: %s -> %s", + gst_format_get_name (_this->m_gst_subtitle_segment.format), + gst_format_get_name (format)); + gst_segment_init (&_this->m_gst_subtitle_segment, format); + } + + gst_segment_set_newsegment_full (&_this->m_gst_subtitle_segment, update, rate, + applied_rate, format, start, stop, position); + GST_DEBUG_OBJECT (pad, "New subtitle segment: %" GST_SEGMENT_FORMAT, + &_this->m_gst_subtitle_segment); + } + gst_event_unref (event); +// +out: +// gst_object_unref (_this); + return ret; +} + +GstCaps* eServiceMP3::gstGhostpadGetCAPS(GstPad * pad) +{ +// eDebug("eServiceMP3::gstGhostpadGetCAPS"); + return gst_static_pad_template_get_caps(&subsinktemplate); +} + +gboolean eServiceMP3::gstGhostpadAcceptCAPS(GstPad * pad, GstCaps * caps) +{ + GstCaps *templ_caps = gst_static_pad_template_get_caps (&subsinktemplate); + gboolean ret = gst_caps_can_intersect (templ_caps, caps); + +// eDebug("gstGhostpadAcceptCAPS templ=%s, given=%s ret=%i", gst_caps_to_string(templ_caps), gst_caps_to_string(caps), ret); + gst_caps_unref (templ_caps); + + return ret; +} + +void eServiceMP3::gstGhostpadLink(gpointer user_data, GstCaps * caps) +{ + GstStructure *s; + GstPad *sinkpad; + eServiceMP3 *_this = (eServiceMP3*)user_data; + + // FIXME: Need to cache events from the ghostpad and pass them forward + // now... and keep track of the segment and pass newsegment events + // downstream. + s = gst_caps_get_structure (caps, 0); + + GstPad *ghostpad = gst_element_get_static_pad(_this->m_gst_subtitlebin, "sink"); + GstElement *appsink = gst_bin_get_by_name(GST_BIN(_this->m_gst_subtitlebin), "subtitle_sink"); + GstElement *dvdsubdec = gst_bin_get_by_name(GST_BIN(_this->m_gst_subtitlebin), "vobsubtitle_decoder"); + + gst_ghost_pad_set_target(GST_GHOST_PAD(ghostpad), NULL); + gst_element_unlink(dvdsubdec, appsink); + int ret = -1; + + if ( gst_structure_has_name (s, "video/x-dvd-subpicture") && dvdsubdec ) + { + sinkpad = gst_element_get_static_pad (dvdsubdec, "sink"); + ret = gst_element_link_pads (dvdsubdec, "src", appsink, "sink"); +// eDebug("gstGhostpadLink:: dvdsubdec+appsink = %i", ret); + } + else + { + sinkpad = gst_element_get_static_pad (appsink, "sink"); +// eDebug("gstGhostpadLink:: appsink"); + } + + gst_ghost_pad_set_target (GST_GHOST_PAD(ghostpad), sinkpad); +} + +GstFlowReturn eServiceMP3::gstGhostpadBufferAlloc(GstPad *pad, guint64 offset, guint size, GstCaps *caps, GstBuffer **buf) +{ + eServiceMP3 *_this = (eServiceMP3*) g_object_get_data (G_OBJECT (pad), "application-instance"); + +// eDebug("eServiceMP3::gstGhostpadBufferAlloc prevcaps=%s newcaps=%s", gst_caps_to_string(_this->m_gst_prev_subtitle_caps), gst_caps_to_string(caps)); + if (!GST_PAD_CAPS (pad) || !gst_caps_is_equal (_this->m_gst_prev_subtitle_caps, caps)) + gstGhostpadLink (_this, caps); + + return _this->m_ghost_pad_buffer_alloc (pad, offset, size, caps, buf); +} + +void eServiceMP3::gstGhostpadHasCAPS(GstPad *pad, GParamSpec * unused, gpointer user_data) +{ + eServiceMP3 *_this = (eServiceMP3*)user_data; + + gst_object_ref (pad); + + _this->m_pump.send(Message(3, pad)); } +// after messagepump +void eServiceMP3::gstGhostpadHasCAPS_synced(GstPad *pad) +{ + GstCaps *caps; + + g_object_get (G_OBJECT (pad), "caps", &caps, NULL); + +// eDebug("gstGhostpadHasCAPS:: signal::caps = %s", gst_caps_to_string(caps)); + + if (caps) + { + subtitleStream subs; + +// eDebug("gstGhostpadHasCAPS_synced %p %d", pad, m_subtitleStreams.size()); + + if (!m_subtitleStreams.empty()) + subs = m_subtitleStreams[m_currentSubtitleStream]; + else { + subs.type = stUnknown; + subs.pad = pad; + } + + if ( subs.type == stUnknown ) + { + GstTagList *tags; +// eDebug("gstGhostpadHasCAPS::m_subtitleStreams[%i].type == stUnknown...", m_currentSubtitleStream); + + gchar *g_lang; + g_signal_emit_by_name (m_gst_playbin, "get-text-tags", m_currentSubtitleStream, &tags); + + g_lang = g_strdup_printf ("und"); + if ( tags && gst_is_tag_list(tags) ) + gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); + + subs.language_code = std::string(g_lang); + GstPad *ghostpad = gst_element_get_static_pad(m_gst_subtitlebin, "sink"); + subs.type = getSubtitleType(ghostpad); + + if (!m_subtitleStreams.empty()) + m_subtitleStreams[m_currentSubtitleStream] = subs; + else + m_subtitleStreams.push_back(subs); + + g_free (g_lang); + } + +// eDebug("gstGhostpadHasCAPS:: m_gst_prev_subtitle_caps=%s equal=%i",gst_caps_to_string(m_gst_prev_subtitle_caps),gst_caps_is_equal(m_gst_prev_subtitle_caps, caps)); + + if (!GST_PAD_CAPS (pad) || !gst_caps_is_equal (m_gst_prev_subtitle_caps, caps)) + gstGhostpadLink(this, caps); + + m_gst_prev_subtitle_caps = gst_caps_copy(caps); + + gst_caps_unref (caps); + } + + gst_object_unref (pad); +} + +GstFlowReturn eServiceMP3::gstGhostpadChainFunction(GstPad * pad, GstBuffer * buffer) +{ + GstFlowReturn ret = GST_FLOW_OK; + + eServiceMP3 *_this = (eServiceMP3*)g_object_get_data (G_OBJECT (pad), "application-instance"); + +// gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer); +// gint64 duration_ns = GST_BUFFER_DURATION(buffer); + size_t len = GST_BUFFER_SIZE(buffer); + + unsigned char line[len+1]; + memcpy(line, GST_BUFFER_DATA(buffer), len); + line[len] = 0; +// eDebug("gstGhostpadChainFunction buffer: '%s' caps: %s ", line, gst_caps_to_string(GST_BUFFER_CAPS(buffer))); + + ret = _this->m_ghost_pad_chain_function(pad, buffer); +// eDebug("original chain func returns %i", ret); + return ret; +} + + +// void eServiceMP3::gstCBsubtitleLink(GObject *obj, GParamSpec *pspec, gpointer user_data) +// { +// +// eServiceMP3 *_this = (eServiceMP3*)user_data; +// eDebug("gstCBsubtitleCAPS:: m_currentSubtitleStream=%i, m_subtitleStreams.size()=%i", _this->m_currentSubtitleStream, _this->m_subtitleStreams.size()); +// +// if ( _this->m_currentSubtitleStream >= (int)_this->m_subtitleStreams.size() ) +// { +// eDebug("return invalid stream count"); +// return; +// } +// +// subtitleStream subs = _this->m_subtitleStreams[_this->m_currentSubtitleStream]; +// +// if ( subs.type == stUnknown ) +// { +// GstTagList *tags; +// eDebug("gstCBsubtitleCAPS::m_subtitleStreams[%i].type == stUnknown...", _this->m_currentSubtitleStream); +// +// gchar *g_lang; +// g_signal_emit_by_name (_this->m_gst_playbin, "get-text-tags", _this->m_currentSubtitleStream, &tags); +// +// g_lang = g_strdup_printf ("und"); +// if ( tags && gst_is_tag_list(tags) ) +// gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); +// subs.language_code = std::string(g_lang); +// +// subs.type = getSubtitleType(GST_PAD(obj)); +// +// _this->m_subtitleStreams[_this->m_currentSubtitleStream] = subs; +// +// g_free (g_lang); +// } +// +// gstCBsubtitleLink(subs.type, _this); +// } + +// void eServiceMP3::gstCBsubtitleLink(subtype_t type, gpointer user_data) +// { +// eServiceMP3 *_this = (eServiceMP3*)user_data; +// +// if ( type == stVOB ) +// { +// GstPad *ghostpad = gst_element_get_static_pad(_this->m_gst_subtitlebin, "sink"); +// GstElement *dvdsubdec = gst_bin_get_by_name(GST_BIN(_this->m_gst_subtitlebin), "vobsubtitle_decoder"); +// GstPad *subdecsinkpad = gst_element_get_static_pad (dvdsubdec, "sink"); +// int ret = gst_ghost_pad_set_target((GstGhostPad*)ghostpad, subdecsinkpad); +// GstElement *appsink = gst_bin_get_by_name(GST_BIN(_this->m_gst_subtitlebin), "subtitle_sink"); +// ret += gst_element_link(dvdsubdec, appsink); +// eDebug("gstCBsubtitleLink:: dvdsubdec=%p, subdecsinkpad=%p, ghostpad=%p, set target & link=%i", dvdsubdec, subdecsinkpad, ghostpad, ret); +// } +// else if ( type < stVOB && type > stUnknown ) +// { +// GstPad *ghostpad = gst_element_get_static_pad(_this->m_gst_subtitlebin, "sink"); +// GstElement *appsink = gst_bin_get_by_name(GST_BIN(_this->m_gst_subtitlebin), "subtitle_sink"); +// GstPad *appsinkpad = gst_element_get_static_pad (appsink, "sink"); +// GstElement *dvdsubdec = gst_bin_get_by_name(GST_BIN(_this->m_gst_subtitlebin), "vobsubtitle_decoder"); +// gst_element_unlink(dvdsubdec, appsink); +// int ret = gst_ghost_pad_set_target((GstGhostPad*)ghostpad, appsinkpad); +// eDebug("gstCBsubtitleLink:: appsink=%p, appsinkpad=%p, ghostpad=%p, set target=%i", appsink, appsinkpad, ghostpad, ret); +// } +// else +// { +// eDebug("gstCBsubtitleLink:: unsupported subtitles"); +// } +// } +/* +gboolean eServiceMP3::gstCBsubtitleDrop(GstPad *pad, GstBuffer *buffer, gpointer user_data) +{ + eDebug("gstCBsubtitleDrop"); + + gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer); + gint64 duration_ns = GST_BUFFER_DURATION(buffer); + size_t len = GST_BUFFER_SIZE(buffer); + + unsigned char line[len+1]; + memcpy(line, GST_BUFFER_DATA(buffer), len); + line[len] = 0; + eDebug("dropping buffer '%s' ", line); + return false; +}*/ + + void eServiceMP3::pullSubtitle() { - GstElement *sink; - g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL); - if (sink) + GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_subtitlebin), "subtitle_sink"); +// GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); + + if (appsink) { while (m_subs_to_pull && m_subtitle_pages.size() < 2) { @@ -1514,28 +1948,55 @@ void eServiceMP3::pullSubtitle() { eSingleLocker l(m_subs_to_pull_lock); --m_subs_to_pull; - g_signal_emit_by_name (sink, "pull-buffer", &buffer); + g_signal_emit_by_name (appsink, "pull-buffer", &buffer); } if (buffer) { gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer); gint64 duration_ns = GST_BUFFER_DURATION(buffer); size_t len = GST_BUFFER_SIZE(buffer); - unsigned char line[len+1]; - memcpy(line, GST_BUFFER_DATA(buffer), len); - line[len] = 0; - eDebug("got new subtitle @ buf_pos = %lld ns (in pts=%lld): '%s' ", buf_pos, buf_pos/11111, line); - ePangoSubtitlePage page; - gRGB rgbcol(0xD0,0xD0,0xD0); - page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line)); - page.show_pts = buf_pos / 11111L; - page.m_timeout = duration_ns / 1000000; - m_subtitle_pages.push_back(page); - pushSubtitles(); + eDebug("pullSubtitle m_subtitleStreams[m_currentSubtitleStream].type=%i",m_subtitleStreams[m_currentSubtitleStream].type); + + if ( m_subtitleStreams[m_currentSubtitleStream].type ) + { + if ( m_subtitleStreams[m_currentSubtitleStream].type < stVOB ) + { + unsigned char line[len+1]; + SubtitlePage page; + memcpy(line, GST_BUFFER_DATA(buffer), len); + line[len] = 0; + eDebug("got new text subtitle @ buf_pos = %lld ns (in pts=%lld): '%s' ", buf_pos, buf_pos/11111, line); + gRGB rgbcol(0xD0,0xD0,0xD0); + page.type = SubtitlePage::Pango; + page.pango_page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line)); + page.pango_page.m_show_pts = buf_pos / 11111L; + page.pango_page.m_timeout = duration_ns / 1000000; + m_subtitle_pages.push_back(page); + if (m_subtitle_pages.size()==1) + pushSubtitles(); + } + else if ( m_subtitleStreams[m_currentSubtitleStream].type == stVOB ) + { + SubtitlePage page; + eDebug("got new subpicture @ buf_pos = %lld ns (in pts=%lld), duration=%lld ns, len=%i bytes. ", buf_pos, buf_pos/11111, duration_ns, len); + page.type = SubtitlePage::Vob; + page.vob_page.m_pixmap = new gPixmap(eSize(720, 576), 32, 1); + memcpy(page.vob_page.m_pixmap->surface->data, GST_BUFFER_DATA(buffer), len); + page.vob_page.m_show_pts = buf_pos / 11111L; + page.vob_page.m_timeout = duration_ns / 1000; + m_subtitle_pages.push_back(page); + if (m_subtitle_pages.size()==1) + pushSubtitles(); + } + else + { + eDebug("unsupported subpicture... ignoring"); + } + } gst_buffer_unref(buffer); } } - gst_object_unref(sink); + gst_object_unref(appsink); } else eDebug("no subtitle sink!"); @@ -1543,45 +2004,64 @@ void eServiceMP3::pullSubtitle() void eServiceMP3::pushSubtitles() { - ePangoSubtitlePage page; - pts_t running_pts; while ( !m_subtitle_pages.empty() ) { + SubtitlePage &frontpage = m_subtitle_pages.front(); + pts_t running_pts; + gint64 diff_ms = 0; + gint64 show_pts; + getPlayPosition(running_pts); - page = m_subtitle_pages.front(); - gint64 diff_ms = ( page.show_pts - running_pts ) / 90; - eDebug("eServiceMP3::pushSubtitles show_pts = %lld running_pts = %lld diff = %lld", page.show_pts, running_pts, diff_ms); - if (diff_ms < -100) - { - GstFormat fmt = GST_FORMAT_TIME; - gint64 now; - if (gst_element_query_position(m_gst_playbin, &fmt, &now) != -1) - { - now /= 11111; - diff_ms = abs((now - running_pts) / 90); - eDebug("diff < -100ms check decoder/pipeline diff: decoder: %lld, pipeline: %lld, diff: %lld", running_pts, now, diff_ms); - if (diff_ms > 100000) - { - eDebug("high decoder/pipeline difference.. assume decoder has now started yet.. check again in 1sec"); - m_subtitle_sync_timer->start(1000, true); - break; - } + + if (m_decoder_time_valid_state < 4) { + ++m_decoder_time_valid_state; + if (m_prev_decoder_time == running_pts) + m_decoder_time_valid_state = 0; + if (m_decoder_time_valid_state < 4) { +// if (m_decoder_time_valid_state) +// eDebug("%d: decoder time not valid! prev %lld, now %lld\n", m_decoder_time_valid_state, m_prev_decoder_time/90, running_pts/90); +// else +// eDebug("%d: decoder time not valid! now %lld\n", m_decoder_time_valid_state, running_pts/90); + m_subtitle_sync_timer->start(25, true); + m_prev_decoder_time = running_pts; + break; } - else - eDebug("query position for decoder/pipeline check failed!"); - eDebug("subtitle to late... drop"); + } + + if (frontpage.type == SubtitlePage::Pango) + show_pts = frontpage.pango_page.m_show_pts; + else + show_pts = frontpage.vob_page.m_show_pts; + + diff_ms = ( show_pts - running_pts ) / 90; + eDebug("check subtitle: decoder: %lld, show_pts: %lld, diff: %lld ms", running_pts/90, show_pts/90, diff_ms); + + if ( diff_ms < -100 ) + { + eDebug("subtitle too late... drop"); m_subtitle_pages.pop_front(); } else if ( diff_ms > 20 ) { -// eDebug("start recheck timer"); - m_subtitle_sync_timer->start(diff_ms > 1000 ? 1000 : diff_ms, true); + eDebug("start timer"); + m_subtitle_sync_timer->start(diff_ms, true); break; } else // immediate show { - if (m_subtitle_widget) - m_subtitle_widget->setPage(page); + if ( m_subtitle_widget ) + { + eDebug("show!\n"); + if ( frontpage.type == SubtitlePage::Pango) + m_subtitle_widget->setPage(frontpage.pango_page); + else + { + m_subtitle_widget->setPixmap(frontpage.vob_page.m_pixmap, eRect(0, 0, 720, 576)); + eDebug("blit vobsub pixmap... hide in %i ms", frontpage.vob_page.m_timeout); + m_subtitle_hide_timer->start(frontpage.vob_page.m_timeout, true); + } + m_subtitle_widget->show(); + } m_subtitle_pages.pop_front(); } } @@ -1589,12 +2069,26 @@ void eServiceMP3::pushSubtitles() pullSubtitle(); } +void eServiceMP3::hideSubtitles() +{ +// eDebug("eServiceMP3::hideSubtitles()"); + if ( m_subtitle_widget ) + m_subtitle_widget->hide(); +} + RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple) { +// eDebug ("eServiceMP3::enableSubtitles m_currentSubtitleStream=%i this=%p",m_currentSubtitleStream, this); ePyObject entry; int tuplesize = PyTuple_Size(tuple); int pid, type; gint text_pid = 0; + eSingleLocker l(m_subs_to_pull_lock); + +// GstPad *pad = 0; +// g_signal_emit_by_name (m_gst_playbin, "get-text-pad", m_currentSubtitleStream, &pad); +// gst_element_get_static_pad(m_gst_subtitlebin, "sink"); +// gulong subprobe_handler_id = gst_pad_add_buffer_probe (pad, G_CALLBACK (gstCBsubtitleDrop), NULL); if (!PyTuple_Check(tuple)) goto error_out; @@ -1609,12 +2103,14 @@ RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple) goto error_out; type = PyInt_AsLong(entry); +// eDebug ("eServiceMP3::enableSubtitles new pid=%i",pid); if (m_currentSubtitleStream != pid) { - eSingleLocker l(m_subs_to_pull_lock); g_object_set (G_OBJECT (m_gst_playbin), "current-text", pid, NULL); +// eDebug ("eServiceMP3::enableSubtitles g_object_set current-text = %i", pid); m_currentSubtitleStream = pid; m_subs_to_pull = 0; + m_prev_decoder_time = -1; m_subtitle_pages.clear(); } @@ -1625,6 +2121,9 @@ RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple) g_object_get (G_OBJECT (m_gst_playbin), "current-text", &text_pid, NULL); eDebug ("eServiceMP3::switched to subtitle stream %i", text_pid); +// gst_pad_remove_buffer_probe (pad, subprobe_handler_id); + + m_event((iPlayableService*)this, evUpdatedInfo); return 0; @@ -1651,26 +2150,25 @@ PyObject *eServiceMP3::getCachedSubtitle() PyObject *eServiceMP3::getSubtitleList() { - eDebug("eServiceMP3::getSubtitleList"); - +// eDebug("eServiceMP3::getSubtitleList"); ePyObject l = PyList_New(0); - int stream_count[sizeof(subtype_t)]; - for ( unsigned int i = 0; i < sizeof(subtype_t); i++ ) - stream_count[i] = 0; - + int stream_idx = 0; + for (std::vector::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream) { subtype_t type = IterSubtitleStream->type; ePyObject tuple = PyTuple_New(5); +// eDebug("eServiceMP3::getSubtitleList idx=%i type=%i, code=%s", stream_idx, int(type), (IterSubtitleStream->language_code).c_str()); PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2)); - PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_count[type])); + PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_idx)); PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(type))); PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0)); PyTuple_SET_ITEM(tuple, 4, PyString_FromString((IterSubtitleStream->language_code).c_str())); PyList_Append(l, tuple); Py_DECREF(tuple); - stream_count[type]++; + stream_idx++; } + eDebug("eServiceMP3::getSubtitleList finished"); return l; } diff --git a/lib/service/servicemp3.h b/lib/service/servicemp3.h index b864a10..c620151 100644 --- a/lib/service/servicemp3.h +++ b/lib/service/servicemp3.h @@ -46,7 +46,7 @@ public: typedef struct _GstElement GstElement; typedef enum { atUnknown, atMPEG, atMP3, atAC3, atDTS, atAAC, atPCM, atOGG, atFLAC } audiotype_t; -typedef enum { stPlainText, stSSA, stSRT } subtype_t; +typedef enum { stUnknown, stPlainText, stSSA, stASS, stSRT, stVOB, stPGS } subtype_t; typedef enum { ctNone, ctMPEGTS, ctMPEGPS, ctMKV, ctAVI, ctMP4, ctVCD, ctCDA } containertype_t; class eServiceMP3: public iPlayableService, public iPauseableService, @@ -167,7 +167,7 @@ public: int bufferPercent; int avgInRate; int avgOutRate; - long long bufferingLeft; + int64_t bufferingLeft; bufferInfo() :bufferPercent(0), avgInRate(0), avgOutRate(0), bufferingLeft(-1) { @@ -197,24 +197,74 @@ private: }; int m_state; GstElement *m_gst_playbin; + GstElement *m_gst_subtitlebin; GstTagList *m_stream_tags; - eFixedMessagePump m_pump; + + struct Message + { + Message() + :type(-1) + {} + Message(int type) + :type(type) + {} + Message(int type, GstPad *pad) + :type(type) + { + d.pad=pad; + } + + int type; + union { + GstPad *pad; // for msg type 3 + } d; + }; + + eFixedMessagePump m_pump; std::string m_error_message; audiotype_t gstCheckAudioPad(GstStructure* structure); void gstBusCall(GstBus *bus, GstMessage *msg); static GstBusSyncReply gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data); - static void gstCBsubtitleAvail(GstElement *element, gpointer user_data); - GstPad* gstCreateSubtitleSink(eServiceMP3* _this, subtype_t type); - void gstPoll(const int&); static void gstHTTPSourceSetAgent(GObject *source, GParamSpec *unused, gpointer user_data); + static void gstCBsubtitleAvail(GstElement *element, gpointer user_data); + static GstCaps* gstGhostpadGetCAPS (GstPad * pad); + static gboolean gstGhostpadAcceptCAPS(GstPad * pad, GstCaps * caps); + static void gstGhostpadLink(gpointer user_data, GstCaps * caps); + static GstFlowReturn gstGhostpadBufferAlloc(GstPad *pad, guint64 offset, guint size, GstCaps *caps, GstBuffer **buf); + static void gstGhostpadHasCAPS(GstPad *pad, GParamSpec * unused, gpointer user_data); + static gboolean gstGhostpadSinkEvent(GstPad * pad, GstEvent * event); + static GstFlowReturn gstGhostpadChainFunction(GstPad * pad, GstBuffer * buffer); +/* static void gstCBsubtitleCAPS(GObject *obj, GParamSpec *pspec, gpointer user_data); + static void gstCBsubtitleLink(subtype_t type, gpointer user_data); + static gboolean gstCBsubtitleDrop(GstPad *pad, GstBuffer *buffer, gpointer user_data);*/ + void gstPoll(const Message&); + void gstGhostpadHasCAPS_synced(GstPad *pad); + + GstPadBufferAllocFunction m_ghost_pad_buffer_alloc; + GstPadChainFunction m_ghost_pad_chain_function; + GstPadEventFunction m_ghost_pad_subtitle_sink_event; + GstCaps *m_gst_prev_subtitle_caps; + GstSegment m_gst_subtitle_segment; + GstPadEventFunction m_gst_sink_event; - std::list m_subtitle_pages; + struct SubtitlePage + { + enum { Unknown, Pango, Vob } type; + ePangoSubtitlePage pango_page; + eVobSubtitlePage vob_page; + }; + + std::list m_subtitle_pages; ePtr m_subtitle_sync_timer; - + ePtr m_subtitle_hide_timer; ePtr m_streamingsrc_timeout; + pts_t m_prev_decoder_time; + int m_decoder_time_valid_state; + void pushSubtitles(); void pullSubtitle(); + void hideSubtitles(); void sourceTimeout(); int m_subs_to_pull; sourceStream m_sourceinfo;