mirror of https://github.com/neutrinolabs/xrdp
parent
d1fc67102a
commit
cae2adb75e
|
@ -1,8 +1,17 @@
|
|||
#include "decoderthread.h"
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* o need to maintain aspect ratio while resizing
|
||||
* o clicking in the middle of the slider bar shuld move the slider to the middle
|
||||
* o need to be able to rewind the move when it is done playing
|
||||
* o need to be able to load another move and play it w/o restarting player
|
||||
* o pause button needs to work
|
||||
* o need images for btns
|
||||
*/
|
||||
|
||||
DecoderThread::DecoderThread()
|
||||
{
|
||||
vsi = NULL;
|
||||
channel = NULL;
|
||||
geometry.setX(0);
|
||||
geometry.setY(0);
|
||||
|
@ -10,16 +19,13 @@ DecoderThread::DecoderThread()
|
|||
geometry.setHeight(0);
|
||||
stream_id = 101;
|
||||
elapsedTime = 0;
|
||||
la_seekPos = 0;
|
||||
la_seekPos = -1;
|
||||
videoTimer = NULL;
|
||||
audioTimer = NULL;
|
||||
}
|
||||
|
||||
void DecoderThread::run()
|
||||
{
|
||||
int64_t start_time;
|
||||
int64_t duration;
|
||||
|
||||
/* TODO what happens if we get called a 2nd time while we are still running */
|
||||
|
||||
/* need a media file */
|
||||
if (filename.length() == 0)
|
||||
{
|
||||
|
@ -27,89 +33,72 @@ void DecoderThread::run()
|
|||
"Please select a media file to play");
|
||||
return;
|
||||
}
|
||||
|
||||
/* connect to remote client */
|
||||
if (openVirtualChannel())
|
||||
return;
|
||||
|
||||
vsi = (VideoStateInfo *) av_mallocz(sizeof(VideoStateInfo));
|
||||
if (vsi == NULL)
|
||||
{
|
||||
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)
|
||||
void DecoderThread::startMediaPlay()
|
||||
{
|
||||
geometry.setX(x);
|
||||
geometry.setY(y);
|
||||
geometry.setWidth(width);
|
||||
geometry.setHeight(height);
|
||||
MediaPacket *mediaPkt;
|
||||
int is_video_frame;
|
||||
int rv;
|
||||
|
||||
#if 0
|
||||
qDebug() << "decoderThread:signal" <<
|
||||
"" << geometry.x() <<
|
||||
"" << geometry.y() <<
|
||||
"" << geometry.width() <<
|
||||
"" << geometry.height();
|
||||
#endif
|
||||
/* 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);
|
||||
|
||||
if (channel)
|
||||
/* 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)
|
||||
{
|
||||
xrdpvr_set_geometry(channel, 101, geometry.x(), geometry.y(),
|
||||
geometry.width(), geometry.height());
|
||||
}
|
||||
/* fill the audio/video queues with initial data; thereafter */
|
||||
/* data will be filled by pktTimerCallback() */
|
||||
if ((audioQueue.count() >= 3000) || (videoQueue.count() >= 3000))
|
||||
{
|
||||
//pktTimer->start(50);
|
||||
|
||||
//videoTimer->start(1500);
|
||||
//audioTimer->start(500);
|
||||
|
||||
playVideo = new PlayVideo(NULL, &videoQueue, channel, 101);
|
||||
playVideoThread = new QThread(this);
|
||||
connect(playVideoThread, SIGNAL(started()), playVideo, SLOT(play()));
|
||||
playVideo->moveToThread(playVideoThread);
|
||||
playVideoThread->start();
|
||||
|
||||
playAudio = new PlayAudio(NULL, &audioQueue, channel, 101);
|
||||
playAudioThread = new QThread(this);
|
||||
connect(playAudioThread, SIGNAL(started()), playAudio, SLOT(play()));
|
||||
playAudio->moveToThread(playAudioThread);
|
||||
playAudioThread->start();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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 */
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_video_frame)
|
||||
videoQueue.enqueue(mediaPkt);
|
||||
else
|
||||
audioQueue.enqueue(mediaPkt);
|
||||
|
||||
} /* end while (1) */
|
||||
}
|
||||
|
||||
void DecoderThread::on_mediaSeek(int value)
|
||||
|
@ -117,6 +106,15 @@ void DecoderThread::on_mediaSeek(int value)
|
|||
mutex.lock();
|
||||
la_seekPos = value;
|
||||
mutex.unlock();
|
||||
|
||||
qDebug() << "media seek value=" << value;
|
||||
|
||||
/* pktTimer stops at end of media; need to restart it */
|
||||
if (!pktTimer->isActive())
|
||||
{
|
||||
updateSlider();
|
||||
pktTimer->start(100);
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderThread::setFilename(QString filename)
|
||||
|
@ -124,94 +122,125 @@ void DecoderThread::setFilename(QString filename)
|
|||
this->filename = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Open a virtual connection to remote client
|
||||
*
|
||||
* @return 0 on success, -1 on failure
|
||||
******************************************************************************/
|
||||
int DecoderThread::openVirtualChannel()
|
||||
void DecoderThread::stopPlayer()
|
||||
{
|
||||
/* is channel already open? */
|
||||
if (channel)
|
||||
return -1;
|
||||
|
||||
/* 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;
|
||||
pktTimer->stop();
|
||||
audioQueue.clear();
|
||||
videoQueue.clear();
|
||||
}
|
||||
|
||||
int DecoderThread::closeVirtualChannel()
|
||||
void DecoderThread::pausePlayer()
|
||||
{
|
||||
/* channel must be opened first */
|
||||
if (!channel)
|
||||
return -1;
|
||||
|
||||
WTSVirtualChannelClose(channel);
|
||||
return 0;
|
||||
pktTimer->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief this is a temp hack while we figure out how to set up the right
|
||||
* context for avcodec_decode_video2() on the server side; the workaround
|
||||
* is to send the first 1MB of the media file to the server end which
|
||||
* reads this file and sets up its context
|
||||
*
|
||||
* @return 0 on success, -1 on failure
|
||||
******************************************************************************/
|
||||
int DecoderThread::sendMetadataFile()
|
||||
void DecoderThread::resumePlayer()
|
||||
{
|
||||
if (xrdpvr_create_metadata_file(channel, filename.toAscii().data()))
|
||||
pktTimer->start(100);
|
||||
}
|
||||
|
||||
void DecoderThread::close()
|
||||
{
|
||||
}
|
||||
|
||||
void DecoderThread::audioTimerCallback()
|
||||
{
|
||||
MediaPacket *pkt;
|
||||
int delayInMs;
|
||||
|
||||
if (audioQueue.isEmpty())
|
||||
{
|
||||
emit on_decoderErrorMsg("I/O Error",
|
||||
"An error occurred while sending data to remote client");
|
||||
return -1;
|
||||
qDebug() << "audioTimerCallback: got empty";
|
||||
audioTimer->setInterval(100);
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
pkt = audioQueue.dequeue();
|
||||
delayInMs = (int) ((float) pkt->delay_in_us / 1000.0);
|
||||
send_audio_pkt(channel, 101, pkt->av_pkt);
|
||||
delete pkt;
|
||||
|
||||
//qDebug() << "audioTimerCallback: delay :" << delayInMs;
|
||||
|
||||
audioTimer->setInterval(delayInMs);
|
||||
}
|
||||
|
||||
int DecoderThread::sendVideoFormat()
|
||||
void DecoderThread::videoTimerCallback()
|
||||
{
|
||||
if (xrdpvr_set_video_format(channel, stream_id))
|
||||
MediaPacket *pkt;
|
||||
int delayInMs;
|
||||
|
||||
if (videoQueue.isEmpty())
|
||||
{
|
||||
emit on_decoderErrorMsg("I/O Error",
|
||||
"Error sending video format to remote client");
|
||||
return -1;
|
||||
qDebug() << "videoTimerCallback: GOT EMPTY";
|
||||
videoTimer->setInterval(100);
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
pkt = videoQueue.dequeue();
|
||||
delayInMs = (int) ((float) pkt->delay_in_us / 1000.0);
|
||||
send_video_pkt(channel, 101, pkt->av_pkt);
|
||||
delete pkt;
|
||||
updateSlider();
|
||||
//qDebug() << "videoTimerCallback: delay :" << delayInMs;
|
||||
videoTimer->setInterval(delayInMs);
|
||||
}
|
||||
|
||||
int DecoderThread::sendAudioFormat()
|
||||
void DecoderThread::pktTimerCallback()
|
||||
{
|
||||
if (xrdpvr_set_audio_format(channel, stream_id))
|
||||
{
|
||||
emit on_decoderErrorMsg("I/O Error",
|
||||
"Error sending audio format to remote client");
|
||||
return -1;
|
||||
}
|
||||
MediaPacket *mediaPkt;
|
||||
int is_video_frame;
|
||||
int rv;
|
||||
|
||||
return 0;
|
||||
while (1)
|
||||
{
|
||||
qDebug() << "pktTimerCallback: audioCount=" << audioQueue.count() << "videoCount=" << videoQueue.count();
|
||||
#if 1
|
||||
if ((audioQueue.count() >= 20) || (videoQueue.count() >= 20))
|
||||
return;
|
||||
#else
|
||||
if (videoQueue.count() >= 60)
|
||||
return;
|
||||
#endif
|
||||
mediaPkt = new MediaPacket;
|
||||
rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
|
||||
&is_video_frame,
|
||||
&mediaPkt->delay_in_us);
|
||||
if (rv < 0)
|
||||
{
|
||||
/* looks like we reached end of file */
|
||||
qDebug() << "###### looks like we reached EOF";
|
||||
pktTimer->stop();
|
||||
// LK_TODO set some flag so audio/video timer also stop when q is empty
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_video_frame)
|
||||
videoQueue.enqueue(mediaPkt);
|
||||
else
|
||||
audioQueue.enqueue(mediaPkt);
|
||||
}
|
||||
}
|
||||
|
||||
int DecoderThread::sendGeometry()
|
||||
void DecoderThread::updateSlider()
|
||||
{
|
||||
int rv;
|
||||
if (elapsedTime == 0)
|
||||
elapsedTime = av_gettime();
|
||||
|
||||
rv = xrdpvr_set_geometry(channel, stream_id, geometry.x(), geometry.y(),
|
||||
geometry.width(), geometry.height());
|
||||
/* time elapsed in 1/100th sec units since play started */
|
||||
emit on_elapsedtime((av_gettime() - elapsedTime) / 10000);
|
||||
|
||||
if (rv)
|
||||
mutex.lock();
|
||||
if (la_seekPos >= 0)
|
||||
{
|
||||
emit on_decoderErrorMsg("I/O Error",
|
||||
"Error sending screen geometry to remote client");
|
||||
return -1;
|
||||
qDebug() << "seeking to" << la_seekPos;
|
||||
//audioTimer->stop();
|
||||
//videoTimer->stop();
|
||||
xrdpvr_seek_media(la_seekPos, 0);
|
||||
elapsedTime = av_gettime() - la_seekPos * 1000000;
|
||||
//audioTimer->start(10);
|
||||
//videoTimer->start(10);
|
||||
la_seekPos = -1;
|
||||
}
|
||||
return 0;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
|
|
@ -14,9 +14,14 @@
|
|||
#include <QString>
|
||||
#include <QRect>
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <QQueue>
|
||||
|
||||
#include <xrdpapi.h>
|
||||
#include <xrdpvr.h>
|
||||
#include <mediapacket.h>
|
||||
#include <playvideo.h>
|
||||
#include <playaudio.h>
|
||||
|
||||
/* ffmpeg related stuff */
|
||||
extern "C"
|
||||
|
@ -25,28 +30,34 @@ extern "C"
|
|||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
class DecoderThread : public QThread
|
||||
class DecoderThread : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/* public methods */
|
||||
DecoderThread();
|
||||
|
||||
void setFilename(QString filename);
|
||||
void stopPlayer();
|
||||
void pausePlayer();
|
||||
void resumePlayer();
|
||||
void oneTimeDeinit();
|
||||
void close();
|
||||
void run();
|
||||
void startMediaPlay();
|
||||
|
||||
public slots:
|
||||
void on_geometryChanged(int x, int y, int width, int height);
|
||||
void on_mediaSeek(int value);
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
typedef struct _VideoStateInfo
|
||||
{
|
||||
AVFormatContext *pFormatCtx;
|
||||
} VideoStateInfo;
|
||||
/* private variables */
|
||||
QQueue<MediaPacket *> audioQueue;
|
||||
QQueue<MediaPacket *> videoQueue;
|
||||
|
||||
VideoStateInfo *vsi;
|
||||
QTimer *videoTimer;
|
||||
QTimer *audioTimer;
|
||||
QTimer *pktTimer;
|
||||
QString filename;
|
||||
void *channel;
|
||||
int stream_id;
|
||||
|
@ -55,14 +66,26 @@ private:
|
|||
QMutex mutex;
|
||||
int64_t la_seekPos; /* locked access; must hold mutex */
|
||||
|
||||
int openVirtualChannel();
|
||||
int closeVirtualChannel();
|
||||
int sendMetadataFile();
|
||||
int sendVideoFormat();
|
||||
int sendAudioFormat();
|
||||
int sendGeometry();
|
||||
PlayVideo *playVideo;
|
||||
QThread *playVideoThread;
|
||||
PlayAudio *playAudio;
|
||||
QThread *playAudioThread;
|
||||
|
||||
/* private functions */
|
||||
int sendMetadataFile();
|
||||
int sendAudioFormat();
|
||||
int sendVideoFormat();
|
||||
int sendGeometry();
|
||||
void updateSlider();
|
||||
|
||||
private slots:
|
||||
/* private slots */
|
||||
void audioTimerCallback();
|
||||
void videoTimerCallback();
|
||||
void pktTimerCallback();
|
||||
|
||||
signals:
|
||||
/* private signals */
|
||||
void on_progressUpdate(int percent);
|
||||
void on_decoderErrorMsg(QString title, QString msg);
|
||||
void on_mediaDurationInSeconds(int duration);
|
||||
|
|
|
@ -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),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
/* connect to remote client */
|
||||
interface = new OurInterface();
|
||||
if (interface->oneTimeInit())
|
||||
{
|
||||
oneTimeInitSuccess = false;
|
||||
|
||||
/* connection to remote client failed; error msg has */
|
||||
/* already been displayed so it's ok to close app now */
|
||||
QTimer::singleShot(1000, qApp, SLOT(quit()));
|
||||
}
|
||||
|
||||
oneTimeInitSuccess = true;
|
||||
remoteClientInited = false;
|
||||
ui->setupUi(this);
|
||||
acceptSliderMove = false;
|
||||
decoderThread = new DecoderThread();
|
||||
setupUI();
|
||||
vcrFlag = 0;
|
||||
|
||||
/* LK_TODO */
|
||||
#if 0
|
||||
decoder = new Decoder(this);
|
||||
connect(this, SIGNAL(onGeometryChanged(int, int, int, int)),
|
||||
decoder, SLOT(onGeometryChanged(int, int, int, int)));
|
||||
#endif
|
||||
connect(this, SIGNAL(onGeometryChanged(int,int,int,int)),
|
||||
interface, SLOT(onGeometryChanged(int,int,int,int)));
|
||||
|
||||
/* 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)));
|
||||
connect(interface, SIGNAL(onMediaDurationInSeconds(int)),
|
||||
this, SLOT(onMediaDurationInSeconds(int)));
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
if (oneTimeInitSuccess)
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = QMessageBox::question(this, "Closing application",
|
||||
"Do you really want to close vrplayer?",
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (rv == QMessageBox::No)
|
||||
{
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
decoderThread->exit(0);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent *e)
|
||||
void MainWindow::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
QRect rect;
|
||||
|
||||
getVdoGeometry(&rect);
|
||||
emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height());
|
||||
interface->sendGeometry(rect);
|
||||
}
|
||||
|
||||
void MainWindow::moveEvent(QMoveEvent *e)
|
||||
void MainWindow::moveEvent(QMoveEvent *)
|
||||
{
|
||||
QRect rect;
|
||||
|
||||
getVdoGeometry(&rect);
|
||||
emit on_geometryChanged(rect.x(), rect.y(), rect.width(), rect.height());
|
||||
interface->sendGeometry(rect);
|
||||
}
|
||||
|
||||
void MainWindow::setupUI()
|
||||
|
@ -100,8 +86,8 @@ void MainWindow::setupUI()
|
|||
slider->setOrientation(Qt::Horizontal);
|
||||
slider->setMinimumHeight(20);
|
||||
slider->setMaximumHeight(20);
|
||||
connect(slider, SIGNAL(actionTriggered(int)), this, SLOT(on_sliderActionTriggered(int)));
|
||||
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(on_sliderValueChanged(int)));
|
||||
connect(slider, SIGNAL(actionTriggered(int)), this, SLOT(onSliderActionTriggered(int)));
|
||||
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int)));
|
||||
|
||||
/* setup label to display media duration */
|
||||
lblDuration = new QLabel("00:00:00");
|
||||
|
@ -115,19 +101,23 @@ void MainWindow::setupUI()
|
|||
hboxLayoutMiddle->addWidget(lblDuration);
|
||||
|
||||
/* setup play button */
|
||||
btnPlay = new QPushButton("P");
|
||||
btnPlay = new QPushButton("Play");
|
||||
btnPlay->setMinimumHeight(40);
|
||||
btnPlay->setMaximumHeight(40);
|
||||
btnPlay->setMinimumWidth(40);
|
||||
btnPlay->setMaximumWidth(40);
|
||||
connect(btnPlay, SIGNAL(clicked(bool)), this, SLOT(on_btnPlayClicked(bool)));
|
||||
btnPlay->setCheckable(true);
|
||||
connect(btnPlay, SIGNAL(clicked(bool)),
|
||||
this, SLOT(onBtnPlayClicked(bool)));
|
||||
|
||||
/* setup stop button */
|
||||
btnStop = new QPushButton("S");
|
||||
btnStop = new QPushButton("Stop");
|
||||
btnStop->setMinimumHeight(40);
|
||||
btnStop->setMaximumHeight(40);
|
||||
btnStop->setMinimumWidth(40);
|
||||
btnStop->setMaximumWidth(40);
|
||||
connect(btnStop, SIGNAL(clicked(bool)),
|
||||
this, SLOT(onBtnStopClicked(bool)));
|
||||
|
||||
/* setup rewind button */
|
||||
btnRewind = new QPushButton("R");
|
||||
|
@ -135,12 +125,14 @@ void MainWindow::setupUI()
|
|||
btnRewind->setMaximumHeight(40);
|
||||
btnRewind->setMinimumWidth(40);
|
||||
btnRewind->setMaximumWidth(40);
|
||||
connect(btnRewind, SIGNAL(clicked(bool)),
|
||||
this, SLOT(onBtnRewindClicked(bool)));
|
||||
|
||||
/* add buttons to bottom panel */
|
||||
hboxLayoutBottom = new QHBoxLayout;
|
||||
hboxLayoutBottom->addWidget(btnPlay);
|
||||
hboxLayoutBottom->addWidget(btnStop);
|
||||
hboxLayoutBottom->addWidget(btnRewind);
|
||||
//hboxLayoutBottom->addWidget(btnRewind);
|
||||
hboxLayoutBottom->addStretch();
|
||||
|
||||
/* add all three layouts to one vertical layout */
|
||||
|
@ -171,7 +163,7 @@ void MainWindow::openMediaFile()
|
|||
filename = QFileDialog::getOpenFileName(this, "Select Media File",
|
||||
filename);
|
||||
}
|
||||
decoderThread->setFilename(filename);
|
||||
interface->setFilename(filename);
|
||||
}
|
||||
|
||||
void MainWindow::getVdoGeometry(QRect *rect)
|
||||
|
@ -187,48 +179,120 @@ void MainWindow::getVdoGeometry(QRect *rect)
|
|||
rect->setHeight(lblVideo->geometry().height());
|
||||
}
|
||||
|
||||
void MainWindow::clearDisplay()
|
||||
{
|
||||
QPixmap pixmap(100,100);
|
||||
pixmap.fill(QColor(0x00, 0x00, 0x00));
|
||||
QPainter painter(&pixmap);
|
||||
painter.setBrush(QBrush(Qt::black));
|
||||
lblVideo->setPixmap(pixmap);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* actions and slots go here *
|
||||
******************************************************************************/
|
||||
|
||||
void MainWindow::on_actionOpen_Media_File_triggered()
|
||||
{
|
||||
if (vcrFlag != 0)
|
||||
onBtnStopClicked(false);
|
||||
|
||||
openMediaFile();
|
||||
if (filename.length() == 0)
|
||||
{
|
||||
/* cancel btn was clicked */
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteClientInited)
|
||||
{
|
||||
remoteClientInited = false;
|
||||
interface->deInitRemoteClient();
|
||||
interface->initRemoteClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
interface->initRemoteClient();
|
||||
}
|
||||
|
||||
playVideo = interface->getPlayVideoInstance();
|
||||
if (playVideo)
|
||||
{
|
||||
connect(playVideo, SIGNAL(onElapsedtime(int)),
|
||||
this, SLOT(onElapsedTime(int)));
|
||||
}
|
||||
|
||||
remoteClientInited = true;
|
||||
interface->playMedia();
|
||||
|
||||
if (vcrFlag != 0)
|
||||
{
|
||||
interface->setVcrOp(VCR_PLAY);
|
||||
btnPlay->setText("Pause");
|
||||
vcrFlag = VCR_PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionExit_triggered()
|
||||
{
|
||||
/* TODO: confirm app exit */
|
||||
clearDisplay();
|
||||
this->close();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionPlay_Media_triggered()
|
||||
void MainWindow::onBtnPlayClicked(bool)
|
||||
{
|
||||
// LK_TODO do we need this? if yes, should be same as on_btnPlayClicked()
|
||||
#if 1
|
||||
decoderThread->start();
|
||||
#else
|
||||
if (!decoder)
|
||||
return;
|
||||
if (vcrFlag == 0)
|
||||
{
|
||||
/* first time play button has been clicked */
|
||||
on_actionOpen_Media_File_triggered();
|
||||
btnPlay->setText("Pause");
|
||||
vcrFlag = VCR_PLAY;
|
||||
}
|
||||
else if (vcrFlag == VCR_PLAY)
|
||||
{
|
||||
/* btn clicked while in play mode - enter pause mode */
|
||||
btnPlay->setText("Play");
|
||||
interface->setVcrOp(VCR_PAUSE);
|
||||
vcrFlag = VCR_PAUSE;
|
||||
}
|
||||
else if (vcrFlag == VCR_PAUSE)
|
||||
{
|
||||
/* btn clicked while in pause mode - enter play mode */
|
||||
btnPlay->setText("Pause");
|
||||
interface->setVcrOp(VCR_PLAY);
|
||||
vcrFlag = VCR_PLAY;
|
||||
}
|
||||
|
||||
decoder->init(filename);
|
||||
#endif
|
||||
else if (vcrFlag == VCR_STOP)
|
||||
{
|
||||
/* btn clicked while stopped - enter play mode */
|
||||
btnPlay->setText("Play");
|
||||
interface->setVcrOp(VCR_PLAY);
|
||||
vcrFlag = VCR_PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_decoderError(QString title, QString msg)
|
||||
void MainWindow::onBtnRewindClicked(bool)
|
||||
{
|
||||
QMessageBox::information(this, title, msg);
|
||||
if (playVideo)
|
||||
playVideo->onMediaSeek(0);
|
||||
}
|
||||
|
||||
void MainWindow::on_btnPlayClicked(bool flag)
|
||||
void MainWindow::onBtnStopClicked(bool)
|
||||
{
|
||||
if (filename.length() == 0)
|
||||
openMediaFile();
|
||||
vcrFlag = VCR_STOP;
|
||||
btnPlay->setText("Play");
|
||||
interface->setVcrOp(VCR_STOP);
|
||||
|
||||
decoderThread->start();
|
||||
/* reset slider */
|
||||
slider->setSliderPosition(0);
|
||||
lblCurrentPos->setText("00:00:00");
|
||||
|
||||
/* clear screen by filling it with black */
|
||||
clearDisplay();
|
||||
}
|
||||
|
||||
void MainWindow::on_mediaDurationInSeconds(int duration)
|
||||
void MainWindow::onMediaDurationInSeconds(int duration)
|
||||
{
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
|
@ -239,6 +303,9 @@ void MainWindow::on_mediaDurationInSeconds(int duration)
|
|||
slider->setMinimum(0);
|
||||
slider->setMaximum(duration * 100); /* in hundredth of a sec */
|
||||
slider->setValue(0);
|
||||
slider->setSliderPosition(0);
|
||||
lblCurrentPos->setText("00:00:00");
|
||||
qDebug() << "media_duration=" << duration << " in hundredth of a sec:" << duration * 100;
|
||||
|
||||
/* convert from seconds to hours:minutes:seconds */
|
||||
hours = duration / 3600;
|
||||
|
@ -258,7 +325,7 @@ void MainWindow::on_mediaDurationInSeconds(int duration)
|
|||
/**
|
||||
* time elapsed in 1/100th sec units since play started
|
||||
******************************************************************************/
|
||||
void MainWindow::on_elapsedTime(int val)
|
||||
void MainWindow::onElapsedTime(int val)
|
||||
{
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
|
@ -266,9 +333,18 @@ void MainWindow::on_elapsedTime(int val)
|
|||
int duration = val / 100;
|
||||
char buf[20];
|
||||
|
||||
if (vcrFlag == VCR_STOP)
|
||||
{
|
||||
qDebug() << "onElapsedTime: not updating slider coz of VCR_STOP";
|
||||
return;
|
||||
}
|
||||
|
||||
/* if slider bar is down, do not update */
|
||||
if (slider->isSliderDown())
|
||||
{
|
||||
qDebug() << "onElapsedTime: not updating slider coz slider is down";
|
||||
return;
|
||||
}
|
||||
|
||||
/* update progress bar */
|
||||
slider->setSliderPosition(val);
|
||||
|
@ -289,16 +365,17 @@ void MainWindow::on_elapsedTime(int val)
|
|||
lblCurrentPos->setText(QString(buf));
|
||||
}
|
||||
|
||||
void MainWindow::on_sliderValueChanged(int value)
|
||||
void MainWindow::onSliderValueChanged(int value)
|
||||
{
|
||||
if (acceptSliderMove)
|
||||
{
|
||||
acceptSliderMove = false;
|
||||
emit on_mediaSeek(value / 100);
|
||||
if (playVideo)
|
||||
playVideo->onMediaSeek(value / 100);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_sliderActionTriggered(int action)
|
||||
void MainWindow::onSliderActionTriggered(int action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
|
@ -319,7 +396,7 @@ void MainWindow::on_sliderActionTriggered(int action)
|
|||
|
||||
#if 1
|
||||
// LK_TODO delete this
|
||||
void MainWindow::mouseMoveEvent(QMouseEvent *e)
|
||||
void MainWindow::mouseMoveEvent(QMouseEvent *)
|
||||
{
|
||||
//qDebug() << "mouseMoveEvent: x=" << e->globalX() << "y=" << e->globalY();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#ifdef _STDINT_H
|
||||
#undef _STDINT_H
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QFileDialog>
|
||||
#include <QDebug>
|
||||
|
@ -14,12 +22,32 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QTimer>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
|
||||
#include "xrdpapi.h"
|
||||
#include "xrdpvr.h"
|
||||
#include "decoder.h"
|
||||
#include "decoderthread.h"
|
||||
#include "ourinterface.h"
|
||||
#include "playvideo.h"
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
/* 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
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
|
@ -30,20 +58,23 @@ public:
|
|||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
signals:
|
||||
void onGeometryChanged(int x, int y, int widht, int height);
|
||||
|
||||
public slots:
|
||||
void onSliderValueChanged(int value);
|
||||
|
||||
private slots:
|
||||
void on_actionOpen_Media_File_triggered();
|
||||
void on_actionExit_triggered();
|
||||
void on_actionPlay_Media_triggered();
|
||||
void on_decoderError(QString title, QString msg);
|
||||
void on_btnPlayClicked(bool flag);
|
||||
void on_mediaDurationInSeconds(int duration);
|
||||
void on_elapsedTime(int secs);
|
||||
void on_sliderActionTriggered(int value);
|
||||
void on_sliderValueChanged(int value);
|
||||
|
||||
signals:
|
||||
void on_geometryChanged(int x, int y, int widht, int height);
|
||||
void on_mediaSeek(int value);
|
||||
void onBtnPlayClicked(bool flag);
|
||||
void onBtnRewindClicked(bool flag);
|
||||
void onBtnStopClicked(bool flag);
|
||||
|
||||
void onMediaDurationInSeconds(int duration);
|
||||
void onElapsedTime(int secs);
|
||||
void onSliderActionTriggered(int value);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
|
@ -54,10 +85,6 @@ protected:
|
|||
private:
|
||||
Ui::MainWindow *ui;
|
||||
|
||||
QString filename;
|
||||
Decoder *decoder;
|
||||
DecoderThread *decoderThread;
|
||||
|
||||
/* for UI */
|
||||
QLabel *lblCurrentPos;
|
||||
QLabel *lblDuration;
|
||||
|
@ -73,10 +100,22 @@ private:
|
|||
QWidget *window;
|
||||
bool acceptSliderMove;
|
||||
|
||||
/* private stuff */
|
||||
OurInterface *interface;
|
||||
PlayVideo *playVideo;
|
||||
QString filename;
|
||||
bool oneTimeInitSuccess;
|
||||
bool remoteClientInited;
|
||||
void *channel;
|
||||
int stream_id;
|
||||
int64_t elapsedTime; /* elapsed time in usecs since play started */
|
||||
int vcrFlag;
|
||||
|
||||
/* private methods */
|
||||
void setupUI();
|
||||
void openMediaFile();
|
||||
void getVdoGeometry(QRect *rect);
|
||||
void clearDisplay();
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionOpen_Media_File"/>
|
||||
<addaction name="actionPlay_Media"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
|
@ -56,11 +55,6 @@
|
|||
<string>Exit application</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPlay_Media">
|
||||
<property name="text">
|
||||
<string>Play Media</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
|
|
@ -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\
|
||||
mainwindow.cpp \
|
||||
decoder.cpp \
|
||||
decoderthread.cpp
|
||||
playvideo.cpp \
|
||||
mainwindow.cpp \
|
||||
mediapacket.cpp \
|
||||
playaudio.cpp \
|
||||
demuxmedia.cpp \
|
||||
ourinterface.cpp
|
||||
|
||||
HEADERS += mainwindow.h \
|
||||
decoder.h \
|
||||
decoderthread.h
|
||||
mediapacket.h \
|
||||
playvideo.h \
|
||||
playaudio.h \
|
||||
demuxmedia.h \
|
||||
ourinterface.h
|
||||
|
||||
FORMS += mainwindow.ui
|
||||
|
||||
|
|
155
xrdpvr/xrdpvr.c
155
xrdpvr/xrdpvr.c
|
@ -133,6 +133,10 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
|
|||
{
|
||||
int i;
|
||||
|
||||
printf("$$$$$$ xrdpvr_play_media: setting audioTimeout & videoTimeout to -1\n");
|
||||
g_psi.videoTimeout = -1;
|
||||
g_psi.audioTimeout = -1;
|
||||
|
||||
/* register all available fileformats and codecs */
|
||||
av_register_all();
|
||||
|
||||
|
@ -221,11 +225,106 @@ xrdpvr_play_media(void *channel, int stream_id, char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int xrdpvr_play_frame(void *channel, int stream_id)
|
||||
static int firstAudioPkt = 1;
|
||||
static int firstVideoPkt = 1;
|
||||
|
||||
int xrdpvr_get_frame(void **av_pkt_ret, int *is_video_frame, int *delay_in_us)
|
||||
{
|
||||
AVPacket *av_pkt;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
printf("xrdpvr_play_frame: entered\n");
|
||||
double dts;
|
||||
int delay_in_us;
|
||||
|
||||
if (av_read_frame(g_psi.p_format_ctx, &av_pkt) < 0)
|
||||
{
|
||||
|
@ -236,12 +335,55 @@ int xrdpvr_play_frame(void *channel, int stream_id)
|
|||
if (av_pkt.stream_index == g_audio_index)
|
||||
{
|
||||
xrdpvr_send_audio_data(channel, stream_id, av_pkt.size, av_pkt.data);
|
||||
usleep(1000 * 1);
|
||||
|
||||
dts = av_pkt.dts;
|
||||
dts *= av_q2d(g_psi.p_format_ctx->streams[g_audio_index]->time_base);
|
||||
|
||||
*audioTimeout = (int) ((dts - g_psi.audioTimeout) * 1000000);
|
||||
*videoTimeout = -1;
|
||||
|
||||
if (g_psi.audioTimeout > dts)
|
||||
{
|
||||
g_psi.audioTimeout = dts;
|
||||
delay_in_us = 1000 * 40;
|
||||
}
|
||||
else
|
||||
{
|
||||
delay_in_us = (int) ((dts - g_psi.audioTimeout) * 1000000);
|
||||
g_psi.audioTimeout = dts;
|
||||
}
|
||||
|
||||
//printf("audio delay: %d\n", delay_in_us);
|
||||
usleep(delay_in_us);
|
||||
//usleep(1000 * 1);
|
||||
}
|
||||
else if (av_pkt.stream_index == g_video_index)
|
||||
{
|
||||
xrdpvr_send_video_data(channel, stream_id, av_pkt.size, av_pkt.data);
|
||||
usleep(1000 * 40); // was 50
|
||||
|
||||
dts = av_pkt.dts;
|
||||
dts *= av_q2d(g_psi.p_format_ctx->streams[g_video_index]->time_base);
|
||||
|
||||
//printf("xrdpvr_play_frame:video: saved=%f dts=%f\n", g_psi.videoTimeout, dts);
|
||||
|
||||
*videoTimeout = (int) ((dts - g_psi.videoTimeout) * 1000000);
|
||||
*audioTimeout = -1;
|
||||
|
||||
if (g_psi.videoTimeout > dts)
|
||||
{
|
||||
g_psi.videoTimeout = dts;
|
||||
delay_in_us = 1000 * 40;
|
||||
//printf("xrdpvr_play_frame:video1: saved=%f dts=%f delay_in_us=%d\n", g_psi.videoTimeout, dts, delay_in_us);
|
||||
}
|
||||
else
|
||||
{
|
||||
delay_in_us = (int) ((dts - g_psi.videoTimeout) * 1000000);
|
||||
g_psi.videoTimeout = dts;
|
||||
//printf("xrdpvr_play_frame:video2: saved=%f dts=%f delay_in_us=%d\n", g_psi.videoTimeout, dts, delay_in_us);
|
||||
}
|
||||
|
||||
//printf("video delay: %d\n", delay_in_us);
|
||||
usleep(delay_in_us);
|
||||
}
|
||||
|
||||
av_free_packet(&av_pkt);
|
||||
|
@ -256,6 +398,9 @@ xrdpvr_seek_media(int64_t pos, int backward)
|
|||
|
||||
printf("xrdpvr_seek_media() entered\n");
|
||||
|
||||
g_psi.audioTimeout = -1;
|
||||
g_psi.videoTimeout = -1;
|
||||
|
||||
seek_flag = (backward) ? AVSEEK_FLAG_BACKWARD : 0;
|
||||
|
||||
seek_target = av_rescale_q(pos * AV_TIME_BASE,
|
||||
|
|
|
@ -38,9 +38,12 @@ int xrdpvr_set_audio_format(void *channel, uint32_t stream_id);
|
|||
int xrdpvr_send_video_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data);
|
||||
int xrdpvr_send_audio_data(void *channel, uint32_t stream_id, uint32_t data_len, uint8_t *data);
|
||||
int xrdpvr_create_metadata_file(void *channel, char *filename);
|
||||
int xrdpvr_play_frame(void *channel, int stream_id);
|
||||
int xrdpvr_play_frame(void *channel, int stream_id, int *vdoTimeout, int *audioTimeout);
|
||||
void xrdpvr_get_media_duration(int64_t *start_time, int64_t *duration);
|
||||
int xrdpvr_seek_media(int64_t pos, int backward);
|
||||
int xrdpvr_get_frame(void **av_pkt_ret, int *is_video_frame, int *delay_in_us);
|
||||
int send_audio_pkt(void *channel, int stream_id, void *pkt_p);
|
||||
int send_video_pkt(void *channel, int stream_id, void *pkt_p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -187,6 +187,8 @@ typedef struct _player_state_info
|
|||
|
||||
int audio_stream_index;
|
||||
int video_stream_index;
|
||||
double audioTimeout;
|
||||
double videoTimeout;
|
||||
|
||||
/* LK_TODO delete this after we fix the problem */
|
||||
AVFrame *frame;
|
||||
|
|
Loading…
Reference in New Issue