diff --git a/MM.cpp b/MM.cpp index 0a17dd1..97098ec 100644 --- a/MM.cpp +++ b/MM.cpp @@ -21,7 +21,7 @@ const QString MM::VERSION = "0.1.1"; const QString MM::FILE_EXTENSION = "mmp"; -const QString MM::VIDEO_FILES_FILTER = "*.mov *.mp4 *.avi *.ogg *.ogv *.mpeg *.mpeg1 *.mpeg4 *.mpg *.mpg2 *.mp2 *.mjpq *.mjp *.wmv"; +const QString MM::VIDEO_FILES_FILTER = "*.mov *.mp4 *.avi *.ogg *.ogv *.mpeg *.mpeg1 *.mpeg4 *.mpg *.mpg2 *.mp2 *.mjpq *.mjp *.wmv *sock"; const QString MM::IMAGE_FILES_FILTER = "*.jpg *.jpeg *.gif *.png *.tiff *.tif *.bmp"; const QColor MM::WHITE("#f6f5f5"); diff --git a/MainWindow.cpp b/MainWindow.cpp index 4f4f28e..f45931a 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -623,7 +623,8 @@ bool MainWindow::clearProject() return true; } -uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, bool isImage) +uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, + bool isImage, bool live) { // Cannot create image with already existing id. if (Paint::getUidAllocator().exists(paintId)) @@ -635,7 +636,7 @@ uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, boo 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); @@ -1467,18 +1468,23 @@ void MainWindow::setCurrentFile(const QString &fileName) bool MainWindow::importMediaFile(const QString &fileName, 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, isImage); + uint mediaId = createMediaPaint(NULL_UID, fileName, 0, 0, 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 5664293..ac3165b 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -115,7 +115,7 @@ public slots: bool clearProject(); /// Create or replace a media paint (or image). - uid createMediaPaint(uid paintId, QString uri, float x, float y, bool isImage); + uid createMediaPaint(uid paintId, QString uri, float x, float y, bool isImage, bool live); /// Create or replace a color paint. uid createColorPaint(uid paintId, QColor color); diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 7869dea..f363fe2 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"; @@ -130,7 +145,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), @@ -145,6 +160,8 @@ _width(640), _height(480), _data(NULL), _seekEnabled(false), +_live(live), +_attached(false), _movieReady(false), _uri(uri) { @@ -212,6 +229,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; @@ -256,6 +288,15 @@ bool MediaImpl::loadMovie(QString filename) gst_bin_add_many (GST_BIN (_pipeline), _source, _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 (_videoQueue, _videoColorSpace, capsFilter, videoScale, _videoSink, NULL)) { g_printerr ("Video elements could not be linked.\n"); unloadMovie(); @@ -263,8 +304,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; @@ -278,12 +320,39 @@ 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 +// gchar* audioCapsText = g_strdup_printf ("audio/x-raw-float,channels=1,rate=%d,signed=(boolean)true,width=%d,depth=%d,endianness=BYTE_ORDER", +// Engine::signalInfo().sampleRate(), (int)(sizeof(Signal_T)*8), (int)(sizeof(Signal_T)*8) ); +// GstCaps* audioCaps = gst_caps_from_string (audioCapsText); +// g_object_set (_audioSink, "emit-signals", TRUE, +// "caps", audioCaps, +//// "max-buffers", 1, // only one buffer (the last) is maintained in the queue +//// "drop", TRUE, // ... other buffers are dropped +// NULL); +// g_signal_connect (_audioSink, "new-buffer", G_CALLBACK (VideoImpl::gstNewAudioBufferCallback), &_newAudioBufferHandlerData); +// gst_caps_unref (audioCaps); +// g_free (audioCapsText); // Configure video appsink. GstCaps *videoCaps = gst_caps_from_string ("video/x-raw,format=RGBA"); @@ -291,6 +360,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); @@ -299,7 +369,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."; @@ -324,7 +394,6 @@ bool MediaImpl::runVideo() { //std::cout << "VideoImpl::runVideo: read frame #" << _videoNewBufferCounter << std::endl; } - /* TODO: This causes the texture to be loaded always in Mapper.cpp . The * problem if this is not set is: When we have more than one shape, a * shape that has a new buffer coming in will overdraw the old buffer of the @@ -365,8 +434,12 @@ bool MediaImpl::_preRun() { // Check for end-of-stream or terminate. if (_eos() || _terminate) + { + _setFinished(true); resetMovie(); - + } + else + _setFinished(false); if (!_movieReady || !_padHandlerData.videoIsConnected) return false; @@ -398,11 +471,21 @@ 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; case GST_MESSAGE_EOS: g_print("End-Of-Stream reached.\n"); +// _terminate = true; +// _finish(); break; case GST_MESSAGE_STATE_CHANGED: @@ -417,6 +500,9 @@ void MediaImpl::_postRun() gst_element_state_get_name(oldState), gst_element_state_get_name(newState)); +// if (oldState == GST_STATE_PAUSED && newState == GST_STATE_READY) +// gst_adapter_clear(_audioBufferAdapter); + if (newState == GST_STATE_PLAYING) { // Check if seeking is allowed. @@ -460,6 +546,11 @@ void MediaImpl::_setReady(bool ready) _movieReady = ready; } +void MediaImpl::_setFinished(bool finished) { + Q_UNUSED(finished); + // qDebug() << "Clip " << (finished ? "finished" : "not finished"); +} + void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::GstPadHandlerData* data) { g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (newPad), GST_ELEMENT_NAME (src)); GstPad *sinkPad = NULL; diff --git a/MediaImpl.h b/MediaImpl.h index 4db9902..bd798a3 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.videoIsConnected; } @@ -61,8 +63,9 @@ public: bool runVideo(); // void runAudio(); bool loadMovie(QString filename); - bool setPlayState(bool play); + void setAttached(bool attach); + void resetMovie(); protected: @@ -78,7 +81,7 @@ private: bool _preRun(); void _postRun(); void _setReady(bool ready); -// void _setFinished(bool finished); + void _setFinished(bool finished); public: // GStreamer callbacks. @@ -112,7 +115,6 @@ public: return &this->_queueOutputBuffer; } - private: //locals QString _currentMovie; @@ -121,6 +123,7 @@ private: GstBus *_bus; GstElement *_pipeline; GstElement *_source; + GstElement *_deserializer; GstElement *_audioQueue; GstElement *_audioConvert; GstElement *_audioResample; @@ -130,6 +133,7 @@ private: GstElement *_audioSink; GstElement *_videoSink; GstSample *_frame; + GSource *_pollSource; GstPadHandlerData _padHandlerData; @@ -138,6 +142,8 @@ private: uchar *_data; bool _seekEnabled; + bool _live; + bool _attached; int _audioNewBufferCounter; @@ -148,6 +154,7 @@ private: private: QString _uri; + }; #endif /* ifndef */ 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 6254d74..d9aa419 100644 --- a/Paint.h +++ b/Paint.h @@ -198,7 +198,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 e646da8..0e7375d 100644 --- a/ProjectReader.cpp +++ b/ProjectReader.cpp @@ -96,7 +96,7 @@ void ProjectReader::parsePaint(const QDomElement& paint) QString x = paint.firstChildElement("x").text(); QString y = paint.firstChildElement("y").text(); - uid id = _window->createMediaPaint(paintAttrId.toInt(), uri, x.toFloat(), y.toFloat(), paintAttrType == "image"); + uid id = _window->createMediaPaint(paintAttrId.toInt(), uri, x.toFloat(), y.toFloat(), paintAttrType == "image", false); if (id == NULL_UID) _xml.raiseError(QObject::tr("Cannot create media with uri %1.").arg(uri)); }