o developer checkin

o modified vrplayer to use threads
ulab-next
Laxmikant Rashinkar 12 years ago
parent d1fc67102a
commit cae2adb75e

@ -1,8 +1,17 @@
#include "decoderthread.h" #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() DecoderThread::DecoderThread()
{ {
vsi = NULL;
channel = NULL; channel = NULL;
geometry.setX(0); geometry.setX(0);
geometry.setY(0); geometry.setY(0);
@ -10,16 +19,13 @@ DecoderThread::DecoderThread()
geometry.setHeight(0); geometry.setHeight(0);
stream_id = 101; stream_id = 101;
elapsedTime = 0; elapsedTime = 0;
la_seekPos = 0; la_seekPos = -1;
videoTimer = NULL;
audioTimer = NULL;
} }
void DecoderThread::run() 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 */ /* need a media file */
if (filename.length() == 0) if (filename.length() == 0)
{ {
@ -27,89 +33,72 @@ void DecoderThread::run()
"Please select a media file to play"); "Please select a media file to play");
return; return;
} }
}
/* connect to remote client */ void DecoderThread::startMediaPlay()
if (openVirtualChannel()) {
return; MediaPacket *mediaPkt;
int is_video_frame;
vsi = (VideoStateInfo *) av_mallocz(sizeof(VideoStateInfo)); int rv;
if (vsi == NULL)
/* 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", /* fill the audio/video queues with initial data; thereafter */
"Memory allocation failed, system out of memory"); /* data will be filled by pktTimerCallback() */
return; if ((audioQueue.count() >= 3000) || (videoQueue.count() >= 3000))
} {
//pktTimer->start(50);
/* 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); //videoTimer->start(1500);
emit on_mediaDurationInSeconds(duration); //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) playAudio = new PlayAudio(NULL, &audioQueue, channel, 101);
{ playAudioThread = new QThread(this);
if (elapsedTime == 0) connect(playAudioThread, SIGNAL(started()), playAudio, SLOT(play()));
elapsedTime = av_gettime(); playAudio->moveToThread(playAudioThread);
playAudioThread->start();
/* time elapsed in 1/100th sec units since play started */ return;
emit on_elapsedtime((av_gettime() - elapsedTime) / 10000); }
mutex.lock(); mediaPkt = new MediaPacket;
if (la_seekPos) rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
&is_video_frame,
&mediaPkt->delay_in_us);
if (rv < 0)
{ {
qDebug() << "seeking to" << la_seekPos; /* looks like we reached end of file */
xrdpvr_seek_media(la_seekPos, 0); break;
elapsedTime = av_gettime() - la_seekPos * 1000000;
la_seekPos = 0;
} }
mutex.unlock();
}
/* perform clean up */
xrdpvr_deinit_player(channel, 101);
/* clean up resources */ if (is_video_frame)
closeVirtualChannel(); videoQueue.enqueue(mediaPkt);
if (vsi) else
av_free(vsi); audioQueue.enqueue(mediaPkt);
}
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) } /* end while (1) */
{
xrdpvr_set_geometry(channel, 101, geometry.x(), geometry.y(),
geometry.width(), geometry.height());
}
} }
void DecoderThread::on_mediaSeek(int value) void DecoderThread::on_mediaSeek(int value)
@ -117,6 +106,15 @@ void DecoderThread::on_mediaSeek(int value)
mutex.lock(); mutex.lock();
la_seekPos = value; la_seekPos = value;
mutex.unlock(); 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) void DecoderThread::setFilename(QString filename)
@ -124,94 +122,125 @@ void DecoderThread::setFilename(QString filename)
this->filename = filename; this->filename = filename;
} }
/** void DecoderThread::stopPlayer()
* @brief Open a virtual connection to remote client
*
* @return 0 on success, -1 on failure
******************************************************************************/
int DecoderThread::openVirtualChannel()
{ {
/* is channel already open? */ pktTimer->stop();
if (channel) audioQueue.clear();
return -1; videoQueue.clear();
}
/* open a virtual channel and connect to remote client */ void DecoderThread::pausePlayer()
channel = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, "xrdpvr", 0); {
if (channel == NULL) pktTimer->stop();
{
emit on_decoderErrorMsg("Connection failure",
"Error connecting to remote client");
return -1;
}
return 0;
} }
int DecoderThread::closeVirtualChannel() void DecoderThread::resumePlayer()
{ {
/* channel must be opened first */ pktTimer->start(100);
if (!channel) }
return -1;
WTSVirtualChannelClose(channel); void DecoderThread::close()
return 0; {
} }
/** void DecoderThread::audioTimerCallback()
* @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())) MediaPacket *pkt;
int delayInMs;
if (audioQueue.isEmpty())
{ {
emit on_decoderErrorMsg("I/O Error", qDebug() << "audioTimerCallback: got empty";
"An error occurred while sending data to remote client"); audioTimer->setInterval(100);
return -1; 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", qDebug() << "videoTimerCallback: GOT EMPTY";
"Error sending video format to remote client"); videoTimer->setInterval(100);
return -1; 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", qDebug() << "pktTimerCallback: audioCount=" << audioQueue.count() << "videoCount=" << videoQueue.count();
"Error sending audio format to remote client"); #if 1
return -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(), /* time elapsed in 1/100th sec units since play started */
geometry.width(), geometry.height()); emit on_elapsedtime((av_gettime() - elapsedTime) / 10000);
if (rv) mutex.lock();
if (la_seekPos >= 0)
{ {
emit on_decoderErrorMsg("I/O Error", qDebug() << "seeking to" << la_seekPos;
"Error sending screen geometry to remote client"); //audioTimer->stop();
return -1; //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();
} }

