From: ghost Date: Fri, 18 Feb 2011 15:37:49 +0000 (+0100) Subject: Merge branch 'bug_690_fix_oled_lcd_clear_on_shutdown' into experimental X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=commitdiff_plain;h=82606cdc060c9c8c6f3f5d6909623f6eafe9076c;hp=281453adb0ff8b3e86eb47aca241284d0cfd7313 Merge branch 'bug_690_fix_oled_lcd_clear_on_shutdown' into experimental --- diff --git a/data/encoding.conf b/data/encoding.conf old mode 100644 new mode 100755 index a3cefe6..36f619d --- a/data/encoding.conf +++ b/data/encoding.conf @@ -6,6 +6,7 @@ gre ISO8859-7 pol ISO8859-2 rus ISO8859-5 bul ISO8859-5 +tha ISO8859-11 cze ISO6397 ces ISO6397 slo ISO6397 @@ -31,25 +32,32 @@ slk ISO6397 0xc95 0x3 # Astra 23.5°E 12.109 H - SkyLink 0xbc6 0x3 # Astra 23.5°E 12.525 V - CS Link / SkyLink 0xbc7 0x3 # Astra 23.5°E 12.565 H - SkyLink +200 318 #Hotbird Eutelsat (Eurosport) +300 318 #Hotbird Eutelsat (Eurosport, Animal Pl.HD) 400 318 #Hotbird 13.0 Cyfra+ 1000 318 #Hotbird 13.0 Grupa ITI 1500 318 #Hotbird 13.0 Cyfra+ 1600 318 #Hotbird 13.0 Cyfra+ +2800 318 #Hotbird 13.0 MTV Networks (Comedy Central) 7400 113 #Hotbird 13.0 Cyfrowy Polsat 7800 113 #Hotbird 13.0 Cyfrowy Polsat 7900 113 #Hotbird 13.0 Cyfrowy Polsat 8100 113 #Hotbird 13.0 Universal (Cyfra+) +8100 318 #Hotbird 13.0 Eutelsat (Universal) 11000 318 #Hotbird 13.0 Cyfra+ 11400 318 #Hotbird 13.0 Cyfra+ +11600 318 #Hotbird 13.0 BBC HD, ITI +11700 318 #Hotbird 13.0 Eurosport2 11900 318 #Hotbird 13.0 Cyfra+ 12200 318 #Hotbird 13.0 Disney Channel Polska and other 12800 318 #Hotbird 13.0 Viacom ... MTV / VH1 Polska 13000 318 #Hotbird 13.0 BBC Polska and other +13100 318 #Hotbird 13.0 Crime and Investigation 13200 113 #Hotbird 13.0 Cyfrowy Polsat #Fallback encoding table for single transponders #ISO6397 also enables two byte char encoding #TSID ONID ISO8859-X -12800 318 ISO6397 #Viacom ... MTV / VH1 Polska +#12800 318 ISO6397 #Viacom ... MTV / VH1 Polska 21100 126 ISO8859-9 # Digital Platform 7°E 10.928 H 30.000 2/3 8PSK 41200 126 ISO8859-9 # Digital Platform 7°E 11.451 V 25.066 2/3 50100 126 ISO8859-9 # Digital Platform 7°E 11.471 H 30.000 3/4 diff --git a/lib/driver/rcconsole.cpp b/lib/driver/rcconsole.cpp index 97a6e7c..77f4322 100644 --- a/lib/driver/rcconsole.cpp +++ b/lib/driver/rcconsole.cpp @@ -103,7 +103,7 @@ class eRCConsoleInit eRCConsoleDriver driver; eRCConsole device; public: - eRCConsoleInit(): driver("/dev/vc/0"), device(&driver) + eRCConsoleInit(): driver("/dev/tty0"), device(&driver) { } }; diff --git a/lib/dvb/frontend.cpp b/lib/dvb/frontend.cpp index ea9e738..b5de46c 100644 --- a/lib/dvb/frontend.cpp +++ b/lib/dvb/frontend.cpp @@ -1860,11 +1860,11 @@ int eDVBFrontend::tuneLoopInt() // called by m_tuneTimer int slotid = sec_fe->m_slotid; // FIXMEEEEEE hardcoded i2c devices for dm7025 and dm8000 if (slotid < 2) - sprintf(dev, "/dev/i2c/%d", slotid); + sprintf(dev, "/dev/i2c-%d", slotid); else if (slotid == 2) - sprintf(dev, "/dev/i2c/2"); // first nim socket on DM8000 use /dev/i2c/2 + sprintf(dev, "/dev/i2c-2"); // first nim socket on DM8000 use /dev/i2c-2 else if (slotid == 3) - sprintf(dev, "/dev/i2c/4"); // second nim socket on DM8000 use /dev/i2c/4 + sprintf(dev, "/dev/i2c-4"); // second nim socket on DM8000 use /dev/i2c-4 int fd = ::open(dev, O_RDWR); unsigned char data[2]; diff --git a/lib/gdi/accel.cpp b/lib/gdi/accel.cpp index fc739e9..bd1439f 100644 --- a/lib/gdi/accel.cpp +++ b/lib/gdi/accel.cpp @@ -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..b215b10 100644 --- a/lib/gdi/bcm.cpp +++ b/lib/gdi/bcm.cpp @@ -23,10 +23,10 @@ static int exec_list(void); int bcm_accel_init(void) { - fb_fd = open("/dev/fb/0", O_RDWR); + fb_fd = open("/dev/fb0", O_RDWR); if (fb_fd < 0) { - perror("/dev/fb/0"); + perror("/dev/fb0"); return 1; } if (exec_list()) @@ -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/fb.cpp b/lib/gdi/fb.cpp index cde3e35..7ee555c 100644 --- a/lib/gdi/fb.cpp +++ b/lib/gdi/fb.cpp @@ -83,12 +83,12 @@ nolfb: int fbClass::showConsole(int state) { - int fd=open("/dev/vc/0", O_RDWR); + int fd=open("/dev/tty0", O_RDWR); if(fd>=0) { if(ioctl(fd, KDSETMODE, state?KD_TEXT:KD_GRAPHICS)<0) { - eDebug("setting /dev/vc/0 status failed."); + eDebug("setting /dev/tty0 status failed."); } close(fd); } diff --git a/lib/gdi/fb.h b/lib/gdi/fb.h index 5447380..fcb5ff2 100644 --- a/lib/gdi/fb.h +++ b/lib/gdi/fb.h @@ -19,7 +19,7 @@ class fbClass int m_number_of_pages; int m_phys_mem; #ifdef SWIG - fbClass(const char *fb="/dev/fb/0"); + fbClass(const char *fb="/dev/fb0"); ~fbClass(); public: #else @@ -41,7 +41,7 @@ public: unsigned int Stride() { return stride; } fb_cmap *CMAP() { return &cmap; } - fbClass(const char *fb="/dev/fb/0"); + fbClass(const char *fb="/dev/fb0"); ~fbClass(); // low level gfx stuff 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/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/Plugins/Extensions/DVDBurn/Process.py b/lib/python/Plugins/Extensions/DVDBurn/Process.py index e00dedd..2e7435b 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/Process.py +++ b/lib/python/Plugins/Extensions/DVDBurn/Process.py @@ -174,7 +174,8 @@ class DemuxTask(Task): print "[DemuxTask::cleanup]" self.mplex_streamfiles = [ self.mplex_videofile ] for pid in self.relevantAudioPIDs: - self.mplex_streamfiles.append(self.mplex_audiofiles[pid]) + if pid in self.mplex_audiofiles: + self.mplex_streamfiles.append(self.mplex_audiofiles[pid]) print self.mplex_streamfiles if failed: diff --git a/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp b/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp index ccacf3c..6d1397d 100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp +++ b/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp @@ -749,7 +749,7 @@ PyObject *eServiceDVD::getSubtitleList() ePyObject tuple = PyTuple_New(5); PyTuple_SetItem(tuple, 0, PyInt_FromLong(2)); PyTuple_SetItem(tuple, 1, PyInt_FromLong(spu_id+1)); - PyTuple_SetItem(tuple, 2, PyInt_FromLong(3)); + PyTuple_SetItem(tuple, 2, PyInt_FromLong(5)); PyTuple_SetItem(tuple, 3, PyInt_FromLong(0)); PyTuple_SetItem(tuple, 4, PyString_FromString(spu_string)); PyList_Append(l, tuple); diff --git a/lib/python/Screens/AudioSelection.py b/lib/python/Screens/AudioSelection.py index b3b82b4..4c68962 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) @@ -160,7 +159,7 @@ class AudioSelection(Screen, ConfigListScreen): number = "%x%02x" % (x[3],x[2]) elif x[0] == 2: - types = ("UTF-8 text","SSA / AAS",".SRT file","VOB") + types = (_(""), "UTF-8 text", "SSA", "AAS", ".SRT file", "VOB", "PGS (unsupported)") description = types[x[2]] streams.append((x, "", number, description, language, selected)) @@ -189,7 +188,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/Tools/Directories.py b/lib/python/Tools/Directories.py index 518db49..8e4d004 100755 --- a/lib/python/Tools/Directories.py +++ b/lib/python/Tools/Directories.py @@ -57,7 +57,7 @@ defaultPaths = { SCOPE_USERETC: ("", PATH_DONTCREATE), # user home directory SCOPE_DEFAULTDIR: (eEnv.resolve("${datadir}/enigma2/defaults/"), PATH_CREATE), - SCOPE_DEFAULTPARTITION: ("/dev/mtdblock/6", PATH_DONTCREATE), + SCOPE_DEFAULTPARTITION: ("/dev/mtdblock6", PATH_DONTCREATE), SCOPE_DEFAULTPARTITIONMOUNTDIR: (eEnv.resolve("${datadir}/enigma2/dealer"), PATH_CREATE), SCOPE_METADIR: (eEnv.resolve("${datadir}/meta"), PATH_CREATE), } 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 d54997a..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, @@ -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;