o development checkin

o added vrplayer
ulab-next
Laxmikant Rashinkar 12 years ago
parent 309f2225b1
commit 4c67aad4c4

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

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

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

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

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

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

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

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

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

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

@ -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) return 0;
}
int xrdpvr_play_frame(void *channel, int stream_id)
{ {
if (av_pkt.stream_index == audio_index) AVPacket av_pkt;
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); xrdpvr_send_audio_data(channel, stream_id, av_pkt.size, av_pkt.data);
usleep(1000 * 1); usleep(1000 * 1);
} }
else if (av_pkt.stream_index == 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 usleep(1000 * 40); // was 50
} }
}
av_free_packet(&av_pkt); 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; return 0;
} }
/****************************************************************************** void
* xrdpvr_get_media_duration(int64_t *start_time, int64_t *duration)
* code below this is local to this file and cannot be accessed externally; {
* this code communicates with the xrdpvr plugin in NeutrinoRDP; *start_time = g_psi.p_format_ctx->start_time / AV_TIME_BASE;
* NeutrinoRDP is a fork of FreeRDP 1.0.1 *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,6 +23,8 @@
#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
@ -30,6 +32,15 @@ extern "C" {
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
@ -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…
Cancel
Save