@ -14,9 +14,14 @@
#include <QString> #include <QString>
#include <QRect> #include <QRect>
#include <QMutex> #include <QMutex>
#include <QTimer>
#include <QQueue>
#include <xrdpapi.h> #include <xrdpapi.h>
#include <xrdpvr.h> #include <xrdpvr.h>
#include <mediapacket.h>
#include <playvideo.h>
#include <playaudio.h>
/* ffmpeg related stuff */ /* ffmpeg related stuff */
extern "C" extern "C"
@ -25,28 +30,34 @@ extern "C"
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
} }
class DecoderThread : public QThread class DecoderThread : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
/* public methods */
DecoderThread(); DecoderThread();
void setFilename(QString filename); void setFilename(QString filename);
void stopPlayer();
void pausePlayer();
void resumePlayer();
void oneTimeDeinit();
void close();
void run();
void startMediaPlay();
public slots: public slots:
void on_geometryChanged(int x, int y, int width, int height);
void on_mediaSeek(int value); void on_mediaSeek(int value);
protected:
void run();
private: private:
typedef struct _VideoStateInfo /* private variables */
{ QQueue<MediaPacket *> audioQueue;
AVFormatContext *pFormatCtx; QQueue<MediaPacket *> videoQueue;
} VideoStateInfo;
VideoStateInfo *vsi; QTimer *videoTimer;
QTimer *audioTimer;
QTimer *pktTimer;
QString filename; QString filename;
void *channel; void *channel;
int stream_id; int stream_id;
@ -55,14 +66,26 @@ private:
QMutex mutex; QMutex mutex;
int64_t la_seekPos; /* locked access; must hold mutex */ int64_t la_seekPos; /* locked access; must hold mutex */
int openVirtualChannel(); PlayVideo *playVideo;
int closeVirtualChannel(); QThread *playVideoThread;
int sendMetadataFile(); PlayAudio *playAudio;
int sendVideoFormat(); QThread *playAudioThread;
int sendAudioFormat();
int sendGeometry(); /* private functions */
int sendMetadataFile();
int sendAudioFormat();
int sendVideoFormat();
int sendGeometry();
void updateSlider();
private slots:
/* private slots */
void audioTimerCallback();
void videoTimerCallback();
void pktTimerCallback();
signals: signals:
/* private signals */
void on_progressUpdate(int percent); void on_progressUpdate(int percent);
void on_decoderErrorMsg(QString title, QString msg); void on_decoderErrorMsg(QString title, QString msg);
void on_mediaDurationInSeconds(int duration); void on_mediaDurationInSeconds(int duration);

@ -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;
}

@ -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

