/* * VideoUriDecodeBinImpl.cpp * * (c) 2016 Vasilis Liaskovitis -- vliaskov@gmail.com * (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com * (c) 2013 Alexandre Quessy -- alexandre(@)quessy(.)net * (c) 2012 Jean-Sebastien Senecal * (c) 2004 Mathieu Guindon, Julien Keable * Based on code from Drone http://github.com/sofian/drone * Based on code from the GStreamer Tutorials http://docs.gstreamer.com/display/GstSDK/Tutorials * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "VideoUriDecodeBinImpl.h" #include #include namespace mmp { VideoUriDecodeBinImpl::VideoUriDecodeBinImpl() : _uridecodebin0(NULL) { } void VideoUriDecodeBinImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, VideoUriDecodeBinImpl* p) { Q_UNUSED(src); #ifdef VIDEO_IMPL_VERBOSE #ifndef Q_OS_OSX // NOTE: This line was causing a problem on Mac OSX: it caused the software to freeze when loading a new movie. qDebug() << "Received new pad '" << GST_PAD_NAME(newPad) << "' from '" << GST_ELEMENT_NAME (src) << "'." << endl; #endif #endif GstPad *sinkPad = NULL; // Check the new pad's type. GstCaps *newPadCaps = gst_pad_query_caps (newPad, NULL); GstStructure *newPadStruct = gst_caps_get_structure (newPadCaps, 0); const gchar *newPadType = gst_structure_get_name (newPadStruct); gchar *newPadStructStr = gst_structure_to_string(newPadStruct); #ifdef VIDEO_IMPL_VERBOSE qDebug() << "Structure is " << newPadStructStr << "." << endl; #endif g_free(newPadStructStr); bool isVideoPad = g_str_has_prefix (newPadType, "video/x-raw"); bool isAudioPad = g_str_has_prefix (newPadType, "audio/x-raw"); // Check for video pads. if (isVideoPad) { sinkPad = gst_element_get_static_pad (p->_queue0, "sink"); gst_structure_get_int(newPadStruct, "width", &p->_width); gst_structure_get_int(newPadStruct, "height", &p->_height); } // Check for audio pads. else if (isAudioPad) { if (!p->createAudioComponents()) { qWarning() << "Problem creating audio components." << endl; goto exit; } sinkPad = gst_element_get_static_pad (p->_audioqueue0, "sink"); } // Other types: ignore. else { qDebug() << " It has type '" << newPadType << "' which is not raw audio/video: ignored." << endl; goto exit; } // If our converter is already linked, we have nothing to do here. if (gst_pad_is_linked (sinkPad)) { // Best prefixes. if (isVideoPad || isAudioPad) { qDebug() << " Found a better pad." << endl; GstPad* oldPad = gst_pad_get_peer(sinkPad); gst_pad_unlink(oldPad, sinkPad); g_object_unref(oldPad); } else { #ifdef VIDEO_IMPL_VERBOSE qDebug() << " We are already linked: ignoring." << endl; #endif goto exit; } } // Attempt the link. if (GST_PAD_LINK_FAILED (gst_pad_link (newPad, sinkPad))) { #ifdef VIDEO_IMPL_VERBOSE qDebug() << " Type is '" << newPadType << "' but link failed." << endl; #endif // ifdef goto exit; } else { if (isVideoPad) p->videoConnect(); else if (isAudioPad) p->audioConnect(); else qWarning() << "Error: this pad is neither valid audio or video." << endl; #ifdef VIDEO_IMPL_VERBOSE qDebug() << " Link succeeded (type '" << newPadType << "')." << endl; #endif // ifdef } exit: // Unreference the new pad's caps, if we got them. if (newPadCaps != NULL) { gst_caps_unref (newPadCaps); } // Unreference the sink pad. if (sinkPad != NULL) { gst_object_unref (sinkPad); } } bool VideoUriDecodeBinImpl::loadMovie(const QString& path) { VideoImpl::loadMovie(path); _uridecodebin0 = gst_element_factory_make("uridecodebin", NULL); if ( !_uridecodebin0) { qWarning() << "Not all elements could be created." << endl; unloadMovie(); return (-1); } // Build the pipeline. Note that we are NOT linking the source at this // point. We will do it later. gst_bin_add_many (GST_BIN (_pipeline), _uridecodebin0, NULL); // Process URI. QByteArray ba = path.toLocal8Bit(); gchar *filename_tmp = g_strdup((gchar*) path.toUtf8().constData()); gchar* uri = (gchar*) path.toUtf8().constData(); if (! gst_uri_is_valid(uri)) { // Try to convert filename to URI. GError* error = NULL; qDebug() << "Calling gst_filename_to_uri : " << uri << endl; uri = gst_filename_to_uri(filename_tmp, &error); if (error) { qDebug() << "Filename to URI error: " << error->message << endl; g_clear_error(&error); gst_object_unref (uri); freeResources(); return false; } } g_free(filename_tmp); // Connect to the pad-added signal // Extract meta info. GError* error = NULL; GstDiscoverer* discoverer = gst_discoverer_new(5*GST_SECOND, &error); if (!discoverer) { qDebug() << "Error creating discoverer: " << error->message << endl; g_clear_error (&error); return false; } GstDiscovererInfo* info = gst_discoverer_discover_uri(discoverer, uri, &error); if (!info) { qDebug() << "Error getting discoverer info: " << error->message << endl; g_clear_error (&error); return false; } GstDiscovererResult result = gst_discoverer_info_get_result(info); switch (result) { case GST_DISCOVERER_URI_INVALID: qDebug()<< "Invalid URI '" << uri << "'" << endl; break; case GST_DISCOVERER_ERROR: qDebug()<< "Discoverer error: " << error->message << endl; break; case GST_DISCOVERER_TIMEOUT: qDebug() << "Timeout" << endl; break; case GST_DISCOVERER_BUSY: qDebug() << "Busy" << endl; break; case GST_DISCOVERER_MISSING_PLUGINS:{ const GstStructure *s; gchar *str; s = gst_discoverer_info_get_misc (info); str = gst_structure_to_string (s); qDebug() << "Missing plugins: " << str << endl; g_free (str); break; } case GST_DISCOVERER_OK: qDebug() << "Discovered '" << uri << "'" << endl; break; } g_clear_error (&error); if (result != GST_DISCOVERER_OK) { qDebug() << "This URI cannot be played" << endl; return false; } // Gather info from video. GList *videoStreams = gst_discoverer_info_get_video_streams (info); if (!videoStreams) { qDebug() << "This URI does not contain any video streams" << endl; return false; } // Retrieve meta-info. _width = gst_discoverer_video_info_get_width((GstDiscovererVideoInfo*)videoStreams->data); _height = gst_discoverer_video_info_get_height((GstDiscovererVideoInfo*)videoStreams->data); _duration = gst_discoverer_info_get_duration(info); _seekEnabled = gst_discoverer_info_get_seekable(info); // Free everything. g_object_unref(discoverer); gst_discoverer_info_unref(info); gst_discoverer_stream_info_list_free(videoStreams); // Connect pad signal. g_signal_connect (_uridecodebin0, "pad-added", G_CALLBACK (VideoUriDecodeBinImpl::gstPadAddedCallback), this); // Set uri of decoder. g_object_set (_uridecodebin0, "uri", uri, NULL); setPlayState(true); return true; } VideoUriDecodeBinImpl::~VideoUriDecodeBinImpl() { } }