void eServiceMP3::gstTextpadHasCAPS_synced(GstPad *pad) { GstCaps *caps; g_object_get (G_OBJECT (pad), "caps", &caps, NULL); eDebug("gstTextpadHasCAPS:: signal::caps = %s", gst_caps_to_string(caps)); if (caps) { subtitleStream subs; // eDebug("gstGhostpadHasCAPS_synced %p %d", pad, m_subtitleStreams.size()); if (m_currentSubtitleStream >= 0 && m_currentSubtitleStream < m_subtitleStreams.size()) 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); subs.type = getSubtitleType(pad); if (m_currentSubtitleStream >= 0 && m_currentSubtitleStream < m_subtitleStreams.size()) 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)); gst_caps_unref (caps); } }
void QGstreamerPlayerSession::getStreamsInfo() { GstFormat format = GST_FORMAT_TIME; gint64 duration = 0; if (gst_element_query_duration(m_playbin, &format, &duration)) { int newDuration = duration / 1000000; if (m_duration != newDuration) { m_duration = newDuration; emit durationChanged(m_duration); } } //check if video is available: bool haveAudio = false; bool haveVideo = false; m_streamProperties.clear(); m_streamTypes.clear(); if (m_usePlaybin2) { gint audioStreamsCount = 0; gint videoStreamsCount = 0; gint textStreamsCount = 0; g_object_get(G_OBJECT(m_playbin), "n-audio", &audioStreamsCount, NULL); g_object_get(G_OBJECT(m_playbin), "n-video", &videoStreamsCount, NULL); g_object_get(G_OBJECT(m_playbin), "n-text", &textStreamsCount, NULL); haveAudio = audioStreamsCount > 0; haveVideo = videoStreamsCount > 0; m_playbin2StreamOffset[QMediaStreamsControl::AudioStream] = 0; m_playbin2StreamOffset[QMediaStreamsControl::VideoStream] = audioStreamsCount; m_playbin2StreamOffset[QMediaStreamsControl::SubPictureStream] = audioStreamsCount+videoStreamsCount; for (int i=0; i<audioStreamsCount; i++) m_streamTypes.append(QMediaStreamsControl::AudioStream); for (int i=0; i<videoStreamsCount; i++) m_streamTypes.append(QMediaStreamsControl::VideoStream); for (int i=0; i<textStreamsCount; i++) m_streamTypes.append(QMediaStreamsControl::SubPictureStream); for (int i=0; i<m_streamTypes.count(); i++) { QMediaStreamsControl::StreamType streamType = m_streamTypes[i]; QMap<QtMultimediaKit::MetaData, QVariant> streamProperties; int streamIndex = i - m_playbin2StreamOffset[streamType]; GstTagList *tags = 0; switch (streamType) { case QMediaStreamsControl::AudioStream: g_signal_emit_by_name(G_OBJECT(m_playbin), "get-audio-tags", streamIndex, &tags); break; case QMediaStreamsControl::VideoStream: g_signal_emit_by_name(G_OBJECT(m_playbin), "get-video-tags", streamIndex, &tags); break; case QMediaStreamsControl::SubPictureStream: g_signal_emit_by_name(G_OBJECT(m_playbin), "get-text-tags", streamIndex, &tags); break; default: break; } if (tags && gst_is_tag_list(tags)) { gchar *languageCode = 0; if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) streamProperties[QtMultimediaKit::Language] = QString::fromUtf8(languageCode); //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode); g_free (languageCode); } m_streamProperties.append(streamProperties); } } else { // PlayBin 1 enum { GST_STREAM_TYPE_UNKNOWN, GST_STREAM_TYPE_AUDIO, GST_STREAM_TYPE_VIDEO, GST_STREAM_TYPE_TEXT, GST_STREAM_TYPE_SUBPICTURE, GST_STREAM_TYPE_ELEMENT }; GList* streamInfo; g_object_get(G_OBJECT(m_playbin), "stream-info", &streamInfo, NULL); for (; streamInfo != 0; streamInfo = g_list_next(streamInfo)) { gint type; gchar *languageCode = 0; GObject* obj = G_OBJECT(streamInfo->data); g_object_get(obj, "type", &type, NULL); g_object_get(obj, "language-code", &languageCode, NULL); if (type == GST_STREAM_TYPE_VIDEO) haveVideo = true; QMediaStreamsControl::StreamType streamType = QMediaStreamsControl::UnknownStream; switch (type) { case GST_STREAM_TYPE_VIDEO: streamType = QMediaStreamsControl::VideoStream; haveVideo = true; break; case GST_STREAM_TYPE_AUDIO: streamType = QMediaStreamsControl::AudioStream; haveAudio = true; break; case GST_STREAM_TYPE_SUBPICTURE: streamType = QMediaStreamsControl::SubPictureStream; break; default: streamType = QMediaStreamsControl::UnknownStream; break; } QMap<QtMultimediaKit::MetaData, QVariant> streamProperties; streamProperties[QtMultimediaKit::Language] = QString::fromUtf8(languageCode); m_streamProperties.append(streamProperties); m_streamTypes.append(streamType); } } if (haveAudio != m_audioAvailable) { m_audioAvailable = haveAudio; emit audioAvailableChanged(m_audioAvailable); } if (haveVideo != m_videoAvailable) { m_videoAvailable = haveVideo; emit videoAvailableChanged(m_videoAvailable); } emit streamsChanged(); }
void eServiceMP3::gstBusCall(GstMessage *msg) { if (!msg) return; gchar *sourceName; GstObject *source; source = GST_MESSAGE_SRC(msg); if (!GST_IS_OBJECT(source)) return; sourceName = gst_object_get_name(source); #if 0 gchar *string; if (gst_message_get_structure(msg)) string = gst_structure_to_string(gst_message_get_structure(msg)); else string = g_strdup(GST_MESSAGE_TYPE_NAME(msg)); eDebug("eTsRemoteSource::gst_message from %s: %s", sourceName, string); g_free(string); #endif switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_EOS: m_event((iPlayableService*)this, evEOF); break; case GST_MESSAGE_STATE_CHANGED: { if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) break; GstState old_state, new_state; gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); if(old_state == new_state) break; eDebug("eServiceMP3::state transition %s -> %s", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state); switch(transition) { case GST_STATE_CHANGE_NULL_TO_READY: { } break; case GST_STATE_CHANGE_READY_TO_PAUSED: { GstElement *subsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); if (subsink) { #ifdef GSTREAMER_SUBTITLE_SYNC_MODE_BUG /* * HACK: disable sync mode for now, gstreamer suffers from a bug causing sparse streams to loose sync, after pause/resume / skip * see: https://bugzilla.gnome.org/show_bug.cgi?id=619434 * Sideeffect of using sync=false is that we receive subtitle buffers (far) ahead of their * display time. * Not too far ahead for subtitles contained in the media container. * But for external srt files, we could receive all subtitles at once. * And not just once, but after each pause/resume / skip. * So as soon as gstreamer has been fixed to keep sync in sparse streams, sync needs to be re-enabled. */ g_object_set (G_OBJECT (subsink), "sync", FALSE, NULL); #endif #if 0 /* we should not use ts-offset to sync with the decoder time, we have to do our own decoder timekeeping */ g_object_set (G_OBJECT (subsink), "ts-offset", -2L * GST_SECOND, NULL); /* late buffers probably will not occur very often */ g_object_set (G_OBJECT (subsink), "max-lateness", 0L, NULL); /* avoid prerolling (it might not be a good idea to preroll a sparse stream) */ g_object_set (G_OBJECT (subsink), "async", TRUE, NULL); #endif eDebug("eServiceMP3::subsink properties set!"); gst_object_unref(subsink); } setAC3Delay(ac3_delay); setPCMDelay(pcm_delay); } break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: { if ( m_sourceinfo.is_streaming && m_streamingsrc_timeout ) m_streamingsrc_timeout->stop(); } break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: { } break; case GST_STATE_CHANGE_PAUSED_TO_READY: { } break; case GST_STATE_CHANGE_READY_TO_NULL: { } break; } break; } case GST_MESSAGE_ERROR: { gchar *debug; GError *err; gst_message_parse_error (msg, &err, &debug); g_free (debug); eWarning("Gstreamer error: %s (%i) from %s", err->message, err->code, sourceName ); if ( err->domain == GST_STREAM_ERROR ) { if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) { if ( g_strrstr(sourceName, "videosink") ) m_event((iPlayableService*)this, evUser+11); else if ( g_strrstr(sourceName, "audiosink") ) m_event((iPlayableService*)this, evUser+10); } } g_error_free(err); break; } case GST_MESSAGE_INFO: { gchar *debug; GError *inf; gst_message_parse_info (msg, &inf, &debug); g_free (debug); if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE ) { if ( g_strrstr(sourceName, "videosink") ) m_event((iPlayableService*)this, evUser+14); } g_error_free(inf); break; } case GST_MESSAGE_TAG: { GstTagList *tags, *result; gst_message_parse_tag(msg, &tags); result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE); if (result) { if (m_stream_tags) gst_tag_list_free(m_stream_tags); m_stream_tags = result; } const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0); if ( gv_image ) { GstBuffer *buf_image; buf_image = gst_value_get_buffer (gv_image); int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644); int ret = write(fd, GST_BUFFER_DATA(buf_image), GST_BUFFER_SIZE(buf_image)); close(fd); eDebug("eServiceMP3::/tmp/.id3coverart %d bytes written ", ret); m_event((iPlayableService*)this, evUser+13); } gst_tag_list_free(tags); m_event((iPlayableService*)this, evUpdatedInfo); break; } case GST_MESSAGE_ASYNC_DONE: { if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) break; GstTagList *tags; gint i, active_idx, n_video = 0, n_audio = 0, n_text = 0; g_object_get (m_gst_playbin, "n-video", &n_video, NULL); g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL); g_object_get (m_gst_playbin, "n-text", &n_text, NULL); eDebug("eServiceMP3::async-done - %d video, %d audio, %d subtitle", n_video, n_audio, n_text); if ( n_video + n_audio <= 0 ) stop(); active_idx = 0; m_audioStreams.clear(); m_subtitleStreams.clear(); for (i = 0; i < n_audio; i++) { audioStream audio; gchar *g_codec, *g_lang; GstPad* pad = 0; g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad); GstCaps* caps = gst_pad_get_negotiated_caps(pad); if (!caps) 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"); g_signal_emit_by_name (m_gst_playbin, "get-audio-tags", i, &tags); if ( tags && gst_is_tag_list(tags) ) { gst_tag_list_get_string(tags, GST_TAG_AUDIO_CODEC, &g_codec); gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); gst_tag_list_free(tags); } audio.language_code = std::string(g_lang); audio.codec = std::string(g_codec); eDebug("eServiceMP3::audio stream=%i codec=%s language=%s", i, g_codec, g_lang); m_audioStreams.push_back(audio); g_free (g_lang); g_free (g_codec); gst_caps_unref(caps); } for (i = 0; i < n_text; i++) { gchar *g_codec = NULL, *g_lang = NULL; g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags); subtitleStream subs; 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 codec=%s", i, g_lang, g_codec); GstPad* pad = 0; g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad); if ( pad ) g_signal_connect (G_OBJECT (pad), "notify::caps", G_CALLBACK (gstTextpadHasCAPS), this); subs.type = getSubtitleType(pad, g_codec); m_subtitleStreams.push_back(subs); g_free (g_lang); } m_event((iPlayableService*)this, evUpdatedInfo); if ( m_errorInfo.missing_codec != "" ) { if ( m_errorInfo.missing_codec.find("video/") == 0 || ( m_errorInfo.missing_codec.find("audio/") == 0 && getNumberOfTracks() == 0 ) ) m_event((iPlayableService*)this, evUser+12); } break; } case GST_MESSAGE_ELEMENT: { if (const GstStructure *msgstruct = gst_message_get_structure(msg)) { if ( gst_is_missing_plugin_message(msg) ) { GstCaps *caps; gst_structure_get (msgstruct, "detail", GST_TYPE_CAPS, &caps, NULL); std::string codec = (const char*) gst_caps_to_string(caps); gchar *description = gst_missing_plugin_message_get_description(msg); if ( description ) { eDebug("eServiceMP3::m_errorInfo.missing_codec = %s", codec.c_str()); m_errorInfo.error_message = "GStreamer plugin " + (std::string)description + " not available!\n"; m_errorInfo.missing_codec = codec.substr(0,(codec.find_first_of(','))); g_free(description); } gst_caps_unref(caps); } else { const gchar *eventname = gst_structure_get_name(msgstruct); if ( eventname ) { if (!strcmp(eventname, "eventSizeChanged") || !strcmp(eventname, "eventSizeAvail")) { gst_structure_get_int (msgstruct, "aspect_ratio", &m_aspect); gst_structure_get_int (msgstruct, "width", &m_width); gst_structure_get_int (msgstruct, "height", &m_height); if (strstr(eventname, "Changed")) m_event((iPlayableService*)this, evVideoSizeChanged); } else if (!strcmp(eventname, "eventFrameRateChanged") || !strcmp(eventname, "eventFrameRateAvail")) { gst_structure_get_int (msgstruct, "frame_rate", &m_framerate); if (strstr(eventname, "Changed")) m_event((iPlayableService*)this, evVideoFramerateChanged); } else if (!strcmp(eventname, "eventProgressiveChanged") || !strcmp(eventname, "eventProgressiveAvail")) { gst_structure_get_int (msgstruct, "progressive", &m_progressive); if (strstr(eventname, "Changed")) m_event((iPlayableService*)this, evVideoProgressiveChanged); } } } } break; } case GST_MESSAGE_BUFFERING: { GstBufferingMode mode; gst_message_parse_buffering(msg, &(m_bufferInfo.bufferPercent)); gst_message_parse_buffering_stats(msg, &mode, &(m_bufferInfo.avgInRate), &(m_bufferInfo.avgOutRate), &(m_bufferInfo.bufferingLeft)); m_event((iPlayableService*)this, evBuffering); break; } case GST_MESSAGE_STREAM_STATUS: { GstStreamStatusType type; GstElement *owner; gst_message_parse_stream_status (msg, &type, &owner); if ( type == GST_STREAM_STATUS_TYPE_CREATE && m_sourceinfo.is_streaming ) { if ( GST_IS_PAD(source) ) owner = gst_pad_get_parent_element(GST_PAD(source)); else if ( GST_IS_ELEMENT(source) ) owner = GST_ELEMENT(source); else owner = 0; if ( owner ) { GstElementFactory *factory = gst_element_get_factory(GST_ELEMENT(owner)); const gchar *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); if (!strcmp(name, "souphttpsrc")) { m_streamingsrc_timeout->start(HTTP_TIMEOUT*1000, true); g_object_set (G_OBJECT (owner), "timeout", HTTP_TIMEOUT, NULL); eDebug("eServiceMP3::GST_STREAM_STATUS_TYPE_CREATE -> setting timeout on %s to %is", name, HTTP_TIMEOUT); } } if ( GST_IS_PAD(source) ) gst_object_unref(owner); } break; } default: break; } g_free (sourceName); }