@ -10,70 +10,56 @@ MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::MainWindow) 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); ui->setupUi(this);
acceptSliderMove = false; acceptSliderMove = false;
decoderThread = new DecoderThread();
setupUI(); setupUI();
vcrFlag = 0;
/* LK_TODO */ connect(this, SIGNAL(onGeometryChanged(int,int,int,int)),
#if 0 interface, SLOT(onGeometryChanged(int,int,int,int)));
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)), connect(interface, SIGNAL(onMediaDurationInSeconds(int)),
this, SLOT(on_decoderError(QString, QString))); this, SLOT(onMediaDurationInSeconds(int)));
connect(decoderThread, SIGNAL(on_mediaDurationInSeconds(int)),
this, SLOT(on_mediaDurationInSeconds(int)));
connect(this, SIGNAL(on_mediaSeek(int)), decoderThread, SLOT(on_mediaSeek(int)));
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
{ {
delete ui; if (oneTimeInitSuccess)
delete ui;
} }
void MainWindow::closeEvent(QCloseEvent *event) 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(); event->accept();
} }
void MainWindow::resizeEvent(QResizeEvent *e) void MainWindow::resizeEvent(QResizeEvent *)
{ {
QRect rect; QRect rect;
getVdoGeometry(&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; QRect rect;
getVdoGeometry(&rect); getVdoGeometry(&rect);
emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height()); interface->sendGeometry(rect);
} }
void MainWindow::setupUI() void MainWindow::setupUI()
@ -100,8 +86,8 @@ void MainWindow::setupUI()
slider->setOrientation(Qt::Horizontal); slider->setOrientation(Qt::Horizontal);
slider->setMinimumHeight(20); slider->setMinimumHeight(20);
slider->setMaximumHeight(20); slider->setMaximumHeight(20);
connect(slider, SIGNAL(actionTriggered(int)), this, SLOT(on_sliderActionTriggered(int))); connect(slider, SIGNAL(actionTriggered(int)), this, SLOT(onSliderActionTriggered(int)));
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(on_sliderValueChanged(int))); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int)));
/* setup label to display media duration */ /* setup label to display media duration */
lblDuration = new QLabel("00:00:00"); lblDuration = new QLabel("00:00:00");
@ -115,19 +101,23 @@ void MainWindow::setupUI()
hboxLayoutMiddle->addWidget(lblDuration); hboxLayoutMiddle->addWidget(lblDuration);
/* setup play button */ /* setup play button */
btnPlay = new QPushButton("P"); btnPlay = new QPushButton("Play");
btnPlay->setMinimumHeight(40); btnPlay->setMinimumHeight(40);
btnPlay->setMaximumHeight(40); btnPlay->setMaximumHeight(40);
btnPlay->setMinimumWidth(40); btnPlay->setMinimumWidth(40);
btnPlay->setMaximumWidth(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 */ /* setup stop button */
btnStop = new QPushButton("S"); btnStop = new QPushButton("Stop");
btnStop->setMinimumHeight(40); btnStop->setMinimumHeight(40);
btnStop->setMaximumHeight(40); btnStop->setMaximumHeight(40);
btnStop->setMinimumWidth(40); btnStop->setMinimumWidth(40);
btnStop->setMaximumWidth(40); btnStop->setMaximumWidth(40);
connect(btnStop, SIGNAL(clicked(bool)),
this, SLOT(onBtnStopClicked(bool)));
/* setup rewind button */ /* setup rewind button */
btnRewind = new QPushButton("R"); btnRewind = new QPushButton("R");
@ -135,12 +125,14 @@ void MainWindow::setupUI()
btnRewind->setMaximumHeight(40); btnRewind->setMaximumHeight(40);
btnRewind->setMinimumWidth(40); btnRewind->setMinimumWidth(40);
btnRewind->setMaximumWidth(40); btnRewind->setMaximumWidth(40);
connect(btnRewind, SIGNAL(clicked(bool)),
this, SLOT(onBtnRewindClicked(bool)));
/* add buttons to bottom panel */ /* add buttons to bottom panel */
hboxLayoutBottom = new QHBoxLayout; hboxLayoutBottom = new QHBoxLayout;
hboxLayoutBottom->addWidget(btnPlay); hboxLayoutBottom->addWidget(btnPlay);
hboxLayoutBottom->addWidget(btnStop); hboxLayoutBottom->addWidget(btnStop);
hboxLayoutBottom->addWidget(btnRewind); //hboxLayoutBottom->addWidget(btnRewind);
hboxLayoutBottom->addStretch(); hboxLayoutBottom->addStretch();
/* add all three layouts to one vertical layout */ /* add all three layouts to one vertical layout */
@ -171,7 +163,7 @@ void MainWindow::openMediaFile()
filename = QFileDialog::getOpenFileName(this, "Select Media File", filename = QFileDialog::getOpenFileName(this, "Select Media File",
filename); filename);
} }
decoderThread->setFilename(filename); interface->setFilename(filename);
} }
void MainWindow::getVdoGeometry(QRect *rect) void MainWindow::getVdoGeometry(QRect *rect)
@ -187,48 +179,120 @@ void MainWindow::getVdoGeometry(QRect *rect)
rect->setHeight(lblVideo->geometry().height()); 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 * * actions and slots go here *
******************************************************************************/ ******************************************************************************/
void MainWindow::on_actionOpen_Media_File_triggered() void MainWindow::on_actionOpen_Media_File_triggered()
{ {
if (vcrFlag != 0)
onBtnStopClicked(false);
openMediaFile(); 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() void MainWindow::on_actionExit_triggered()
{ {
/* TODO: confirm app exit */ clearDisplay();
this->close(); 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 (vcrFlag == 0)
#if 1 {
decoderThread->start(); /* first time play button has been clicked */
#else on_actionOpen_Media_File_triggered();
if (!decoder) btnPlay->setText("Pause");
return; 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); else if (vcrFlag == VCR_STOP)
#endif {
/* 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) vcrFlag = VCR_STOP;
openMediaFile(); 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 hours = 0;
int minutes = 0; int minutes = 0;
@ -239,6 +303,9 @@ void MainWindow::on_mediaDurationInSeconds(int duration)
slider->setMinimum(0); slider->setMinimum(0);
slider->setMaximum(duration * 100); /* in hundredth of a sec */ slider->setMaximum(duration * 100); /* in hundredth of a sec */
slider->setValue(0); 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 */ /* convert from seconds to hours:minutes:seconds */
hours = duration / 3600; hours = duration / 3600;
@ -258,7 +325,7 @@ void MainWindow::on_mediaDurationInSeconds(int duration)
/** /**
* time elapsed in 1/100th sec units since play started * 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 hours = 0;
int minutes = 0; int minutes = 0;
@ -266,9 +333,18 @@ void MainWindow::on_elapsedTime(int val)
int duration = val / 100; int duration = val / 100;
char buf[20]; 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 bar is down, do not update */
if (slider->isSliderDown()) if (slider->isSliderDown())
{
qDebug() << "onElapsedTime: not updating slider coz slider is down";
return; return;
}
/* update progress bar */ /* update progress bar */
slider->setSliderPosition(val); slider->setSliderPosition(val);
@ -289,16 +365,17 @@ void MainWindow::on_elapsedTime(int val)
lblCurrentPos->setText(QString(buf)); lblCurrentPos->setText(QString(buf));
} }
void MainWindow::on_sliderValueChanged(int value) void MainWindow::onSliderValueChanged(int value)
{ {
if (acceptSliderMove) if (acceptSliderMove)
{ {
acceptSliderMove = false; 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) switch (action)
{ {
@ -319,7 +396,7 @@ void MainWindow::on_sliderActionTriggered(int action)
#if 1 #if 1
// LK_TODO delete this // LK_TODO delete this
void MainWindow::mouseMoveEvent(QMouseEvent *e) void MainWindow::mouseMoveEvent(QMouseEvent *)
{ {
//qDebug() << "mouseMoveEvent: x=" << e->globalX() << "y=" << e->globalY(); //qDebug() << "mouseMoveEvent: x=" << e->globalX() << "y=" << e->globalY();
} }

@ -1,6 +1,14 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define 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 <QMainWindow>
#include <QFileDialog> #include <QFileDialog>
#include <QDebug> #include <QDebug>
@ -14,12 +22,32 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QPushButton> #include <QPushButton>
#include <QSlider> #include <QSlider>
#include <QTimer>
#include <QPixmap>
#include <QPainter>
#include "xrdpapi.h"
#include "xrdpvr.h"
#include "decoder.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 { #define VCR_PLAY 1
class MainWindow; #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 class MainWindow : public QMainWindow
@ -30,20 +58,23 @@ public:
explicit MainWindow(QWidget *parent = 0); explicit MainWindow(QWidget *parent = 0);
~MainWindow(); ~MainWindow();
signals:
void onGeometryChanged(int x, int y, int widht, int height);
public slots:
void onSliderValueChanged(int value);
private slots: private slots:
void on_actionOpen_Media_File_triggered(); void on_actionOpen_Media_File_triggered();
void on_actionExit_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 onBtnPlayClicked(bool flag);
void on_geometryChanged(int x, int y, int widht, int height); void onBtnRewindClicked(bool flag);
void on_mediaSeek(int value); void onBtnStopClicked(bool flag);
void onMediaDurationInSeconds(int duration);
void onElapsedTime(int secs);
void onSliderActionTriggered(int value);
protected: protected:
void resizeEvent(QResizeEvent *e); void resizeEvent(QResizeEvent *e);
@ -54,10 +85,6 @@ protected:
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
QString filename;
Decoder *decoder;
DecoderThread *decoderThread;
/* for UI */ /* for UI */
QLabel *lblCurrentPos; QLabel *lblCurrentPos;
QLabel *lblDuration; QLabel *lblDuration;
@ -73,10 +100,22 @@ private:
QWidget *window; QWidget *window;
bool acceptSliderMove; 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 */ /* private methods */
void setupUI(); void setupUI();
void openMediaFile(); void openMediaFile();
void getVdoGeometry(QRect *rect); void getVdoGeometry(QRect *rect);
void clearDisplay();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

@ -28,7 +28,6 @@
<string>File</string> <string>File</string>
</property> </property>
<addaction name="actionOpen_Media_File"/> <addaction name="actionOpen_Media_File"/>
<addaction name="actionPlay_Media"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionExit"/> <addaction name="actionExit"/>
</widget> </widget>
@ -56,11 +55,6 @@
<string>Exit application</string> <string>Exit application</string>
</property> </property>
</action> </action>
<action name="actionPlay_Media">
<property name="text">
<string>Play Media</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources/> <resources/>

@ -0,0 +1,5 @@
#include "mediapacket.h"
MediaPacket::MediaPacket()
{
}

@ -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

@ -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);
}

@ -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

@ -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;
}
}

@ -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

@ -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

@ -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

@ -11,13 +11,19 @@ TEMPLATE = app
SOURCES += main.cpp\ SOURCES += main.cpp\
mainwindow.cpp \ playvideo.cpp \
decoder.cpp \ mainwindow.cpp \
decoderthread.cpp mediapacket.cpp \
playaudio.cpp \
demuxmedia.cpp \
ourinterface.cpp
HEADERS += mainwindow.h \ HEADERS += mainwindow.h \
decoder.h \ mediapacket.h \
decoderthread.h playvideo.h \
playaudio.h \
demuxmedia.h \
ourinterface.h
FORMS += mainwindow.ui FORMS += mainwindow.ui

@ -133,6 +133,10 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
{ {
int i; 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 */ /* register all available fileformats and codecs */
av_register_all(); av_register_all();
@ -221,11 +225,106 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
return 0; 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) 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) if (av_pkt.stream_index == g_audio_index)
{ {
xrdpvr_send_audio_data(channel, stream_id, av_pkt.size, av_pkt.data); 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) else if (av_pkt.stream_index == g_video_index)
{ {
xrdpvr_send_video_data(channel, stream_id, av_pkt.size, av_pkt.data); 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); av_free_packet(&av_pkt);
@ -256,6 +398,9 @@ xrdpvr_seek_media(int64_t pos, int backward)
printf("xrdpvr_seek_media() entered\n"); printf("xrdpvr_seek_media() entered\n");
g_psi.audioTimeout = -1;
g_psi.videoTimeout = -1;
seek_flag = (backward) ? AVSEEK_FLAG_BACKWARD : 0; seek_flag = (backward) ? AVSEEK_FLAG_BACKWARD : 0;
seek_target = av_rescale_q(pos * AV_TIME_BASE, seek_target = av_rescale_q(pos * AV_TIME_BASE,

@ -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_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_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_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); void xrdpvr_get_media_duration(int64_t *start_time, int64_t *duration);
int xrdpvr_seek_media(int64_t pos, int backward); 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 #ifdef __cplusplus
} }

@ -187,6 +187,8 @@ typedef struct _player_state_info
int audio_stream_index; int audio_stream_index;
int video_stream_index; int video_stream_index;
double audioTimeout;
double videoTimeout;
/* LK_TODO delete this after we fix the problem */ /* LK_TODO delete this after we fix the problem */
AVFrame *frame; AVFrame *frame;

Loading…
Cancel
Save