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