Merge branch 'develop' of github.com:mapmapteam/mapmap into features-options

This commit is contained in:
baydam
2014-10-21 13:15:33 +00:00
16 changed files with 574 additions and 240 deletions
+7
View File
@@ -30,6 +30,9 @@
* used in classical producers/consumers multithreaded scenario. The
* template parameter is the class which can be put/get to/from the
* queue.
*
* We could probably use GAsyncQueue instead.
* See also https://github.com/aalex/toonloop/blob/master/src/concurrentqueue.h
*/
template<class T>
class AsyncQueue
@@ -43,6 +46,10 @@ public:
return this->buffer.size();
}
/**
* Appends an element to the queue.
* Does not manage ref/unref copy/free memory.
*/
void put(const T& item)
{
QMutexLocker locker(&mutex);
+4
View File
@@ -19,7 +19,11 @@
#include "MM.h"
const QString MM::APPLICATION_NAME = "MapMap";
const QString MM::VERSION = "0.1.1";
const QString MM::COPYRIGHT_OWNERS = "Sofian Audry, Alexandre Quessy, Mike Latona, Vasilis Liaskovitis, Dame Diongue";
const QString MM::ORGANIZATION_NAME = "MapMap";
const QString MM::ORGANIZATION_DOMAIN = "mapmap.info";
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 *sock";
const QString MM::IMAGE_FILES_FILTER = "*.jpg *.jpeg *.gif *.png *.tiff *.tif *.bmp";
+4
View File
@@ -29,7 +29,11 @@ class MM
{
public:
// General.
static const QString APPLICATION_NAME;
static const QString VERSION;
static const QString COPYRIGHT_OWNERS;
static const QString ORGANIZATION_NAME;
static const QString ORGANIZATION_DOMAIN;
static const QString FILE_EXTENSION;
static const QString VIDEO_FILES_FILTER;
static const QString IMAGE_FILES_FILTER;
+9
View File
@@ -23,11 +23,20 @@
MainApplication::MainApplication(int &argc, char *argv[])
: QApplication(argc, argv)
{
// Initialize GStreamer.
gst_init (NULL, NULL);
// Set application information.
setApplicationName(MM::APPLICATION_NAME);
setApplicationVersion(MM::VERSION);
setOrganizationName(MM::ORGANIZATION_NAME);
setOrganizationDomain(MM::ORGANIZATION_DOMAIN);
}
MainApplication::~MainApplication()
{
// Deinitialize GStreamer.
gst_deinit();
}
bool MainApplication::notify(QObject *receiver, QEvent *event)
+2
View File
@@ -21,8 +21,10 @@
#ifndef MAINAPPLICATION_H_
#define MAINAPPLICATION_H_
#include <gst/gst.h>
#include <QApplication>
#include <QDebug>
#include "MM.h"
class MainApplication : public QApplication
{
+63 -38
View File
@@ -86,7 +86,6 @@ MainWindow::~MainWindow()
void MainWindow::handlePaintItemSelectionChanged()
{
// Set current paint.
QListWidgetItem* item = paintList->currentItem();
currentSelectedItem = item;
@@ -97,13 +96,12 @@ void MainWindow::handlePaintItemSelectionChanged()
if (paintItemSelected)
{
// Set current paint.
uid idx = getItemId(*item);
if (currentPaintId != idx) {
setCurrentPaint(idx);
// Unselect current mapping.
uid paintId = getItemId(*item);
// Unselect current mapping.
if (currentPaintId != paintId)
removeCurrentMapping();
mappingList->clearSelection();
}
// Set current paint.
setCurrentPaint(paintId);
}
else
removeCurrentPaint();
@@ -121,23 +119,19 @@ void MainWindow::handleMappingItemSelectionChanged()
{
if (mappingList->selectedItems().empty())
{
removeCurrentMapping();
removeCurrentMapping();
}
else
{
QListWidgetItem* item = mappingList->currentItem();
QListWidgetItem* item = mappingList->currentItem();
currentSelectedItem = item;
currentSelectedItem = item;
// Get current mapping uid.
uid idm = getItemId(*item);
// Set current paint and mappings.
Mapping::ptr mapping = mappingManager->getMappingById(idm);
uid paintId = mapping->getPaint()->getId();
if (currentPaintId != paintId) {
setCurrentPaint(paintId);
}
setCurrentMapping(mapping->getId());
// Set current paint and mappings.
uid mappingId = getItemId(*item);
Mapping::ptr mapping = mappingManager->getMappingById(mappingId);
uid paintId = mapping->getPaint()->getId();
setCurrentMapping(mappingId);
setCurrentPaint(paintId);
}
@@ -173,6 +167,7 @@ void MainWindow::handleMappingIndexesMoved()
void MainWindow::handleItemSelected(QListWidgetItem* item)
{
Q_UNUSED(item);
// Change currently selected item.
currentSelectedItem = item;
}
@@ -538,8 +533,7 @@ void MainWindow::about()
// Pop-up about dialog.
QMessageBox::about(this, tr("About MapMap"),
tr("<h2><img src=\":mapmap-title\"/> %1</h2>"
"<p>Copyright &copy; 2013 Sofian Audry, Alexandre Quessy, "
"Mike Latona and Vasilis Liaskovitis.</p>"
"<p>Copyright &copy; 2013 %2.</p>"
"<p>MapMap is a free software for video mapping.</p>"
"<p>Projection mapping, also known as video mapping and spatial augmented reality, "
"is a projection technology used to turn objects, often irregularly shaped, into "
@@ -556,7 +550,7 @@ void MainWindow::about()
"La Francophonie.</p>"
"<p>http://mapmap.info<br />"
"http://www.francophonie.org</p>"
).arg(MM::VERSION));
).arg(MM::VERSION, MM::COPYRIGHT_OWNERS));
// Restart video playback. XXX Hack
videoTimer->start();
@@ -569,24 +563,34 @@ void MainWindow::updateStatusBar()
// formulaLabel->setText(spreadsheet->currentFormula());
}
/**
* Called when the user wants to delete an item.
*
* Deletes either a Paint or a Mapping.
*/
void MainWindow::deleteItem()
{
bool isMappingTabSelected = (mappingSplitter == contentTab->currentWidget());
bool isPaintTabSelected = (paintSplitter == contentTab->currentWidget());
if (currentSelectedItem)
{
if (currentSelectedItem->listWidget() == mappingList)
if (isMappingTabSelected) //currentSelectedItem->listWidget() == mappingList)
{
// Delete mapping.
deleteMapping( getItemId(*mappingList->currentItem()) );
//currentSelectedItem = NULL;
}
else if (currentSelectedItem->listWidget() == paintList)
else if (isPaintTabSelected) //currentSelectedItem->listWidget() == paintList)
{
// Delete paint.
deletePaint( getItemId(*paintList->currentItem()), false );
//currentSelectedItem = NULL;
}
else
{
qCritical() << "Selected item neither a mapping nor a paint." << endl;
}
}
}
@@ -1214,6 +1218,14 @@ void MainWindow::createActions()
// Output window should be displayed for full screen option to be available.
connect(displayOutputWindow, SIGNAL(toggled(bool)), outputWindowFullScreen, SLOT(setEnabled(bool)));
// outputWindowHasCursor = new QAction(tr("O&utput window has cursor"), this);
// outputWindowHasCursor->setStatusTip(tr("Show cursor in output window"));
// outputWindowHasCursor->setIconVisibleInMenu(false);
// outputWindowHasCursor->setCheckable(true);
// outputWindowHasCursor->setChecked(true);
// connect(outputWindowHasCursor, SIGNAL(toggled(bool)), outputWindow, SLOT(setFullScreen(bool)));
// Toggle display of canvas controls.
displayCanvasControls = new QAction(tr("&Display canvas controls"), this);
// displayCanvasControls->setShortcut(tr("Ctrl+E"));
@@ -1241,9 +1253,14 @@ void MainWindow::createActions()
connect(stickyVertices, SIGNAL(toggled(bool)), outputWindow->getCanvas(), SLOT(enableStickyVertices(bool)));
}
void MainWindow::enableFullscreen()
void MainWindow::startFullScreen()
{
outputWindowFullScreen->setEnabled(true);
// Remove canvas controls.
displayCanvasControls->setChecked(false);
// Display output window.
displayOutputWindow->setChecked(true);
// Send fullscreen.
outputWindowFullScreen->setChecked(true);
}
void MainWindow::createMenus()
@@ -1298,6 +1315,7 @@ void MainWindow::createMenus()
viewMenu->addAction(outputWindowFullScreen);
viewMenu->addAction(displayCanvasControls);
viewMenu->addAction(stickyVertices);
//viewMenu->addAction(outputWindowHasCursor);
// Run.
runMenu = menuBar->addMenu(tr("&Run"));
@@ -2048,35 +2066,42 @@ QIcon MainWindow::createImageIcon(const QString& filename) {
void MainWindow::setCurrentPaint(int uid)
{
if (currentPaintId != uid) {
currentPaintId = uid;
paintList->setCurrentRow( getItemRowFromId(*paintList, uid) );
if (uid != NULL_UID)
if (uid == NULL_UID)
removeCurrentPaint();
else {
if (currentPaintId != uid) {
currentPaintId = uid;
paintList->setCurrentRow( getItemRowFromId(*paintList, uid) );
paintPropertyPanel->setCurrentWidget(paintGuis[uid]->getPropertiesEditor());
}
_hasCurrentPaint = true;
}
_hasCurrentPaint = true;
}
void MainWindow::setCurrentMapping(int uid)
{
if (currentMappingId != uid) {
mappingList->setCurrentRow( getItemRowFromId(*mappingList, uid) );
currentMappingId = uid;
if (uid != NULL_UID)
if (uid == NULL_UID)
removeCurrentMapping();
else {
if (currentMappingId != uid) {
currentMappingId = uid;
mappingList->setCurrentRow( getItemRowFromId(*mappingList, uid) );
mappingPropertyPanel->setCurrentWidget(mappers[uid]->getPropertiesEditor());
}
_hasCurrentMapping = true;
_hasCurrentMapping = true;
}
}
void MainWindow::removeCurrentPaint() {
_hasCurrentPaint = false;
currentPaintId = NULL_UID;
paintList->clearSelection();
}
void MainWindow::removeCurrentMapping() {
_hasCurrentMapping = false;
currentMappingId = NULL_UID;
mappingList->clearSelection();
}
void MainWindow::startOscReceiver()
+2 -1
View File
@@ -265,6 +265,7 @@ private:
QAction *rewindAction;
QAction *displayOutputWindow;
//QAction *outputWindowHasCursor;
QAction *outputWindowFullScreen;
QAction *displayCanvasControls;
QAction *stickyVertices;
@@ -347,7 +348,7 @@ public:
void removeCurrentPaint();
void removeCurrentMapping();
void enableFullscreen();
void startFullScreen();
void setOscPort(QString portNumber);
public:
// Constants. ///////////////////////////////////////////////////////////////////////////////////////
+2
View File
@@ -324,12 +324,14 @@ void TextureMapper::_preDraw(QPainter* painter)
glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
// Copy bits to texture iff necessary.
texture->lockMutex();
if (texture->bitsHaveChanged())
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
texture->getWidth(), texture->getHeight(), 0, GL_RGBA,
GL_UNSIGNED_BYTE, texture->getBits());
}
texture->unlockMutex();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+276 -143
View File
@@ -52,9 +52,16 @@ int MediaImpl::getHeight() const
return _padHandlerData.height;
}
const uchar* MediaImpl::getBits() const
const uchar* MediaImpl::getBits()
{
return _data;
// Reset bits changed.
_bitsChanged = false;
// Return data.
if (_currentFrameSample == NULL)
return NULL;
else
return _data;
}
QString MediaImpl::getUri() const
@@ -83,49 +90,20 @@ void MediaImpl::build()
MediaImpl::~MediaImpl()
{
// Free all resources.
freeResources();
/* _data points to gstreamer-allocated data, we don't manage it ourselves */
//if (_data)
// free(_data);
}
bool MediaImpl::_videoPull()
{
GstSample *sample = _queueInputBuffer.get();
if (sample == NULL)
{
// Either means we are not playing or we have reached EOS.
return false;
}
else
{
// Pull current frame buffer.
GstBuffer *buffer = gst_sample_get_buffer(sample);
GstMapInfo map;
if (gst_buffer_map(buffer, &map, GST_MAP_READ))
{
// For debugging:
//gst_util_dump_mem(map.data, map.size)
_data = map.data;
gst_buffer_unmap(buffer, &map);
if(this->_frame != NULL)
_queueOutputBuffer.put(this->_frame);
_frame = sample;
}
return true;
}
// Free mutex locker object.
delete _mutexLocker;
}
bool MediaImpl::_eos() const
{
if (_movieReady)
{
Q_ASSERT( _videoSink );
Q_ASSERT( _appsink0 );
gboolean videoEos;
g_object_get (G_OBJECT (_videoSink), "eos", &videoEos, NULL);
g_object_get (G_OBJECT (_appsink0), "eos", &videoEos, NULL);
return (bool) (videoEos);
}
else
@@ -134,14 +112,49 @@ bool MediaImpl::_eos() const
GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p)
{
GstSample *sample;
sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_videoSink));
//g_signal_emit_by_name (p->_videoSink, "pull-sample", &sample);
p->getQueueInputBuffer()->put(sample);
if (p->getQueueOutputBuffer()->size() > 1) {
sample = p->getQueueOutputBuffer()->get();
gst_sample_unref(sample);
// Make it thread-safe.
p->lockMutex();
// Get next frame.
GstSample *sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_appsink0));
// Unref last frame.
p->_freeCurrentSample();
// Set current frame.
p->_currentFrameSample = sample;
// For live sources, video dimensions have not been set, because
// gstPadAddedCallback is never called. Fix dimensions from first sample /
// caps we receive.
if (p->_isSharedMemorySource && ( p->_padHandlerData.width == -1 ||
p->_padHandlerData.height == -1)) {
GstCaps *caps = gst_sample_get_caps(sample);
GstStructure *structure;
structure = gst_caps_get_structure(caps, 0);
gst_structure_get_int(structure, "width", &p->_padHandlerData.width);
gst_structure_get_int(structure, "height", &p->_padHandlerData.height);
// g_print("Size is %u x %u\n", _padHandlerData.width, _padHandlerData.height);
}
// Try to retrieve data bits of frame.
GstMapInfo& map = p->_mapInfo;
GstBuffer *buffer = gst_sample_get_buffer( sample );
if (gst_buffer_map(buffer, &map, GST_MAP_READ))
{
p->_currentFrameBuffer = buffer;
// For debugging:
//gst_util_dump_mem(map.data, map.size)
// Retrieve data from map info.
p->_data = map.data;
// Bits have changed.
p->_bitsChanged = true;
}
p->unlockMutex();
return GST_FLOW_OK;
}
@@ -149,24 +162,29 @@ MediaImpl::MediaImpl(const QString uri, bool live) :
_currentMovie(""),
_bus(NULL),
_pipeline(NULL),
_source(NULL),
_videoQueue(NULL),
_videoConvert(NULL),
_videoColorSpace(NULL),
_audioSink(NULL),
_videoSink(NULL),
_frame(NULL),
_width(640),
_height(480),
_uridecodebin0(NULL),
_queue0(NULL),
_videoconvert0(NULL),
//_audioSink(NULL),
_appsink0(NULL),
_currentFrameSample(NULL),
_currentFrameBuffer(NULL),
_bitsChanged(false),
_width(640), // unused
_height(480), // unused
_data(NULL),
_seekEnabled(false),
_live(live),
_isSharedMemorySource(live),
_attached(false),
_movieReady(false),
_uri(uri)
{
_pollSource = NULL;
if (uri != "")
{
loadMovie(uri);
}
_mutexLocker = new QMutexLocker(&_mutex);
}
void MediaImpl::unloadMovie()
@@ -198,26 +216,40 @@ void MediaImpl::freeResources()
_pipeline = NULL;
}
_source = NULL;
_videoQueue = NULL;
_videoConvert = NULL;
_videoColorSpace = NULL;
_audioSink = NULL;
_videoSink = NULL;
_frame = NULL;
// Reset pipeline elements.
_uridecodebin0 = NULL;
_queue0 = NULL;
_videoconvert0 = NULL;
_appsink0 = NULL;
// Reset pad handler.
_padHandlerData = GstPadHandlerData();
// Unref the shmsrc poller.
if (_pollSource)
{
g_source_unref(_pollSource);
_pollSource = NULL;
}
qDebug() << "Freeing remaining samples/buffers" << endl;
// Frees current sample and buffer.
_freeCurrentSample();
// Resets bits changed.
_bitsChanged = false;
}
void MediaImpl::resetMovie()
{
// TODO: Check if we can still seek when we reach EOS. It seems like it's then impossible and we
// have to reload but it seems weird so we should check.
if (!_eos() && _seekEnabled)
// XXX: There used to be an issue that when we reached EOS (_eos() == true) we could not seek anymore.
if (_seekEnabled)
{
qDebug() << "Seeking at position 0.";
gst_element_seek_simple (_pipeline, GST_FORMAT_TIME,
(GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 0);
this->_frame = NULL;
this->_currentFrameSample = NULL;
_setReady(true);
}
else
@@ -233,8 +265,11 @@ 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)) {
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;
}
@@ -255,56 +290,108 @@ bool MediaImpl::loadMovie(QString filename)
_uri = filename;
qDebug() << "Opening movie: " << filename << ".";
this->_frame = NULL;
// Free previously allocated structures
unloadMovie();
// Initialize GStreamer.
gst_init (NULL, NULL);
GstElement *capsFilter = NULL;
GstElement *videoScale = NULL;
GstElement *capsfilter0 = NULL;
GstElement *videoscale0 = NULL;
// Create the elements.
_source = gst_element_factory_make ("uridecodebin", "source");
_videoQueue = gst_element_factory_make ("queue", "vqueue");
_videoColorSpace = gst_element_factory_make ("videoconvert", "vcolorspace");
videoScale = gst_element_factory_make ("videoscale", "videoscale0");
capsFilter = gst_element_factory_make ("capsfilter", "capsfilter0");
_videoSink = gst_element_factory_make ("appsink", "vsink");
if (_isSharedMemorySource)
{
_shmsrc0 = gst_element_factory_make ("shmsrc", "shmsrc0");
_gdpdepay0 = gst_element_factory_make ("gdpdepay", "gdpdepay0");
_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);
}
else {
_uridecodebin0 = gst_element_factory_make ("uridecodebin", "uridecodebin0");
}
_queue0 = gst_element_factory_make ("queue", "queue0");
_videoconvert0 = gst_element_factory_make ("videoconvert", "videoconvert0");
videoscale0 = gst_element_factory_make ("videoscale", "videoscale0");
capsfilter0 = gst_element_factory_make ("capsfilter", "capsfilter0");
_appsink0 = gst_element_factory_make ("appsink", "appsink0");
// Prepare handler data.
_padHandlerData.videoToConnect = _videoQueue;
_padHandlerData.videoSink = _videoSink;
_padHandlerData.videoToConnect = _queue0;
_padHandlerData.videoSink = _appsink0;
_padHandlerData.videoIsConnected = false;
// Create the empty pipeline.
_pipeline = gst_pipeline_new ( "video-source-pipeline" );
if (!_pipeline || !_source ||
!_videoQueue || !_videoColorSpace || ! videoScale || ! capsFilter || ! _videoSink)
if (!_pipeline ||
!_queue0 || !_videoconvert0 || ! videoscale0 || ! capsfilter0 || ! _appsink0)
{
g_printerr ("Not all elements could be created.\n");
if (! _pipeline) g_printerr("_pipeline");
if (! _queue0) g_printerr("_queue0");
if (! _videoconvert0) g_printerr("_videoconvert0");
if (! videoscale0) g_printerr("videoscale0");
if (! capsfilter0) g_printerr("capsfilter0");
if (! _appsink0) g_printerr("_appsink0");
unloadMovie();
return -1;
}
if (_isSharedMemorySource)
{
if (! _shmsrc0 || ! _gdpdepay0)
{
g_printerr ("Not all elements could be created.\n");
if (! _shmsrc0) g_printerr("_shmsrc0");
if (! _gdpdepay0) g_printerr("_gdpdepay0");
unloadMovie();
return -1;
}
}
else
{
if (! _uridecodebin0)
{
g_printerr ("Not all elements could be created.\n");
if (! _uridecodebin0) g_printerr("_uridecodebin0");
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), _source,
_videoQueue, _videoColorSpace, videoScale, capsFilter, _videoSink, NULL);
gst_bin_add_many (GST_BIN (_pipeline),
_isSharedMemorySource ? _shmsrc0 : _uridecodebin0, _queue0,
_videoconvert0, videoscale0, capsfilter0, _appsink0, 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");
// special case for shmsrc
if (_isSharedMemorySource)
{
gst_bin_add (GST_BIN(_pipeline), _gdpdepay0);
if (! gst_element_link_many (_shmsrc0, _gdpdepay0, _queue0, NULL))
{
g_printerr ("Could not link shmsrc, deserializer and video queue.\n");
}
}
else
{
if (! gst_element_link (_uridecodebin0, _queue0))
{
g_printerr ("Could not link uridecodebin to video queue.\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");
if (! gst_element_link_many (_queue0, _videoconvert0, capsfilter0, videoscale0, _appsink0, NULL))
{
g_printerr ("Could not link video queue, colorspace converter, caps filter, scaler and app sink.\n");
unloadMovie();
return false;
}
@@ -312,12 +399,13 @@ bool MediaImpl::loadMovie(QString filename)
// Process URI.
QByteArray ba = filename.toLocal8Bit();
gchar* uri = (gchar*) filename.toUtf8().constData();
if (!_live && !gst_uri_is_valid(uri))
if (! _isSharedMemorySource && ! gst_uri_is_valid(uri))
{
// Try to convert filename to URI.
GError* error = NULL;
uri = gst_filename_to_uri(uri, &error);
if (error) {
if (error)
{
qDebug() << "Filename to URI error: " << error->message;
g_error_free(error);
gst_object_unref (uri);
@@ -326,23 +414,26 @@ bool MediaImpl::loadMovie(QString filename)
}
}
if (_live)
if (_isSharedMemorySource)
{
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.
// Connect to the pad-added signal
if (!_live) {
g_object_set (_source, "uri", uri, NULL);
g_signal_connect (_source, "pad-added", G_CALLBACK (MediaImpl::gstPadAddedCallback), &_padHandlerData);
if (! _isSharedMemorySource)
{
g_object_set (_uridecodebin0, "uri", uri, NULL);
g_signal_connect (_uridecodebin0, "pad-added", G_CALLBACK (MediaImpl::gstPadAddedCallback), &_padHandlerData);
}
else {
else
{
//qDebug() << "LIVE mode" << uri;
g_object_set (_source, "socket-path", uri, NULL);
g_object_set (_source, "is-live", TRUE, NULL);
g_object_set (_shmsrc0, "socket-path", uri, NULL);
g_object_set (_shmsrc0, "is-live", TRUE, NULL);
_padHandlerData.videoIsConnected = true;
}
@@ -362,64 +453,58 @@ bool MediaImpl::loadMovie(QString filename)
// Configure video appsink.
GstCaps *videoCaps = gst_caps_from_string ("video/x-raw,format=RGBA");
g_object_set (capsFilter, "caps", videoCaps, NULL);
g_object_set (_videoSink, "emit-signals", TRUE,
g_object_set (capsfilter0, "caps", videoCaps, NULL);
g_object_set (_appsink0, "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);
g_signal_connect (_appsink0, "new-sample", G_CALLBACK (MediaImpl::gstNewSampleCallback), this);
gst_caps_unref (videoCaps);
// Listen to the bus.
_bus = gst_element_get_bus (_pipeline);
// Start playing.
if (!_live && !setPlayState(true))
if (! _isSharedMemorySource && ! setPlayState(true))
{
return false;
}
qDebug() << "Pipeline started.";
//_movieReady = true;
return true;
}
bool MediaImpl::runVideo() {
if (!_preRun())
return false;
bool bitsChanged = false;
// Check if we have some frames in the input buffer.
if (_queueInputBuffer.size() > 0) {
// Pull video.
if (_videoPull())
bitsChanged = true;
//std::cout << "VideoImpl::runVideo: read frame #" << _videoNewBufferCounter << std::endl;
void MediaImpl::update()
{
// Check for end-of-stream or terminate.
if (_eos() || _terminate)
{
_setFinished(true);
resetMovie();
}
/* 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
* shape on top. This implementation seems to be fast enough that
* _videoNewBufferCounter is often 1 or 0. If bitsChanged is often switching
* between true and false (as in the case described above), than the shape
* textures will appear to be flickering/alternating. Maybe a better solution is
* needed (in the GL layer or here?)*/
else
bitsChanged = true;
{
_setFinished(false);
}
_postRun();
return bitsChanged;
// // Check if movie is ready and connected.
// if (!isReady())
// {
// _bitsChanged = false;
// }
//
// Check gstreamer messages on bus.
_checkMessages();
}
bool MediaImpl::setPlayState(bool play)
{
if (_pipeline == NULL)
{
return false;
}
GstStateChangeReturn ret = gst_element_set_state (_pipeline, (play ? GST_STATE_PLAYING : GST_STATE_PAUSED));
if (ret == GST_STATE_CHANGE_FAILURE)
@@ -445,15 +530,18 @@ bool MediaImpl::_preRun()
resetMovie();
}
else
{
_setFinished(false);
}
if (!_movieReady ||
!_padHandlerData.videoIsConnected)
{
return false;
}
return true;
}
void MediaImpl::_postRun()
void MediaImpl::_checkMessages()
{
// Parse message.
if (_bus != NULL)
@@ -462,12 +550,13 @@ void MediaImpl::_postRun()
_bus, 0,
(GstMessageType) (GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
if (msg != NULL) {
if (msg != NULL)
{
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE (msg)) {
switch (GST_MESSAGE_TYPE (msg))
{
case GST_MESSAGE_ERROR:
gst_message_parse_error(msg, &err, &debug_info);
g_printerr("Error received from element %s: %s\n",
@@ -477,9 +566,12 @@ void MediaImpl::_postRun()
g_clear_error(&err);
g_free(debug_info);
if (!_live)
if (!_isSharedMemorySource)
{
_terminate = true;
else {
}
else
{
_attached = false;
gst_element_set_state (_pipeline, GST_STATE_PAUSED);
gst_element_set_state (_pipeline, GST_STATE_NULL);
@@ -489,7 +581,9 @@ void MediaImpl::_postRun()
break;
case GST_MESSAGE_EOS:
// Automatically loop back.
g_print("End-Of-Stream reached.\n");
resetMovie();
// _terminate = true;
// _finish();
break;
@@ -552,12 +646,33 @@ void MediaImpl::_setReady(bool ready)
_movieReady = ready;
}
void MediaImpl::_setFinished(bool finished) {
void MediaImpl::_setFinished(bool finished)
{
Q_UNUSED(finished);
// qDebug() << "Clip " << (finished ? "finished" : "not finished");
}
void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::GstPadHandlerData* data) {
void MediaImpl::_freeCurrentSample() {
if (_currentFrameBuffer != NULL)
{
gst_buffer_unmap(_currentFrameBuffer, &_mapInfo);
}
if (_currentFrameSample != NULL)
{
gst_sample_unref(_currentFrameSample);
}
_currentFrameSample = NULL;
_currentFrameBuffer = NULL;
_data = NULL;
}
/**
* FIXME: remove GOTO
*/
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;
@@ -565,7 +680,9 @@ void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::
GstCaps *newPadCaps = gst_pad_query_caps (newPad, NULL);
GstStructure *newPadStruct = gst_caps_get_structure (newPadCaps, 0);
const gchar *newPadType = gst_structure_get_name (newPadStruct);
g_print("Structure is %s\n", gst_structure_to_string(newPadStruct));
gchar *newPadStructStr = gst_structure_to_string(newPadStruct);
g_print("Structure is %s\n", newPadStructStr);
g_free(newPadStructStr);
if (g_str_has_prefix (newPadType, "video/x-raw"))
{
sinkPad = gst_element_get_static_pad (data->videoToConnect, "sink");
@@ -598,7 +715,8 @@ void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::
}
// Attempt the link
if (GST_PAD_LINK_FAILED (gst_pad_link (newPad, sinkPad))) {
if (GST_PAD_LINK_FAILED (gst_pad_link (newPad, sinkPad)))
{
g_print (" Type is '%s' but link failed.\n", newPadType);
goto exit;
} else {
@@ -609,9 +727,24 @@ void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::
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);
}
}
void MediaImpl::lockMutex()
{
_mutexLocker->relock();
}
void MediaImpl::unlockMutex()
{
_mutexLocker->unlock();
}
+126 -31
View File
@@ -30,13 +30,14 @@
#include <gst/app/gstappsink.h>
#include <QtGlobal>
#include <QtOpenGL>
#include <QMutex>
#include <QWaitCondition>
#if __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <tr1/memory>
#include "AsyncQueue.h"
/**
* Private declaration of the video player.
@@ -46,25 +47,82 @@
class MediaImpl
{
public:
/**
* Constructor.
* This media player works for both video files and shared memory sockets.
* If live is true, it's a shared memory socket.
*/
MediaImpl(const QString uri, bool live);
~MediaImpl();
// void setUri(const QString uri);
/**
* Returns whether or not GStreamer video support is ok.
*/
static bool hasVideoSupport();
/**
* Sets up the player.
* Basically calls loadMovie().
*/
void build();
/**
* Returns the width of the video image.
*/
int getWidth() const;
/**
* Returns the height of the video image.
*/
int getHeight() const;
/**
* Returns the path to the media file being played.
*/
QString getUri() const;
/**
* When using the shared memory source, returns whether or not we
* are attached to a shared memory socket.
*/
bool getAttached();
const uchar* getBits() const;
bool isReady() const { return _padHandlerData.videoIsConnected; }
/**
* Returns the raw image of the last video frame.
* It is currently unused!
*/
const uchar* getBits();
/// Returns true if the image has changed.
bool runVideo();
// void runAudio();
/// Returns true iff bits have changed since last call to getBits().
bool bitsHaveChanged() const { return _bitsChanged; }
/**
* Checks if the pipeline is ready.
*
* Returns whether or not the elements in the pipeline are connected,
* and if we are using shmsrc, if the shared memory socket is being read.
*/
bool isReady() const { return _movieReady && _padHandlerData.videoIsConnected; }
/**
* Performs regular updates (checks if movie is ready and checks messages).
*/
void update();
// void runAudio();
/**
* Loads a new movie file.
*
* Creates a new GStreamer pipeline, opens a movie or a shmsrc socket.
*/
bool loadMovie(QString filename);
bool setPlayState(bool play);
/**
* Tells the MediaImpl that we are actually reading from a shmsrc.
* Called from the GStreamer callback of the shmsrc.
*/
void setAttached(bool attach);
void resetMovie();
@@ -74,16 +132,23 @@ protected:
void freeResources();
private:
bool _videoPull();
/**
* Checks if we reached the end of the video file.
*
* Returns false if the pipeline is not ready yet.
*/
bool _eos() const;
// void _finish();
// void _init();
// void _finish();
// void _init();
bool _preRun();
void _postRun();
void _checkMessages();
void _setReady(bool ready);
void _setFinished(bool finished);
void _freeCurrentSample();
public:
// GStreamer callbacks.
@@ -108,54 +173,84 @@ public:
// GStreamer callback that plugs the audio/video pads into the proper elements when they
// are made available by the source.
static void gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::GstPadHandlerData* data);
AsyncQueue<GstSample*> *getQueueInputBuffer() {
return &this->_queueInputBuffer;
}
AsyncQueue<GstSample*> *getQueueOutputBuffer() {
return &this->_queueOutputBuffer;
}
/// Locks mutex (default = no effect).
void lockMutex();
/// Unlocks mutex (default = no effect).
void unlockMutex();
private:
//locals
/**
* Path of the movie being played.
*/
QString _currentMovie;
// gstreamer
// gstreamer elements
GstBus *_bus;
GstElement *_pipeline;
GstElement *_source;
GstElement *_deserializer;
GstElement *_audioQueue;
GstElement *_audioConvert;
GstElement *_audioResample;
GstElement *_videoQueue;
GstElement *_videoConvert;
GstElement *_videoColorSpace;
GstElement *_audioSink;
GstElement *_videoSink;
GstSample *_frame;
GstElement *_uridecodebin0;
GstElement *_shmsrc0;
GstElement *_gdpdepay0;
//GstElement *_audioQueue;
//GstElement *_audioConvert;
//GstElement *_audioResample;
GstElement *_queue0;
GstElement *_videoconvert0;
//GstElement *_audioSink;
GstElement *_appsink0;
/**
* Temporary contains the image data of the last frame.
*/
GstSample *_currentFrameSample;
GstBuffer *_currentFrameBuffer;
GstMapInfo _mapInfo;
bool _bitsChanged;
/**
* shmsrc socket poller.
*/
GSource *_pollSource;
GstPadHandlerData _padHandlerData;
// unused
int _width;
// unused
int _height;
/**
* Raw image data of the last video frame.
*/
uchar *_data;
bool _seekEnabled;
bool _live;
/**
* Whether or not we are reading video from a shmsrc.
*/
bool _isSharedMemorySource;
/**
* Whether or not we are attached to a shmsrc, if using a shmsrc.
*/
bool _attached;
int _audioNewBufferCounter;
bool _terminate;
bool _movieReady;
AsyncQueue<GstSample*> _queueInputBuffer;
AsyncQueue<GstSample*> _queueOutputBuffer;
QMutex _mutex;
QMutexLocker* _mutexLocker;
private:
/**
* Path of the movie file being played.
*/
QString _uri;
static const int MAX_SAMPLES_IN_BUFFER_QUEUES = 30;
};
#endif /* ifndef */
+27 -7
View File
@@ -36,6 +36,24 @@ OutputGLWindow::OutputGLWindow(MainWindow* mainWindow, QWidget* parent, const QG
// Save window geometry.
_geometry = saveGeometry();
_pointerIsVisible = true;
}
void OutputGLWindow::setCursorVisible(bool visible)
{
_pointerIsVisible = visible;
if (_pointerIsVisible)
{
this->setCursor(Qt::ArrowCursor);
_pointerIsVisible = true;
}
else
{
this->setCursor(Qt::BlankCursor);
_pointerIsVisible = false;
}
}
void OutputGLWindow::closeEvent(QCloseEvent *event)
@@ -47,14 +65,16 @@ void OutputGLWindow::closeEvent(QCloseEvent *event)
void OutputGLWindow::keyPressEvent(QKeyEvent *event)
{
// Escape from full screen mode.
if (isFullScreen() && event->key() == Qt::Key_Escape)
if (event->key() == Qt::Key_Escape)
{
setFullScreen(false);
emit fullScreenToggled(false);
}
else if (event->key() == Qt::Key_Escape)
{
// pass
if (isFullScreen())
{
setFullScreen(false);
emit fullScreenToggled(false);
} else {
setFullScreen(true);
emit fullScreenToggled(true);
}
}
else
{
+6
View File
@@ -2,6 +2,7 @@
* OutputGLWindow.h
*
* (c) 2014 Sofian Audry -- info(@)sofianaudry(.)com
* (c) 2014 Alexandre Quessy -- alexandre(@)quessy(.)net
*
* 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
@@ -22,6 +23,7 @@
#include <QDialog>
#include <QtGlobal>
#include <QTimer>
#include "DestinationGLCanvas.h"
// TODO: add SLOT for mySetVisible
@@ -47,10 +49,14 @@ signals:
public:
DestinationGLCanvas* getCanvas() const { return canvas; }
void setPointerHasMoved();
void setCursorVisible(bool visible);
private:
DestinationGLCanvas* canvas;
QByteArray _geometry;
bool _pointerIsVisible;
};
#endif /* OutputGLWINDOW_H_ */
+16 -3
View File
@@ -86,8 +86,7 @@ int Media::getHeight() const
}
void Media::update() {
if (impl_->runVideo())
bitsChanged = true;
impl_->update();
}
void Media::play()
@@ -105,11 +104,25 @@ void Media::rewind()
impl_->resetMovie();
}
const uchar* Media::_getBits() const
void Media::lockMutex() {
impl_->lockMutex();
}
void Media::unlockMutex() {
impl_->unlockMutex();
}
const uchar* Media::getBits()
{
return this->impl_->getBits();
}
bool Media::bitsHaveChanged() const
{
return this->impl_->bitsHaveChanged();
}
bool Media::hasVideoSupport()
{
return MediaImpl::hasVideoSupport();
+28 -16
View File
@@ -25,8 +25,8 @@
#include <QtGlobal>
#include <QtOpenGL>
#include <string>
#include <QColor>
#include <QMutex>
#if __APPLE__
#include <OpenGL/gl.h>
@@ -76,6 +76,12 @@ public:
/// Rewinds.
virtual void rewind() {}
/// Locks mutex (default = no effect).
virtual void lockMutex() {}
/// Unlocks mutex (default = no effect).
virtual void unlockMutex() {}
void setName(const QString& name) { _name = name; }
QString getName() const { return _name; }
uid getId() const { return _id; }
@@ -118,8 +124,7 @@ protected:
Paint(id),
textureId(0),
x(0),
y(0),
bitsChanged(true)
y(0)
{
glGenTextures(1, &textureId);
}
@@ -134,13 +139,12 @@ public:
GLuint getTextureId() const { return textureId; }
virtual int getWidth() const = 0;
virtual int getHeight() const = 0;
virtual const uchar* getBits() const {
bitsChanged = false;
return _getBits();
}
/// Returns image bits data. Next call to bitsHaveChanged() will be false.
virtual const uchar* getBits() = 0;
/// Returns true iff bits have changed since last call to getBits().
bool bitsHaveChanged() const { return bitsChanged; }
virtual bool bitsHaveChanged() const = 0;
virtual void setPosition(GLfloat xPos, GLfloat yPos) {
x = xPos;
@@ -148,9 +152,6 @@ public:
}
virtual GLfloat getX() const { return x; }
virtual GLfloat getY() const { return y; }
protected:
virtual const uchar* _getBits() const = 0;
};
/**
@@ -184,8 +185,12 @@ public:
virtual int getWidth() const { return image.width(); }
virtual int getHeight() const { return image.height(); }
protected:
virtual const uchar* _getBits() const { return image.bits(); }
virtual const uchar* getBits() {
bitsChanged = false;
return image.bits();
}
virtual bool bitsHaveChanged() const { return bitsChanged; }
};
class MediaImpl; // forward declaration
@@ -215,6 +220,12 @@ public:
/// Rewinds.
virtual void rewind();
/// Locks mutex (default = no effect).
virtual void lockMutex();
/// Unlocks mutex (default = no effect).
virtual void unlockMutex();
virtual QString getType() const
{
return "media";
@@ -222,14 +233,15 @@ public:
virtual int getWidth() const;
virtual int getHeight() const;
virtual const uchar* getBits();
virtual bool bitsHaveChanged() const;
/**
* Checks whether or not video is supported on this platform.
*/
static bool hasVideoSupport();
protected:
virtual const uchar* _getBits() const;
private:
/**
* Private implementation, so that GStreamer headers don't need
+1
View File
@@ -35,6 +35,7 @@ Authors
* Sofian Audry: lead developer, user interface designer, project manager.
* Vasilis Liaskovitis: developer.
* Mike Latona: user interface designer.
* Dame Diongue: developer.
Contributors
------------
+1 -1
View File
@@ -153,7 +153,7 @@ int main(int argc, char *argv[])
if (parser.isSet(fullscreenOption))
{
qDebug() << "TODO: Running in fullscreen mode";
win.enableFullscreen();
win.startFullScreen();
}
#endif