Skip to content

Commit

Permalink
AVPlayer: support manually load(). play() is not replay now
Browse files Browse the repository at this point in the history
load() then play()
  • Loading branch information
wang-bin committed May 30, 2016
1 parent 1b7167e commit 20284f4
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 128 deletions.
6 changes: 2 additions & 4 deletions examples/player/EventFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 143,7 @@ bool EventFilter::eventFilter(QObject *watched, QEvent *event)
player->stepBackward();
break;
case Qt::Key_P:
player->stop();
player->play();
break;
case Qt::Key_Q:
Expand Down Expand Up @@ -289,13 290,11 @@ bool EventFilter::eventFilter(QObject *watched, QEvent *event)
if (e->mimeData()->hasUrls()) {
QString path = e->mimeData()->urls().first().toLocalFile();
player->stop();
player->load(path);
player->play();
player->play(path);
e->acceptProposedAction();
} else {
e->ignore();
}

}
break;
case QEvent::GraphicsSceneContextMenu: {
Expand Down Expand Up @@ -328,7 327,6 @@ void EventFilter::showMenu(const QPoint &p)
menu->exec(p);
}


WindowEventFilter::WindowEventFilter(QWidget *window)
: QObject(window)
, mpWindow(window)
Expand Down
1 change: 0 additions & 1 deletion examples/player/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 203,6 @@ void MainWindow::onSeekFinished(qint64 pos)
void MainWindow::stopUnload()
{
mpPlayer->stop();
mpPlayer->unload();
}

void MainWindow::setupUi()
Expand Down
1 change: 0 additions & 1 deletion qml/QmlAVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 688,6 @@ void QmlAVPlayer::setPlaybackState(PlaybackState playbackState)
break;
case StoppedState:
mpPlayer->stop();
mpPlayer->unload();
m_loading = false;
mPlaybackState = StoppedState;
break;
Expand Down
146 changes: 47 additions & 99 deletions src/AVPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,66 630,6 @@ bool AVPlayer::isLoaded() const
return d->loaded;
}

bool AVPlayer::load(const QString &path, bool reload)
{
setFile(path);
return load(reload);
}

bool AVPlayer::load(bool reload)
{
// TODO: call unload if reload?
if (mediaStatus() == QtAV::LoadingMedia)
return true;
if (isLoaded()) {
// release codec ctx. if not loaded, they are released by avformat. TODO: always let avformat release them?
if (d->adec)
d->adec->setCodecContext(0);
if (d->vdec)
d->vdec->setCodecContext(0);
}
d->loaded = false;
if (!d->current_source.isValid()) {
qDebug("Invalid media source. No file or IODevice was set.");
return false;
}
if (!reload) {
if (d->current_source.type() == QVariant::String) {
reload = d->demuxer.fileName() != d->current_source.toString();
} else {
if (d->current_source.canConvert<QIODevice*>()) {
reload = d->demuxer.ioDevice() != d->current_source.value<QIODevice*>();
} else { // MediaIO
reload = d->demuxer.mediaIO() != d->current_source.value<QtAV::MediaIO*>();
}
}
reload = reload || !d->demuxer.isLoaded();
}
if (reload) {
d->status = LoadingMedia;
if (isAsyncLoad()) {
class LoadWorker : public QRunnable {
public:
LoadWorker(AVPlayer *player) : m_player(player) {}
virtual void run() {
if (!m_player)
return;
m_player->loadInternal();
}
private:
AVPlayer* m_player;
};
// TODO: thread pool has a max thread limit
loaderThreadPool()->start(new LoadWorker(this));
return true;
}
loadInternal();
} else {
d->loaded = true;
}
return d->loaded;
}

