mirror of
https://github.com/mapmapteam/mapmap.git
synced 2026-06-16 12:33:19 +02:00
Merge branch 'develop' of github.com:mapmapteam/mapmap into features-options
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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 © 2013 Sofian Audry, Alexandre Quessy, "
|
||||
"Mike Latona and Vasilis Liaskovitis.</p>"
|
||||
"<p>Copyright © 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
@@ -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. ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
------------
|
||||
|
||||
Reference in New Issue
Block a user