<item level="0" text="TV System">config.av.tvsystem</item>
<item level="1" text="WSS on 4:3">config.av.wss</item>
<item level="1" text="AC3 default">config.av.defaultac3</item>
- <item level="1" text="General AC3 delay">config.av.generalAC3delay</item>
- <item level="1" text="General PCM delay">config.av.generalPCMdelay</item>
+ <item level="1" text="General AC3 delay (ms)">config.av.generalAC3delay</item>
+ <item level="1" text="General PCM delay (ms)">config.av.generalPCMdelay</item>
<item level="1" text="AC3 downmix" requires="CanDownmixAC3">config.av.downmix_ac3</item>
<item level="1" text="Auto scart switching" requires="ScartSwitch">config.av.vcrswitch</item>
</setup>
int eTSMPEGDecoder::m_pcm_delay=-1,
eTSMPEGDecoder::m_ac3_delay=-1;
-RESULT eTSMPEGDecoder::setPCMDelay(int delay)
+RESULT eTSMPEGDecoder::setHwPCMDelay(int delay)
{
- if (m_decoder == 0 && delay != m_pcm_delay )
+ if (delay != m_pcm_delay )
{
FILE *fp = fopen("/proc/stb/audio/audio_delay_pcm", "w");
if (fp)
return -1;
}
-RESULT eTSMPEGDecoder::setAC3Delay(int delay)
+RESULT eTSMPEGDecoder::setHwAC3Delay(int delay)
{
- if ( m_decoder == 0 && delay != m_ac3_delay )
+ if ( delay != m_ac3_delay )
{
FILE *fp = fopen("/proc/stb/audio/audio_delay_bitstream", "w");
if (fp)
return -1;
}
+
+RESULT eTSMPEGDecoder::setPCMDelay(int delay)
+{
+ return m_decoder == 0 ? setHwPCMDelay(delay) : -1;
+}
+
+RESULT eTSMPEGDecoder::setAC3Delay(int delay)
+{
+ return m_decoder == 0 ? setHwAC3Delay(delay) : -1;
+}
+
eTSMPEGDecoder::eTSMPEGDecoder(eDVBDemux *demux, int decoder)
: m_demux(demux),
m_vpid(-1), m_vtype(-1), m_apid(-1), m_atype(-1), m_pcrpid(-1), m_textpid(-1),
int getVideoProgressive();
int getVideoFrameRate();
int getVideoAspect();
+ static RESULT setHwPCMDelay(int delay);
+ static RESULT setHwAC3Delay(int delay);
};
#endif
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 */
}
off_t last = 0;
off_t last2 = 0;
pts_t lastc = 0;
+ ts += 1; // Add rounding error margin
for (std::map<off_t, pts_t>::const_iterator i(m_access_points.begin()); i != m_access_points.end(); ++i)
{
pts_t delta = getDelta(i->first);
int eDVBTSTools::findNextPicture(off_t &offset, size_t &len, int &distance, int frame_types)
{
- int nr_frames = 0;
+ int nr_frames, direction;
// eDebug("trying to move %d frames at %llx", distance, offset);
frame_types = frametypeI; /* TODO: intelligent "allow IP frames when not crossing an I-Frame */
- int direction = distance > 0 ? 0 : -1;
- distance = abs(distance);
-
off_t new_offset = offset;
size_t new_len = len;
int first = 1;
+ if (distance > 0) {
+ direction = 0;
+ nr_frames = 0;
+ } else {
+ direction = -1;
+ nr_frames = -1;
+ distance = -distance+1;
+ }
while (distance > 0)
{
int dir = direction;
// eDebug("we moved %d, %d to go frames (now at %llx)", dir, distance, new_offset);
- if (distance >= 0 || first)
+ if (distance >= 0 || direction == 0)
{
first = 0;
offset = new_offset;
len = new_len;
nr_frames += abs(dir);
+ }
+ else if (first) {
+ first = 0;
+ offset = new_offset;
+ len = new_len;
+ nr_frames += abs(dir) + distance; // never jump forward during rewind
}
}
from config import config, ConfigSlider, ConfigSelection, ConfigYesNo, \
- ConfigEnableDisable, ConfigSubsection, ConfigBoolean, ConfigNumber, ConfigNothing, NoSave
+ ConfigEnableDisable, ConfigSubsection, ConfigBoolean, ConfigSelectionNumber, ConfigNothing, NoSave
from enigma import eAVSwitch, getDesktop
from SystemInfo import SystemInfo
from os import path as os_path
config.av.tvsystem = ConfigSelection(choices = {"pal": _("PAL"), "ntsc": _("NTSC"), "multinorm": _("multinorm")}, default="pal")
config.av.wss = ConfigEnableDisable(default = True)
config.av.defaultac3 = ConfigYesNo(default = False)
- config.av.generalAC3delay = ConfigNumber(default = 0)
- config.av.generalPCMdelay = ConfigNumber(default = 0)
+ config.av.generalAC3delay = ConfigSelectionNumber(-1000, 1000, 25, default = 0)
+ config.av.generalPCMdelay = ConfigSelectionNumber(-1000, 1000, 25, default = 0)
config.av.vcrswitch = ConfigEnableDisable(default = False)
iAVSwitch = AVSwitch()
"ts": "movie",
"avi": "movie",
"divx": "movie",
+ "m4v": "movie",
"mpg": "movie",
"mpeg": "movie",
"mkv": "movie",
lnb_choices = {
"universal_lnb": _("Universal LNB"),
-# "unicable": _("Unicable"),
+ "unicable": _("Unicable"),
"c_band": _("C-Band"),
"user_defined": _("User defined")}
ConfigText.onDeselect(self, session)
self.hidden = True
+# lets the user select between [min, min+stepwidth, min+(stepwidth*2)..., maxval] with maxval <= max depending
+# on the stepwidth
+# min, max, stepwidth, default are int values
+# wraparound: pressing RIGHT key at max value brings you to min value and vice versa if set to True
+class ConfigSelectionNumber(ConfigSelection):
+ def __init__(self, min, max, stepwidth, default = None, wraparound = False):
+ self.wraparound = wraparound
+ if default is None:
+ default = min
+ default = str(default)
+ choices = []
+ step = min
+ while step <= max:
+ choices.append(str(step))
+ step += stepwidth
+
+ ConfigSelection.__init__(self, choices, default)
+
+ def getValue(self):
+ return int(self.text)
+
+ def setValue(self, val):
+ self.text = str(val)
+
+ def handleKey(self, key):
+ if not self.wraparound:
+ if key == KEY_RIGHT:
+ if len(self.choices) == (self.choices.index(self.value) + 1):
+ return
+ if key == KEY_LEFT:
+ if self.choices.index(self.value) == 0:
+ return
+ ConfigSelection.handleKey(self, key)
+
+
+
class ConfigNumber(ConfigText):
def __init__(self, default = 0):
ConfigText.__init__(self, str(default), fixed_size = False)
def __init__(self, list, default = None):
if default is not None:
default = str(default)
- ConfigSelection.__init__(self, choices = [(str(orbpos), desc) for (orbpos, desc, flags) in list], default = default)
+ choices = [(str(orbpos), desc) for (orbpos, desc, flags) in list]
+ choices.sort(key = lambda x: int(x[0]))
+
+ ConfigSelection.__init__(self, choices = choices, default = default)
def getOrbitalPosition(self):
if self.value == "":
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)
+ 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()
# '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|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|m2ts|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"] = self.filelist
self.playlist = MyPlayList()
iPlayableService.evSOF: self.__evSOF,
})
- self.minSpeedBackward = useSeekBackHack and 16 or 0
-
class InfoBarSeekActionMap(HelpableActionMap):
def __init__(self, screen, *args, **kwargs):
HelpableActionMap.__init__(self, screen, *args, **kwargs)
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:
+# 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)
def makeStateBackward(self, n):
- minspeed = config.seek.stepwise_minspeed.value
- repeat = int(config.seek.stepwise_repeat.value)
- if self.minSpeedBackward and n < self.minSpeedBackward:
- r = (self.minSpeedBackward - 1)/ n + 1
- if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
- r = max(r, repeat)
- return (0, -n * r, r, "<< %dx" % n)
- elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
- return (0, -n * repeat, repeat, "<< %dx" % n)
- else:
+# 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)
def makeStateSlowMotion(self, n):
if config.seek.on_pause.value == "play":
self.unPauseService()
elif config.seek.on_pause.value == "step":
- self.doSeekRelative(0)
+ self.doSeekRelative(1)
elif config.seek.on_pause.value == "last":
self.setSeekState(self.lastseekstate)
self.lastseekstate = self.SEEK_STATE_PLAY
self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
self.doSeekRelative(-6)
elif seekstate == self.SEEK_STATE_PAUSE:
- self.doSeekRelative(-3)
+ self.doSeekRelative(-1)
elif self.isStateForward(seekstate):
speed = seekstate[1]
if seekstate[2]:
/* note: this requires gstreamer 0.10.x and a big list of plugins. */
/* it's currently hardcoded to use a big-endian alsasink as sink. */
+#include <lib/base/ebase.h>
#include <lib/base/eerror.h>
+#include <lib/base/init_num.h>
+#include <lib/base/init.h>
+#include <lib/base/nconfig.h>
#include <lib/base/object.h>
-#include <lib/base/ebase.h>
-#include <string>
+#include <lib/dvb/decoder.h>
+#include <lib/components/file_eraser.h>
+#include <lib/gui/esubtitle.h>
#include <lib/service/servicemp3.h>
#include <lib/service/service.h>
-#include <lib/components/file_eraser.h>
-#include <lib/base/init_num.h>
-#include <lib/base/init.h>
+
+#include <string>
+
#include <gst/gst.h>
#include <gst/pbutils/missing-plugins.h>
#include <sys/stat.h>
-/* for subtitles */
-#include <lib/gui/esubtitle.h>
// eServiceFactoryMP3
extensions.push_back("vob");
extensions.push_back("wav");
extensions.push_back("wave");
+ extensions.push_back("m4v");
extensions.push_back("mkv");
extensions.push_back("avi");
extensions.push_back("divx");
extensions.push_back("mp4");
extensions.push_back("mov");
extensions.push_back("m4a");
+ extensions.push_back("m2ts");
sc->addServiceFactory(eServiceFactoryMP3::id, this, extensions);
}
}
// eServiceMP3
+int eServiceMP3::ac3_delay,
+ eServiceMP3::pcm_delay;
eServiceMP3::eServiceMP3(eServiceReference ref)
:m_ref(ref), m_pump(eApp, 1)
sourceinfo.containertype = ctAVI;
sourceinfo.is_video = TRUE;
}
- else if ( strcasecmp(ext, ".mp4") == 0 || strcasecmp(ext, ".mov") == 0)
+ else if ( strcasecmp(ext, ".mp4") == 0 || strcasecmp(ext, ".mov") == 0 || strcasecmp(ext, ".m4v") == 0)
{
sourceinfo.containertype = ctMP4;
sourceinfo.is_video = TRUE;
return 0;
}
-
int eServiceMP3::getInfo(int w)
{
const gchar *tag = 0;
return 0;
}
+RESULT eServiceMP3::audioDelay(ePtr<iAudioDelay> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
int eServiceMP3::getNumberOfTracks()
{
return m_audioStreams.size();
g_object_set (G_OBJECT (sink), "emit-signals", TRUE, NULL);
gst_object_unref(sink);
}
+ setAC3Delay(ac3_delay);
+ setPCMDelay(pcm_delay);
} break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
{
return 0;
}
+int eServiceMP3::getAC3Delay()
+{
+ return ac3_delay;
+}
+
+int eServiceMP3::getPCMDelay()
+{
+ return pcm_delay;
+}
+
+void eServiceMP3::setAC3Delay(int delay)
+{
+ if (!m_gst_playbin || m_state != stRunning)
+ return;
+ else
+ {
+ GstElement *sink;
+ std::string config_delay;
+ int config_delay_int = delay;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
+ config_delay_int += atoi(config_delay.c_str());
+
+ g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
+
+ if (!sink)
+ return;
+ else {
+ gchar *name = gst_element_get_name(sink);
+
+ if (strstr(name, "dvbaudiosink"))
+ eTSMPEGDecoder::setHwAC3Delay(config_delay_int);
+ g_free(name);
+ gst_object_unref(sink);
+ }
+ }
+}
+
+void eServiceMP3::setPCMDelay(int delay)
+{
+ if (!m_gst_playbin || m_state != stRunning)
+ return;
+ else
+ {
+ GstElement *sink;
+ std::string config_delay;
+ int config_delay_int = delay;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
+ config_delay_int += atoi(config_delay.c_str());
+
+ g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
+
+ if (!sink)
+ return;
+ else {
+ gchar *name = gst_element_get_name(sink);
+
+ if (strstr(name, "dvbaudiosink"))
+ eTSMPEGDecoder::setHwPCMDelay(config_delay_int);
+ else {
+ // this is realy untested..and not used yet
+ gint64 offset = config_delay_int;
+ offset *= 1000000; // milli to nano
+ g_object_set (G_OBJECT (m_gst_playbin), "ts-offset", offset, NULL);
+ }
+ g_free(name);
+ gst_object_unref(sink);
+ }
+ }
+}
#else
#warning gstreamer not available, not building media player
typedef enum { stPlainText, stSSA, stSRT } subtype_t;
typedef enum { ctNone, ctMPEGTS, ctMPEGPS, ctMKV, ctAVI, ctMP4, ctVCD, ctCDA } containertype_t;
-class eServiceMP3: public iPlayableService, public iPauseableService,
- public iServiceInformation, public iSeekableService, public iAudioTrackSelection, public iAudioChannelSelection, public iSubtitleOutput, public iStreamedService, public Object
+class eServiceMP3: public iPlayableService, public iPauseableService,
+ public iServiceInformation, public iSeekableService, public iAudioTrackSelection, public iAudioChannelSelection,
+ public iSubtitleOutput, public iStreamedService, public iAudioDelay, public Object
{
DECLARE_REF(eServiceMP3);
public:
RESULT audioTracks(ePtr<iAudioTrackSelection> &ptr);
RESULT audioChannel(ePtr<iAudioChannelSelection> &ptr);
RESULT subtitle(ePtr<iSubtitleOutput> &ptr);
+ RESULT audioDelay(ePtr<iAudioDelay> &ptr);
// not implemented (yet)
RESULT frontendInfo(ePtr<iFrontendInformation> &ptr) { ptr = 0; return -1; }
RESULT subServices(ePtr<iSubserviceList> &ptr) { ptr = 0; return -1; }
RESULT timeshift(ePtr<iTimeshiftService> &ptr) { ptr = 0; return -1; }
RESULT cueSheet(ePtr<iCueSheet> &ptr) { ptr = 0; return -1; }
- RESULT audioDelay(ePtr<iAudioDelay> &ptr) { ptr = 0; return -1; }
+
RESULT rdsDecoder(ePtr<iRdsDecoder> &ptr) { ptr = 0; return -1; }
RESULT keys(ePtr<iServiceKeys> &ptr) { ptr = 0; return -1; }
RESULT stream(ePtr<iStreamableService> &ptr) { ptr = 0; return -1; }
PyObject *getBufferCharge();
int setBufferSize(int size);
+ // iAudioDelay
+ int getAC3Delay();
+ int getPCMDelay();
+ void setAC3Delay(int);
+ void setPCMDelay(int);
+
struct audioStream
{
GstPad* pad;
}
};
private:
+ static int pcm_delay;
+ static int ac3_delay;
int m_currentAudioStream;
int m_currentSubtitleStream;
int selectAudioStream(int i);