summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vrplayer/README.txt13
-rw-r--r--vrplayer/decoder.cpp72
-rw-r--r--vrplayer/decoder.h39
-rw-r--r--vrplayer/decoderthread.cpp217
-rw-r--r--vrplayer/decoderthread.h72
-rw-r--r--vrplayer/main.cpp11
-rw-r--r--vrplayer/mainwindow.cpp326
-rw-r--r--vrplayer/mainwindow.h82
-rw-r--r--vrplayer/mainwindow.ui68
-rw-r--r--vrplayer/vrplayer.pro30
-rw-r--r--xrdpvr/xrdpvr.c138
-rw-r--r--xrdpvr/xrdpvr.h17
-rw-r--r--xrdpvr/xrdpvr_internal.h28
13 files changed, 1058 insertions, 55 deletions
diff --git a/vrplayer/README.txt b/vrplayer/README.txt
new file mode 100644
index 00000000..91a911fa
--- /dev/null
+++ b/vrplayer/README.txt
@@ -0,0 +1,13 @@
+
+A QT based media player that runs on a RDP server and
+redirects audio/video to the client where it is decoded
+and rendered locally
+
+To build vrplayer, installl QT 4.x , then run
+
+qmake
+make
+
+To run vrplayer, include xrdpapi/.libs and xrdpvr/.libs in
+your LD_LIBRARY_PATH
+
diff --git a/vrplayer/decoder.cpp b/vrplayer/decoder.cpp
new file mode 100644
index 00000000..b1741997
--- /dev/null
+++ b/vrplayer/decoder.cpp
@@ -0,0 +1,72 @@
+#include "decoder.h"
+
+Decoder::Decoder(QObject *parent) :
+ QObject(parent)
+{
+ channel = NULL;
+}
+
+/*
+ * inititialize the decoder
+ *
+ * @return 0 on success, -1 on failure
+ *****************************************************************************/
+int Decoder::init(QString filename)
+{
+ if (channel)
+ return -1;
+
+ /* open a virtual channel and connect to remote client */
+ channel = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, "xrdpvr", 0);
+ if (channel == NULL)
+ {
+ qDebug() << "WTSVirtualChannelOpenEx() failed\n";
+ return -1;
+ }
+
+ /* initialize the player */
+ if (xrdpvr_init_player(channel, 101, filename.toAscii().data()))
+ {
+ fprintf(stderr, "failed to initialize the player\n");
+ return -1;
+ }
+
+#if 1
+ sleep(1);
+ qDebug() << "sleeping 1";
+ xrdpvr_set_geometry(channel, 101, mainWindowGeometry.x(),
+ mainWindowGeometry.y(), mainWindowGeometry.width(),
+ mainWindowGeometry.height());
+ qDebug() << "set geometry to:" << mainWindowGeometry.x() <<
+ "" << mainWindowGeometry.y() <<
+ "" << mainWindowGeometry.width() <<
+ "" << mainWindowGeometry.height();
+#endif
+
+ /* send compressed media data to client; client will decompress */
+ /* the media and play it locally */
+ xrdpvr_play_media(channel, 101, filename.toAscii().data());
+
+ /* perform clean up */
+ xrdpvr_deinit_player(channel, 101);
+
+ WTSVirtualChannelClose(channel);
+
+ return 0;
+}
+
+void Decoder::onGeometryChanged(QRect *g)
+{
+#if 1
+ mainWindowGeometry.setX(g->x());
+ mainWindowGeometry.setY(g->y());
+ mainWindowGeometry.setWidth(g->width());
+ mainWindowGeometry.setHeight(g->height());
+#else
+ if (!channel)
+ return;
+
+ xrdpvr_set_geometry(channel, 101, g->x(), g->y(), g->width(), g->height());
+ qDebug() << "sent geometry";
+#endif
+}
diff --git a/vrplayer/decoder.h b/vrplayer/decoder.h
new file mode 100644
index 00000000..34b152e7
--- /dev/null
+++ b/vrplayer/decoder.h
@@ -0,0 +1,39 @@
+#ifndef DECODER_H
+#define DECODER_H
+
+#include <QObject>
+#include <QDebug>
+#include <QRect>
+
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
+#include <libavformat/avformat.h>
+#include <xrdpapi.h>
+#include <xrdpvr.h> /* LK_TODO is this required? */
+
+class Decoder : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Decoder(QObject *parent = 0);
+ int init(QString filename);
+ //int deinit();
+ //int setWindow(QRectangle rect);
+
+private:
+ void *channel;
+ QRect mainWindowGeometry;
+
+signals:
+
+public slots:
+ void onGeometryChanged(QRect *geometry);
+};
+
+#endif // DECODER_H
diff --git a/vrplayer/decoderthread.cpp b/vrplayer/decoderthread.cpp
new file mode 100644
index 00000000..2ee7a524
--- /dev/null
+++ b/vrplayer/decoderthread.cpp
@@ -0,0 +1,217 @@
+#include "decoderthread.h"
+
+DecoderThread::DecoderThread()
+{
+ vsi = NULL;
+ channel = NULL;
+ geometry.setX(0);
+ geometry.setY(0);
+ geometry.setWidth(0);
+ geometry.setHeight(0);
+ stream_id = 101;
+ elapsedTime = 0;
+ la_seekPos = 0;
+}
+
+void DecoderThread::run()
+{
+ int64_t start_time;
+ int64_t duration;
+
+ /* TODO what happens if we get called a 2nd time while we are still running */
+
+ /* need a media file */
+ if (filename.length() == 0)
+ {
+ emit on_decoderErrorMsg("No media file",
+ "Please select a media file to play");
+ return;
+ }
+
+ /* connect to remote client */
+ if (openVirtualChannel())
+ return;
+
+ vsi = (VideoStateInfo *) av_mallocz(sizeof(VideoStateInfo));
+ if (vsi == NULL)
+ {
+ emit on_decoderErrorMsg("Resource error",
+ "Memory allocation failed, system out of memory");
+ return;
+ }
+
+ /* register all formats/codecs */
+ av_register_all();
+
+ if (sendMetadataFile())
+ return;
+
+ if (sendVideoFormat())
+ return;
+
+ if (sendAudioFormat())
+ return;
+
+ if (sendGeometry())
+ return;
+
+ xrdpvr_play_media(channel, 101, filename.toAscii().data());
+
+ xrdpvr_get_media_duration(&start_time, &duration);
+ emit on_mediaDurationInSeconds(duration);
+
+ qDebug() << "start_time=" << start_time << " duration=" << duration;
+
+ while (xrdpvr_play_frame(channel, 101) == 0)
+ {
+ if (elapsedTime == 0)
+ elapsedTime = av_gettime();
+
+ /* time elapsed in 1/100th sec units since play started */
+ emit on_elapsedtime((av_gettime() - elapsedTime) / 10000);
+
+ mutex.lock();
+ if (la_seekPos)
+ {
+ qDebug() << "seeking to" << la_seekPos;
+ xrdpvr_seek_media(la_seekPos, 0);
+ elapsedTime = av_gettime() - la_seekPos * 1000000;
+ la_seekPos = 0;
+ }
+ mutex.unlock();
+ }
+
+ /* perform clean up */
+ xrdpvr_deinit_player(channel, 101);
+
+ /* clean up resources */
+ closeVirtualChannel();
+ if (vsi)
+ av_free(vsi);
+}
+
+void DecoderThread::on_geometryChanged(int x, int y, int width, int height)
+{
+ geometry.setX(x);
+ geometry.setY(y);
+ geometry.setWidth(width);
+ geometry.setHeight(height);
+
+#if 0
+ qDebug() << "decoderThread:signal" <<
+ "" << geometry.x() <<
+ "" << geometry.y() <<
+ "" << geometry.width() <<
+ "" << geometry.height();
+#endif
+
+ if (channel)
+ {
+ xrdpvr_set_geometry(channel, 101, geometry.x(), geometry.y(),
+ geometry.width(), geometry.height());
+ }
+}
+
+void DecoderThread::on_mediaSeek(int value)
+{
+ mutex.lock();
+ la_seekPos = value;
+ mutex.unlock();
+}
+
+void DecoderThread::setFilename(QString filename)
+{
+ this->filename = filename;
+}
+
+/**
+ * @brief Open a virtual connection to remote client
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+int DecoderThread::openVirtualChannel()
+{
+ /* is channel already open? */
+ if (channel)
+ return -1;
+
+ /* open a virtual channel and connect to remote client */
+ channel = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, "xrdpvr", 0);
+ if (channel == NULL)
+ {
+ emit on_decoderErrorMsg("Connection failure",
+ "Error connecting to remote client");
+ return -1;
+ }
+ return 0;
+}
+
+int DecoderThread::closeVirtualChannel()
+{
+ /* channel must be opened first */
+ if (!channel)
+ return -1;
+
+ WTSVirtualChannelClose(channel);
+ return 0;
+}
+
+/**
+ * @brief this is a temp hack while we figure out how to set up the right
+ * context for avcodec_decode_video2() on the server side; the workaround
+ * is to send the first 1MB of the media file to the server end which
+ * reads this file and sets up its context
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+int DecoderThread::sendMetadataFile()
+{
+ if (xrdpvr_create_metadata_file(channel, filename.toAscii().data()))
+ {
+ emit on_decoderErrorMsg("I/O Error",
+ "An error occurred while sending data to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+int DecoderThread::sendVideoFormat()
+{
+ if (xrdpvr_set_video_format(channel, stream_id))
+ {
+ emit on_decoderErrorMsg("I/O Error",
+ "Error sending video format to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+int DecoderThread::sendAudioFormat()
+{
+ if (xrdpvr_set_audio_format(channel, stream_id))
+ {
+ emit on_decoderErrorMsg("I/O Error",
+ "Error sending audio format to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+int DecoderThread::sendGeometry()
+{
+ int rv;
+
+ rv = xrdpvr_set_geometry(channel, stream_id, geometry.x(), geometry.y(),
+ geometry.width(), geometry.height());
+
+ if (rv)
+ {
+ emit on_decoderErrorMsg("I/O Error",
+ "Error sending screen geometry to remote client");
+ return -1;
+ }
+ return 0;
+}
diff --git a/vrplayer/decoderthread.h b/vrplayer/decoderthread.h
new file mode 100644
index 00000000..0f8a1c12
--- /dev/null
+++ b/vrplayer/decoderthread.h
@@ -0,0 +1,72 @@
+#ifndef DECODERTHREAD_H
+#define DECODERTHREAD_H
+
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
+#include <QThread>
+#include <QDebug>
+#include <QString>
+#include <QRect>
+#include <QMutex>
+
+#include <xrdpapi.h>
+#include <xrdpvr.h>
+
+/* ffmpeg related stuff */
+extern "C"
+{
+ #include <libavformat/avformat.h>
+ #include <libavcodec/avcodec.h>
+}
+
+class DecoderThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ DecoderThread();
+ void setFilename(QString filename);
+
+public slots:
+ void on_geometryChanged(int x, int y, int width, int height);
+ void on_mediaSeek(int value);
+
+protected:
+ void run();
+
+private:
+ typedef struct _VideoStateInfo
+ {
+ AVFormatContext *pFormatCtx;
+ } VideoStateInfo;
+
+ VideoStateInfo *vsi;
+ QString filename;
+ void *channel;
+ int stream_id;
+ QRect geometry;
+ int64_t elapsedTime; /* elapsed time in usecs since play started */
+ QMutex mutex;
+ int64_t la_seekPos; /* locked access; must hold mutex */
+
+ int openVirtualChannel();
+ int closeVirtualChannel();
+ int sendMetadataFile();
+ int sendVideoFormat();
+ int sendAudioFormat();
+ int sendGeometry();
+
+signals:
+ void on_progressUpdate(int percent);
+ void on_decoderErrorMsg(QString title, QString msg);
+ void on_mediaDurationInSeconds(int duration);
+ void on_elapsedtime(int val); /* in hundredth of a sec */
+};
+
+#endif // DECODERTHREAD_H
diff --git a/vrplayer/main.cpp b/vrplayer/main.cpp
new file mode 100644
index 00000000..d951345f
--- /dev/null
+++ b/vrplayer/main.cpp
@@ -0,0 +1,11 @@
+#include <QtGui/QApplication>
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/vrplayer/mainwindow.cpp b/vrplayer/mainwindow.cpp
new file mode 100644
index 00000000..7f41eee9
--- /dev/null
+++ b/vrplayer/mainwindow.cpp
@@ -0,0 +1,326 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+/*
+ * TODO
+ * o should we use tick marks in QSlider?
+ */
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+ acceptSliderMove = false;
+ decoderThread = new DecoderThread();
+ setupUI();
+
+/* LK_TODO */
+#if 0
+ decoder = new Decoder(this);
+ connect(this, SIGNAL(onGeometryChanged(int, int, int, int)),
+ decoder, SLOT(onGeometryChanged(int, int, int, int)));
+#endif
+
+ /* register for signals/slots with decoderThread */
+ connect(this, SIGNAL(on_geometryChanged(int,int,int,int)),
+ decoderThread, SLOT(on_geometryChanged(int,int,int,int)));
+
+ connect(decoderThread, SIGNAL(on_elapsedtime(int)),
+ this, SLOT(on_elapsedTime(int)));
+
+ connect(decoderThread, SIGNAL(on_decoderErrorMsg(QString, QString)),
+ this, SLOT(on_decoderError(QString, QString)));
+
+ connect(decoderThread, SIGNAL(on_mediaDurationInSeconds(int)),
+ this, SLOT(on_mediaDurationInSeconds(int)));
+
+ connect(this, SIGNAL(on_mediaSeek(int)), decoderThread, SLOT(on_mediaSeek(int)));
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ int rv;
+
+ rv = QMessageBox::question(this, "Closing application",
+ "Do you really want to close vrplayer?",
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (rv == QMessageBox::No)
+ {
+ event->ignore();
+ return;
+ }
+ decoderThread->exit(0);
+ event->accept();
+}
+
+void MainWindow::resizeEvent(QResizeEvent *e)
+{
+ QRect rect;
+
+ getVdoGeometry(&rect);
+ emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void MainWindow::moveEvent(QMoveEvent *e)
+{
+ QRect rect;
+
+ getVdoGeometry(&rect);
+ emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void MainWindow::setupUI()
+{
+ /* setup area to display video */
+ lblVideo = new QLabel();
+ lblVideo->setMinimumWidth(320);
+ lblVideo->setMinimumHeight(200);
+ QPalette palette = lblVideo->palette();
+ palette.setColor(lblVideo->backgroundRole(), Qt::black);
+ palette.setColor(lblVideo->foregroundRole(), Qt::black);
+ lblVideo->setAutoFillBackground(true);
+ lblVideo->setPalette(palette);
+ hboxLayoutTop = new QHBoxLayout;
+ hboxLayoutTop->addWidget(lblVideo);
+
+ /* setup label to display current pos in media */
+ lblCurrentPos = new QLabel("00:00:00");
+ lblCurrentPos->setMinimumHeight(20);
+ lblCurrentPos->setMaximumHeight(20);
+
+ /* setup slider to seek into media */
+ slider = new QSlider();
+ slider->setOrientation(Qt::Horizontal);
+ slider->setMinimumHeight(20);
+ slider->setMaximumHeight(20);
+ connect(slider, SIGNAL(actionTriggered(int)), this, SLOT(on_sliderActionTriggered(int)));
+ connect(slider, SIGNAL(valueChanged(int)), this, SLOT(on_sliderValueChanged(int)));
+
+ /* setup label to display media duration */
+ lblDuration = new QLabel("00:00:00");
+ lblDuration->setMinimumHeight(20);
+ lblDuration->setMaximumHeight(20);
+
+ /* add above three widgets to mid layout */
+ hboxLayoutMiddle = new QHBoxLayout;
+ hboxLayoutMiddle->addWidget(lblCurrentPos);
+ hboxLayoutMiddle->addWidget(slider);
+ hboxLayoutMiddle->addWidget(lblDuration);
+
+ /* setup play button */
+ btnPlay = new QPushButton("P");
+ btnPlay->setMinimumHeight(40);
+ btnPlay->setMaximumHeight(40);
+ btnPlay->setMinimumWidth(40);
+ btnPlay->setMaximumWidth(40);
+ connect(btnPlay, SIGNAL(clicked(bool)), this, SLOT(on_btnPlayClicked(bool)));
+
+ /* setup stop button */
+ btnStop = new QPushButton("S");
+ btnStop->setMinimumHeight(40);
+ btnStop->setMaximumHeight(40);
+ btnStop->setMinimumWidth(40);
+ btnStop->setMaximumWidth(40);
+
+ /* setup rewind button */
+ btnRewind = new QPushButton("R");
+ btnRewind->setMinimumHeight(40);
+ btnRewind->setMaximumHeight(40);
+ btnRewind->setMinimumWidth(40);
+ btnRewind->setMaximumWidth(40);
+
+ /* add buttons to bottom panel */
+ hboxLayoutBottom = new QHBoxLayout;
+ hboxLayoutBottom->addWidget(btnPlay);
+ hboxLayoutBottom->addWidget(btnStop);
+ hboxLayoutBottom->addWidget(btnRewind);
+ hboxLayoutBottom->addStretch();
+
+ /* add all three layouts to one vertical layout */
+ vboxLayout = new QVBoxLayout;
+ vboxLayout->addLayout(hboxLayoutTop);
+ vboxLayout->addLayout(hboxLayoutMiddle);
+ vboxLayout->addLayout(hboxLayoutBottom);
+
+ /* add all of them to central widget */
+ window = new QWidget;
+ window->setLayout(vboxLayout);
+ this->setCentralWidget(window);
+}
+
+void MainWindow::openMediaFile()
+{
+ /* TODO take last stored value from QSettings */
+
+ if (filename.length() == 0)
+ {
+ /* no previous selection - open user's home folder TODO */
+ // TODO filename = QFileDialog::getOpenFileName(this, "Select Media File", "/");
+ filename = QFileDialog::getOpenFileName(this, "Select Media File", "/home/lk/vbox_share");
+ }
+ else
+ {
+ /* show last selected file */
+ filename = QFileDialog::getOpenFileName(this, "Select Media File",
+ filename);
+ }
+ decoderThread->setFilename(filename);
+}
+
+void MainWindow::getVdoGeometry(QRect *rect)
+{
+ int x = geometry().x() + lblVideo->geometry().x();
+
+ int y = pos().y() + lblVideo->geometry().y() +
+ ui->mainToolBar->geometry().height() * 4 + 10;
+
+ rect->setX(x);
+ rect->setY(y);
+ rect->setWidth(lblVideo->geometry().width());
+ rect->setHeight(lblVideo->geometry().height());
+}
+
+/*******************************************************************************
+ * actions and slots go here *
+ ******************************************************************************/
+
+void MainWindow::on_actionOpen_Media_File_triggered()
+{
+ openMediaFile();
+}
+
+void MainWindow::on_actionExit_triggered()
+{
+ /* TODO: confirm app exit */
+ this->close();
+}
+
+void MainWindow::on_actionPlay_Media_triggered()
+{
+ // LK_TODO do we need this? if yes, should be same as on_btnPlayClicked()
+#if 1
+ decoderThread->start();
+#else
+ if (!decoder)
+ return;
+
+ decoder->init(filename);
+#endif
+}
+
+void MainWindow::on_decoderError(QString title, QString msg)
+{
+ QMessageBox::information(this, title, msg);
+}
+
+void MainWindow::on_btnPlayClicked(bool flag)
+{
+ if (filename.length() == 0)
+ openMediaFile();
+
+ decoderThread->start();
+}
+
+void MainWindow::on_mediaDurationInSeconds(int duration)
+{
+ int hours = 0;
+ int minutes = 0;
+ int secs = 0;
+ char buf[20];
+
+ /* setup progress bar */
+ slider->setMinimum(0);
+ slider->setMaximum(duration * 100); /* in hundredth of a sec */
+ slider->setValue(0);
+
+ /* convert from seconds to hours:minutes:seconds */
+ hours = duration / 3600;
+ if (hours)
+ duration -= (hours * 3600);
+
+ minutes = duration / 60;
+ if (minutes)
+ duration -= minutes * 60;
+
+ secs = duration;
+
+ sprintf(buf, "%.2d:%.2d:%.2d", hours, minutes, secs);
+ lblDuration->setText(QString(buf));
+}
+
+/**
+ * time elapsed in 1/100th sec units since play started
+ ******************************************************************************/
+void MainWindow::on_elapsedTime(int val)
+{
+ int hours = 0;
+ int minutes = 0;
+ int secs = 0;
+ int duration = val / 100;
+ char buf[20];
+
+ /* if slider bar is down, do not update */
+ if (slider->isSliderDown())
+ return;
+
+ /* update progress bar */
+ slider->setSliderPosition(val);
+
+ /* convert from seconds to hours:minutes:seconds */
+ hours = duration / 3600;
+ if (hours)
+ duration -= (hours * 3600);
+
+ minutes = duration / 60;
+ if (minutes)
+ duration -= minutes * 60;
+
+ secs = duration;
+
+ /* update current position in progress bar */
+ sprintf(buf, "%.2d:%.2d:%.2d", hours, minutes, secs);
+ lblCurrentPos->setText(QString(buf));
+}
+
+void MainWindow::on_sliderValueChanged(int value)
+{
+ if (acceptSliderMove)
+ {
+ acceptSliderMove = false;
+ emit on_mediaSeek(value / 100);
+ }
+}
+
+void MainWindow::on_sliderActionTriggered(int action)
+{
+ switch (action)
+ {
+ case QAbstractSlider::SliderPageStepAdd:
+ acceptSliderMove = true;
+ break;
+
+ case QAbstractSlider::SliderPageStepSub:
+ acceptSliderMove = true;
+ break;
+
+ case QAbstractSlider::SliderMove:
+ if (slider->isSliderDown())
+ acceptSliderMove = true;
+ break;
+ }
+}
+
+#if 1
+// LK_TODO delete this
+void MainWindow::mouseMoveEvent(QMouseEvent *e)
+{
+ //qDebug() << "mouseMoveEvent: x=" << e->globalX() << "y=" << e->globalY();
+}
+#endif
diff --git a/vrplayer/mainwindow.h b/vrplayer/mainwindow.h
new file mode 100644
index 00000000..ed392ea2
--- /dev/null
+++ b/vrplayer/mainwindow.h
@@ -0,0 +1,82 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QFileDialog>
+#include <QDebug>
+#include <QMessageBox>
+#include <QCloseEvent>
+#include <QMoveEvent>
+#include <QPoint>
+#include <QRect>
+#include <QLabel>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QPushButton>
+#include <QSlider>
+
+#include "decoder.h"
+#include "decoderthread.h"
+
+namespace Ui {
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+private slots:
+ void on_actionOpen_Media_File_triggered();
+ void on_actionExit_triggered();
+ void on_actionPlay_Media_triggered();
+ void on_decoderError(QString title, QString msg);
+ void on_btnPlayClicked(bool flag);
+ void on_mediaDurationInSeconds(int duration);
+ void on_elapsedTime(int secs);
+ void on_sliderActionTriggered(int value);
+ void on_sliderValueChanged(int value);
+
+signals:
+ void on_geometryChanged(int x, int y, int widht, int height);
+ void on_mediaSeek(int value);
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+ void closeEvent(QCloseEvent *e);
+ void moveEvent(QMoveEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+
+private:
+ Ui::MainWindow *ui;
+
+ QString filename;
+ Decoder *decoder;
+ DecoderThread *decoderThread;
+
+ /* for UI */
+ QLabel *lblCurrentPos;
+ QLabel *lblDuration;
+ QLabel *lblVideo;
+ QHBoxLayout *hboxLayoutTop;
+ QHBoxLayout *hboxLayoutMiddle;
+ QHBoxLayout *hboxLayoutBottom;
+ QVBoxLayout *vboxLayout;
+ QPushButton *btnPlay;
+ QPushButton *btnStop;
+ QPushButton *btnRewind;
+ QSlider *slider;
+ QWidget *window;
+ bool acceptSliderMove;
+
+ /* private methods */
+ void setupUI();
+ void openMediaFile();
+ void getVdoGeometry(QRect *rect);
+};
+
+#endif // MAINWINDOW_H
diff --git a/vrplayer/mainwindow.ui b/vrplayer/mainwindow.ui
new file mode 100644
index 00000000..7d4a81c6
--- /dev/null
+++ b/vrplayer/mainwindow.ui
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralWidget"/>
+ <widget class="QMenuBar" name="menuBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionOpen_Media_File"/>
+ <addaction name="actionPlay_Media"/>
+ <addaction name="separator"/>
+ <addaction name="actionExit"/>
+ </widget>
+ <addaction name="menuFile"/>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ <action name="actionOpen_Media_File">
+ <property name="text">
+ <string>Open Media File</string>
+ </property>
+ </action>
+ <action name="actionExit">
+ <property name="text">
+ <string>Exit application</string>
+ </property>
+ <property name="toolTip">
+ <string>Exit application</string>
+ </property>
+ </action>
+ <action name="actionPlay_Media">
+ <property name="text">
+ <string>Play Media</string>
+ </property>
+ </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/vrplayer/vrplayer.pro b/vrplayer/vrplayer.pro
new file mode 100644
index 00000000..9aac8aff
--- /dev/null
+++ b/vrplayer/vrplayer.pro
@@ -0,0 +1,30 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2012-11-13T11:52:36
+#
+#-------------------------------------------------
+
+QT += core gui
+
+TARGET = vrplayer
+TEMPLATE = app
+
+
+SOURCES += main.cpp\
+ mainwindow.cpp \
+ decoder.cpp \
+ decoderthread.cpp
+
+HEADERS += mainwindow.h \
+ decoder.h \
+ decoderthread.h
+
+FORMS += mainwindow.ui
+
+# added by LK
+INCLUDEPATH += ../xrdpvr
+INCLUDEPATH += ../xrdpapi
+
+LIBS += -L../xrdpvr/.libs -lxrdpvr
+LIBS += -L../xrdpapi/.libs -lxrdpapi
+LIBS += -L/usr/lib/x86_64-linux-gnu -lavformat -lavcodec -lavutil
diff --git a/xrdpvr/xrdpvr.c b/xrdpvr/xrdpvr.c
index 6293818e..66dfea5a 100644
--- a/xrdpvr/xrdpvr.c
+++ b/xrdpvr/xrdpvr.c
@@ -25,6 +25,8 @@
/* globals */
PLAYER_STATE_INFO g_psi;
+int g_video_index = -1;
+int g_audio_index = -1;
/**
* initialize the media player
@@ -129,10 +131,6 @@ xrdpvr_deinit_player(void *channel, int stream_id)
int
xrdpvr_play_media(void *channel, int stream_id, char *filename)
{
- AVPacket av_pkt;
-
- int video_index = -1;
- int audio_index = -1;
int i;
/* register all available fileformats and codecs */
@@ -161,19 +159,19 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
for (i = 0; i < g_psi.p_format_ctx->nb_streams; i++)
{
if (g_psi.p_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
- video_index < 0)
+ g_video_index < 0)
{
- video_index = i;
+ g_video_index = i;
}
if (g_psi.p_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
- audio_index < 0)
+ g_audio_index < 0)
{
- audio_index = i;
+ g_audio_index = i;
}
}
- if ((audio_index < 0) && (video_index < 0))
+ if ((g_audio_index < 0) && (g_video_index < 0))
{
/* close file and return with error */
printf("ERROR: no audio/video stream found in %s\n", filename);
@@ -181,12 +179,12 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
return -1;
}
- g_psi.audio_stream_index = audio_index;
- g_psi.video_stream_index = video_index;
+ g_psi.audio_stream_index = g_audio_index;
+ g_psi.video_stream_index = g_video_index;
/* get pointers to codex contexts for both streams */
- g_psi.p_audio_codec_ctx = g_psi.p_format_ctx->streams[audio_index]->codec;
- g_psi.p_video_codec_ctx = g_psi.p_format_ctx->streams[video_index]->codec;
+ g_psi.p_audio_codec_ctx = g_psi.p_format_ctx->streams[g_audio_index]->codec;
+ g_psi.p_video_codec_ctx = g_psi.p_format_ctx->streams[g_video_index]->codec;
/* find decoder for audio stream */
g_psi.p_audio_codec = avcodec_find_decoder(g_psi.p_audio_codec_ctx->codec_id);
@@ -220,32 +218,99 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
return -1;
}
- while (av_read_frame(g_psi.p_format_ctx, &av_pkt) >= 0)
+ return 0;
+}
+
+int xrdpvr_play_frame(void *channel, int stream_id)
+{
+ AVPacket av_pkt;
+
+ printf("xrdpvr_play_frame: entered\n");
+
+ if (av_read_frame(g_psi.p_format_ctx, &av_pkt) < 0)
{
- if (av_pkt.stream_index == audio_index)
- {
- xrdpvr_send_audio_data(channel, stream_id, av_pkt.size, av_pkt.data);
- usleep(1000 * 1);
- }
- else if (av_pkt.stream_index == video_index)
- {
- xrdpvr_send_video_data(channel, stream_id, av_pkt.size, av_pkt.data);
- usleep(1000 * 40); // was 50
- }
+ printf("xrdpvr_play_frame: av_read_frame failed\n");
+ return -1;
+ }
+
+ if (av_pkt.stream_index == g_audio_index)
+ {
+ xrdpvr_send_audio_data(channel, stream_id, av_pkt.size, av_pkt.data);
+ usleep(1000 * 1);
+ }
+ else if (av_pkt.stream_index == g_video_index)
+ {
+ xrdpvr_send_video_data(channel, stream_id, av_pkt.size, av_pkt.data);
+ usleep(1000 * 40); // was 50
}
av_free_packet(&av_pkt);
+ return 0;
+}
+
+int
+xrdpvr_seek_media(int64_t pos, int backward)
+{
+ int64_t seek_target;
+ int seek_flag;
+
+ printf("xrdpvr_seek_media() entered\n");
+ seek_flag = (backward) ? AVSEEK_FLAG_BACKWARD : 0;
+
+ seek_target = av_rescale_q(pos * AV_TIME_BASE,
+ AV_TIME_BASE_Q,
+ g_psi.p_format_ctx->streams[g_video_index]->time_base);
+
+
+ if(av_seek_frame(g_psi.p_format_ctx, g_video_index, seek_target, seek_flag) < 0)
+ {
+ printf("media seek error\n");
+ return -1;
+ }
+ printf("xrdpvr_seek_media: success\n");
return 0;
}
-/******************************************************************************
- *
- * code below this is local to this file and cannot be accessed externally;
- * this code communicates with the xrdpvr plugin in NeutrinoRDP;
- * NeutrinoRDP is a fork of FreeRDP 1.0.1
- *
- *****************************************************************************/
+void
+xrdpvr_get_media_duration(int64_t *start_time, int64_t *duration)
+{
+ *start_time = g_psi.p_format_ctx->start_time / AV_TIME_BASE;
+ *duration = g_psi.p_format_ctx->duration / AV_TIME_BASE;
+}
+
+int
+xrdpvr_set_geometry(void *channel, int stream_id, int xpos, int ypos, int width, int height)
+{
+ STREAM *s;
+ char *cptr;
+ int rv;
+ int len;
+
+printf("xrdpvr_set_geometry: entered; x=%d y=%d\n", xpos, ypos);
+
+ stream_new(s, MAX_PDU_SIZE);
+
+ stream_ins_u32_le(s, 0); /* number of bytes to follow */
+ stream_ins_u32_le(s, CMD_SET_GEOMETRY);
+ stream_ins_u32_le(s, stream_id);
+ stream_ins_u32_le(s, xpos);
+ stream_ins_u32_le(s, ypos);
+ stream_ins_u32_le(s, width);
+ stream_ins_u32_le(s, height);
+
+ /* insert number of bytes in stream */
+ len = stream_length(s) - 4;
+ cptr = s->p;
+ s->p = s->data;
+ stream_ins_u32_le(s, len);
+ s->p = cptr;
+
+ /* write data to virtual channel */
+ rv = xrdpvr_write_to_client(channel, s);
+ stream_free(s);
+ return rv;
+}
/**
* set video format
@@ -255,7 +320,7 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
*
* @return 0 on success, -1 on error
*****************************************************************************/
-static int
+int
xrdpvr_set_video_format(void *channel, uint32_t stream_id)
{
STREAM *s;
@@ -290,7 +355,7 @@ xrdpvr_set_video_format(void *channel, uint32_t stream_id)
*
* @return 0 on success, -1 on error
*****************************************************************************/
-static int
+int
xrdpvr_set_audio_format(void *channel, uint32_t stream_id)
{
STREAM *s;
@@ -327,7 +392,7 @@ xrdpvr_set_audio_format(void *channel, uint32_t stream_id)
*
* @return 0 on success, -1 on error
*****************************************************************************/
-static int
+int
xrdpvr_send_video_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data)
{
STREAM *s;
@@ -371,7 +436,7 @@ xrdpvr_send_video_data(void *channel, uint32_t stream_id, uint32_t data_len, uin
*
* @return 0 on success, -1 on error
*****************************************************************************/
-static int
+int
xrdpvr_send_audio_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data)
{
STREAM *s;
@@ -408,7 +473,7 @@ xrdpvr_send_audio_data(void *channel, uint32_t stream_id, uint32_t data_len, uin
*
* @return 0 on success, -1 on error
*****************************************************************************/
-static int
+int
xrdpvr_create_metadata_file(void *channel, char *filename)
{
STREAM *s;
@@ -518,3 +583,4 @@ xrdpvr_write_to_client(void *channel, STREAM *s)
usleep(1000 * 3);
}
}
+
diff --git a/xrdpvr/xrdpvr.h b/xrdpvr/xrdpvr.h
index 88e30e76..4368db1f 100644
--- a/xrdpvr/xrdpvr.h
+++ b/xrdpvr/xrdpvr.h
@@ -23,13 +23,24 @@
#ifndef __XRDPVR_H__
#define __XRDPVR_H__
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-int xrdpvr_init_player(void *channel, int stream_id, char *filename);
-int xrdpvr_deinit_player(void *channel, int stream_id);
-int xrdpvr_play_media(void *channel, int stream_id, char *filename);
+int xrdpvr_init_player(void *channel, int stream_id, char *filename);
+int xrdpvr_deinit_player(void *channel, int stream_id);
+int xrdpvr_play_media(void *channel, int stream_id, char *filename);
+int xrdpvr_set_geometry(void *channel, int stream_id, int xpos, int ypos, int width, int height);
+int xrdpvr_set_video_format(void *channel, uint32_t stream_id);
+int xrdpvr_set_audio_format(void *channel, uint32_t stream_id);
+int xrdpvr_send_video_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data);
+int xrdpvr_send_audio_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data);
+int xrdpvr_create_metadata_file(void *channel, char *filename);
+int xrdpvr_play_frame(void *channel, int stream_id);
+void xrdpvr_get_media_duration(int64_t *start_time, int64_t *duration);
+int xrdpvr_seek_media(int64_t pos, int backward);
#ifdef __cplusplus
}
diff --git a/xrdpvr/xrdpvr_internal.h b/xrdpvr/xrdpvr_internal.h
index 7efde4ee..995306c3 100644
--- a/xrdpvr/xrdpvr_internal.h
+++ b/xrdpvr/xrdpvr_internal.h
@@ -38,6 +38,7 @@
#define CMD_CLOSE_META_DATA_FILE 6
#define CMD_WRITE_META_DATA 7
#define CMD_DEINIT_XRDPVR 8
+#define CMD_SET_GEOMETRY 9
/* max number of bytes we can send in one pkt */
#define MAX_PDU_SIZE 1600
@@ -60,7 +61,7 @@ typedef struct stream
* @param _s stream to create and init
* @param _len number of bytes to store in stream
******************************************************************************/
-#define stream_new(_s, _len) \
+#define stream_new(_s, _len) \
do \
{ \
(_s) = (STREAM *) calloc(1, sizeof(STREAM)); \
@@ -73,7 +74,7 @@ typedef struct stream
/**
* create a stream from an existing buffer
******************************************************************************/
-#define stream_from_buffer(_s, _buf, _buf_len) \
+#define stream_from_buffer(_s, _buf, _buf_len) \
do \
{ \
(_s) = (STREAM *) calloc(1, sizeof(STREAM)); \
@@ -89,7 +90,7 @@ typedef struct stream
*
* @param _s the stream whose resources are to be released
******************************************************************************/
-#define stream_free(_s) \
+#define stream_free(_s) \
do \
{ \
if (!(_s)->from_buf) \
@@ -104,14 +105,14 @@ typedef struct stream
#define stream_length(_s) (int) ((_s)->p - (_s)->data)
/** insert a 8 bit value into stream */
-#define stream_ins_u8(_s, _val) \
+#define stream_ins_u8(_s, _val) \
do \
{ \
*(_s)->p++ = (unsigned char) (_val); \
} while(0)
/** insert a 16 bit value into stream */
-#define stream_ins_u16_le(_s, _val) \
+#define stream_ins_u16_le(_s, _val) \
do \
{ \
*(_s)->p++ = (unsigned char) ((_val) >> 0); \
@@ -119,7 +120,7 @@ typedef struct stream
} while (0)
/** insert a 32 bit value into stream */
-#define stream_ins_u32_le(_s, _val) \
+#define stream_ins_u32_le(_s, _val) \
do \
{ \
*(_s)->p++ = (unsigned char) ((_val) >> 0); \
@@ -129,7 +130,7 @@ typedef struct stream
} while (0)
/** insert a 64 bit value into stream */
-#define stream_ins_u64_le(_s, _val) \
+#define stream_ins_u64_le(_s, _val) \
do \
{ \
*(_s)->p++ = (unsigned char) ((_val) >> 0); \
@@ -143,7 +144,7 @@ typedef struct stream
} while (0)
/** insert array of chars into stream */
-#define stream_ins_byte_array(_s, _ba, _count) \
+#define stream_ins_byte_array(_s, _ba, _count) \
do \
{ \
memcpy((_s)->p, (_ba), (_count)); \
@@ -151,14 +152,14 @@ typedef struct stream
} while (0)
/** extract a 8 bit value from stream */
-#define stream_ext_u8(_s, _v) \
+#define stream_ext_u8(_s, _v) \
do \
{ \
(_v) = (u8) *(_s)->p++; \
} while (0)
/** extract a 16 bit value from stream */
-#define stream_ext_u16_le(_s, _v) \
+#define stream_ext_u16_le(_s, _v) \
do \
{ \
(_v) = (u16) ((_s)->p[1] << 8 | (_s)->p[0]); \
@@ -166,7 +167,7 @@ typedef struct stream
} while (0)
/** extract a 32 bit value from stream */
-#define stream_ext_u32_le(_s, _v) \
+#define stream_ext_u32_le(_s, _v) \
do \
{ \
(_v) = (u32) ((_s)->p[3] << 24 | \
@@ -193,11 +194,6 @@ typedef struct _player_state_info
} PLAYER_STATE_INFO;
-static int xrdpvr_set_video_format(void *channel, uint32_t stream_id);
-static int xrdpvr_set_audio_format(void *channel, uint32_t stream_id);
-static int xrdpvr_send_video_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data);
-static int xrdpvr_send_audio_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data);
-static int xrdpvr_create_metadata_file(void *channel, char *filename);
static int xrdpvr_write_to_client(void *channel, STREAM *s);
#endif /* __XRDPVR_INTERNAL_H__ */