continue;
}
- size_t iframe_len;
- /* try to align to iframe */
- int direction = pts < 0 ? -1 : 1;
- m_tstools.findFrame(offset, iframe_len, direction);
-
- eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx (skipped additional %d frames due to iframe re-align)", relative, pts, offset, direction);
+ eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
}
m_pvr_thread->setStreamMode(1);
m_pvr_thread->setScatterGather(this);
+ m_event(this, evtPreStart);
+
if (m_pvr_thread->start(file, m_pvr_fd_dst))
{
delete m_pvr_thread;
from Components.MultiContent import MultiContentEntryText
from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
from Components.VideoWindow import VideoWindow
+from Components.Label import Label
from Screens.InfoBarGenerics import InfoBarSeek, InfoBarCueSheetSupport
from Components.GUIComponent import GUIComponent
from enigma import eListboxPythonMultiContent, eListbox, gFont, iPlayableService, RT_HALIGN_RIGHT
<widget source="session.CurrentService" render="Label" position="135,405" size="450,50" font="Regular;22" halign="center" valign="center">
<convert type="ServiceName">Name</convert>
</widget>
- <widget source="session.CurrentService" render="Label" position="50,450" zPosition="1" size="620,25" font="Regular;20" halign="center" valign="center">
+ <widget source="session.CurrentService" render="Label" position="320,450" zPosition="1" size="420,25" font="Regular;20" halign="left" valign="center">
<convert type="ServicePosition">Position,Detailed</convert>
</widget>
- <eLabel position="62,98" size="179,274" backgroundColor="#505555" />
- <eLabel position="64,100" size="175,270" backgroundColor="#000000" />
- <widget source="cutlist" position="64,100" zPosition="1" size="175,270" scrollbarMode="showOnDemand" transparent="1" render="Listbox" >
+ <widget name="SeekState" position="210,450" zPosition="1" size="100,25" halign="right" font="Regular;20" valign="center" />
+ <eLabel position="48,98" size="204,274" backgroundColor="#505555" />
+ <eLabel position="50,100" size="200,270" backgroundColor="#000000" />
+ <widget source="cutlist" position="50,100" zPosition="1" size="200,270" scrollbarMode="showOnDemand" transparent="1" render="Listbox" >
<convert type="TemplatedMultiContent">
{"template": [
MultiContentEntryText(size=(125, 20), text = 1, backcolor = MultiContentTemplateColor(3)),
self["Timeline"] = ServicePositionGauge(self.session.nav)
self["cutlist"] = List(self.getCutlist())
self["cutlist"].onSelectionChanged.append(self.selectionChanged)
+ self["SeekState"] = Label()
+ self.onPlayStateChanged.append(self.updateStateLabel)
+ self.updateStateLabel(self.seekstate)
self["Video"] = VideoWindow(decoder = 0)
})
# to track new entries we save the last version of the cutlist
- self.last_cuts = [ ]
+ self.last_cuts = self.getCutlist()
self.cut_start = None
+ self.inhibit_seek = False
self.onClose.append(self.__onClose)
def __onClose(self):
- self.session.nav.playService(self.old_service)
+ self.session.nav.playService(self.old_service, forceRestart=True)
+ def updateStateLabel(self, state):
+ self["SeekState"].setText(state[3].strip())
+
def showTutorial(self):
if not self.tutorial_seen:
self.tutorial_seen = True
return r
def selectionChanged(self):
- where = self["cutlist"].getCurrent()
- if where is None:
- print "no selection"
- return
- pts = where[0][0]
- seek = self.getSeek()
- if seek is None:
- print "no seek"
- return
- seek.seekTo(pts)
+ if not self.inhibit_seek:
+ where = self["cutlist"].getCurrent()
+ if where is None:
+ print "no selection"
+ return
+ pts = where[0][0]
+ seek = self.getSeek()
+ if seek is None:
+ print "no seek"
+ return
+ seek.seekTo(pts)
def refillList(self):
print "cue sheet changed, refilling"
self.downloadCuesheet()
- # get the first changed entry, and select it
+ # get the first changed entry, counted from the end, and select it
new_list = self.getCutlist()
self["cutlist"].list = new_list
- for i in range(min(len(new_list), len(self.last_cuts))):
- if new_list[i] != self.last_cuts[i]:
- self["cutlist"].setIndex(i)
+ l1 = len(new_list)
+ l2 = len(self.last_cuts)
+ for i in range(min(l1, l2)):
+ if new_list[l1-i-1] != self.last_cuts[l2-i-1]:
+ self["cutlist"].setIndex(l1-i-1)
break
self.last_cuts = new_list
def getStateForPosition(self, pos):
- state = 0 # in
-
- # when first point is "in", the beginning is "out"
- if len(self.cut_list) and self.cut_list[0][1] == 0:
- state = 1
-
+ state = -1
for (where, what) in self.cut_list:
- if where < pos:
- if what == 0: # in
- state = 0
- elif what == 1: # out
+ if what in [0, 1]:
+ if where < pos:
+ state = what
+ elif where == pos:
state = 1
+ elif state == -1:
+ state = 1 - what
+ if state == -1:
+ state = 0
return state
def showMenu(self):
in_after = None
for (where, what) in self.cut_list:
- if what == 1 and where < self.context_position: # out
+ if what == 1 and where <= self.context_position: # out
out_before = (where, what)
elif what == 0 and where < self.context_position: # in, before out
out_before = None
- elif what == 0 and where > self.context_position and in_after is None:
+ elif what == 0 and where >= self.context_position and in_after is None:
in_after = (where, what)
if out_before is not None:
if in_after is not None:
self.cut_list.remove(in_after)
+ self.inhibit_seek = True
self.uploadCuesheet()
+ self.inhibit_seek = False
elif result == CutListContextMenu.RET_MARK:
self.__addMark()
elif result == CutListContextMenu.RET_DELETEMARK:
self.cut_list.remove(self.context_nearest_mark)
+ self.inhibit_seek = True
self.uploadCuesheet()
+ self.inhibit_seek = False
elif result == CutListContextMenu.RET_REMOVEBEFORE:
# remove in/out marks before current position
for (where, what) in self.cut_list[:]:
self.cut_list.remove((where, what))
# add 'in' point
bisect.insort(self.cut_list, (self.context_position, 0))
+ self.inhibit_seek = True
self.uploadCuesheet()
+ self.inhibit_seek = False
elif result == CutListContextMenu.RET_REMOVEAFTER:
# remove in/out marks after current position
for (where, what) in self.cut_list[:]:
self.cut_list.remove((where, what))
# add 'out' point
bisect.insort(self.cut_list, (self.context_position, 1))
+ self.inhibit_seek = True
self.uploadCuesheet()
+ self.inhibit_seek = False
elif result == CutListContextMenu.RET_GRABFRAME:
self.grabFrame()
m_is_pvr = !m_reference.path.empty();
m_timeshift_enabled = m_timeshift_active = 0, m_timeshift_changed = 0;
- m_skipmode = 0;
+ m_skipmode = m_fastforward = m_slowmotion = 0;
CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
m_event((iPlayableService*)this, evUpdatedInfo);
break;
}
+ case eDVBServicePMTHandler::eventPreStart:
+ loadCuesheet();
+ break;
case eDVBServicePMTHandler::eventEOF:
m_event((iPlayableService*)this, evEOF);
break;
m_event_handler.inject(event, 0);
m_event_handler.inject(empty, 1);
}
- loadCuesheet();
m_event(this, evStart);
}
return 0;
eDebug("eDVBServicePlay::setSlowMotion(%d)", ratio);
setFastForward_internal(0);
if (m_decoder)
+ {
+ m_slowmotion = ratio;
return m_decoder->setSlowMotion(ratio);
+ }
else
return -1;
}
return setFastForward_internal(ratio);
}
-RESULT eDVBServicePlay::setFastForward_internal(int ratio)
+RESULT eDVBServicePlay::setFastForward_internal(int ratio, bool final_seek)
{
- int skipmode, ffratio;
-
+ int skipmode, ffratio, ret = 0;
+ pts_t pos=0;
+
if (ratio > 8)
{
skipmode = ratio;
if (m_cue)
m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
}
-
+
m_skipmode = skipmode;
-
+
+ if (final_seek)
+ eDebug("trickplay stopped .. ret %d, pos %lld", getPlayPosition(pos), pos);
+
+ m_fastforward = ffratio;
+
if (!m_decoder)
return -1;
-
+
if (ffratio == 0)
; /* return m_decoder->play(); is done in caller*/
else if (ffratio != 1)
- return m_decoder->setFastForward(ffratio);
+ ret = m_decoder->setFastForward(ffratio);
else
- return m_decoder->setTrickmode();
- return 0;
+ ret = m_decoder->setTrickmode();
+
+ if (pos)
+ eDebug("final seek after trickplay ret %d", seekTo(pos));
+
+ return ret;
}
RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
RESULT eDVBServicePlay::pause()
{
eDebug("eDVBServicePlay::pause");
- setFastForward_internal(0);
+ setFastForward_internal(0, m_slowmotion || m_fastforward > 1);
if (m_decoder)
{
+ m_slowmotion = 0;
m_is_paused = 1;
return m_decoder->pause();
} else
RESULT eDVBServicePlay::unpause()
{
eDebug("eDVBServicePlay::unpause");
- setFastForward_internal(0);
+ setFastForward_internal(0, m_slowmotion || m_fastforward > 1);
if (m_decoder)
{
+ m_slowmotion = 0;
m_is_paused = 0;
return m_decoder->play();
} else
{
eDVBServicePMTHandler::program program;
eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+ pts_t position = -1;
if (h.getProgramInfo(program))
return -1;
apidtype = program.audioStreams[stream].type;
}
+ if (i != -1 && apid != m_current_audio_pid && (m_is_pvr || m_timeshift_active))
+ eDebug("getPlayPosition ret %d, pos %lld in selectAudioStream", getPlayPosition(position), position);
+
m_current_audio_pid = apid;
if (m_is_primary && m_decoder->setAudioPID(apid, apidtype))
return -4;
}
+ if (position != -1)
+ eDebug("seekTo ret %d", seekTo(position));
+
int rdsPid = apid;
/* if we are not in PVR mode, timeshift is not active and we are not in pip mode, check if we need to enable the rds reader */
m_new_subtitle_page_connection = 0;
m_rds_decoder_event_connection = 0;
m_video_event_connection = 0;
- m_is_paused = m_skipmode = 0; /* not supported in live mode */
+ m_is_paused = m_skipmode = m_fastforward = m_slowmotion = 0; /* not supported in live mode */
/* free the timeshift service handler, we need the resources */
m_service_handler_timeshift.free();
r.path = m_timeshift_file;
m_cue = new eCueSheet();
+ m_cue->seekTo(0, -1000);
m_service_handler_timeshift.tune(r, 1, m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now.");
pause();
updateDecoder(); /* mainly to switch off PCR, and to set pause */
-
+
m_event((iPlayableService*)this, evSeekableStatusChanged);
}
}
}
- std::string config_delay;
- int config_delay_int = 0;
- if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
- config_delay_int = atoi(config_delay.c_str());
- m_decoder->setAC3Delay(ac3_delay == -1 ? config_delay_int : ac3_delay + config_delay_int);
-
- if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
- config_delay_int = atoi(config_delay.c_str());
- else
- config_delay_int = 0;
- m_decoder->setPCMDelay(pcm_delay == -1 ? config_delay_int : pcm_delay + config_delay_int);
+ setAC3Delay(ac3_delay == -1 ? 0 : ac3_delay);
+ setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay);
m_decoder->setVideoPID(vpid, vpidtype);
selectAudioStream();
{
if (m_dvb_service)
m_dvb_service->setCacheEntry(eDVBService::cAC3DELAY, delay ? delay : -1);
- if (m_decoder)
- m_decoder->setAC3Delay(delay);
+ if (m_decoder) {
+ std::string config_delay;
+ int config_delay_int = 0;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
+ config_delay_int = atoi(config_delay.c_str());
+ m_decoder->setAC3Delay(delay + config_delay_int);
+ }
}
void eDVBServicePlay::setPCMDelay(int delay)
{
if (m_dvb_service)
m_dvb_service->setCacheEntry(eDVBService::cPCMDELAY, delay ? delay : -1);
- if (m_decoder)
- m_decoder->setPCMDelay(delay);
+ if (m_decoder) {
+ std::string config_delay;
+ int config_delay_int = 0;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
+ config_delay_int = atoi(config_delay.c_str());
+ else
+ config_delay_int = 0;
+ m_decoder->setPCMDelay(delay + config_delay_int);
+ }
}
void eDVBServicePlay::video_event(struct iTSMPEGDecoder::videoEvent event)