From f3e94f743c86bdbce65e07cd18eb8193d9b21d09 Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Wed, 25 Jun 2014 22:15:51 +0200 Subject: [PATCH] Support shmsrc (live source) If the imported file is a socket path, we mark the media as "live". We continuously poll for the socket path (in case e.g. it disappears / reappears). As soon as the source socket patch exists, we connect a shmsrc ! gdpdepay pipeline (instead of a normal uridecodebin element for file-based media) and we set the pipeline state to PLAYING (playAction button is not needed at the moment for live sources, but we can change behaviour to only start the pipeline if play is selected, like with normal file-based media) In case of a GST_MESSAGE_ERROR, the polling function keeps looking for the socket path until it exists again. The existing shmsrc pipeline is re-used once the live source is transmitting again. Tested with live source gst-launch-1.0: gst-launch-1.0 uridecodebin uri=file:////opt/Videos/test.avi ! queue ! videoconvert ! video/x-raw, format="RGBA" ! gdppay ! shmsink socket-path=/tmp/sock shm-size=100000000 The live source was interrupted and restarted again, and the shmsrc in mapmap is able to pick up the reappearing media stream. Signed-off-by: Vasilis Liaskovitis --- MainWindow.cpp | 13 ++++-- MainWindow.h | 3 +- MediaImpl.cpp | 101 +++++++++++++++++++++++++++++++++++++++++----- MediaImpl.h | 13 ++++-- Paint.cpp | 4 +- Paint.h | 2 +- ProjectReader.cpp | 2 +- 7 files changed, 116 insertions(+), 22 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 8ae59ec..e7a8bb4 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -578,7 +578,8 @@ bool MainWindow::clearProject() return true; } -uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, Paint::ptr oldPaint, bool isImage) +uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, + Paint::ptr oldPaint, bool isImage, bool live) { // Cannot create image with already existing id. if (Paint::getUidAllocator().exists(paintId)) @@ -590,7 +591,7 @@ uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, Pai if (isImage) tex = new Image(uri, paintId); else - tex = new Media(uri, paintId); + tex = new Media(uri, live, paintId); // Create new image with corresponding ID. tex->setPosition(x, y); @@ -1412,18 +1413,24 @@ void MainWindow::setCurrentFile(const QString &fileName) bool MainWindow::importMediaFile(const QString &fileName, Paint::ptr oldPaint, bool isImage) { QFile file(fileName); + bool live = false; if (!file.open(QIODevice::ReadOnly)) { + if (file.isSequential()) + live = true; + else { QMessageBox::warning(this, tr("MapMap Project"), tr("Cannot read file %1:\n%2.") .arg(file.fileName()) .arg(file.errorString())); return false; + } } QApplication::setOverrideCursor(Qt::WaitCursor); // Add media file to model. - uint mediaId = createMediaPaint(NULL_UID, fileName, 0, 0, oldPaint, isImage); + uint mediaId = createMediaPaint(NULL_UID, fileName, 0, 0, oldPaint, isImage, + live); // Initialize position (center). std::tr1::shared_ptr media = std::tr1::static_pointer_cast(mappingManager->getPaintById(mediaId)); diff --git a/MainWindow.h b/MainWindow.h index 49f5376..9cc339f 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -111,7 +111,8 @@ public slots: bool clearProject(); /// Create or replace a media paint (or image). - uid createMediaPaint(uid paintId, QString uri, float x, float y, Paint::ptr oldPaint, bool isImage); + uid createMediaPaint(uid paintId, QString uri, float x, float y, Paint::ptr +oldPaint, bool isImage, bool live); /// Create or replace a color paint. uid createColorPaint(uid paintId, QColor color, Paint::ptr oldPaint); diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 1793a5a..0242d4f 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -57,6 +57,21 @@ const uchar* MediaImpl::getBits() const return _data; } +QString MediaImpl::getUri() const +{ + return _uri; +} + +bool MediaImpl::getAttached() +{ + return _attached; +} + +void MediaImpl::setAttached(bool attach) +{ + _attached = attach; +} + void MediaImpl::build() { qDebug() << "Building video impl"; @@ -179,7 +194,7 @@ GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) return GST_FLOW_OK; } -MediaImpl::MediaImpl(const QString uri) : +MediaImpl::MediaImpl(const QString uri, bool live) : _currentMovie(""), _bus(NULL), _pipeline(NULL), @@ -200,7 +215,9 @@ _data(NULL), _seekEnabled(false), //_audioNewBufferCounter(0), _movieReady(false), -_uri(uri) +_uri(uri), +_live(live), +_attached(false) { if (uri != "") loadMovie(uri); @@ -299,6 +316,21 @@ void MediaImpl::resetMovie() } } +gboolean +gstPollShmsrc (void *user_data) +{ + MediaImpl *p = (MediaImpl*) user_data; + if (g_file_test(p->getUri().toUtf8().constData() , G_FILE_TEST_EXISTS) && !p->getAttached()) { + if (!p->setPlayState(true)) { + qDebug() << "tried to attach, but starting pipeline failed!" << endl; + return false; + } + //qDebug() << "attached, started pipeline!" << endl; + p->setAttached(true); + } + return true; +} + bool MediaImpl::loadMovie(QString filename) { _uri = filename; @@ -317,7 +349,19 @@ bool MediaImpl::loadMovie(QString filename) GstElement *videoScale = NULL; // Create the elements. - _source = gst_element_factory_make ("uridecodebin", "source"); + if (!_live) + _source = gst_element_factory_make ("uridecodebin", "source"); + else { + _source = gst_element_factory_make ("shmsrc", "source"); + _deserializer = gst_element_factory_make ("gdpdepay", "deserializer"); + _pollSource = g_timeout_source_new (500); + g_source_set_callback (_pollSource, + gstPollShmsrc, + this, + NULL); + g_source_attach (_pollSource, g_main_context_default()); + g_source_unref (_pollSource); + } // _audioQueue = gst_element_factory_make ("queue", "aqueue"); // _audioConvert = gst_element_factory_make ("audioconvert", "aconvert"); @@ -343,7 +387,7 @@ bool MediaImpl::loadMovie(QString filename) // Create the empty pipeline. _pipeline = gst_pipeline_new ( "video-source-pipeline" ); - if (!_pipeline || !_source || + if (!_pipeline || !_source || (_live && !_deserializer) || // !_audioQueue || !_audioConvert || !_audioResample || !_audioSink || !_videoQueue || !_videoColorSpace || ! videoScale || ! capsFilter || ! _videoSink) { @@ -358,6 +402,15 @@ bool MediaImpl::loadMovie(QString filename) // _audioQueue, _audioConvert, _audioResample, _audioSink, _videoQueue, _videoColorSpace, videoScale, capsFilter, _videoSink, NULL); + if (_live) { + gst_bin_add (GST_BIN(_pipeline), _deserializer); + if (!gst_element_link_many (_source, _deserializer, _videoQueue, NULL)) { + g_printerr ("Video elements could not be linked.\n"); + } + } + else if (!gst_element_link (_source, _videoQueue)) + g_printerr ("Video elements could not be linked.\n"); + // if (!gst_element_link_many(_audioQueue, _audioConvert, _audioResample, _audioSink, NULL)) { // g_printerr ("Audio elements could not be linked.\n"); // unloadMovie(); @@ -371,8 +424,9 @@ bool MediaImpl::loadMovie(QString filename) } // Process URI. + QByteArray ba = filename.toLocal8Bit(); gchar* uri = (gchar*) filename.toUtf8().constData(); - if (!gst_uri_is_valid(uri)) + if (!_live && !gst_uri_is_valid(uri)) { // Try to convert filename to URI. GError* error = NULL; @@ -386,12 +440,25 @@ bool MediaImpl::loadMovie(QString filename) } } + if (_live) + uri = (gchar*) ba.data(); + // Set URI to be played. qDebug() << "URI for uridecodebin: " << uri; // FIXME: sometimes it's just the path to the directory that is given, not the file itself. - g_object_set (_source, "uri", uri, NULL); + + // Connect to the pad-added signal - g_signal_connect (_source, "pad-added", G_CALLBACK (MediaImpl::gstPadAddedCallback), &_padHandlerData); + if (!_live) { + g_object_set (_source, "uri", uri, NULL); + g_signal_connect (_source, "pad-added", G_CALLBACK (MediaImpl::gstPadAddedCallback), &_padHandlerData); + } + else { + //qDebug() << "LIVE mode" << uri; + g_object_set (_source, "socket-path", uri, NULL); + g_object_set (_source, "is-live", TRUE, NULL); + _padHandlerData.videoIsConnected = true; + } // Configure audio appsink. // TODO: change from mono to stereo @@ -414,6 +481,7 @@ bool MediaImpl::loadMovie(QString filename) g_object_set (_videoSink, "emit-signals", TRUE, "max-buffers", 1, // only one buffer (the last) is maintained in the queue "drop", TRUE, // ... other buffers are dropped + "sync", TRUE, NULL); g_signal_connect (_videoSink, "new-sample", G_CALLBACK (MediaImpl::gstNewSampleCallback), this); gst_caps_unref (videoCaps); @@ -422,7 +490,7 @@ bool MediaImpl::loadMovie(QString filename) _bus = gst_element_get_bus (_pipeline); // Start playing. - if (!setPlayState(true)) + if (!_live && !setPlayState(true)) return false; qDebug() << "Pipeline started."; @@ -523,7 +591,7 @@ bool MediaImpl::setPlayState(bool play) bool MediaImpl::_preRun() { // Check for end-of-stream or terminate. - if (_eos() || _terminate) + if ((_eos() || _terminate) && !_live) { _setFinished(true); resetMovie(); @@ -543,8 +611,12 @@ bool MediaImpl::_preRun() // resetMovie(); if (!_movieReady || - !_padHandlerData.isConnected()) + !_padHandlerData.isConnected()) { + qDebug() << "movieReady" << _movieReady << " padhandler " << + _padHandlerData.isConnected() << endl; + return false; + } return true; } @@ -573,7 +645,14 @@ void MediaImpl::_postRun() g_clear_error(&err); g_free(debug_info); - _terminate = true; + if (!_live) + _terminate = true; + else { + _attached = false; + gst_element_set_state (_pipeline, GST_STATE_PAUSED); + gst_element_set_state (_pipeline, GST_STATE_NULL); + gst_element_set_state (_pipeline, GST_STATE_READY); + } // _finish(); break; diff --git a/MediaImpl.h b/MediaImpl.h index 7470bf0..e659d98 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -45,7 +45,7 @@ class MediaImpl { public: - MediaImpl(const QString uri); + MediaImpl(const QString uri, bool live); ~MediaImpl(); // void setUri(const QString uri); @@ -53,6 +53,8 @@ public: void build(); int getWidth() const; int getHeight() const; + QString getUri() const; + bool getAttached(); const uchar* getBits() const; bool isReady() const { return _padHandlerData.isConnected(); } @@ -61,8 +63,9 @@ public: bool runVideo(); // void runAudio(); bool loadMovie(QString filename); - bool setPlayState(bool play); + void setAttached(bool attach); + void resetMovie(); protected: @@ -129,7 +132,6 @@ public: return &this->queue_output_buf; } - private: // PlugOut *_VIDEO_OUT; // PlugOut *_AUDIO_OUT; @@ -146,6 +148,7 @@ private: GstBus *_bus; GstElement *_pipeline; GstElement *_source; + GstElement *_deserializer; GstElement *_audioQueue; GstElement *_audioConvert; GstElement *_audioResample; @@ -155,6 +158,7 @@ private: GstElement *_audioSink; GstElement *_videoSink; GstSample *_frame; + GSource *_pollSource; // GstAdapter *_audioBufferAdapter; @@ -166,6 +170,8 @@ private: uchar *_data; bool _seekEnabled; + bool _live; + bool _attached; int _audioNewBufferCounter; @@ -177,6 +183,7 @@ private: private: QString _uri; + }; //! Fast converts 24-bits color to 32 bits (alpha is set to specified alpha value). diff --git a/Paint.cpp b/Paint.cpp index 337e8f8..6a221b2 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -51,12 +51,12 @@ bool Image::setUri(const QString &uri) /* Implementation of the Video class */ -Media::Media(const QString uri_, uid id): +Media::Media(const QString uri_, bool live, uid id): Texture(id), uri(uri_), impl_(NULL) { - impl_ = new MediaImpl(uri_); + impl_ = new MediaImpl(uri_, live); } // vertigo diff --git a/Paint.h b/Paint.h index 98c03d5..bf2018d 100644 --- a/Paint.h +++ b/Paint.h @@ -194,7 +194,7 @@ class Media : public Texture protected: QString uri; public: - Media(const QString uri_, uid id=NULL_UID); + Media(const QString uri_, bool live, uid id=NULL_UID); virtual ~Media(); const QString getUri() const { diff --git a/ProjectReader.cpp b/ProjectReader.cpp index 7448d4e..7406296 100644 --- a/ProjectReader.cpp +++ b/ProjectReader.cpp @@ -97,7 +97,7 @@ void ProjectReader::parsePaint(const QDomElement& paint) QString y = paint.firstChildElement("y").text(); uid id = _window->createMediaPaint(paintAttrId.toInt(), uri, x.toFloat(), y.toFloat(), - std::tr1::shared_ptr(static_cast(0)), paintAttrType == "image"); + std::tr1::shared_ptr(static_cast(0)), paintAttrType == "image", false); if (id == NULL_UID) _xml.raiseError(QObject::tr("Cannot create media with uri %1.").arg(uri)); }