+
+void eServiceMP3::gstCBsubtitleAvail(GstElement *appsink, gpointer 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);
+}
+
+void eServiceMP3::pullSubtitle()
+{
+ GstElement *sink;
+ g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL);
+ if (sink)
+ {
+ while (m_subs_to_pull && m_subtitle_pages.size() < 2)
+ {
+ GstBuffer *buffer;
+ {
+ eSingleLocker l(m_subs_to_pull_lock);
+ --m_subs_to_pull;
+ g_signal_emit_by_name (sink, "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();
+ gst_buffer_unref(buffer);
+ }
+ }
+ gst_object_unref(sink);
+ }
+ else
+ eDebug("no subtitle sink!");
+}
+
+void eServiceMP3::pushSubtitles()
+{
+ ePangoSubtitlePage page;
+ pts_t running_pts;
+ while ( !m_subtitle_pages.empty() )
+ {
+ 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;
+ }
+ }
+ else
+ eDebug("query position for decoder/pipeline check failed!");
+ eDebug("subtitle to 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);
+ break;
+ }
+ else // immediate show
+ {
+ if (m_subtitle_widget)
+ m_subtitle_widget->setPage(page);
+ m_subtitle_pages.pop_front();
+ }
+ }
+ if (m_subtitle_pages.empty())
+ pullSubtitle();
+}
+
+RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple)
+{
+ ePyObject entry;
+ int tuplesize = PyTuple_Size(tuple);
+ int pid, type;
+ gint text_pid = 0;
+
+ if (!PyTuple_Check(tuple))
+ goto error_out;
+ if (tuplesize < 1)
+ goto error_out;
+ entry = PyTuple_GET_ITEM(tuple, 1);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ pid = PyInt_AsLong(entry);
+ entry = PyTuple_GET_ITEM(tuple, 2);
+ if (!PyInt_Check(entry))
+ goto error_out;
+ type = PyInt_AsLong(entry);
+
+ if (m_currentSubtitleStream != pid)
+ {
+ eSingleLocker l(m_subs_to_pull_lock);
+ g_object_set (G_OBJECT (m_gst_playbin), "current-text", pid, NULL);
+ m_currentSubtitleStream = pid;
+ m_subs_to_pull = 0;
+ m_subtitle_pages.clear();
+ }
+
+ m_subtitle_widget = 0;
+ m_subtitle_widget = new eSubtitleWidget(parent);
+ m_subtitle_widget->resize(parent->size()); /* full size */
+
+ g_object_get (G_OBJECT (m_gst_playbin), "current-text", &text_pid, NULL);
+
+ eDebug ("eServiceMP3::switched to subtitle stream %i", text_pid);
+
+ return 0;
+
+error_out:
+ eDebug("eServiceMP3::enableSubtitles needs a tuple as 2nd argument!\n"
+ "for gst subtitles (2, subtitle_stream_count, subtitle_type)");
+ return -1;
+}
+
+RESULT eServiceMP3::disableSubtitles(eWidget *parent)
+{
+ eDebug("eServiceMP3::disableSubtitles");
+ m_subtitle_pages.clear();
+ delete m_subtitle_widget;
+ m_subtitle_widget = 0;
+ return 0;
+}
+
+PyObject *eServiceMP3::getCachedSubtitle()
+{
+// eDebug("eServiceMP3::getCachedSubtitle");
+ Py_RETURN_NONE;
+}
+
+PyObject *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;
+
+ for (std::vector<subtitleStream>::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream)
+ {
+ subtype_t type = IterSubtitleStream->type;
+ ePyObject tuple = PyTuple_New(5);
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2));
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_count[type]));
+ 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]++;
+ }
+ return l;
+}
+
+RESULT eServiceMP3::streamed(ePtr<iStreamedService> &ptr)
+{
+ ptr = this;
+ return 0;
+}
+
+PyObject *eServiceMP3::getBufferCharge()
+{
+ ePyObject tuple = PyTuple_New(5);
+ PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(m_bufferInfo.bufferPercent));
+ PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(m_bufferInfo.avgInRate));
+ PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(m_bufferInfo.avgOutRate));
+ PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(m_bufferInfo.bufferingLeft));
+ PyTuple_SET_ITEM(tuple, 4, PyInt_FromLong(m_buffer_size));
+ return tuple;
+}
+
+int eServiceMP3::setBufferSize(int size)
+{
+ m_buffer_size = size;
+ g_object_set (G_OBJECT (m_gst_playbin), "buffer-size", m_buffer_size, NULL);
+ return 0;
+}
+
+int eServiceMP3::getAC3Delay()
+{
+ return ac3_delay;
+}
+
+int eServiceMP3::getPCMDelay()
+{
+ return pcm_delay;
+}
+
+void eServiceMP3::setAC3Delay(int delay)
+{
+ ac3_delay = delay;
+ if (!m_gst_playbin || m_state != stRunning)
+ return;
+ else
+ {
+ GstElement *sink;
+ int config_delay_int = delay;
+ g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
+
+ if (sink)
+ {
+ std::string config_delay;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalAC3delay", config_delay) == 0)
+ config_delay_int += atoi(config_delay.c_str());
+ gst_object_unref(sink);
+ }
+ else
+ {
+ eDebug("dont apply ac3 delay when no video is running!");
+ config_delay_int = 0;
+ }
+
+ g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
+
+ if (sink)
+ {
+ 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)
+{
+ pcm_delay = delay;
+ if (!m_gst_playbin || m_state != stRunning)
+ return;
+ else
+ {
+ GstElement *sink;
+ int config_delay_int = delay;
+ g_object_get (G_OBJECT (m_gst_playbin), "video-sink", &sink, NULL);
+
+ if (sink)
+ {
+ std::string config_delay;
+ if(ePythonConfigQuery::getConfigValue("config.av.generalPCMdelay", config_delay) == 0)
+ config_delay_int += atoi(config_delay.c_str());
+ gst_object_unref(sink);
+ }
+ else
+ {
+ eDebug("dont apply pcm delay when no video is running!");
+ config_delay_int = 0;
+ }
+
+ g_object_get (G_OBJECT (m_gst_playbin), "audio-sink", &sink, NULL);
+
+ if (sink)
+ {
+ 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);
+ }
+ }
+}
+