|
|
|
|
|
|
|
|
|
|
|
// not used
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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()
|
|
|
|
{
|
|
|
|
channel = NULL;
|
|
|
|
geometry.setX(0);
|
|
|
|
geometry.setY(0);
|
|
|
|
geometry.setWidth(0);
|
|
|
|
geometry.setHeight(0);
|
|
|
|
stream_id = 101;
|
|
|
|
elapsedTime = 0;
|
|
|
|
la_seekPos = -1;
|
|
|
|
videoTimer = NULL;
|
|
|
|
audioTimer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::run()
|
|
|
|
{
|
|
|
|
/* need a media file */
|
|
|
|
if (filename.length() == 0)
|
|
|
|
{
|
|
|
|
emit on_decoderErrorMsg("No media file",
|
|
|
|
"Please select a media file to play");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::startMediaPlay()
|
|
|
|
{
|
|
|
|
MediaPacket *mediaPkt;
|
|
|
|
int is_video_frame;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
/* setup video timer; each time this timer fires, it sends */
|
|
|
|
/* one video pkt to the client then resets the callback duration */
|
|
|
|
videoTimer = new QTimer;
|
|
|
|
connect(videoTimer, SIGNAL(timeout()), this, SLOT(videoTimerCallback()));
|
|
|
|
//videoTimer->start(1500);
|
|
|
|
|
|
|
|
/* setup audio timer; does the same as above, but with audio pkts */
|
|
|
|
audioTimer = new QTimer;
|
|
|
|
connect(audioTimer, SIGNAL(timeout()), this, SLOT(audioTimerCallback()));
|
|
|
|
//audioTimer->start(500);
|
|
|
|
|
|
|
|
/* setup pktTimer; each time this timer fires, it reads AVPackets */
|
|
|
|
/* and puts them into audio/video Queues */
|
|
|
|
pktTimer = new QTimer;
|
|
|
|
connect(pktTimer, SIGNAL(timeout()), this, SLOT(pktTimerCallback()));
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
this->filename = filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::stopPlayer()
|
|
|
|
{
|
|
|
|
pktTimer->stop();
|
|
|
|
audioQueue.clear();
|
|
|
|
videoQueue.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::pausePlayer()
|
|
|
|
{
|
|
|
|
pktTimer->stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::resumePlayer()
|
|
|
|
{
|
|
|
|
pktTimer->start(100);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::close()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::audioTimerCallback()
|
|
|
|
{
|
|
|
|
MediaPacket *pkt;
|
|
|
|
int delayInMs;
|
|
|
|
|
|
|
|
if (audioQueue.isEmpty())
|
|
|
|
{
|
|
|
|
qDebug() << "audioTimerCallback: got empty";
|
|
|
|
audioTimer->setInterval(100);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::videoTimerCallback()
|
|
|
|
{
|
|
|
|
MediaPacket *pkt;
|
|
|
|
int delayInMs;
|
|
|
|
|
|
|
|
if (videoQueue.isEmpty())
|
|
|
|
{
|
|
|
|
qDebug() << "videoTimerCallback: GOT EMPTY";
|
|
|
|
videoTimer->setInterval(100);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt = videoQueue.dequeue();
|
|
|
|
delayInMs = (int) 10; // ((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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::pktTimerCallback()
|
|
|
|
{
|
|
|
|
MediaPacket *mediaPkt;
|
|
|
|
int is_video_frame;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecoderThread::updateSlider()
|
|
|
|
{
|
|
|
|
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 >= 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();
|
|
|
|
}
|