#include "aniplayer.h"
+#include <QTimerEvent>
+#include <QSettings>
-AniPlayer::AniPlayer(QObject *parent) : QObject(parent), m_state(Stopped)
+AniPlayer::AniPlayer(QObject *parent) : QObject(parent), m_state(NoFileLoaded)
{
}
return m_currentFile;
}
+qint64 AniPlayer::tickInterval() const
+{
+ return m_tickInterval;
+}
+
+ChapterList AniPlayer::chapters() const
+{
+ return m_chapters;
+}
+
+Chapter AniPlayer::chapter(int i) const
+{
+ return m_chapters[i];
+}
+
+StreamList AniPlayer::streams() const
+{
+ return m_streams;
+}
+
+Stream *AniPlayer::stream(int i) const
+{
+ return m_streams[i];
+}
+
bool AniPlayer::open(const QString &file)
{
if (file == m_currentFile)
m_currentFile = file;
emit currentFileChanged(m_currentFile);
emit totalTimeChanged(totalTime());
+ emit videoSizeChanged(videoSize());
return true;
}
void AniPlayer::play()
{
if (state() == Paused || state() == Stopped)
- if (iplay())
- setState(Playing);
+ if (iplay())
+ setState(Playing);
}
void AniPlayer::pause()
void AniPlayer::togglePause()
{
- if (state() == Paused)
- play();
- else
+ if (state() == Playing)
pause();
+ else
+ play();
}
bool AniPlayer::seek(qint64 position)
seek(currentTime() + msec);
}
+bool AniPlayer::seek(const Chapter &chapter)
+{
+ return seek(chapter.time);
+}
+
+bool AniPlayer::seekToChapter(int i)
+{
+ return seek(m_chapters[i].time);
+}
+
bool AniPlayer::setVolume(double percent)
{
- return isetVolume(percent);
+ if(!isetVolume(percent))
+ return false;
+
+ emit volumeChanged(percent);
+ return true;
}
void AniPlayer::volumeUp(int by)
setVolume(newVolume);
}
+void AniPlayer::setMuted(bool muted)
+{
+ if (isetMuted(muted))
+ emit mutedChanged(muted);
+}
+
+void AniPlayer::setTickInterval(qint64 tickInterval)
+{
+ if (m_tickInterval != tickInterval) {
+ m_tickInterval = tickInterval;
+ emit tickIntervalChanged(tickInterval);
+ }
+}
+
+bool AniPlayer::changeToStream(Stream *stream)
+{
+ return ichangeToStream(stream);
+}
+
+bool AniPlayer::changeToStream(int i)
+{
+ return changeToStream(m_streams[i]);
+}
+
+void AniPlayer::timerEvent(QTimerEvent *e)
+{
+ if (e->timerId() != tickTimer.timerId())
+ return;
+
+ const qint64 ct = currentTime();
+ if (ct < lastTick + m_tickInterval)
+ return;
+
+ lastTick = ct;
+ emit tick(ct);
+}
+
+bool AniPlayer::seekInternal(qint64 position)
+{
+ qint64 ret = seek(position);
+ emit currentTimeChanged(position);
+ return ret;
+}
void AniPlayer::setState(AniPlayer::State newState)
{
if (m_state == newState)
return;
+ if (newState == Playing)
+ {
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ tickTimer.start(TICK_TIMER_INTERVAL, this);
+#else
+ tickTimer.start(TICK_TIMER_INTERVAL, Qt::PreciseTimer, this);
+#endif
+ lastTick = 0;
+ }
+ else
+ tickTimer.stop();
+
State oldState = m_state;
m_state = newState;
emit stateChanged(newState);
emit stateChanged(newState, oldState);
}
+
+void AniPlayer::setChapters(const ChapterList &chapters)
+{
+ m_chapters = chapters;
+ emit chaptersChanged(chapters);
+}
+
+void AniPlayer::setStreams(const StreamList &streams)
+{
+ qDeleteAll(m_streams);
+ m_streams = streams;
+ emit streamsChanged(streams);
+}
+
+
+void AniPlayer::fileFinished()
+{
+ ifileFinished();
+ setState(Stopped);
+ emit playbackFinished();
+}
+
#include <QObject>
#include <QString>
#include <QSize>
+#include <QBasicTimer>
class VideoWidget;
+class QTimerEvent;
+
+struct ANIPLAYER2SHARED_EXPORT Chapter
+{
+ QString name;
+ qint64 time;
+};
+typedef QList<Chapter> ChapterList;
+
+enum StreamType
+{
+ VideoStream,
+ AudioStream,
+ SubtitleStream,
+ OtherStream
+};
+
+struct ANIPLAYER2SHARED_EXPORT Stream
+{
+ QString name;
+ StreamType type;
+ QString description;
+};
+typedef QList<Stream *> StreamList;
class ANIPLAYER2SHARED_EXPORT AniPlayer : public QObject
{
Q_PROPERTY(qint64 currentTime READ currentTime WRITE seek STORED false)
Q_PROPERTY(qint64 totalTime READ totalTime STORED false)
Q_PROPERTY(double volume READ volume WRITE setVolume NOTIFY volumeChanged STORED false)
+ Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(QSize videoSize READ videoSize)
Q_PROPERTY(QString currentFile READ currentFile NOTIFY currentFileChanged)
+ Q_PROPERTY(qint64 tickInterval READ tickInterval WRITE setTickInterval NOTIFY tickIntervalChanged)
+ Q_PROPERTY(ChapterList chapters READ chapters NOTIFY chaptersChanged)
+ Q_PROPERTY(StreamList streams READ streams NOTIFY streamsChanged)
public:
enum State
{
+ NoFileLoaded,
Stopped,
Playing,
Paused,
virtual qint64 totalTime() const = 0;
virtual double volume() const = 0;
+ virtual bool isMuted() const = 0;
virtual QSize videoSize() const = 0;
QString currentFile() const;
+ qint64 tickInterval() const;
+
+ ChapterList chapters() const;
+ Chapter chapter(int i) const;
+
+ StreamList streams() const;
+ Stream *stream(int i) const;
+
public slots:
bool open(const QString &file);
void play();
bool seek(qint64 position);
void skip(qint64 msec = 85000);
+ bool seek(const Chapter &chapter);
+ bool seekToChapter(int i);
bool setVolume(double percent);
void volumeUp(int by = 5);
void volumeDown(int by = 5);
void changeVolume(int by = 5);
+ void setMuted(bool muted);
+
+ void setTickInterval(qint64 tickInterval);
+
+ bool changeToStream(Stream *stream);
+ bool changeToStream(int i);
signals:
void stateChanged(AniPlayer::State newState);
void stateChanged(AniPlayer::State newState, AniPlayer::State oldState);
void volumeChanged(double newPercent);
+ void mutedChanged(bool muted);
void currentFileChanged(QString file);
+ void videoSizeChanged(QSize newSize);
+ void currentTimeChanged(qint64 newCurrentTime);
void totalTimeChanged(qint64 newTotalTime);
+ void playbackFinished();
+
+ void tick(qint64 current);
+ void tickIntervalChanged(qint64 arg);
+ void chaptersChanged(ChapterList chapters);
+ void streamsChanged(StreamList streams);
+
+protected slots:
+ void fileFinished();
+
protected:
+ // Don't call i* methods directly.
+ // Call the versions without i.
+
+ // Open file
virtual bool iopen(const QString &file) = 0;
+
+ // Playback
virtual bool iplay() = 0;
virtual bool ipause() = 0;
virtual bool istop() = 0;
+ virtual void ifileFinished() {}
+ // Settings
virtual bool iseek(qint64) = 0;
virtual bool isetVolume(double) = 0;
+ virtual bool isetMuted(bool) = 0;
+ virtual bool ichangeToStream(Stream *stream) = 0;
+
+ void timerEvent(QTimerEvent *);
+
+ bool seekInternal(qint64 position);
void setState(State newState);
+ void setChapters(const ChapterList &chapters);
+ void setStreams(const StreamList &streams);
State m_state;
QString m_currentFile;
+
+ ChapterList m_chapters;
+ StreamList m_streams;
+
+ qint64 m_tickInterval;
+ qint64 lastTick;
+ QBasicTimer tickTimer;
+ static const int TICK_TIMER_INTERVAL = 16;
};
#endif // ANIPLAYER_H
DEPENDPATH += $$PWD
LIBS += -laniplayer2
LIBS += -L$$PWD/../build
+
+include(../qtsingleapplication/qtsingleapplication.pri)
DEFINES += ANIPLAYER2_LIBRARY
-SOURCES += aniplayer.cpp
-
HEADERS += aniplayer2_global.h \
- aniplayer.h\
- videowidget.h
-
+ constants.h \
+ aniplayerapplication.h \
+ aniplayer.h \
+ videowidget.h \
+ volumeslider.h
-HEADERS += \
+SOURCES += aniplayer.cpp \
+ videowidget.cpp \
+ aniplayerapplication.cpp \
+ volumeslider.cpp
-SOURCES += \
- videowidget.cpp
-
include(../config.pri)
+include(../qtsingleapplication/qtsingleapplication.pri)
+
+REV = $$system(git show-ref --head -s HEAD)
+DEFINES += REVISION=\"$${REV}\"
#include "aniplayerapplication.h"
+#include <QSettings>
+#include "constants.h"
+
AniPlayerApplication::AniPlayerApplication(int &argc, char **argv) :
QtSingleApplication(argc, argv)
{
+ QSettings::setDefaultFormat(QSettings::IniFormat);
+ setApplicationName(::applicationName);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ setApplicationDisplayName(::applicationName);
+#endif
+ setApplicationVersion(::applicationVersion);
+ setOrganizationName(::organizationName);
+}
+
+void AniPlayerApplication::handleMessage(const QString &message)
+{
+ if (message.left(4) != "open")
+ return;
+
+ QString file;
+
+ int pos = -1;
+ if ((pos = message.indexOf(' ')) != -1)
+ file = message.mid(pos + 1);
+
+ if (!file.isEmpty())
+ emit openFileRequested(file);
+}
+
+const char * const AniPlayerApplication::revision()
+{
+#ifndef STRINGIFY
+# define STRINGIFY_INTERNAL(x) #x
+# define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#endif
+
+#if defined(REVISION)
+ static const char *const revisionString = STRINGIFY(REVISION);
+#else
+ static const char *const revisionString = "Unknown revision";
+#endif
+ return revisionString;
}
#include "aniplayer2_global.h"
#include <QtSingleApplication>
-class AniPlayerApplication : public QtSingleApplication
+class AniPlayer;
+
+#ifdef qApp
+# undef qApp
+#endif
+#define qApp (AniPlayerApplication::instance())
+
+class ANIPLAYER2SHARED_EXPORT AniPlayerApplication : public QtSingleApplication
{
Q_OBJECT
public:
explicit AniPlayerApplication(int &argc, char **argv);
signals:
+ void openFileRequested(const QString &file);
public slots:
+ void handleMessage(const QString &message);
+
+public:
+ inline static const AniPlayerApplication *instance()
+ {
+ return static_cast<AniPlayerApplication *>(QtSingleApplication::instance());
+ }
+ static const char *const revision();
};
#endif // ANIPLAYERAPPLICATION_H
--- /dev/null
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+static const char *applicationName = "AniPlayer";
+static const char *applicationVersion = "2.0.0B1";
+static const char *organizationName = "APTX";
+
+#endif // CONSTANTS_H
--- /dev/null
+#include "volumeslider.h"
+
+#include <QSlider>
+#include <QPushButton>
+#include <QHBoxLayout>
+#include <QStyle>
+
+VolumeSlider::VolumeSlider(QWidget *parent) :
+ QWidget(parent)
+{
+ QHBoxLayout *layout = new QHBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+
+ muteButton = new QPushButton;
+ muteButton->setCheckable(true);
+ muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume));
+
+ layout->addWidget(muteButton);
+
+ slider = new QSlider;
+ slider->setOrientation(Qt::Horizontal);
+ slider->setMinimum(0);
+ slider->setMaximum(10000);
+
+ layout->addWidget(slider);
+
+ connect(muteButton, SIGNAL(clicked(bool)), this, SLOT(setMutedByUser(bool)));
+ connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(setVolumeByUser(int)));
+}
+
+double VolumeSlider::volume() const
+{
+ return double(slider->value()) / 10000;
+}
+
+bool VolumeSlider::isMuted() const
+{
+ return muteButton->isChecked();
+}
+
+
+void VolumeSlider::setVolume(double newVolume)
+{
+ if (volume() == newVolume)
+ return;
+
+ slider->setValue(int(10000 * newVolume));
+ emit volumeChanged(newVolume);
+}
+
+void VolumeSlider::setMuted(bool mute)
+{
+ if (isMuted() == mute)
+ return;
+
+ muteButton->setChecked(mute);
+ if (mute)
+ muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolumeMuted));
+ else
+ muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume));
+
+ emit mutedChanged(mute);
+}
+
+void VolumeSlider::setMutedByUser(bool mute)
+{
+ if (mute)
+ muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolumeMuted));
+ else
+ muteButton->setIcon(style()->standardIcon(QStyle::SP_MediaVolume));
+ emit mutedChangedByUser(mute);
+}
+
+void VolumeSlider::setVolumeByUser(int newVolume)
+{
+ emit volumeChangedByUser(double(newVolume) / 10000);
+}
--- /dev/null
+#ifndef VOLUMESLIDER_H
+#define VOLUMESLIDER_H
+
+#include "aniplayer2_global.h"
+#include <QWidget>
+
+class QSlider;
+class QPushButton;
+
+class ANIPLAYER2SHARED_EXPORT VolumeSlider : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(double volume READ volume WRITE setVolume NOTIFY volumeChanged USER true)
+ Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
+
+
+public:
+ explicit VolumeSlider(QWidget *parent = 0);
+
+ double volume() const;
+ bool isMuted() const;
+
+signals:
+ void volumeChanged(double volume);
+ void volumeChangedByUser(double volume);
+
+ void mutedChanged(bool muted);
+ void mutedChangedByUser(bool muted);
+
+public slots:
+ void setVolume(double newVolume);
+ void setVolumeByUser(int newVolume);
+
+ void setMuted(bool mute);
+ void setMutedByUser(bool mute);
+
+private:
+ QSlider *slider;
+ QPushButton *muteButton;
+};
+
+#endif // VOLUMESLIDER_H
include(../config.pri)
include(../aniplayer2/aniplayer2.pri)
-LIBS += -lstrmiids -lole32
+LIBS += -lstrmiids -lole32 -lOleAut32 -lUser32
AniPlayerDShow::AniPlayerDShow(QObject *parent) :
AniPlayer(parent)
{
- HRESULT hr;
-
- hr = CoInitialize(NULL);
- if (FAILED(hr))
- qDebug() << "Failed to initialize COM";
-
m_videoWidget = new VideoWidgetDShow(this);
d = new AniPlayerDShowInternal(this);
}
AniPlayerDShow::~AniPlayerDShow()
{
- CoUninitialize();
+ delete d;
+ delete m_videoWidget;
}
QSize AniPlayerDShow::videoSize() const
return d->volume();
}
+bool AniPlayerDShow::isMuted() const
+{
+ return d->isMuted();
+}
+
bool AniPlayerDShow::iopen(const QString &file)
{
if (!d->OpenFile(reinterpret_cast<const wchar_t *>(file.utf16())))
bool AniPlayerDShow::istop()
{
- return d->stop();
+ bool ret = d->stop();
+ if (ret)
+ seekInternal(0);
+ return ret;
}
bool AniPlayerDShow::iseek(qint64 position)
{
return d->setVolume(volume);
}
+
+bool AniPlayerDShow::isetMuted(bool muted)
+{
+ return d->setMuted(muted);
+}
+
+bool AniPlayerDShow::ichangeToStream(Stream *stream)
+{
+ return d->changeToStream(static_cast<StreamInternal *>(stream));
+}
+
+void AniPlayerDShow::ifileFinished()
+{
+ istop();
+}
class ANIPLAYER2_DSHOWSHARED_EXPORT AniPlayerDShow : public AniPlayer
{
+ friend struct AniPlayerDShowInternal;
Q_OBJECT
public:
explicit AniPlayerDShow(QObject *parent = 0);
qint64 totalTime() const;
double volume() const;
+ bool isMuted() const;
signals:
bool iseek(qint64 position);
bool isetVolume(double volume);
+ bool isetMuted(bool muted);
+ bool ichangeToStream(Stream *stream);
+ void ifileFinished();
private:
AniPlayerDShowInternal *d;
#include "videowidgetdshow.h"
#include "aniplayerdshow.h"
-#include <Dshow.h>
+#include <dshow.h>
+#include <initguid.h>
+#include <qnetwork.h>
+
#include <Evr.h>
+#include <QDebug>
+
HRESULT IsPinConnected(IPin *pPin, BOOL *pResult);
HRESULT IsPinDirection(IPin *pPin, PIN_DIRECTION dir, BOOL *pResult);
HRESULT AddFilterByCLSID(IGraphBuilder *pGraph, REFGUID clsid, IBaseFilter **ppF, LPCWSTR wszName);
HRESULT FindConnectedPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin);
+void _FreeMediaType(AM_MEDIA_TYPE& mt);
+void _DeleteMediaType(AM_MEDIA_TYPE *pmt);
+
+DEFINE_GUID(MEDIATYPE_Subtitle, 0xE487EB08, 0x6B26, 0x4be9, 0x9D, 0xD3, 0x99, 0x34, 0x34, 0xD3, 0x13, 0xFD);
+
AniPlayerDShowInternal::AniPlayerDShowInternal(AniPlayerDShow *player_) : player(player_)
{
pGraph = 0;
pVideoDisplay = 0;
pAudioControl = 0;
+
+ mutedVolume = -1;
+
+ HRESULT hr;
+
+ hr = CoInitialize(NULL);
+ if (FAILED(hr))
+ qDebug() << "Failed to initialize COM";
+
}
AniPlayerDShowInternal::~AniPlayerDShowInternal()
{
TearDownGraph();
+
+ CoUninitialize();
}
bool AniPlayerDShowInternal::play()
qint64 AniPlayerDShowInternal::totalTime() const
{
+ if (!pSeeking)
+ return -1;
+
HRESULT hr = pSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
if (FAILED(hr))
return -1;
qint64 AniPlayerDShowInternal::currentTime() const
{
+ if (!pSeeking)
+ return -1;
+
HRESULT hr = pSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
if (FAILED(hr))
return -1;
bool AniPlayerDShowInternal::seek(qint64 position)
{
+ if (!pSeeking)
+ return false;
+
// Positions are in 100-nanoseconds, convert from miliseconds
position *= 10000;
double AniPlayerDShowInternal::volume() const
{
+ if (!pAudioControl)
+ return -1;
+
+ if (isMuted())
+ return mutedVolume;
+
long lvolume;
HRESULT hr = pAudioControl->get_Volume(&lvolume);
bool AniPlayerDShowInternal::setVolume(double volume)
{
+ if (!pAudioControl)
+ return false;
+
+ if (isMuted())
+ {
+ mutedVolume = volume;
+ return true;
+ }
+
volume = 1 - volume;
long lvolume = -long(volume * 10000.0);
return true;
}
+bool AniPlayerDShowInternal::isMuted() const
+{
+ return mutedVolume >= 0;
+}
+
+bool AniPlayerDShowInternal::setMuted(bool muted)
+{
+ if (muted == isMuted())
+ return true;
+
+ if (muted)
+ {
+ double currentVolume = volume();
+
+ if (currentVolume < 0)
+ return false;
+
+ if (!setVolume(0))
+ return false;
+
+ mutedVolume = currentVolume;
+ return true;
+ }
+
+ double newVolume = mutedVolume;
+ mutedVolume = -1;
+
+ if (setVolume(newVolume))
+ return true;
+
+ mutedVolume = newVolume;
+ return false;
+}
+
+bool AniPlayerDShowInternal::changeToStream(StreamInternal *stream)
+{
+ Q_ASSERT(streamFilters.contains(stream->filter));
+
+ IAMStreamSelect *pStreamSelect = 0;
+ HRESULT hr = stream->filter->QueryInterface(IID_PPV_ARGS(&pStreamSelect));
+ if (SUCCEEDED(hr))
+ hr = pStreamSelect->Enable(stream->streamNo, AMSTREAMSELECTENABLE_ENABLE);
+ SafeRelease(&pStreamSelect);
+
+ return SUCCEEDED(hr);
+}
+
void AniPlayerDShowInternal::handleNotifications()
{
+ if (!pEvent)
+ return;
+
+ long evCode;
+ LONG_PTR param1, param2;
+ while (SUCCEEDED(pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0)))
+ {
+ pEvent->FreeEventParams(evCode, param1, param2);
+ switch (evCode)
+ {
+ case EC_COMPLETE: // Fall through.
+ case EC_USERABORT: // Fall through.
+ case EC_ERRORABORT:
+ player->fileFinished();
+ return;
+ case EC_LENGTH_CHANGED:
+ emit player->totalTimeChanged(totalTime());
+ break;
+ case EC_VIDEO_SIZE_CHANGED:
+ emit player->videoSizeChanged(videoSize());
+ }
+ }
}
HRESULT AniPlayerDShowInternal::InitializeGraph()
{
goto done;
}
-/*
+
// Set up event notification.
- hr = pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
+ hr = pEvent->SetNotifyWindow((OAHWND)hwnd(), WM_APP + 1, NULL);
if (FAILED(hr))
{
goto done;
}
-*/
+
// m_state = STATE_STOPPED;
done:
void AniPlayerDShowInternal::TearDownGraph()
{
-/*
+
// Stop sending event messages
if (pEvent)
{
pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
}
-*/
+
+ for (auto i = streamFilters.constBegin(); i != streamFilters.constEnd(); ++i)
+ SafeRelease(&const_cast<IBaseFilter *>(*i));
+ streamFilters.clear();
SafeRelease(&pGraph);
SafeRelease(&pControl);
SafeRelease(&pEvent);
// Try to render the streams.
hr = RenderStreams(pSource);
+ {
+ IAMExtendedSeeking *pExtendedSeeking = 0;
+
+ HRESULT hr = pSource->QueryInterface(IID_IAMExtendedSeeking, IID_PPV_ARGS_Helper(&pExtendedSeeking));
+
+ ChapterList chapters;
+ if (SUCCEEDED(hr))
+ {
+ long count;
+ hr = pExtendedSeeking->get_MarkerCount(&count);
+
+ // These "markers" actually start at 1
+ for (long i = 1; i <= count; ++i)
+ {
+ BSTR name = 0;
+ double time;
+ Chapter chapter;
+
+ hr = pExtendedSeeking->GetMarkerName(i, &name);
+
+ if (SUCCEEDED(hr))
+ chapter.name = QString::fromUtf16((ushort *)name);
+
+ SysFreeString(name);
+ if (FAILED(hr))
+ continue;
+
+ // Time is returned in seconds
+ hr = pExtendedSeeking->GetMarkerTime(i, &time);
+ if (FAILED(hr))
+ continue;
+
+ chapter.time = qint64(time * 1000.0);
+
+ chapters << chapter;
+ }
+ player->setChapters(chapters);
+ }
+
+ SafeRelease(&pExtendedSeeking);
+ }
+
+ {
+ StreamList streams;
+ IEnumFilters *enumFilters = 0;
+
+ HRESULT hr = pGraph->EnumFilters(&enumFilters);
+
+ if (SUCCEEDED(hr))
+ {
+ IBaseFilter *filter = 0;
+ while (enumFilters->Next(1, &filter, NULL) == S_OK)
+ {
+ IAMStreamSelect *pStreamSelect = 0;
+ HRESULT hr = filter->QueryInterface(IID_PPV_ARGS(&pStreamSelect));
+
+ if (SUCCEEDED(hr))
+ {
+ DWORD streamCount;
+ hr = pStreamSelect->Count(&streamCount);
+
+ if (SUCCEEDED(hr))
+ {
+ for (long streamNo = 0; streamNo < (long)streamCount; ++streamNo)
+ {
+ AM_MEDIA_TYPE *mediaType = 0;
+ DWORD pdwFlags;
+ LCID lcid;
+ DWORD pdwGroup;
+ WCHAR *wname = 0;
+
+ QString name;
+
+ hr = pStreamSelect->Info(streamNo, &mediaType, &pdwFlags, &lcid, &pdwGroup, &wname, NULL, NULL);
+
+ if (mediaType)
+ _DeleteMediaType(mediaType);
+
+ if (SUCCEEDED(hr))
+ name = QString::fromUtf16((ushort *) wname);
+
+ if (wname)
+ CoTaskMemFree(wname);
+
+ if (FAILED(hr))
+ continue;
+
+ filter->AddRef();
+ streamFilters << filter;
+
+ StreamType type;
+
+ if (IsEqualGUID(mediaType->majortype, MEDIATYPE_Video))
+ type = VideoStream;
+ else if (IsEqualGUID(mediaType->majortype, MEDIATYPE_Audio))
+ type = AudioStream;
+ else if (IsEqualGUID(mediaType->majortype, MEDIATYPE_Subtitle)
+ || IsEqualGUID(mediaType->majortype, MEDIATYPE_Text))
+ type = SubtitleStream;
+ else
+ type = OtherStream;
+
+
+
+ StreamInternal *stream = new StreamInternal;
+ stream->name = name;
+ stream->type = type;
+ if (lcid)
+ {
+ WCHAR wdesc[85];
+ int rBytes = GetLocaleInfo(lcid, LOCALE_SNAME, wdesc, sizeof(wdesc));
+ if (rBytes)
+ stream->description = QString::fromUtf16((ushort *)wdesc);
+ }
+ stream->streamNo = streamNo;
+ stream->filter = filter;
+
+ streams << stream;
+// qDebug() << "Stream" << streamNo << name << "Group:" << pdwGroup << "LCID:" << QString::number(lcid, 16) << stream->description;
+ }
+ }
+ }
+ SafeRelease(&pStreamSelect);
+ SafeRelease(&filter);
+ }
+ }
+ SafeRelease(&enumFilters);
+
+ player->setStreams(streams);
+ }
+
+
done:
if (FAILED(hr))
{
return hr;
}
+HWND AniPlayerDShowInternal::hwnd() const
+{
+ return (HWND) player->videoWidget()->winId();
+}
+
+
+HRESULT AniPlayerDShowInternal::FinalizeGraph()
+{
+ if (pEVR == NULL)
+ {
+ return S_OK;
+ }
+
+ BOOL bRemoved;
+ HRESULT hr = RemoveUnconnectedRenderer(pGraph, pEVR, &bRemoved);
+ if (bRemoved)
+ {
+ SafeRelease(&pEVR);
+ SafeRelease(&pVideoDisplay);
+ }
+ return hr;
+}
+
HRESULT IsPinConnected(IPin *pPin, BOOL *pResult)
{
IPin *pTmp = NULL;
return hr;
}
-HWND AniPlayerDShowInternal::hwnd() const
-{
- return (HWND) player->videoWidget()->winId();
-}
-
-
-HRESULT AniPlayerDShowInternal::FinalizeGraph()
+void _FreeMediaType(AM_MEDIA_TYPE& mt)
{
- if (pEVR == NULL)
+ if (mt.cbFormat != 0)
{
- return S_OK;
+ CoTaskMemFree((PVOID)mt.pbFormat);
+ mt.cbFormat = 0;
+ mt.pbFormat = NULL;
+ }
+ if (mt.pUnk != NULL)
+ {
+ // pUnk should not be used.
+ mt.pUnk->Release();
+ mt.pUnk = NULL;
}
+}
- BOOL bRemoved;
- HRESULT hr = RemoveUnconnectedRenderer(pGraph, pEVR, &bRemoved);
- if (bRemoved)
+void _DeleteMediaType(AM_MEDIA_TYPE *pmt)
+{
+ if (pmt != NULL)
{
- SafeRelease(&pEVR);
- SafeRelease(&pVideoDisplay);
+ _FreeMediaType(*pmt);
+ CoTaskMemFree(pmt);
}
- return hr;
}
#include <windows.h>
-class AniPlayerDShow;
+#include "aniplayerdshow.h"
struct IBaseFilter;
struct IGraphBuilder;
struct IMediaControl;
-struct IMediaEvent;
+struct IMediaEventEx;
struct IMediaSeeking;
struct IMFVideoDisplayControl;
struct IBasicAudio;
+struct StreamInternal : public Stream
+{
+ int streamNo;
+ IBaseFilter *filter;
+};
+
struct AniPlayerDShowInternal
{
public:
double volume() const;
bool setVolume(double volume);
+ bool isMuted() const;
+ bool setMuted(bool muted);
+
+ bool changeToStream(StreamInternal *stream);
void handleNotifications();
IGraphBuilder *pGraph;
IMediaControl *pControl;
- IMediaEvent *pEvent;
+ IMediaEventEx *pEvent;
IMediaSeeking *pSeeking;
IBaseFilter *pEVR;
HWND hwnd() const;
AniPlayerDShow *player;
+
+ QList<IBaseFilter *> streamFilters;
+
+ double mutedVolume;
};
template <class T> void SafeRelease(T **ppT)
# include <windows.h>
#endif
+#include <QDebug>
+
VideoWidgetDShow::VideoWidgetDShow(AniPlayerDShow *player_, QWidget *parent) :
VideoWidget(parent), player(player_)
{
bool VideoWidgetDShow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#ifdef Q_OS_WIN
- if (eventType != "windows_generic_MSG")
- return false;
-
+ // This is the only type of event on windows.
+ Q_UNUSED(eventType)
+// if (eventType != "windows_generic_MSG")
+// return false;
MSG *msg = static_cast<MSG *>(message);
if (msg->message != WM_APP + 1)
return false;
-// player->ha
+ player->handleNotifications();
*result = 0;
return true;
#else
--- /dev/null
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level='asInvoker' uiAccess='false' />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*' />
+ </dependentAssembly>
+ </dependency>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ </application>
+ </compatibility>
+</assembly>
--- /dev/null
+<RCC>
+ <qresource prefix="/"/>
+</RCC>
--- /dev/null
+1 TYPELIB "aniplayer.rc"
+IDI_ICON1 ICON DISCARDABLE "../resource/aniplayer-mikuru.ico"
+1 24 "aniplayer.exe.manifest"
+1 VERSIONINFO
+ FILEVERSION 2,0,0,0
+ PRODUCTVERSION 2,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "APTX\0"
+ VALUE "FileDescription", "aniplayer\0"
+ VALUE "FileExtents", "xxx\0"
+ VALUE "FileOpenName", "Video Files (*.*)\0"
+ VALUE "FileVersion", "2, 0, 0, 0\0"
+ VALUE "InternalName", "aniplayer\0"
+ VALUE "LegalCopyright", "Copyright © 2009 APTX\0"
+ VALUE "MIMEType", "application/x-aniplayer\0"
+ VALUE "OriginalFilename", "aniplayer.exe\0"
+ VALUE "ProductName", "AniPlayer\0"
+ VALUE "ProductVersion", "2, 0, 0, 0\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
#include "mainwindow.h"
-#include <QApplication>
+#include <aniplayerapplication.h>
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
+ AniPlayerApplication a(argc, argv);
+
+ if (a.isRunning())
+ {
+ if (a.arguments().count() > 1)
+ a.sendMessage("open " + a.arguments().at(1));
+
+ return 0;
+ }
+
MainWindow w;
+ QObject::connect(&a, SIGNAL(openFileRequested(QString)), &w, SLOT(play(QString)));
+
w.show();
-
+
return a.exec();
}
#include <QMouseEvent>
#include <QWheelEvent>
-#include <QStandardPaths>
#include <QFileDialog>
+#include <QSettings>
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+# include <QDesktopServices>
+#else
+# include <QStandardPaths>
+#endif
#ifdef Q_OS_WIN
# include <aniplayerdshow.h>
#include <videowidget.h>
#include "menu.h"
#include "seekslider.h"
+#include <volumeslider.h>
+#include "versiondialog.h"
+
+#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
+// setAttribute(Qt::WA_DeleteOnClose);
dragged = mouseMoved = false;
player = new AniPlayerDShow(this);
// connect(videoPlayer->mediaController(), SIGNAL(availableSubtitlesChanged()), this, SLOT(updateSubtitles()));
// connect(m_actions["markWatched"], SIGNAL(triggered()), this, SLOT(markWatched()));
// connect(m_actions["settings"], SIGNAL(triggered()), this, SLOT(anidbSettings()));
-// connect(m_actions["about"], SIGNAL(triggered()), this, SLOT(about()));
+ connect(m_actions["about"], SIGNAL(triggered()), this, SLOT(about()));
connect(m_actions["open"], SIGNAL(triggered()), this, SLOT(open()));
connect(m_actions["play"], SIGNAL(triggered()), player, SLOT(play()));
connect(m_actions["pause"], SIGNAL(triggered()), player, SLOT(pause()));
connect(m_actions["stop"], SIGNAL(triggered()), player, SLOT(stop()));
-// connect(m_actions["toggleStayOnTop"], SIGNAL(toggled(bool)), this, SLOT(toggleStayOnTop()));
-// connect(m_actions["toggleFrameless"], SIGNAL(toggled(bool)), this, SLOT(toggleFrameless()));
-// connect(m_actions["toggleOverlay"], SIGNAL(toggled(bool)), this, SLOT(toggleOverlay()));
+ connect(m_actions["toggleStayOnTop"], SIGNAL(toggled(bool)), this, SLOT(toggleStayOnTop()));
+ connect(m_actions["toggleFrameless"], SIGNAL(toggled(bool)), this, SLOT(toggleFrameless()));
+ connect(m_actions["toggleOverlay"], SIGNAL(toggled(bool)), this, SLOT(toggleOverlay()));
connect(m_actions["volUp"], SIGNAL(triggered()), player, SLOT(volumeUp()));
connect(m_actions["volDown"], SIGNAL(triggered()), player, SLOT(volumeDown()));
connect(m_actions["opSkip"], SIGNAL(triggered()), this, SLOT(opSkip()));
connect(m_actions["back1sec"], SIGNAL(triggered()), this, SLOT(skipback()));
-// connect(videoPlayer->videoWidget(), SIGNAL(menuToggleRequested()), this, SLOT(toggleMenu()));
-
// connect(m_actions["next"], SIGNAL(triggered()), playlist, SLOT(next()));
// connect(m_actions["previous"], SIGNAL(triggered()), playlist, SLOT(previous()));
- connect(player, SIGNAL(totalTimeChanged(qint64)), menu->seekSlider(), SLOT(totalTimeChanged(qint64)));
+ connect(player, SIGNAL(totalTimeChanged(qint64)), menu, SLOT(totalTimeChanged(qint64)));
connect(menu->seekSlider(), SIGNAL(seekRequested(qint64)), player, SLOT(seek(qint64)));
+ connect(player, SIGNAL(tick(qint64)), menu, SLOT(tick(qint64)));
+ connect(player, SIGNAL(currentFileChanged(QString)), this, SLOT(handleFileChange()));
+ connect(player, SIGNAL(chaptersChanged(ChapterList)), this, SLOT(chaptersChanged()));
+ connect(player, SIGNAL(streamsChanged(StreamList)), this, SLOT(streamsChanged()));
+
+ connect(player, SIGNAL(volumeChanged(double)), menu->volumeSlider(), SLOT(setVolume(double)));
+ connect(menu->volumeSlider(), SIGNAL(volumeChangedByUser(double)), player, SLOT(setVolume(double)));
+ connect(player, SIGNAL(mutedChanged(bool)), menu->volumeSlider(), SLOT(setMuted(bool)));
+ connect(menu->volumeSlider(), SIGNAL(mutedChangedByUser(bool)), player, SLOT(setMuted(bool)));
setCentralWidget(player->videoWidget());
- open("E:/Anime/Accel World OVA/Accel World OVA - 01 - OVA - [UTW][3e56ee18].mkv");
+ handleStateChange(player->state(), player->state());
+ //open("E:/Anime/Accel World OVA/Accel World OVA - 01 - OVA - [UTW][3e56ee18].mkv");
+ updateWindowTitle();
+ loadSettings();
}
MainWindow::~MainWindow()
{
+ saveSettings();
delete ui;
}
if (player->currentFile() == "")
{
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ dir = QDesktopServices::storageLocation(QDesktopServices::MoviesLocation);
+#else
dir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
+#endif
}
else
{
void MainWindow::opSkip()
{
- player->skip(/*m_opSkip*/85 * 1000);
+ player->skip(m_opSkip * 1000);
}
void MainWindow::toggleMenu()
void MainWindow::toggleStayOnTop()
{
updateWindowFlags();
+ player->widgetChanged();
}
void MainWindow::toggleFrameless()
{
updateWindowFlags();
+ player->widgetChanged();
}
void MainWindow::toggleOverlay()
{
updateWindowFlags();
+ player->widgetChanged();
}
void MainWindow::about()
{
-// VersionDialog dialog(this);
- // dialog.exec();
+ VersionDialog dialog(this);
+ dialog.exec();
}
void MainWindow::handleStateChange(AniPlayer::State newState, AniPlayer::State oldState)
switch(newState)
{
+ case AniPlayer::NoFileLoaded:
+ m_actions["play"]->setDisabled(true);
+ m_actions["pause"]->setDisabled(true);
+ m_actions["stop"]->setDisabled(true);
+ break;
case AniPlayer::Error:
//menu->showMessage(playerlayer->errorString());
m_actions["play"]->setDisabled(false);
m_actions["pause"]->setDisabled(true);
m_actions["stop"]->setDisabled(true);
-
break;
case AniPlayer::Playing:
m_actions["play"]->setDisabled(true);
}
}
+void MainWindow::handleFileChange()
+{
+ resize(player->videoSize());
+}
+
void MainWindow::mousePressEvent(QMouseEvent *event)
{
+ if (player->videoWidget()->isFullScreen())
+ return;
+
if (event->button() == Qt::LeftButton)
{
dragPosition = event->globalPos() - frameGeometry().topLeft();
void MainWindow::updateCursor()
{
-#ifdef Q_WS_X11
- if (isFullScreen() && menu->isHidden())
- setCursor(QCursor(Qt::BlankCursor));
+ if (player->videoWidget()->isFullScreen() && menu->isHidden())
+ player->videoWidget()->setCursor(QCursor(Qt::BlankCursor));
else
- setCursor(QCursor(Qt::ArrowCursor));
-#else
-// if (videoPlayer->videoWidget()->isFullScreen() && menu->isHidden())
-// videoPlayer->videoWidget()->setCursor(QCursor(Qt::BlankCursor));
-// else
-// videoPlayer->videoWidget()->setCursor(QCursor(Qt::ArrowCursor));
-#endif
+ player->videoWidget()->setCursor(QCursor(Qt::ArrowCursor));
}
void MainWindow::updateWindowFlags()
show();
}
+
+void MainWindow::chaptersChanged()
+{
+/*
+ foreach (const Chapter &c, player->chapters())
+ {
+ qDebug() << "Chapter" << c.name << c.time;
+ }
+*/
+}
+
+void MainWindow::streamsChanged()
+{
+/* foreach (const Stream *c, player->streams())
+ {
+ qDebug() << "Stream" << c->name << c->type << c->description;
+ }
+*/
+}
+
+void MainWindow::saveSettings()
+{
+ QSettings settings;
+
+ settings.beginGroup("settings");
+ settings.setValue("currentFile", player->currentFile());
+ settings.setValue("volume", player->volume());
+ settings.setValue("muted", player->isMuted());
+ settings.setValue("opSkip", m_opSkip);
+ settings.endGroup();
+ settings.beginGroup("videoWindow");
+ settings.setValue("geometry", saveGeometry());
+ settings.setValue("stayOnTop", m_actions["toggleStayOnTop"]->isChecked());
+ settings.setValue("frameless", m_actions["toggleFrameless"]->isChecked());
+ settings.endGroup();
+ settings.beginGroup("menu");
+ settings.setValue("geometry", menu->saveGeometry());
+ settings.setValue("state", menu->saveState());
+ settings.setValue("isVisible", menu->isVisible());
+ settings.endGroup();
+}
+
+void MainWindow::loadSettings()
+{
+ QSettings settings;
+ settings.beginGroup("settings");
+ open(settings.value("currentFile", "").toString());
+ player->setVolume(settings.value("volume", qreal(1.0)).toDouble());
+ player->setMuted(settings.value("muted", false).toBool());
+ m_opSkip = settings.value("opSkip", 85).toInt();
+ settings.endGroup();
+ settings.beginGroup("videoWindow");
+ restoreGeometry(settings.value("geometry", saveGeometry()).toByteArray());
+ m_actions["toggleStayOnTop"]->setChecked(settings.value("stayOnTop", false).toBool());
+ m_actions["toggleFrameless"]->setChecked(settings.value("frameless", false).toBool());
+ settings.endGroup();
+ settings.beginGroup("menu");
+ menu->restoreState(settings.value("state", menu->saveState()).toByteArray());
+ menu->restoreGeometry(settings.value("geometry", menu->saveGeometry()).toByteArray());
+ menu->setVisible(settings.value("isVisible", true).toBool());
+ settings.endGroup();
+ settings.beginGroup("anidbudpapiclient");
+// m_automark = settings.value("automark", 0).toInt();
+// m_automarkPaths = settings.value("paths", QStringList()).toStringList();
+ settings.endGroup();
+}
private slots:
void handleStateChange(AniPlayer::State newState, AniPlayer::State oldState);
+ void handleFileChange();
+
+ void chaptersChanged();
+ void streamsChanged();
protected:
void mousePressEvent(QMouseEvent *event);
void updateWindowFlags();
+ void saveSettings();
+ void loadSettings();
+
QMap<QString, QAction *> m_actions;
Ui::MainWindow *ui;
Menu *menu;
AniPlayer *player;
+ int m_opSkip;
+
+ int m_automark;
+ QStringList m_automarkPaths;
+ bool m_marked;
+ bool m_automarkable;
+
bool mouseMoved;
QPoint dragPosition;
bool dragged;
--- /dev/null
+#include "menu.h"
+
+#include <QHBoxLayout>
+#include <QMouseEvent>
+#include <QTime>
+#include <QLabel>
+#include <QToolBar>
+#include <QStatusBar>
+#include <QApplication>
+
+#include "seekslider.h"
+#include <volumeslider.h>
+
+Menu::Menu(QWidget *parent)
+ : QMainWindow(parent), dragged(false)
+{
+ setWindowFlags(Qt::Tool);
+ setAttribute(Qt::WA_QuitOnClose);
+
+ controlBar = new QToolBar(tr("Control"), this);
+ seekBar = new QToolBar(tr("Seek"), this);
+ timeBar = new QToolBar(tr("Time"), this);
+ volumeBar = new QToolBar(tr("Volume"), this);
+
+ statusBar();
+
+ controlBar->setObjectName("controlBar");
+ volumeBar->setObjectName("volumeBar");
+ timeBar->setObjectName("timeBar");
+ seekBar->setObjectName("seekBar");
+
+ addToolBar(controlBar);
+ addToolBar(volumeBar);
+ addToolBarBreak();
+ addToolBar(timeBar);
+ addToolBar(seekBar);
+ {
+ QWidget *seekBarContents = new QWidget(seekBar);
+ seekBarContents->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ m_seekSlider = new SeekSlider(seekBarContents);
+
+ QHBoxLayout *layout = new QHBoxLayout(seekBarContents);
+// layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(m_seekSlider);
+ seekBar->addWidget(seekBarContents);
+ }
+ {
+ QWidget *timeBarContents = new QWidget(timeBar);
+ timeBarContents->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ timeLabel = new QLabel("0:00:00 / 0:00:00 (0%)", timeBarContents);
+
+ QHBoxLayout *layout = new QHBoxLayout(timeBarContents);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(timeLabel);
+ layout->setSizeConstraint(QLayout::SetMinimumSize);
+ timeBar->addWidget(timeBarContents);
+ }
+ {
+ QWidget *volumeBarContents = new QWidget(volumeBar);
+ m_volumeSlider = new VolumeSlider(this);
+
+ QHBoxLayout *layout = new QHBoxLayout(volumeBarContents);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(m_volumeSlider);
+ volumeBar->addWidget(volumeBarContents);
+ }
+
+ {
+ QWidget *controlBarContents = new QWidget(controlBar);
+
+ QHBoxLayout *layout = new QHBoxLayout(controlBarContents);
+ layout->setSizeConstraint(QLayout::SetMinimumSize);
+ layout->setContentsMargins(0, 0, 0, 0);
+// layout->addWidget(timeLabel, 1);
+// layout->addWidget(m_volumeSlider, 2);
+ controlBar->addWidget(controlBarContents);
+ }
+
+ setWindowTitle(tr("%1 Control Panel").arg(qApp->applicationName()));
+
+ totalTime = " / " + QTime(0, 0, 0, 0).toString("h:mm:ss");
+}
+
+Menu::~Menu()
+{
+}
+
+void Menu::addActions(QList<QAction *> actions)
+{
+ controlBar->addActions(actions);
+}
+
+SeekSlider *Menu::seekSlider() const
+{
+ return m_seekSlider;
+}
+
+VolumeSlider *Menu::volumeSlider() const
+{
+ return m_volumeSlider;
+}
+
+void Menu::showMessage(const QString &message)
+{
+ statusBar()->showMessage(message);
+}
+
+void Menu::tick(qint64 pos)
+{
+ m_seekSlider->tick(pos);
+ int sec = pos / 1000;
+ int min = sec / 60;
+ int hour = min / 60;
+ int msec = pos;
+ timeLabel->setText(
+ QString("%1 %2 (%3%)").arg(
+ QTime(hour, min % 60, sec % 60, msec % 1000).toString("h:mm:ss"),
+ totalTime)
+ .arg(int(pos * double(100) / length))
+ );
+}
+
+void Menu::totalTimeChanged(qint64 time)
+{
+ m_seekSlider->totalTimeChanged(time);
+ length = time;
+ int sec = time / 1000;
+ int min = sec / 60;
+ int hour = min / 60;
+ int msec = time;
+ totalTime = " / " + QTime(hour, min % 60, sec % 60, msec % 1000).toString("h:mm:ss");
+}
+
+void Menu::moveEvent(QMoveEvent *event)
+{
+ QMainWindow::moveEvent(event);
+ emit positionChanged();
+}
+
+void Menu::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ {
+ dragPosition = event->globalPos() - frameGeometry().topLeft();
+ dragged = true;
+ event->accept();
+ }
+}
+
+void Menu::mouseMoveEvent(QMouseEvent *event)
+{
+ if (!dragged)
+ return;
+
+ if (event->buttons() & Qt::LeftButton)
+ {
+ move(event->globalPos() - dragPosition);
+ event->accept();
+ }
+}
+
+void Menu::mouseReleaseEvent(QMouseEvent * /*event*/)
+{
+ if (!dragged)
+ return;
+ dragged = false;
+}
+
+void Menu::resizeEvent(QResizeEvent *event)
+{
+ QMainWindow::resizeEvent(event);
+ emit positionChanged();
+}
--- /dev/null
+#ifndef MENU_H
+#define MENU_H
+
+#include <QMainWindow>
+#include <QList>
+
+class QToolBar;
+class QMouseEvent;
+class QLabel;
+class SeekSlider;
+class VolumeSlider;
+
+class Menu : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ Menu(QWidget *parent = 0);
+ ~Menu();
+
+ void addActions(QList<QAction *> actions);
+
+ SeekSlider *seekSlider() const;
+ VolumeSlider *volumeSlider() const;
+
+public slots:
+ void showMessage(const QString &message);
+ void tick(qint64 msec);
+ void totalTimeChanged(qint64 time);
+
+signals:
+ void positionChanged();
+
+protected:
+ virtual void moveEvent(QMoveEvent *event);
+ virtual void mouseMoveEvent(QMouseEvent *event);
+ virtual void mousePressEvent(QMouseEvent *event);
+ virtual void mouseReleaseEvent(QMouseEvent *event);
+ virtual void resizeEvent(QResizeEvent *event);
+
+private:
+ SeekSlider *m_seekSlider;
+ VolumeSlider *m_volumeSlider;
+
+ QToolBar *controlBar;
+ QToolBar *seekBar;
+ QToolBar *timeBar;
+ QToolBar *volumeBar;
+
+ QLabel *timeLabel;
+ QString totalTime;
+ qint64 length;
+
+ QPoint dragPosition;
+ bool dragged;
+};
+
+#endif // MENU_H
TARGET = player
DESTDIR = ../build
+HEADERS += mainwindow.h \
+ menu.h \
+ seekslider.h \
+ versiondialog.h
SOURCES += main.cpp\
- mainwindow.cpp \
+ mainwindow.cpp \
menu.cpp \
- seekslider.cpp
-
-HEADERS += mainwindow.h \
- menu.h \
- seekslider.h
+ seekslider.cpp \
+ versiondialog.cpp
FORMS += mainwindow.ui \
menu.ui
include(../config.pri)
include(../aniplayer2/aniplayer2.pri)
win32:include(../aniplayer2_dshow/aniplayer2_dshow.pri)
+
+win32 {
+ CONFIG -= embed_manifest_exe
+ RC_FILE += aniplayer.rc
+ LIBS += -luser32
+}
--- /dev/null
+#include "seekslider.h"
+
+#include <QPainter>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QTime>
+#include <QFontMetrics>
+
+#include "aniplayer.h"
+#include <QDebug>
+
+SeekSlider::SeekSlider(QWidget *parent) : QWidget(parent)
+{
+ init();
+}
+
+void SeekSlider::init()
+{
+ ticking = false;
+ m_length = 1000;
+ m_seekPosition = 20;
+ markerWidth = 2;
+ drawMarkerShadow = false;
+ markerShadowEnabled = true;
+ maxPreviousPos = 3;
+
+ setMouseTracking(true);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+}
+
+SeekSlider::~SeekSlider()
+{
+
+}
+
+bool SeekSlider::isMarkerShadowEnabled() const
+{
+ return markerShadowEnabled;
+}
+
+void SeekSlider::setMarkerShadowEnabled(bool enable)
+{
+ markerShadowEnabled = enable;
+}
+
+QSize SeekSlider::sizeHint() const
+{
+ return QSize();
+}
+
+QSize SeekSlider::minimumSizeHint() const
+{
+ return QSize(100, 20);
+}
+
+QSizePolicy SeekSlider::sizePolicy() const
+{
+ return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+}
+
+void SeekSlider::setSeekPosition(qint64 value)
+{
+ if (m_seekPosition == value)
+ return;
+
+ m_seekPosition = value;
+ emit seekPositionChanged(value);
+ update();
+}
+
+void SeekSlider::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+
+ QPainter p(this);
+
+ seekerTop = 0;//height() / 3;
+ seekerLeft = 0;
+ seekerHeight = height() - 1;//height() / 3;
+ seekerWidth = width() - 1;
+
+ const int markerWidth = 2;
+ const int markerPos = time2pos(m_seekPosition);
+ const QColor markerColor(255, 0, 0);
+ const QColor markerShadowColor(255, 0, 0, 100);
+ const QColor previousMarkerColor(255, 255, 255);
+ const QColor watchedPartColor(0, 0, 255, 150);
+ const QColor unwatchedPartColor(0, 255, 0, 100);
+ const QColor toolTipBackgroundColor(0, 0, 0, 150);
+ const QColor toolTipForegroundColor(255, 255, 255);
+
+
+ // border
+ p.drawRect(seekerLeft, seekerTop, seekerWidth, seekerHeight);
+
+ // watched part
+ p.fillRect(seekerLeft + 1, seekerTop + 1, markerPos - seekerLeft - 1, seekerHeight - 1, watchedPartColor);
+ // unwatched part
+ p.fillRect(markerPos + markerWidth / 2, seekerTop + 1, seekerWidth - markerPos, seekerHeight - 1, unwatchedPartColor);
+
+ int i = 1;
+ for (QQueue<qint64>::const_iterator it = previuousPos.constBegin(); it != previuousPos.constEnd(); ++it)
+ {
+ int markerPos = seekerLeft + 1 + markerWidth / 2 + qRound(double(*it) / double(m_length) * double(seekerWidth - markerWidth / 2 - 1));
+ QColor c = previousMarkerColor;
+ c.setAlpha(255 / previuousPos.count() * i);
+ p.fillRect(markerPos - markerWidth / 2, seekerTop + 1, markerWidth, seekerHeight - 1, c);
+ ++i;
+ }
+
+ // marker bar
+ p.fillRect(markerPos - markerWidth / 2, seekerTop + 1, markerWidth, seekerHeight - 1, markerColor);
+
+ // marker shadow (where the marker would move when mouse is clicked)
+ if (drawMarkerShadow && isEnabled())
+ {
+ markerShadowPos = qBound(seekerLeft + 1 + markerWidth / 2, markerShadowPos, seekerLeft + seekerWidth - markerWidth / 2);
+ p.fillRect(markerShadowPos - markerWidth / 2, seekerTop + 1, markerWidth, seekerHeight - 1, markerShadowColor);
+
+ QString time = QTime().addMSecs(pos2time(markerShadowPos)).toString("hh:mm:ss");
+ QRect r = p.fontMetrics().boundingRect(time);
+
+ int xdelta = markerShadowPos + markerWidth / 2 + 1;
+ if (xdelta + r.width() < width())
+ r.translate(xdelta, r.height());
+ else
+ r.translate(markerShadowPos - (markerWidth / 2 + 1) - r.width(), r.height());
+
+ p.fillRect(r, toolTipBackgroundColor);
+ p.setPen(QPen(toolTipForegroundColor));
+ p.drawText(r, time);
+ }
+
+}
+
+void SeekSlider::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+
+ markerShadowPos = event->pos().x();
+ update();
+}
+
+void SeekSlider::enterEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+
+ drawMarkerShadow = true;
+ update();
+}
+
+void SeekSlider::leaveEvent(QEvent *event)
+{
+ Q_UNUSED(event);
+
+ drawMarkerShadow = false;
+ update();
+}
+
+void SeekSlider::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() != Qt::LeftButton)
+ return;
+
+ seek(event->pos().x());
+
+ event->accept();
+}
+
+void SeekSlider::seek(qint64 msec)
+{
+
+}
+
+void SeekSlider::tick(qint64 msec)
+{
+ ticking = true;
+ setSeekPosition(msec);
+ ticking = false;
+}
+
+void SeekSlider::totalTimeChanged(qint64 msec)
+{
+ ticking = true;
+ setLength(msec);
+ previuousPos.clear();
+ ticking = false;
+}
+
+void SeekSlider::seekableChanged(bool isSeekable)
+{
+
+}
+
+void SeekSlider::currentSourceChanged()
+{
+ //this releases the mouse and makes the seek slider stop seeking if the current source has changed
+ QMouseEvent event(QEvent::MouseButtonRelease, QPoint(), Qt::LeftButton, 0, 0);
+// QApplication::sendEvent(this, &event);
+}
+
+void SeekSlider::setLength(qint64 length)
+{
+ m_length = length;
+ update();
+}
+
+void SeekSlider::seek(int x)
+{
+ int newMarkerPos = qBound(seekerLeft + 1, x, seekerLeft + seekerWidth);
+ qint64 newSeekPos = pos2time(newMarkerPos);
+
+ while (!previuousPos.isEmpty() && previuousPos.count() + 1 > maxPreviousPos) previuousPos.dequeue();
+ previuousPos.enqueue(m_seekPosition);
+
+ ticking = true;
+ setSeekPosition(newSeekPos);
+ emit seekRequested(newSeekPos);
+ ticking = false;
+ seek(newSeekPos);
+}
+
+qint64 SeekSlider::pos2time(int pos) const
+{
+ const int halfMarkerWidth = markerWidth / 2;
+ return qint64(double(pos - (seekerLeft + 1 + halfMarkerWidth)) / double(seekerWidth - halfMarkerWidth - 1) * double(m_length));
+}
+
+int SeekSlider::time2pos(qint64 msec) const
+{
+ const int halfMarkerWidth = markerWidth / 2;
+ return seekerLeft + 1 + halfMarkerWidth + qRound(double(msec) / double(m_length) * double(seekerWidth - halfMarkerWidth - 1));
+}
+
+
+qint64 SeekSlider::seekPosition() const
+{
+ return m_seekPosition;
+}
--- /dev/null
+#ifndef SEEKSLIDER_H
+#define SEEKSLIDER_H
+
+#include <QSlider>
+#include <QQueue>
+
+class SeekSlider : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(qint64 seekPosition READ seekPosition WRITE setSeekPosition NOTIFY seekPositionChanged USER true)
+
+ Q_DISABLE_COPY(SeekSlider)
+
+ Q_PROPERTY(bool markerShadowEnabled READ isMarkerShadowEnabled WRITE setMarkerShadowEnabled)
+/*
+ Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking)
+ Q_PROPERTY(int pageStep READ pageStep WRITE setPageStep)
+ Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep)
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
+*/
+public:
+ explicit SeekSlider(QWidget *parent = 0);
+ ~SeekSlider();
+
+ bool isMarkerShadowEnabled() const;
+ void setMarkerShadowEnabled(bool enable = true);
+
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+ QSizePolicy sizePolicy() const;
+/* bool hasTracking() const;
+ void setTracking(bool tracking);
+ int pageStep() const;
+ void setPageStep(int milliseconds);
+ int singleStep() const;
+ void setSingleStep(int milliseconds);
+ Qt::Orientation orientation() const;
+*/
+
+
+
+/*
+ bool event(QEvent *event);
+*/
+ qint64 seekPosition() const;
+
+
+public slots:
+// void setOrientation(Qt::Orientation o);
+
+ void setSeekPosition(qint64 position);
+ void tick(qint64 msec);
+ void totalTimeChanged(qint64 msec);
+
+signals:
+ void seekPositionChanged(qint64 arg);
+ void seekRequested(qint64 position);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+ void enterEvent(QEvent *event);
+ void leaveEvent(QEvent *event);
+ void mouseReleaseEvent(QMouseEvent *ev);
+
+/* void mousePressEvent(QMouseEvent *ev);
+
+ void initStyleOption(QStyleOptionSlider *option) const;
+*/
+private slots:
+ void seek(qint64 msec);
+ void seekableChanged(bool isSeekable);
+ void currentSourceChanged();
+
+private:
+ void init();
+ void setLength(qint64 totalTimeChanged);
+
+ void seek(int x);
+
+ qint64 pos2time(int pos) const;
+ int time2pos(qint64 msec) const;
+
+ bool ticking;
+ bool markerShadowEnabled;
+
+ qint64 m_length;
+ bool m_tracking;
+ int m_pageStep;
+ int m_singleStep;
+ Qt::Orientation m_orientation;
+
+ bool drawMarkerShadow;
+ int markerShadowPos;
+
+ QQueue<qint64> previuousPos;
+ int maxPreviousPos;
+
+ // Seeker Geometry
+ int seekerTop;
+ int seekerLeft;
+ int seekerHeight;
+ int seekerWidth;
+
+ int markerWidth;
+ qint64 m_seekPosition;
+};
+
+#endif // SEEKSLIDER_H
--- /dev/null
+#include "versiondialog.h"
+
+#include <QGridLayout>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QSysInfo>
+
+#include "constants.h"
+#include <aniplayerapplication.h>
+
+VersionDialog::VersionDialog(QWidget *parent) : QDialog(parent)
+{
+ setWindowTitle(tr("About %1").arg(qApp->applicationName()));
+
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ QGridLayout *layout = new QGridLayout(this);
+ layout->setSizeConstraint(QLayout::SetFixedSize);
+
+ QString revision;
+#ifdef REVISION
+ revision = tr("from revision %1").arg(revisionString);
+#endif
+
+ const QString description = tr(
+ "<h3>%1 %2 <font size=\"3\">(%7 bit)</font></h3>"
+ "<p>Built with\tQt %3<br/>"
+ "Running with\tQt %4<br/>"
+ "<br/>"
+ "Built on " __DATE__ " at " __TIME__ " "
+ "%6"
+ "<br/>"
+ "<br/>"
+ "Application Icon (C) 2009 Watarase_Jun"
+ "<br/>"
+ "<br/>"
+ "Copyright (C) 2009 %5. All rights reserved.</p>"
+ "<p>The program is provided AS IS with NO WARRANTY OF ANY KIND, "
+ "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A "
+ "PARTICULAR PURPOSE.</p>")
+ .arg(qApp->applicationName())
+ .arg(qApp->applicationVersion())
+ .arg(QLatin1String(QT_VERSION_STR))
+ .arg(QLatin1String(qVersion()))
+ .arg(qApp->organizationName())
+ .arg(revision)
+ .arg(QSysInfo::WordSize);
+
+ QLabel *copyrightLabel = new QLabel(description);
+ copyrightLabel->setWordWrap(true);
+ copyrightLabel->setOpenExternalLinks(true);
+ copyrightLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
+ QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
+
+ buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole));
+ connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject()));
+
+ layout->addWidget(copyrightLabel, 0, 1, 4, 4);
+ layout->addWidget(buttonBox, 4, 0, 1, 5);
+}
--- /dev/null
+#ifndef VERSIONDIALOG_H
+#define VERSIONDIALOG_H
+
+#include <QDialog>
+
+class VersionDialog : public QDialog
+{
+public:
+ VersionDialog(QWidget *parent = 0);
+};
+
+#endif // VERSIONDIALOG_H
--- /dev/null
+<RCC>
+ <qresource prefix="/">
+ <file alias="icon.png">mikuru-icon-base.png</file>
+ </qresource>
+</RCC>