summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vrplayer/decoderthread.cpp307
-rw-r--r--vrplayer/decoderthread.h55
-rw-r--r--vrplayer/demuxmedia.cpp116
-rw-r--r--vrplayer/demuxmedia.h65
-rw-r--r--vrplayer/mainwindow.cpp211
-rw-r--r--vrplayer/mainwindow.h73
-rw-r--r--vrplayer/mainwindow.ui6
-rw-r--r--vrplayer/mediapacket.cpp5
-rw-r--r--vrplayer/mediapacket.h14
-rw-r--r--vrplayer/ourinterface.cpp219
-rw-r--r--vrplayer/ourinterface.h75
-rw-r--r--vrplayer/playaudio.cpp87
-rw-r--r--vrplayer/playaudio.h59
-rw-r--r--vrplayer/playvideo.cpp173
-rw-r--r--vrplayer/playvideo.h71
-rw-r--r--vrplayer/vrplayer.pro16
-rw-r--r--xrdpvr/xrdpvr.c155
-rw-r--r--xrdpvr/xrdpvr.h5
-rw-r--r--xrdpvr/xrdpvr_internal.h2
19 files changed, 1458 insertions, 256 deletions
diff --git a/vrplayer/decoderthread.cpp b/vrplayer/decoderthread.cpp
index 2ee7a524..e31b1aa3 100644
--- a/vrplayer/decoderthread.cpp
+++ b/vrplayer/decoderthread.cpp
@@ -1,8 +1,17 @@
#include "decoderthread.h"
+/*
+ * TODO:
+ * o need to maintain aspect ratio while resizing
+ * o clicking in the middle of the slider bar shuld move the slider to the middle
+ * o need to be able to rewind the move when it is done playing
+ * o need to be able to load another move and play it w/o restarting player
+ * o pause button needs to work
+ * o need images for btns
+ */
+
DecoderThread::DecoderThread()
{
- vsi = NULL;
channel = NULL;
geometry.setX(0);
geometry.setY(0);
@@ -10,16 +19,13 @@ DecoderThread::DecoderThread()
geometry.setHeight(0);
stream_id = 101;
elapsedTime = 0;
- la_seekPos = 0;
+ la_seekPos = -1;
+ videoTimer = NULL;
+ audioTimer = NULL;
}
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)
{
@@ -27,89 +33,72 @@ void DecoderThread::run()
"Please select a media file to play");
return;
}
+}
- /* connect to remote client */
- if (openVirtualChannel())
- return;
-
- vsi = (VideoStateInfo *) av_mallocz(sizeof(VideoStateInfo));
- if (vsi == NULL)
+void DecoderThread::startMediaPlay()
+{
+ MediaPacket *mediaPkt;
+ int is_video_frame;
+ int rv;
+
+ /* setup video timer; each time this timer fires, it sends */
+ /* one video pkt to the client then resets the callback duration */
+ videoTimer = new QTimer;
+ connect(videoTimer, SIGNAL(timeout()), this, SLOT(videoTimerCallback()));
+ //videoTimer->start(1500);
+
+ /* setup audio timer; does the same as above, but with audio pkts */
+ audioTimer = new QTimer;
+ connect(audioTimer, SIGNAL(timeout()), this, SLOT(audioTimerCallback()));
+ //audioTimer->start(500);
+
+ /* setup pktTimer; each time this timer fires, it reads AVPackets */
+ /* and puts them into audio/video Queues */
+ pktTimer = new QTimer;
+ connect(pktTimer, SIGNAL(timeout()), this, SLOT(pktTimerCallback()));
+
+ while (1)
{
- 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());
+ /* fill the audio/video queues with initial data; thereafter */
+ /* data will be filled by pktTimerCallback() */
+ if ((audioQueue.count() >= 3000) || (videoQueue.count() >= 3000))
+ {
+ //pktTimer->start(50);
- xrdpvr_get_media_duration(&start_time, &duration);
- emit on_mediaDurationInSeconds(duration);
+ //videoTimer->start(1500);
+ //audioTimer->start(500);
- qDebug() << "start_time=" << start_time << " duration=" << duration;
+ playVideo = new PlayVideo(NULL, &videoQueue, channel, 101);
+ playVideoThread = new QThread(this);
+ connect(playVideoThread, SIGNAL(started()), playVideo, SLOT(play()));
+ playVideo->moveToThread(playVideoThread);
+ playVideoThread->start();
- while (xrdpvr_play_frame(channel, 101) == 0)
- {
- if (elapsedTime == 0)
- elapsedTime = av_gettime();
+ playAudio = new PlayAudio(NULL, &audioQueue, channel, 101);
+ playAudioThread = new QThread(this);
+ connect(playAudioThread, SIGNAL(started()), playAudio, SLOT(play()));
+ playAudio->moveToThread(playAudioThread);
+ playAudioThread->start();
- /* time elapsed in 1/100th sec units since play started */
- emit on_elapsedtime((av_gettime() - elapsedTime) / 10000);
+ return;
+ }
- mutex.lock();
- if (la_seekPos)
+ mediaPkt = new MediaPacket;
+ rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
+ &is_video_frame,
+ &mediaPkt->delay_in_us);
+ if (rv < 0)
{
- qDebug() << "seeking to" << la_seekPos;
- xrdpvr_seek_media(la_seekPos, 0);
- elapsedTime = av_gettime() - la_seekPos * 1000000;
- la_seekPos = 0;
+ /* looks like we reached end of file */
+ break;
}
- 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 (is_video_frame)
+ videoQueue.enqueue(mediaPkt);
+ else
+ audioQueue.enqueue(mediaPkt);
- if (channel)
- {
- xrdpvr_set_geometry(channel, 101, geometry.x(), geometry.y(),
- geometry.width(), geometry.height());
- }
+ } /* end while (1) */
}
void DecoderThread::on_mediaSeek(int value)
@@ -117,6 +106,15 @@ void DecoderThread::on_mediaSeek(int value)
mutex.lock();
la_seekPos = value;
mutex.unlock();
+
+ qDebug() << "media seek value=" << value;
+
+ /* pktTimer stops at end of media; need to restart it */
+ if (!pktTimer->isActive())
+ {
+ updateSlider();
+ pktTimer->start(100);
+ }
}
void DecoderThread::setFilename(QString filename)
@@ -124,94 +122,125 @@ 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()
+void DecoderThread::stopPlayer()
{
- /* is channel already open? */
- if (channel)
- return -1;
+ pktTimer->stop();
+ audioQueue.clear();
+ videoQueue.clear();
+}
- /* 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;
+void DecoderThread::pausePlayer()
+{
+ pktTimer->stop();
}
-int DecoderThread::closeVirtualChannel()
+void DecoderThread::resumePlayer()
{
- /* channel must be opened first */
- if (!channel)
- return -1;
+ pktTimer->start(100);
+}
- WTSVirtualChannelClose(channel);
- return 0;
+void DecoderThread::close()
+{
}
-/**
- * @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()
+void DecoderThread::audioTimerCallback()
{
- if (xrdpvr_create_metadata_file(channel, filename.toAscii().data()))
+ MediaPacket *pkt;
+ int delayInMs;
+
+ if (audioQueue.isEmpty())
{
- emit on_decoderErrorMsg("I/O Error",
- "An error occurred while sending data to remote client");
- return -1;
+ qDebug() << "audioTimerCallback: got empty";
+ audioTimer->setInterval(100);
+ return;
}
- return 0;
+ pkt = audioQueue.dequeue();
+ delayInMs = (int) ((float) pkt->delay_in_us / 1000.0);
+ send_audio_pkt(channel, 101, pkt->av_pkt);
+ delete pkt;
+
+ //qDebug() << "audioTimerCallback: delay :" << delayInMs;
+
+ audioTimer->setInterval(delayInMs);
}
-int DecoderThread::sendVideoFormat()
+void DecoderThread::videoTimerCallback()
{
- if (xrdpvr_set_video_format(channel, stream_id))
+ MediaPacket *pkt;
+ int delayInMs;
+
+ if (videoQueue.isEmpty())
{
- emit on_decoderErrorMsg("I/O Error",
- "Error sending video format to remote client");
- return -1;
+ qDebug() << "videoTimerCallback: GOT EMPTY";
+ videoTimer->setInterval(100);
+ return;
}
- return 0;
+ pkt = videoQueue.dequeue();
+ delayInMs = (int) ((float) pkt->delay_in_us / 1000.0);
+ send_video_pkt(channel, 101, pkt->av_pkt);
+ delete pkt;
+ updateSlider();
+ //qDebug() << "videoTimerCallback: delay :" << delayInMs;
+ videoTimer->setInterval(delayInMs);
}
-int DecoderThread::sendAudioFormat()
+void DecoderThread::pktTimerCallback()
{
- if (xrdpvr_set_audio_format(channel, stream_id))
+ MediaPacket *mediaPkt;
+ int is_video_frame;
+ int rv;
+
+ while (1)
{
- emit on_decoderErrorMsg("I/O Error",
- "Error sending audio format to remote client");
- return -1;
- }
+ qDebug() << "pktTimerCallback: audioCount=" << audioQueue.count() << "videoCount=" << videoQueue.count();
+#if 1
+ if ((audioQueue.count() >= 20) || (videoQueue.count() >= 20))
+ return;
+#else
+ if (videoQueue.count() >= 60)
+ return;
+#endif
+ mediaPkt = new MediaPacket;
+ rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
+ &is_video_frame,
+ &mediaPkt->delay_in_us);
+ if (rv < 0)
+ {
+ /* looks like we reached end of file */
+ qDebug() << "###### looks like we reached EOF";
+ pktTimer->stop();
+ // LK_TODO set some flag so audio/video timer also stop when q is empty
+ return;
+ }
- return 0;
+ if (is_video_frame)
+ videoQueue.enqueue(mediaPkt);
+ else
+ audioQueue.enqueue(mediaPkt);
+ }
}
-int DecoderThread::sendGeometry()
+void DecoderThread::updateSlider()
{
- int rv;
+ if (elapsedTime == 0)
+ elapsedTime = av_gettime();
- rv = xrdpvr_set_geometry(channel, stream_id, geometry.x(), geometry.y(),
- geometry.width(), geometry.height());
+ /* time elapsed in 1/100th sec units since play started */
+ emit on_elapsedtime((av_gettime() - elapsedTime) / 10000);
- if (rv)
+ mutex.lock();
+ if (la_seekPos >= 0)
{
- emit on_decoderErrorMsg("I/O Error",
- "Error sending screen geometry to remote client");
- return -1;
+ qDebug() << "seeking to" << la_seekPos;
+ //audioTimer->stop();
+ //videoTimer->stop();
+ xrdpvr_seek_media(la_seekPos, 0);
+ elapsedTime = av_gettime() - la_seekPos * 1000000;
+ //audioTimer->start(10);
+ //videoTimer->start(10);
+ la_seekPos = -1;
}
- return 0;
+ mutex.unlock();
}
diff --git a/vrplayer/decoderthread.h b/vrplayer/decoderthread.h
index 0f8a1c12..e7116fa4 100644
--- a/vrplayer/decoderthread.h
+++ b/vrplayer/decoderthread.h
@@ -14,9 +14,14 @@
#include <QString>
#include <QRect>
#include <QMutex>
+#include <QTimer>
+#include <QQueue>
#include <xrdpapi.h>
#include <xrdpvr.h>
+#include <mediapacket.h>
+#include <playvideo.h>
+#include <playaudio.h>
/* ffmpeg related stuff */
extern "C"
@@ -25,28 +30,34 @@ extern "C"
#include <libavcodec/avcodec.h>
}
-class DecoderThread : public QThread
+class DecoderThread : public QObject
{
Q_OBJECT
public:
+ /* public methods */
DecoderThread();
+
void setFilename(QString filename);
+ void stopPlayer();
+ void pausePlayer();
+ void resumePlayer();
+ void oneTimeDeinit();
+ void close();
+ void run();
+ void startMediaPlay();
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;
+ /* private variables */
+ QQueue<MediaPacket *> audioQueue;
+ QQueue<MediaPacket *> videoQueue;
- VideoStateInfo *vsi;
+ QTimer *videoTimer;
+ QTimer *audioTimer;
+ QTimer *pktTimer;
QString filename;
void *channel;
int stream_id;
@@ -55,14 +66,26 @@ private:
QMutex mutex;
int64_t la_seekPos; /* locked access; must hold mutex */
- int openVirtualChannel();
- int closeVirtualChannel();
- int sendMetadataFile();
- int sendVideoFormat();
- int sendAudioFormat();
- int sendGeometry();
+ PlayVideo *playVideo;
+ QThread *playVideoThread;
+ PlayAudio *playAudio;
+ QThread *playAudioThread;
+
+ /* private functions */
+ int sendMetadataFile();
+ int sendAudioFormat();
+ int sendVideoFormat();
+ int sendGeometry();
+ void updateSlider();
+
+private slots:
+ /* private slots */
+ void audioTimerCallback();
+ void videoTimerCallback();
+ void pktTimerCallback();
signals:
+ /* private signals */
void on_progressUpdate(int percent);
void on_decoderErrorMsg(QString title, QString msg);
void on_mediaDurationInSeconds(int duration);
diff --git a/vrplayer/demuxmedia.cpp b/vrplayer/demuxmedia.cpp
new file mode 100644
index 00000000..7a13619e
--- /dev/null
+++ b/vrplayer/demuxmedia.cpp
@@ -0,0 +1,116 @@
+#include "demuxmedia.h"
+
+DemuxMedia::DemuxMedia(QObject *parent, QQueue<MediaPacket *> *audioQueue,
+ QQueue<MediaPacket *> *videoQueue, void *channel, int stream_id) :
+ QObject(parent)
+{
+ this->audioQueue = audioQueue;
+ this->videoQueue = videoQueue;
+ this->channel = channel;
+ this->stream_id = stream_id;
+ this->threadsStarted = false;
+ this->vcrFlag = 0;
+
+ playAudio = new PlayAudio(NULL, audioQueue, &sendMutex, channel, 101);
+ playAudioThread = new QThread(this);
+ connect(playAudioThread, SIGNAL(started()), playAudio, SLOT(play()));
+ playAudio->moveToThread(playAudioThread);
+
+ playVideo = new PlayVideo(NULL, videoQueue, &sendMutex, channel, 101);
+ playVideoThread = new QThread(this);
+ connect(playVideoThread, SIGNAL(started()), playVideo, SLOT(play()));
+ playVideo->moveToThread(playVideoThread);
+}
+
+void DemuxMedia::setVcrOp(int op)
+{
+ vcrMutex.lock();
+ vcrFlag = op;
+ vcrMutex.unlock();
+
+ if (playVideo)
+ playVideo->setVcrOp(op);
+
+ if (playAudio)
+ playAudio->setVcrOp(op);
+}
+
+void DemuxMedia::startDemuxing()
+{
+ MediaPacket *mediaPkt;
+ int is_video_frame;
+ int rv;
+
+ if ((audioQueue == NULL) || (videoQueue == NULL))
+ return;
+
+ while (1)
+ {
+ vcrMutex.lock();
+ switch (vcrFlag)
+ {
+ case VCR_PLAY:
+ vcrFlag = 0;
+ vcrMutex.unlock();
+ continue;
+ break;
+
+ case VCR_PAUSE:
+ vcrMutex.unlock();
+ usleep(1000 * 100);
+ continue;
+ break;
+
+ case VCR_STOP:
+ vcrMutex.unlock();
+ usleep(1000 * 100);
+ continue;
+ break;
+
+ default:
+ vcrMutex.unlock();
+ break;
+ }
+
+ if ((audioQueue->count() >= 20) || (videoQueue->count() >= 20))
+ {
+ if (!threadsStarted)
+ startAudioVideoThreads();
+
+ usleep(1000 * 20);
+ }
+
+ mediaPkt = new MediaPacket;
+ rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
+ &is_video_frame,
+ &mediaPkt->delay_in_us);
+ if (rv < 0)
+ {
+ /* looks like we reached end of file */
+ delete mediaPkt;
+ usleep(1000 * 100);
+ continue;
+ }
+
+ if (is_video_frame)
+ videoQueue->enqueue(mediaPkt);
+ else
+ audioQueue->enqueue(mediaPkt);
+
+ } /* end while (1) */
+}
+
+PlayVideo * DemuxMedia::getPlayVideoInstance()
+{
+ return this->playVideo;
+}
+
+void DemuxMedia::startAudioVideoThreads()
+{
+ if (threadsStarted)
+ return;
+
+ playVideoThread->start();
+ playAudioThread->start();
+ threadsStarted = true;
+}
diff --git a/vrplayer/demuxmedia.h b/vrplayer/demuxmedia.h
new file mode 100644
index 00000000..ecb25462
--- /dev/null
+++ b/vrplayer/demuxmedia.h
@@ -0,0 +1,65 @@
+#ifndef DEMUXMEDIA_H
+#define DEMUXMEDIA_H
+
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
+#include <QObject>
+#include <QQueue>
+#include <QThread>
+#include <QMutex>
+#include <QDebug>
+
+#include "mediapacket.h"
+#include "playaudio.h"
+#include "playvideo.h"
+
+/* ffmpeg related stuff */
+extern "C"
+{
+ #include <libavformat/avformat.h>
+ #include <libavcodec/avcodec.h>
+}
+
+#define VCR_PLAY 1
+#define VCR_PAUSE 2
+#define VCR_STOP 3
+#define VCR_REWIND 4
+#define VCR_POWER_OFF 5
+
+class DemuxMedia : public QObject
+{
+ Q_OBJECT
+public:
+ explicit DemuxMedia(QObject *parent = 0, QQueue<MediaPacket *> *audioQueue = 0,
+ QQueue<MediaPacket *> *videoQueue = 0, void *channel = 0, int stream_id = 101);
+
+ void setVcrOp(int op);
+
+public slots:
+ void startDemuxing();
+ PlayVideo *getPlayVideoInstance();
+
+private:
+ QQueue<MediaPacket *> *audioQueue;
+ QQueue<MediaPacket *> *videoQueue;
+ QMutex vcrMutex;
+ int vcrFlag;
+ void *channel;
+ PlayVideo *playVideo;
+ QThread *playVideoThread;
+ PlayAudio *playAudio;
+ QThread *playAudioThread;
+ int stream_id;
+ bool threadsStarted;
+ QMutex sendMutex;
+
+ void startAudioVideoThreads();
+};
+
+#endif // DEMUXMEDIA_H
diff --git a/vrplayer/mainwindow.cpp b/vrplayer/mainwindow.cpp
index 7f41eee9..f43d44b3 100644
--- a/vrplayer/mainwindow.cpp
+++ b/vrplayer/mainwindow.cpp
@@ -10,70 +10,56 @@ MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
+ /* connect to remote client */
+ interface = new OurInterface();
+ if (interface->oneTimeInit())
+ {
+ oneTimeInitSuccess = false;
+
+ /* connection to remote client failed; error msg has */
+ /* already been displayed so it's ok to close app now */
+ QTimer::singleShot(1000, qApp, SLOT(quit()));
+ }
+
+ oneTimeInitSuccess = true;
+ remoteClientInited = false;
ui->setupUi(this);
acceptSliderMove = false;
- decoderThread = new DecoderThread();
setupUI();
+ vcrFlag = 0;
-/* 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(this, SIGNAL(onGeometryChanged(int,int,int,int)),
+ interface, SLOT(onGeometryChanged(int,int,int,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)));
+ connect(interface, SIGNAL(onMediaDurationInSeconds(int)),
+ this, SLOT(onMediaDurationInSeconds(int)));
}
MainWindow::~MainWindow()
{
- delete ui;
+ if (oneTimeInitSuccess)
+ 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)
+void MainWindow::resizeEvent(QResizeEvent *)
{
QRect rect;
getVdoGeometry(&rect);
- emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height());
+ interface->sendGeometry(rect);
}
-void MainWindow::moveEvent(QMoveEvent *e)
+void MainWindow::moveEvent(QMoveEvent *)
{
QRect rect;
getVdoGeometry(&rect);
- emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height());
+ interface->sendGeometry(rect);
}
void MainWindow::setupUI()
@@ -100,8 +86,8 @@ void MainWindow::setupUI()
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)));
+ connect(slider, SIGNAL(actionTriggered(int)), this, SLOT(onSliderActionTriggered(int)));
+ connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int)));
/* setup label to display media duration */
lblDuration = new QLabel("00:00:00");
@@ -115,19 +101,23 @@ void MainWindow::setupUI()
hboxLayoutMiddle->addWidget(lblDuration);
/* setup play button */
- btnPlay = new QPushButton("P");
+ btnPlay = new QPushButton("Play");
btnPlay->setMinimumHeight(40);
btnPlay->setMaximumHeight(40);
btnPlay->setMinimumWidth(40);
btnPlay->setMaximumWidth(40);
- connect(btnPlay, SIGNAL(clicked(bool)), this, SLOT(on_btnPlayClicked(bool)));
+ btnPlay->setCheckable(true);
+ connect(btnPlay, SIGNAL(clicked(bool)),
+ this, SLOT(onBtnPlayClicked(bool)));
/* setup stop button */
- btnStop = new QPushButton("S");
+ btnStop = new QPushButton("Stop");
btnStop->setMinimumHeight(40);
btnStop->setMaximumHeight(40);
btnStop->setMinimumWidth(40);
btnStop->setMaximumWidth(40);
+ connect(btnStop, SIGNAL(clicked(bool)),
+ this, SLOT(onBtnStopClicked(bool)));
/* setup rewind button */
btnRewind = new QPushButton("R");
@@ -135,12 +125,14 @@ void MainWindow::setupUI()
btnRewind->setMaximumHeight(40);
btnRewind->setMinimumWidth(40);
btnRewind->setMaximumWidth(40);
+ connect(btnRewind, SIGNAL(clicked(bool)),
+ this, SLOT(onBtnRewindClicked(bool)));
/* add buttons to bottom panel */
hboxLayoutBottom = new QHBoxLayout;
hboxLayoutBottom->addWidget(btnPlay);
hboxLayoutBottom->addWidget(btnStop);
- hboxLayoutBottom->addWidget(btnRewind);
+ //hboxLayoutBottom->addWidget(btnRewind);
hboxLayoutBottom->addStretch();
/* add all three layouts to one vertical layout */
@@ -171,7 +163,7 @@ void MainWindow::openMediaFile()
filename = QFileDialog::getOpenFileName(this, "Select Media File",
filename);
}
- decoderThread->setFilename(filename);
+ interface->setFilename(filename);
}
void MainWindow::getVdoGeometry(QRect *rect)
@@ -187,48 +179,120 @@ void MainWindow::getVdoGeometry(QRect *rect)
rect->setHeight(lblVideo->geometry().height());
}
+void MainWindow::clearDisplay()
+{
+ QPixmap pixmap(100,100);
+ pixmap.fill(QColor(0x00, 0x00, 0x00));
+ QPainter painter(&pixmap);
+ painter.setBrush(QBrush(Qt::black));
+ lblVideo->setPixmap(pixmap);
+}
+
/*******************************************************************************
* actions and slots go here *
******************************************************************************/
void MainWindow::on_actionOpen_Media_File_triggered()
{
+ if (vcrFlag != 0)
+ onBtnStopClicked(false);
+
openMediaFile();
+ if (filename.length() == 0)
+ {
+ /* cancel btn was clicked */
+ return;
+ }
+
+ if (remoteClientInited)
+ {
+ remoteClientInited = false;
+ interface->deInitRemoteClient();
+ interface->initRemoteClient();
+ }
+ else
+ {
+ interface->initRemoteClient();
+ }
+
+ playVideo = interface->getPlayVideoInstance();
+ if (playVideo)
+ {
+ connect(playVideo, SIGNAL(onElapsedtime(int)),
+ this, SLOT(onElapsedTime(int)));
+ }
+
+ remoteClientInited = true;
+ interface->playMedia();
+
+ if (vcrFlag != 0)
+ {
+ interface->setVcrOp(VCR_PLAY);
+ btnPlay->setText("Pause");
+ vcrFlag = VCR_PLAY;
+ }
}
void MainWindow::on_actionExit_triggered()
{
- /* TODO: confirm app exit */
+ clearDisplay();
this->close();
}
-void MainWindow::on_actionPlay_Media_triggered()
+void MainWindow::onBtnPlayClicked(bool)
{
- // LK_TODO do we need this? if yes, should be same as on_btnPlayClicked()
-#if 1
- decoderThread->start();
-#else
- if (!decoder)
- return;
+ if (vcrFlag == 0)
+ {
+ /* first time play button has been clicked */
+ on_actionOpen_Media_File_triggered();
+ btnPlay->setText("Pause");
+ vcrFlag = VCR_PLAY;
+ }
+ else if (vcrFlag == VCR_PLAY)
+ {
+ /* btn clicked while in play mode - enter pause mode */
+ btnPlay->setText("Play");
+ interface->setVcrOp(VCR_PAUSE);
+ vcrFlag = VCR_PAUSE;
+ }
+ else if (vcrFlag == VCR_PAUSE)
+ {
+ /* btn clicked while in pause mode - enter play mode */
+ btnPlay->setText("Pause");
+ interface->setVcrOp(VCR_PLAY);
+ vcrFlag = VCR_PLAY;
+ }
- decoder->init(filename);
-#endif
+ else if (vcrFlag == VCR_STOP)
+ {
+ /* btn clicked while stopped - enter play mode */
+ btnPlay->setText("Play");
+ interface->setVcrOp(VCR_PLAY);
+ vcrFlag = VCR_PLAY;
+ }
}
-void MainWindow::on_decoderError(QString title, QString msg)
+void MainWindow::onBtnRewindClicked(bool)
{
- QMessageBox::information(this, title, msg);
+ if (playVideo)
+ playVideo->onMediaSeek(0);
}
-void MainWindow::on_btnPlayClicked(bool flag)
+void MainWindow::onBtnStopClicked(bool)
{
- if (filename.length() == 0)
- openMediaFile();
+ vcrFlag = VCR_STOP;
+ btnPlay->setText("Play");
+ interface->setVcrOp(VCR_STOP);
+
+ /* reset slider */
+ slider->setSliderPosition(0);
+ lblCurrentPos->setText("00:00:00");
- decoderThread->start();
+ /* clear screen by filling it with black */
+ clearDisplay();
}
-void MainWindow::on_mediaDurationInSeconds(int duration)
+void MainWindow::onMediaDurationInSeconds(int duration)
{
int hours = 0;
int minutes = 0;
@@ -239,6 +303,9 @@ void MainWindow::on_mediaDurationInSeconds(int duration)
slider->setMinimum(0);
slider->setMaximum(duration * 100); /* in hundredth of a sec */
slider->setValue(0);
+ slider->setSliderPosition(0);
+ lblCurrentPos->setText("00:00:00");
+ qDebug() << "media_duration=" << duration << " in hundredth of a sec:" << duration * 100;
/* convert from seconds to hours:minutes:seconds */
hours = duration / 3600;
@@ -258,7 +325,7 @@ void MainWindow::on_mediaDurationInSeconds(int duration)
/**
* time elapsed in 1/100th sec units since play started
******************************************************************************/
-void MainWindow::on_elapsedTime(int val)
+void MainWindow::onElapsedTime(int val)
{
int hours = 0;
int minutes = 0;
@@ -266,9 +333,18 @@ void MainWindow::on_elapsedTime(int val)
int duration = val / 100;
char buf[20];
+ if (vcrFlag == VCR_STOP)
+ {
+ qDebug() << "onElapsedTime: not updating slider coz of VCR_STOP";
+ return;
+ }
+
/* if slider bar is down, do not update */
if (slider->isSliderDown())
+ {
+ qDebug() << "onElapsedTime: not updating slider coz slider is down";
return;
+ }
/* update progress bar */
slider->setSliderPosition(val);
@@ -289,16 +365,17 @@ void MainWindow::on_elapsedTime(int val)
lblCurrentPos->setText(QString(buf));
}
-void MainWindow::on_sliderValueChanged(int value)
+void MainWindow::onSliderValueChanged(int value)
{
if (acceptSliderMove)
{
acceptSliderMove = false;
- emit on_mediaSeek(value / 100);
+ if (playVideo)
+ playVideo->onMediaSeek(value / 100);
}
}
-void MainWindow::on_sliderActionTriggered(int action)
+void MainWindow::onSliderActionTriggered(int action)
{
switch (action)
{
@@ -319,7 +396,7 @@ void MainWindow::on_sliderActionTriggered(int action)
#if 1
// LK_TODO delete this
-void MainWindow::mouseMoveEvent(QMouseEvent *e)
+void MainWindow::mouseMoveEvent(QMouseEvent *)
{
//qDebug() << "mouseMoveEvent: x=" << e->globalX() << "y=" << e->globalY();
}
diff --git a/vrplayer/mainwindow.h b/vrplayer/mainwindow.h
index ed392ea2..ceddb683 100644
--- a/vrplayer/mainwindow.h
+++ b/vrplayer/mainwindow.h
@@ -1,6 +1,14 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
#include <QMainWindow>
#include <QFileDialog>
#include <QDebug>
@@ -14,12 +22,32 @@
#include <QVBoxLayout>
#include <QPushButton>
#include <QSlider>
+#include <QTimer>
+#include <QPixmap>
+#include <QPainter>
+#include "xrdpapi.h"
+#include "xrdpvr.h"
#include "decoder.h"
-#include "decoderthread.h"
+#include "ourinterface.h"
+#include "playvideo.h"
+
+/* ffmpeg related stuff */
+extern "C"
+{
+ #include <libavformat/avformat.h>
+ #include <libavcodec/avcodec.h>
+}
-namespace Ui {
-class MainWindow;
+#define VCR_PLAY 1
+#define VCR_PAUSE 2
+#define VCR_STOP 3
+#define VCR_REWIND 4
+#define VCR_POWER_OFF 5
+
+namespace Ui
+{
+ class MainWindow;
}
class MainWindow : public QMainWindow
@@ -30,20 +58,23 @@ public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
+signals:
+ void onGeometryChanged(int x, int y, int widht, int height);
+
+public slots:
+ void onSliderValueChanged(int value);
+
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);
+ void onBtnPlayClicked(bool flag);
+ void onBtnRewindClicked(bool flag);
+ void onBtnStopClicked(bool flag);
+
+ void onMediaDurationInSeconds(int duration);
+ void onElapsedTime(int secs);
+ void onSliderActionTriggered(int value);
protected:
void resizeEvent(QResizeEvent *e);
@@ -54,10 +85,6 @@ protected:
private:
Ui::MainWindow *ui;
- QString filename;
- Decoder *decoder;
- DecoderThread *decoderThread;
-
/* for UI */
QLabel *lblCurrentPos;
QLabel *lblDuration;
@@ -73,10 +100,22 @@ private:
QWidget *window;
bool acceptSliderMove;
+ /* private stuff */
+ OurInterface *interface;
+ PlayVideo *playVideo;
+ QString filename;
+ bool oneTimeInitSuccess;
+ bool remoteClientInited;
+ void *channel;
+ int stream_id;
+ int64_t elapsedTime; /* elapsed time in usecs since play started */
+ int vcrFlag;
+
/* private methods */
void setupUI();
void openMediaFile();
void getVdoGeometry(QRect *rect);
+ void clearDisplay();
};
#endif // MAINWINDOW_H
diff --git a/vrplayer/mainwindow.ui b/vrplayer/mainwindow.ui
index 7d4a81c6..af924894 100644
--- a/vrplayer/mainwindow.ui
+++ b/vrplayer/mainwindow.ui
@@ -28,7 +28,6 @@
<string>File</string>
</property>
<addaction name="actionOpen_Media_File"/>
- <addaction name="actionPlay_Media"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
@@ -56,11 +55,6 @@
<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/>
diff --git a/vrplayer/mediapacket.cpp b/vrplayer/mediapacket.cpp
new file mode 100644
index 00000000..c9c2b128
--- /dev/null
+++ b/vrplayer/mediapacket.cpp
@@ -0,0 +1,5 @@
+#include "mediapacket.h"
+
+MediaPacket::MediaPacket()
+{
+}
diff --git a/vrplayer/mediapacket.h b/vrplayer/mediapacket.h
new file mode 100644
index 00000000..98dfce9f
--- /dev/null
+++ b/vrplayer/mediapacket.h
@@ -0,0 +1,14 @@
+#ifndef MEDIAPACKET_H
+#define MEDIAPACKET_H
+
+class MediaPacket
+{
+public:
+ MediaPacket();
+
+ void *av_pkt;
+ int delay_in_us;
+ int seq;
+};
+
+#endif // MEDIAPACKET_H
diff --git a/vrplayer/ourinterface.cpp b/vrplayer/ourinterface.cpp
new file mode 100644
index 00000000..fb4ad3dc
--- /dev/null
+++ b/vrplayer/ourinterface.cpp
@@ -0,0 +1,219 @@
+#include "ourinterface.h"
+
+OurInterface::OurInterface(QObject *parent) :
+ QObject(parent)
+{
+ channel = NULL;
+ savedGeometry.setX(0);
+ savedGeometry.setY(0);
+ savedGeometry.setWidth(0);
+ savedGeometry.setHeight(0);
+ stream_id = 101;
+ demuxMedia = 0;
+ //elapsedTime = 0;
+}
+
+int OurInterface::oneTimeInit()
+{
+ /* connect to remote client */
+ if (openVirtualChannel())
+ return -1;
+
+ /* register all formats/codecs */
+ av_register_all();
+
+ return 0;
+}
+
+void OurInterface::oneTimeDeinit()
+{
+ /* clean up resources */
+ closeVirtualChannel();
+}
+
+void OurInterface::initRemoteClient()
+{
+ int64_t start_time;
+ int64_t duration;
+
+ //elapsedTime = 0;
+
+ if (sendMetadataFile())
+ return;
+
+ if (sendVideoFormat())
+ return;
+
+ if (sendAudioFormat())
+ return;
+
+ if (sendGeometry(savedGeometry))
+ return;
+
+ xrdpvr_play_media(channel, 101, filename.toAscii().data());
+
+ xrdpvr_get_media_duration(&start_time, &duration);
+ qDebug() << "ourInterface:initRemoteClient: emit onMediaDurationInSecs: dur=" << duration;
+ emit onMediaDurationInSeconds(duration);
+
+ /* LK_TODO this needs to be undone in deinitRemoteClient() */
+ if (!demuxMedia)
+ {
+ demuxMedia = new DemuxMedia(NULL, &audioQueue, &videoQueue, channel, stream_id);
+ demuxMediaThread = new QThread(this);
+ connect(demuxMediaThread, SIGNAL(started()), demuxMedia, SLOT(startDemuxing()));
+ demuxMedia->moveToThread(demuxMediaThread);
+ playVideo = demuxMedia->getPlayVideoInstance();
+ }
+}
+
+void OurInterface::deInitRemoteClient()
+{
+ /* perform clean up */
+ xrdpvr_deinit_player(channel, 101);
+}
+
+/**
+ * @brief Open a virtual connection to remote client
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+int OurInterface::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_ErrorMsg("Connection failure",
+ "Error connecting to remote client. Application will close now");
+ return -1;
+ }
+ return 0;
+}
+
+int OurInterface::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 OurInterface::sendMetadataFile()
+{
+ if (xrdpvr_create_metadata_file(channel, filename.toAscii().data()))
+ {
+ emit on_ErrorMsg("I/O Error",
+ "An error occurred while sending data to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+int OurInterface::sendVideoFormat()
+{
+ if (xrdpvr_set_video_format(channel, stream_id))
+ {
+ emit on_ErrorMsg("I/O Error",
+ "Error sending video format to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+int OurInterface::sendAudioFormat()
+{
+ if (xrdpvr_set_audio_format(channel, stream_id))
+ {
+ emit on_ErrorMsg("I/O Error",
+ "Error sending audio format to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+int OurInterface::sendGeometry(QRect rect)
+{
+ int rv;
+
+ savedGeometry.setX(rect.x());
+ savedGeometry.setY(rect.y());
+ savedGeometry.setWidth(rect.width());
+ savedGeometry.setHeight(rect.height());
+
+ rv = xrdpvr_set_geometry(channel, stream_id, savedGeometry.x(),
+ savedGeometry.y(), savedGeometry.width(),
+ savedGeometry.height());
+
+ if (rv)
+ {
+ emit on_ErrorMsg("I/O Error",
+ "Error sending screen geometry to remote client");
+ return -1;
+ }
+
+ return 0;
+}
+
+void OurInterface::onGeometryChanged(int x, int y, int width, int height)
+{
+ savedGeometry.setX(x);
+ savedGeometry.setY(y);
+ savedGeometry.setWidth(width);
+ savedGeometry.setHeight(height);
+
+#if 1
+ qDebug() << "OurInterface:signal" <<
+ "" << savedGeometry.x() <<
+ "" << savedGeometry.y() <<
+ "" << savedGeometry.width() <<
+ "" << savedGeometry.height();
+#endif
+
+ qDebug() << "setting geometry:channel=" << channel;
+
+ if (channel)
+ {
+ xrdpvr_set_geometry(channel, 101, savedGeometry.x(), savedGeometry.y(),
+ savedGeometry.width(), savedGeometry.height());
+ }
+}
+
+void OurInterface::setFilename(QString filename)
+{
+ this->filename = filename;
+}
+
+void OurInterface::playMedia()
+{
+ demuxMediaThread->start();
+}
+
+PlayVideo * OurInterface::getPlayVideoInstance()
+{
+ return this->playVideo;
+}
+
+void OurInterface::setVcrOp(int op)
+{
+ if (demuxMedia)
+ demuxMedia->setVcrOp(op);
+}
diff --git a/vrplayer/ourinterface.h b/vrplayer/ourinterface.h
new file mode 100644
index 00000000..ea402048
--- /dev/null
+++ b/vrplayer/ourinterface.h
@@ -0,0 +1,75 @@
+#ifndef OURINTERFACE_H
+#define OURINTERFACE_H
+
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
+#include <QObject>
+#include <QRect>
+#include <QDebug> // LK_TODO
+
+#include "xrdpvr.h"
+#include "xrdpapi.h"
+#include "demuxmedia.h"
+#include "playvideo.h"
+
+/* ffmpeg related stuff */
+extern "C"
+{
+ #include <libavformat/avformat.h>
+ #include <libavcodec/avcodec.h>
+}
+
+class OurInterface : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit OurInterface(QObject *parent = 0);
+
+ /* public methods */
+ int oneTimeInit();
+ void oneTimeDeinit();
+ void initRemoteClient();
+ void deInitRemoteClient();
+ int sendGeometry(QRect rect);
+ void setFilename(QString filename);
+ void playMedia();
+ PlayVideo *getPlayVideoInstance();
+ void setVcrOp(int op);
+
+public slots:
+ void onGeometryChanged(int x, int y, int width, int height);
+
+signals:
+ void on_ErrorMsg(QString title, QString msg);
+ void onMediaDurationInSeconds(int duration);
+
+private:
+
+ /* private stuff */
+ QQueue<MediaPacket *> audioQueue;
+ QQueue<MediaPacket *> videoQueue;
+
+ DemuxMedia *demuxMedia;
+ QThread *demuxMediaThread;
+ PlayVideo *playVideo;
+ QString filename;
+ void *channel;
+ int stream_id;
+ QRect savedGeometry;
+
+ /* private methods */
+ int openVirtualChannel();
+ int closeVirtualChannel();
+ int sendMetadataFile();
+ int sendVideoFormat();
+ int sendAudioFormat();
+};
+
+#endif // INTERFACE_H
diff --git a/vrplayer/playaudio.cpp b/vrplayer/playaudio.cpp
new file mode 100644
index 00000000..0e4ad4ae
--- /dev/null
+++ b/vrplayer/playaudio.cpp
@@ -0,0 +1,87 @@
+#include "playaudio.h"
+#include <QDebug>
+
+PlayAudio::PlayAudio(QObject *parent,
+ QQueue<MediaPacket *> *audioQueue,
+ QMutex *sendMutex,
+ void *channel,
+ int stream_id) :
+ QObject(parent)
+{
+ this->audioQueue = audioQueue;
+ this->sendMutex = sendMutex;
+ this->channel = channel;
+ this->stream_id = stream_id;
+ this->vcrFlag = 0;
+}
+
+void PlayAudio::play()
+{
+ MediaPacket *pkt;
+
+ while (1)
+ {
+ vcrMutex.lock();
+ switch (vcrFlag)
+ {
+ case VCR_PLAY:
+ vcrFlag = 0;
+ vcrMutex.unlock();
+ continue;
+ break;
+
+ case VCR_PAUSE:
+ vcrMutex.unlock();
+ usleep(1000 * 100);
+ continue;
+ break;
+
+ case VCR_STOP:
+ vcrMutex.unlock();
+ clearAudioQ();
+ usleep(1000 * 100);
+ continue;
+ break;
+
+ default:
+ vcrMutex.unlock();
+ goto label1;
+ break;
+ }
+
+label1:
+
+ if (audioQueue->isEmpty())
+ {
+ qDebug() << "PlayAudio::play: GOT EMPTY";
+ usleep(1000 * 100);
+ continue;
+ }
+
+ pkt = audioQueue->dequeue();
+ sendMutex->lock();
+ send_audio_pkt(channel, stream_id, pkt->av_pkt);
+ sendMutex->unlock();
+ delete pkt;
+ usleep(pkt->delay_in_us);
+ }
+}
+
+void PlayAudio::setVcrOp(int op)
+{
+ vcrMutex.lock();
+ this->vcrFlag = op;
+ vcrMutex.unlock();
+}
+
+void PlayAudio::clearAudioQ()
+{
+ MediaPacket *pkt;
+
+ while (!audioQueue->isEmpty())
+ {
+ pkt = audioQueue->dequeue();
+ av_free_packet((AVPacket *) pkt->av_pkt);
+ delete pkt;
+ }
+}
diff --git a/vrplayer/playaudio.h b/vrplayer/playaudio.h
new file mode 100644
index 00000000..c50b435d
--- /dev/null
+++ b/vrplayer/playaudio.h
@@ -0,0 +1,59 @@
+#ifndef PLAYAUDIO_H
+#define PLAYAUDIO_H
+
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
+#include <QObject>
+#include <QQueue>
+#include <QMutex>
+
+#include "mediapacket.h"
+#include "xrdpvr.h"
+
+/* ffmpeg related stuff */
+extern "C"
+{
+ #include <libavformat/avformat.h>
+ #include <libavcodec/avcodec.h>
+}
+
+#define VCR_PLAY 1
+#define VCR_PAUSE 2
+#define VCR_STOP 3
+#define VCR_REWIND 4
+#define VCR_POWER_OFF 5
+
+class PlayAudio : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit PlayAudio(QObject *parent = 0,
+ QQueue<MediaPacket *> *audioQueue = 0,
+ QMutex *sendMutex = 0,
+ void *channel = 0,
+ int stream_id = 101);
+
+ void setVcrOp(int op);
+
+public slots:
+ void play();
+
+private:
+ QQueue<MediaPacket *> *audioQueue;
+ QMutex *sendMutex;
+ QMutex vcrMutex;
+ int vcrFlag;
+ void *channel;
+ int stream_id;
+
+ void clearAudioQ();
+};
+
+#endif // PLAYAUDIO_H
diff --git a/vrplayer/playvideo.cpp b/vrplayer/playvideo.cpp
new file mode 100644
index 00000000..afb70c71
--- /dev/null
+++ b/vrplayer/playvideo.cpp
@@ -0,0 +1,173 @@
+#include "playvideo.h"
+#include <QDebug>
+
+PlayVideo::PlayVideo(QObject *parent,
+ QQueue<MediaPacket *> *videoQueue,
+ QMutex *sendMutex,
+ void *channel,
+ int stream_id) :
+ QObject(parent)
+{
+ this->videoQueue = videoQueue;
+ this->channel = channel;
+ this->sendMutex = sendMutex;
+ this->stream_id = stream_id;
+ elapsedTime = 0;
+ pausedTime = 0;
+ la_seekPos = -1;
+ vcrFlag = 0;
+ isStopped = false;
+}
+
+void PlayVideo::play()
+{
+ MediaPacket *pkt;
+
+ while (1)
+ {
+ vcrMutex.lock();
+ switch (vcrFlag)
+ {
+ case VCR_PLAY:
+ vcrFlag = 0;
+ vcrMutex.unlock();
+ if (pausedTime)
+ {
+ elapsedTime = av_gettime() - pausedTime;
+ pausedTime = 0;
+ }
+ isStopped = false;
+ continue;
+ break;
+
+ case VCR_PAUSE:
+ vcrMutex.unlock();
+ if (!pausedTime)
+ {
+ /* save amount of video played so far */
+ pausedTime = av_gettime() - elapsedTime;
+ }
+ usleep(1000 * 100);
+ isStopped = false;
+ continue;
+ break;
+
+ case VCR_STOP:
+ vcrMutex.unlock();
+ if (isStopped)
+ {
+ usleep(1000 * 100);
+ continue;
+ }
+ clearVideoQ();
+ elapsedTime = 0;
+ pausedTime = 0;
+ la_seekPos = -1;
+ xrdpvr_seek_media(0, 0);
+ isStopped = true;
+ continue;
+ break;
+
+ default:
+ vcrMutex.unlock();
+ goto label1;
+ break;
+ }
+
+label1:
+
+ if (videoQueue->isEmpty())
+ {
+ qDebug() << "PlayVideo::play: GOT EMPTY";
+ usleep(1000 * 100);
+ continue;
+ }
+
+ pkt = videoQueue->dequeue();
+ sendMutex->lock();
+ send_video_pkt(channel, stream_id, pkt->av_pkt);
+ sendMutex->unlock();
+ delete pkt;
+ usleep(pkt->delay_in_us);
+
+ updateMediaPos();
+
+ if (elapsedTime == 0)
+ elapsedTime = av_gettime();
+
+ /* time elapsed in 1/100th sec units since play started */
+ emit onElapsedtime((av_gettime() - elapsedTime) / 10000);
+ }
+}
+
+void PlayVideo::onMediaSeek(int value)
+{
+ posMutex.lock();
+ la_seekPos = value;
+ posMutex.unlock();
+}
+
+void PlayVideo::updateMediaPos()
+{
+#if 0
+ if (elapsedTime == 0)
+ elapsedTime = av_gettime();
+
+ /* time elapsed in 1/100th sec units since play started */
+ emit onElapsedtime((av_gettime() - elapsedTime) / 10000);
+#endif
+
+ posMutex.lock();
+ if (la_seekPos >= 0)
+ {
+ qDebug() << "seeking to" << la_seekPos;
+ xrdpvr_seek_media(la_seekPos, 0);
+ elapsedTime = av_gettime() - la_seekPos * 1000000;
+ la_seekPos = -1;
+ }
+ posMutex.unlock();
+}
+
+void PlayVideo::setVcrOp(int op)
+{
+ vcrMutex.lock();
+ this->vcrFlag = op;
+ vcrMutex.unlock();
+}
+
+void PlayVideo::clearVideoQ()
+{
+ MediaPacket *pkt;
+
+ while (!videoQueue->isEmpty())
+ {
+ pkt = videoQueue->dequeue();
+ av_free_packet((AVPacket *) pkt->av_pkt);
+ delete pkt;
+ }
+}
+
+#if 0
+void DecoderThread::updateSlider()
+{
+ if (elapsedTime == 0)
+ elapsedTime = av_gettime();
+
+ /* time elapsed in 1/100th sec units since play started */
+ emit onElapsedtime((av_gettime() - elapsedTime) / 10000);
+
+ mutex.lock();
+ if (la_seekPos >= 0)
+ {
+ qDebug() << "seeking to" << la_seekPos;
+ //audioTimer->stop();
+ //videoTimer->stop();
+ xrdpvr_seek_media(la_seekPos, 0);
+ elapsedTime = av_gettime() - la_seekPos * 1000000;
+ //audioTimer->start(10);
+ //videoTimer->start(10);
+ la_seekPos = -1;
+ }
+ mutex.unlock();
+}
+#endif
diff --git a/vrplayer/playvideo.h b/vrplayer/playvideo.h
new file mode 100644
index 00000000..e5f68b5e
--- /dev/null
+++ b/vrplayer/playvideo.h
@@ -0,0 +1,71 @@
+#ifndef PLAYVIDEO_H
+#define PLAYVIDEO_H
+
+#ifdef __cplusplus
+#define __STDC_CONSTANT_MACROS
+#ifdef _STDINT_H
+#undef _STDINT_H
+#endif
+#include <stdint.h>
+#endif
+
+#include <QObject>
+#include <QQueue>
+#include <QMutex>
+
+#include "mediapacket.h"
+#include "xrdpvr.h"
+#include "xrdpapi.h"
+
+/* ffmpeg related stuff */
+extern "C"
+{
+ #include <libavformat/avformat.h>
+ #include <libavcodec/avcodec.h>
+}
+
+#define VCR_PLAY 1
+#define VCR_PAUSE 2
+#define VCR_STOP 3
+#define VCR_REWIND 4
+#define VCR_POWER_OFF 5
+
+class PlayVideo : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit PlayVideo(QObject *parent = 0,
+ QQueue<MediaPacket *> *videoQueue = 0,
+ QMutex *sendMutex = 0,
+ void *channel = 0,
+ int stream_id = 101);
+
+ void onMediaSeek(int value);
+ void setVcrOp(int op);
+
+public slots:
+ void play();
+
+signals:
+ void onElapsedtime(int val); /* in hundredth of a sec */
+
+private:
+ QQueue<MediaPacket *> *videoQueue;
+
+ int vcrFlag;
+ QMutex vcrMutex;
+ QMutex *sendMutex;
+ QMutex posMutex;
+ int64_t la_seekPos; /* locked access; must hold posMutex */
+ void *channel;
+ int stream_id;
+ int64_t elapsedTime; /* elapsed time in usecs since play started */
+ int64_t pausedTime; /* time at which stream was paused */
+ bool isStopped;
+
+ void updateMediaPos();
+ void clearVideoQ();
+};
+
+#endif // PLAYVIDEO_H
diff --git a/vrplayer/vrplayer.pro b/vrplayer/vrplayer.pro
index 9aac8aff..1d035382 100644
--- a/vrplayer/vrplayer.pro
+++ b/vrplayer/vrplayer.pro
@@ -11,13 +11,19 @@ TEMPLATE = app
SOURCES += main.cpp\
- mainwindow.cpp \
- decoder.cpp \
- decoderthread.cpp
+ playvideo.cpp \
+ mainwindow.cpp \
+ mediapacket.cpp \
+ playaudio.cpp \
+ demuxmedia.cpp \
+ ourinterface.cpp
HEADERS += mainwindow.h \
- decoder.h \
- decoderthread.h
+ mediapacket.h \
+ playvideo.h \
+ playaudio.h \
+ demuxmedia.h \
+ ourinterface.h
FORMS += mainwindow.ui
diff --git a/xrdpvr/xrdpvr.c b/xrdpvr/xrdpvr.c
index 66dfea5a..b30869a8 100644
--- a/xrdpvr/xrdpvr.c
+++ b/xrdpvr/xrdpvr.c
@@ -133,6 +133,10 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
{
int i;
+ printf("$$$$$$ xrdpvr_play_media: setting audioTimeout & videoTimeout to -1\n");
+ g_psi.videoTimeout = -1;
+ g_psi.audioTimeout = -1;
+
/* register all available fileformats and codecs */
av_register_all();
@@ -221,11 +225,106 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
return 0;
}
-int xrdpvr_play_frame(void *channel, int stream_id)
+static int firstAudioPkt = 1;
+static int firstVideoPkt = 1;
+
+int xrdpvr_get_frame(void **av_pkt_ret, int *is_video_frame, int *delay_in_us)
{
- AVPacket av_pkt;
+ AVPacket *av_pkt;
+ double dts;
+
+ /* alloc an AVPacket */
+ if ((av_pkt = (AVPacket *) malloc(sizeof(AVPacket))) == NULL)
+ return -1;
+
+ /* read one frame into AVPacket */
+ if (av_read_frame(g_psi.p_format_ctx, av_pkt) < 0)
+ {
+ free(av_pkt);
+ return -1;
+ }
+
+ if (av_pkt->stream_index == g_audio_index)
+ {
+ /* got an audio packet */
+ dts = av_pkt->dts;
+ dts *= av_q2d(g_psi.p_format_ctx->streams[g_audio_index]->time_base);
+
+ if (g_psi.audioTimeout < 0)
+ {
+ *delay_in_us = 1000 * 5;
+ g_psi.audioTimeout = dts;
+ }
+ else
+ {
+ *delay_in_us = (int) ((dts - g_psi.audioTimeout) * 1000000);
+ g_psi.audioTimeout = dts;
+ }
+ *is_video_frame = 0;
- printf("xrdpvr_play_frame: entered\n");
+ if (firstAudioPkt)
+ {
+ firstAudioPkt = 0;
+ //printf("##### first audio: dts=%f delay_in_ms=%d\n", dts, *delay_in_us);
+ }
+ }
+ else if (av_pkt->stream_index == g_video_index)
+ {
+ dts = av_pkt->dts;
+
+ //printf("$$$ video raw_dts=%f raw_pts=%f\n", (double) av_pkt->dts, (double) av_pkt->dts);
+
+ dts *= av_q2d(g_psi.p_format_ctx->streams[g_video_index]->time_base);
+
+ if (g_psi.videoTimeout < 0)
+ {
+ *delay_in_us = 1000 * 5;
+ g_psi.videoTimeout = dts;
+ //printf("$$$ negative: videoTimeout=%f\n", g_psi.videoTimeout);
+ }
+ else
+ {
+ //printf("$$$ positive: videoTimeout_b4 =%f\n", g_psi.videoTimeout);
+ *delay_in_us = (int) ((dts - g_psi.videoTimeout) * 1000000);
+ g_psi.videoTimeout = dts;
+ //printf("$$$ positive: videoTimeout_aft=%f\n", g_psi.videoTimeout);
+ }
+ *is_video_frame = 1;
+
+ if (firstVideoPkt)
+ {
+ firstVideoPkt = 0;
+ //printf("$$$ first video: dts=%f delay_in_ms=%d\n", dts, *delay_in_us);
+ }
+ }
+
+ *av_pkt_ret = av_pkt;
+ return 0;
+}
+
+int send_audio_pkt(void *channel, int stream_id, void *pkt_p)
+{
+ AVPacket *av_pkt = (AVPacket *) pkt_p;
+
+ xrdpvr_send_audio_data(channel, stream_id, av_pkt->size, av_pkt->data);
+ av_free_packet(av_pkt);
+ free(av_pkt);
+}
+
+int send_video_pkt(void *channel, int stream_id, void *pkt_p)
+{
+ AVPacket *av_pkt = (AVPacket *) pkt_p;
+
+ xrdpvr_send_video_data(channel, stream_id, av_pkt->size, av_pkt->data);
+ av_free_packet(av_pkt);
+ free(av_pkt);
+}
+
+int xrdpvr_play_frame(void *channel, int stream_id, int *videoTimeout, int *audioTimeout)
+{
+ AVPacket av_pkt;
+ double dts;
+ int delay_in_us;
if (av_read_frame(g_psi.p_format_ctx, &av_pkt) < 0)
{
@@ -236,12 +335,55 @@ int xrdpvr_play_frame(void *channel, int stream_id)
if (av_pkt.stream_index == g_audio_index)
{
xrdpvr_send_audio_data(channel, stream_id, av_pkt.size, av_pkt.data);
- usleep(1000 * 1);
+
+ dts = av_pkt.dts;
+ dts *= av_q2d(g_psi.p_format_ctx->streams[g_audio_index]->time_base);
+
+ *audioTimeout = (int) ((dts - g_psi.audioTimeout) * 1000000);
+ *videoTimeout = -1;
+
+ if (g_psi.audioTimeout > dts)
+ {
+ g_psi.audioTimeout = dts;
+ delay_in_us = 1000 * 40;
+ }
+ else
+ {
+ delay_in_us = (int) ((dts - g_psi.audioTimeout) * 1000000);
+ g_psi.audioTimeout = dts;
+ }
+
+ //printf("audio delay: %d\n", delay_in_us);
+ usleep(delay_in_us);
+ //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
+
+ dts = av_pkt.dts;
+ dts *= av_q2d(g_psi.p_format_ctx->streams[g_video_index]->time_base);
+
+ //printf("xrdpvr_play_frame:video: saved=%f dts=%f\n", g_psi.videoTimeout, dts);
+
+ *videoTimeout = (int) ((dts - g_psi.videoTimeout) * 1000000);
+ *audioTimeout = -1;
+
+ if (g_psi.videoTimeout > dts)
+ {
+ g_psi.videoTimeout = dts;
+ delay_in_us = 1000 * 40;
+ //printf("xrdpvr_play_frame:video1: saved=%f dts=%f delay_in_us=%d\n", g_psi.videoTimeout, dts, delay_in_us);
+ }
+ else
+ {
+ delay_in_us = (int) ((dts - g_psi.videoTimeout) * 1000000);
+ g_psi.videoTimeout = dts;
+ //printf("xrdpvr_play_frame:video2: saved=%f dts=%f delay_in_us=%d\n", g_psi.videoTimeout, dts, delay_in_us);
+ }
+
+ //printf("video delay: %d\n", delay_in_us);
+ usleep(delay_in_us);
}
av_free_packet(&av_pkt);
@@ -256,6 +398,9 @@ xrdpvr_seek_media(int64_t pos, int backward)
printf("xrdpvr_seek_media() entered\n");
+ g_psi.audioTimeout = -1;
+ g_psi.videoTimeout = -1;
+
seek_flag = (backward) ? AVSEEK_FLAG_BACKWARD : 0;
seek_target = av_rescale_q(pos * AV_TIME_BASE,
diff --git a/xrdpvr/xrdpvr.h b/xrdpvr/xrdpvr.h
index 4368db1f..49ec6c77 100644
--- a/xrdpvr/xrdpvr.h
+++ b/xrdpvr/xrdpvr.h
@@ -38,9 +38,12 @@ 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);
+int xrdpvr_play_frame(void *channel, int stream_id, int *vdoTimeout, int *audioTimeout);
void xrdpvr_get_media_duration(int64_t *start_time, int64_t *duration);
int xrdpvr_seek_media(int64_t pos, int backward);
+int xrdpvr_get_frame(void **av_pkt_ret, int *is_video_frame, int *delay_in_us);
+int send_audio_pkt(void *channel, int stream_id, void *pkt_p);
+int send_video_pkt(void *channel, int stream_id, void *pkt_p);
#ifdef __cplusplus
}
diff --git a/xrdpvr/xrdpvr_internal.h b/xrdpvr/xrdpvr_internal.h
index 995306c3..ca15caf1 100644
--- a/xrdpvr/xrdpvr_internal.h
+++ b/xrdpvr/xrdpvr_internal.h
@@ -187,6 +187,8 @@ typedef struct _player_state_info
int audio_stream_index;
int video_stream_index;
+ double audioTimeout;
+ double videoTimeout;
/* LK_TODO delete this after we fix the problem */
AVFrame *frame;