void AVPlayer::loadInternal()
{
QMutexLocker lock(&d->load_mutex);
Expand Down Expand Up @@ -752,14 692,6 @@ void AVPlayer::unload()
Q_UNUSED(lock);
d->loaded = false;
d->demuxer.setInterruptStatus(-1);
/*
* FIXME: no a/v/d thread started and in LoadedMedia status when stop(). but now is running (and to using the decoder).
* then we must wait the threads finished. or use smart ptr for decoders? demuxer is still unsafe
* change to LoadedMedia until all threads started?
* will it happen if playInternal() and unloadInternal() in ui thread?(use singleShort())
*/
if (isPlaying())
stop();

if (d->adec) { // FIXME: crash if audio external=>internal then replay
d->adec->setCodecContext(0);
Expand Down Expand Up @@ -1157,46 1089,62 @@ void AVPlayer::setState(State value)

}

//FIXME: why no d->demuxer will not get an eof if replaying by seek(0)?
bool AVPlayer::load()
{
if (!d->current_source.isValid()) {
qDebug("Invalid media source. No file or IODevice was set.");
return false;
}
if (!d->checkSourceChange() && (mediaStatus() == QtAV::LoadingMedia || mediaStatus() == LoadedMedia))
return true;
if (isLoaded()) {
// release codec ctx. if not loaded, they are released by avformat. TODO: always let avformat release them?
if (d->adec)
d->adec->setCodecContext(0);
if (d->vdec)
d->vdec->setCodecContext(0);
}
d->loaded = false;
d->status = LoadingMedia;
if (!isAsyncLoad()) {
loadInternal();
return d->loaded;
}

class LoadWorker : public QRunnable {
public:
LoadWorker(AVPlayer *player) : m_player(player) {}
virtual void run() {
if (!m_player)
return;
m_player->loadInternal();
}
private:
AVPlayer* m_player;
};
// TODO: thread pool has a max thread limit
loaderThreadPool()->start(new LoadWorker(this));
return true;
}

void AVPlayer::play()
{
//FIXME: bad delay after play from here
bool start_last = d->last_position == -1;
if (isPlaying()) {
qDebug("play() when playing");
if (start_last) {
d->clock->pause(true); //external clock
d->last_position = mediaStopPosition() != kInvalidPosition ? -position() : 0;
d->reset_state = false; //set a new stream number should not reset other states
qDebug("start pos is current position: %lld", d->last_position);
} else {
d->last_position = 0;
qDebug("start pos is stream start time");
}
qint64 last_pos = d->last_position;
if (!d->checkSourceChange())
return;
stop();
// wait here to ensure stopped() and startted() is in correct order
if (d->read_thread->isRunning()) {
d->read_thread->wait(500);
}
if (last_pos < 0)
d->last_position = -last_pos;
}
disconnect(this, SIGNAL(loaded()), this, SLOT(playInternal()));
unload();
loadAndPlay();
// seek(0LL) instead of reload for loaded seekable stream?
}

void AVPlayer::loadAndPlay()
{
if (isAsyncLoad()) {
connect(this, SIGNAL(loaded()), this, SLOT(playInternal()));
load(true);
if (!load()) {
qWarning("load error");
return;
}
if (isLoaded()) { // !asyncLoad() is here
playInternal();
return;
}
loadInternal();
playInternal();
connect(this, SIGNAL(loaded()), this, SLOT(playInternal()));
}

void AVPlayer::playInternal()
Expand Down
10 changes: 10 additions & 0 deletions src/AVPlayerPrivate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 25,7 @@
#include "QtAV/AudioDecoder.h"
#include "QtAV/AudioFormat.h"
#include "QtAV/AudioResampler.h"
#include "QtAV/MediaIO.h"
#include "QtAV/VideoCapture.h"
#include "QtAV/private/AVCompat.h"
#include "utils/Logger.h"
Expand Down Expand Up @@ -164,6 165,15 @@ AVPlayer::Private::~Private() {
}
}

bool AVPlayer::Private::checkSourceChange()
{
if (current_source.type() == QVariant::String)
return demuxer.fileName() != current_source.toString();
if (current_source.canConvert<QIODevice*>())
return demuxer.ioDevice() != current_source.value<QIODevice*>();
return demuxer.mediaIO() != current_source.value<QtAV::MediaIO*>();
}

void AVPlayer::Private::updateNotifyInterval()
{
if (notify_interval <= 0) {
Expand Down
1 change: 1 addition & 0 deletions src/AVPlayerPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 38,7 @@ class AVPlayer::Private
Private();
~Private();

bool checkSourceChange();
void updateNotifyInterval();
void initStatistics();
void initBaseStatistics();
Expand Down
54 changes: 31 additions & 23 deletions src/QtAV/AVPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 44,21 @@ class Filter;
class AudioFilter;
class VideoFilter;
class VideoCapture;

/*!
* \brief The AVPlayer class
* Preload:
* \code
* player->setFile(...);
* player->load();
* do some thing...
* player->play();
* \endcode
* No preload:
* \code
* player->setFile(...);
* player->play();
* \endcode
*/
class Q_AV_EXPORT AVPlayer : public QObject
{
Q_OBJECT
Expand Down Expand Up @@ -110,25 124,7 @@ class Q_AV_EXPORT AVPlayer : public QObject
void setInput(MediaIO* in);
MediaIO* input() const;

// force reload even if already loaded. otherwise only reopen codecs if necessary
QTAV_DEPRECATED bool load(const QString& path, bool reload = true); //deprecated
QTAV_DEPRECATED bool load(bool reload); //deprecated
//
bool isLoaded() const;
/*!
* \brief load
* Load the current media set by setFile();. If already loaded, does nothing and return true.
* If async load, mediaStatus() becomes LoadingMedia and user should connect signal loaded()
* or mediaStatusChanged(QtAV::LoadedMedia) to a slot
* \return true if success or already loaded.
*/
bool load(); //NOT implemented.
/*!
* \brief unload
* If the media is loading or loaded but not playing, unload it.
* Does nothing if isPlaying()
*/
void unload(); //TODO: emit signal?
/*!
* \brief setAsyncLoad
* async load is enabled by default
Expand Down Expand Up @@ -389,13 385,22 @@ class Q_AV_EXPORT AVPlayer : public QObject
void setMediaEndAction(MediaEndAction value);

public slots:
/*!
* \brief load
* Load the current media set by setFile(); Can be used to reload a media and call play() later. If already loaded, does nothing and return true.
* If async load, mediaStatus() becomes LoadingMedia and user should connect signal loaded()
* or mediaStatusChanged(QtAV::LoadedMedia) to a slot
* \return true if success or already loaded.
*/
bool load();

void togglePause();
void pause(bool p = true);
/*!
* \brief play
* Load media and start playback. The same as play(const QString&)
* Load media and start playback. If current media is playing and media source is not changed, nothing to do. If media source is not changed, try to load (not in LoadingStatus or LoadedStatus) and start playback. If media source changed, reload and start playback.
*/
void play(); //replay
void play();
/*!
* \brief stop
* Stop playback. It blocks current thread until the playback is stopped. Will emit signal stopped(). startPosition(), stopPosition(), repeat() are reset
Expand Down Expand Up @@ -562,7 567,6 @@ public slots:
private Q_SLOTS:
void loadInternal(); // simply load
void playInternal(); // simply play
void loadAndPlay();
void stopFromDemuxerThread();
void aboutToQuitApp();
// start/stop notify timer in this thread. use QMetaObject::invokeMethod
Expand All @@ -575,8 579,12 @@ private Q_SLOTS:
protected:
// TODO: set position check timer interval
virtual void timerEvent(QTimerEvent *);

private:
/*!
* \brief unload
* If the media is loading or loaded but not playing, unload it. Internall use only.
*/
void unload(); //TODO: private. call in stop() if not load() by user? or always unload() in stop()?
qint64 normalizedPosition(qint64 pos);
class Private;
QScopedPointer<Private> d;
Expand Down

0 comments on commit 20284f4

Please sign in to comment.