Merge remote-tracking branch 'origin/5.4' into dev
Change-Id: I8b9177d90afac8b834d333efc6c22b6b35dceaf8
This commit is contained in:
@@ -760,7 +760,7 @@ qint64 QAlsaAudioInput::elapsedUSecs() const
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return clockStamp.elapsed()*1000;
|
||||
return clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QAlsaAudioInput::reset()
|
||||
|
||||
@@ -774,7 +774,7 @@ qint64 QAlsaAudioOutput::elapsedUSecs() const
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return clockStamp.elapsed()*1000;
|
||||
return clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QAlsaAudioOutput::reset()
|
||||
|
||||
@@ -83,23 +83,6 @@ public class QtAndroidMediaPlayer
|
||||
|
||||
private volatile int mState = State.Uninitialized;
|
||||
|
||||
private class ProgressWatcher
|
||||
implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
while ((mState & (State.Started)) != 0) {
|
||||
onProgressUpdateNative(getCurrentPosition(), mID);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MediaPlayer OnErrorListener
|
||||
*/
|
||||
@@ -257,8 +240,6 @@ public class QtAndroidMediaPlayer
|
||||
try {
|
||||
mMediaPlayer.start();
|
||||
setState(State.Started);
|
||||
Thread progressThread = new Thread(new ProgressWatcher());
|
||||
progressThread.start();
|
||||
} catch (final IllegalStateException e) {
|
||||
Log.d(TAG, "" + e.getMessage());
|
||||
}
|
||||
@@ -309,7 +290,6 @@ public class QtAndroidMediaPlayer
|
||||
|
||||
try {
|
||||
mMediaPlayer.seekTo(msec);
|
||||
onProgressUpdateNative(msec, mID);
|
||||
} catch (final IllegalStateException e) {
|
||||
Log.d(TAG, "" + e.getMessage());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,36 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class StateChangeNotifier
|
||||
{
|
||||
public:
|
||||
StateChangeNotifier(QAndroidMediaPlayerControl *mp)
|
||||
: mControl(mp)
|
||||
, mPreviousState(mp->state())
|
||||
, mPreviousMediaStatus(mp->mediaStatus())
|
||||
{
|
||||
++mControl->mActiveStateChangeNotifiers;
|
||||
}
|
||||
|
||||
~StateChangeNotifier()
|
||||
{
|
||||
if (--mControl->mActiveStateChangeNotifiers)
|
||||
return;
|
||||
|
||||
if (mPreviousState != mControl->state())
|
||||
Q_EMIT mControl->stateChanged(mControl->state());
|
||||
|
||||
if (mPreviousMediaStatus != mControl->mediaStatus())
|
||||
Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
|
||||
}
|
||||
|
||||
private:
|
||||
QAndroidMediaPlayerControl *mControl;
|
||||
QMediaPlayer::State mPreviousState;
|
||||
QMediaPlayer::MediaStatus mPreviousMediaStatus;
|
||||
};
|
||||
|
||||
|
||||
QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
|
||||
: QMediaPlayerControl(parent),
|
||||
mMediaPlayer(new AndroidMediaPlayer),
|
||||
@@ -55,7 +85,9 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
|
||||
mPendingPosition(-1),
|
||||
mPendingSetMedia(false),
|
||||
mPendingVolume(-1),
|
||||
mPendingMute(-1)
|
||||
mPendingMute(-1),
|
||||
mReloadingMedia(false),
|
||||
mActiveStateChangeNotifiers(0)
|
||||
{
|
||||
connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)),
|
||||
this,SLOT(onBufferingChanged(qint32)));
|
||||
@@ -107,17 +139,14 @@ qint64 QAndroidMediaPlayerControl::position() const
|
||||
if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
|
||||
return duration();
|
||||
|
||||
if ((mState & (AndroidMediaPlayer::Idle
|
||||
| AndroidMediaPlayer::Initialized
|
||||
| AndroidMediaPlayer::Prepared
|
||||
if ((mState & (AndroidMediaPlayer::Prepared
|
||||
| AndroidMediaPlayer::Started
|
||||
| AndroidMediaPlayer::Paused
|
||||
| AndroidMediaPlayer::Stopped
|
||||
| AndroidMediaPlayer::PlaybackCompleted)) == 0) {
|
||||
return (mPendingPosition == -1) ? 0 : mPendingPosition;
|
||||
| AndroidMediaPlayer::PlaybackCompleted))) {
|
||||
return mMediaPlayer->getCurrentPosition();
|
||||
}
|
||||
|
||||
return (mCurrentState == QMediaPlayer::StoppedState) ? 0 : mMediaPlayer->getCurrentPosition();
|
||||
return (mPendingPosition == -1) ? 0 : mPendingPosition;
|
||||
}
|
||||
|
||||
void QAndroidMediaPlayerControl::setPosition(qint64 position)
|
||||
@@ -127,24 +156,25 @@ void QAndroidMediaPlayerControl::setPosition(qint64 position)
|
||||
|
||||
const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
|
||||
|
||||
if ((mState & (AndroidMediaPlayer::Prepared
|
||||
| AndroidMediaPlayer::Started
|
||||
| AndroidMediaPlayer::Paused
|
||||
| AndroidMediaPlayer::PlaybackCompleted)) == 0) {
|
||||
if (mPendingPosition != seekPosition) {
|
||||
mPendingPosition = seekPosition;
|
||||
Q_EMIT positionChanged(seekPosition);
|
||||
}
|
||||
if (seekPosition == this->position())
|
||||
return;
|
||||
}
|
||||
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
|
||||
setMediaStatus(QMediaPlayer::LoadedMedia);
|
||||
|
||||
mMediaPlayer->seekTo(seekPosition);
|
||||
if ((mState & (AndroidMediaPlayer::Prepared
|
||||
| AndroidMediaPlayer::Started
|
||||
| AndroidMediaPlayer::Paused
|
||||
| AndroidMediaPlayer::PlaybackCompleted)) == 0) {
|
||||
mPendingPosition = seekPosition;
|
||||
} else {
|
||||
mMediaPlayer->seekTo(seekPosition);
|
||||
|
||||
if (mPendingPosition != -1) {
|
||||
mPendingPosition = -1;
|
||||
if (mPendingPosition != -1) {
|
||||
mPendingPosition = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT positionChanged(seekPosition);
|
||||
@@ -275,9 +305,11 @@ const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
|
||||
void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
|
||||
QIODevice *stream)
|
||||
{
|
||||
const bool reloading = (mMediaContent == mediaContent);
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
if (!reloading) {
|
||||
mReloadingMedia = (mMediaContent == mediaContent);
|
||||
|
||||
if (!mReloadingMedia) {
|
||||
mMediaContent = mediaContent;
|
||||
mMediaStream = stream;
|
||||
}
|
||||
@@ -286,41 +318,45 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
|
||||
if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
|
||||
mMediaPlayer->release();
|
||||
|
||||
QString mediaPath;
|
||||
|
||||
if (mediaContent.isNull()) {
|
||||
setMediaStatus(QMediaPlayer::NoMedia);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mVideoOutput && !mVideoOutput->isReady()) {
|
||||
// if a video output is set but the video texture is not ready, delay loading the media
|
||||
// since it can cause problems on some hardware
|
||||
mPendingSetMedia = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl url = mediaContent.canonicalUrl();
|
||||
QString mediaPath;
|
||||
if (url.scheme() == QLatin1String("qrc")) {
|
||||
const QString path = url.toString().mid(3);
|
||||
mTempFile.reset(QTemporaryFile::createNativeFile(path));
|
||||
if (!mTempFile.isNull())
|
||||
mediaPath = QStringLiteral("file://") + mTempFile->fileName();
|
||||
} else {
|
||||
mediaPath = url.toString();
|
||||
if (mVideoOutput && !mVideoOutput->isReady()) {
|
||||
// if a video output is set but the video texture is not ready, delay loading the media
|
||||
// since it can cause problems on some hardware
|
||||
mPendingSetMedia = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl url = mediaContent.canonicalUrl();
|
||||
if (url.scheme() == QLatin1String("qrc")) {
|
||||
const QString path = url.toString().mid(3);
|
||||
mTempFile.reset(QTemporaryFile::createNativeFile(path));
|
||||
if (!mTempFile.isNull())
|
||||
mediaPath = QStringLiteral("file://") + mTempFile->fileName();
|
||||
} else {
|
||||
mediaPath = url.toString();
|
||||
}
|
||||
|
||||
if (mVideoSize.isValid() && mVideoOutput)
|
||||
mVideoOutput->setVideoSize(mVideoSize);
|
||||
|
||||
if ((mMediaPlayer->display() == 0) && mVideoOutput)
|
||||
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
|
||||
mMediaPlayer->setDataSource(mediaPath);
|
||||
mMediaPlayer->prepareAsync();
|
||||
}
|
||||
|
||||
if (mVideoSize.isValid() && mVideoOutput)
|
||||
mVideoOutput->setVideoSize(mVideoSize);
|
||||
|
||||
if ((mMediaPlayer->display() == 0) && mVideoOutput)
|
||||
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
|
||||
mMediaPlayer->setDataSource(mediaPath);
|
||||
mMediaPlayer->prepareAsync();
|
||||
|
||||
if (!reloading)
|
||||
if (!mReloadingMedia) {
|
||||
Q_EMIT mediaChanged(mMediaContent);
|
||||
Q_EMIT actualMediaLocationChanged(mediaPath);
|
||||
}
|
||||
|
||||
resetBufferingProgress();
|
||||
|
||||
mReloadingMedia = false;
|
||||
}
|
||||
|
||||
void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
|
||||
@@ -344,6 +380,8 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
|
||||
|
||||
void QAndroidMediaPlayerControl::play()
|
||||
{
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
// We need to prepare the mediaplayer again.
|
||||
if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isNull()) {
|
||||
setMedia(mMediaContent, mMediaStream);
|
||||
@@ -364,6 +402,8 @@ void QAndroidMediaPlayerControl::play()
|
||||
|
||||
void QAndroidMediaPlayerControl::pause()
|
||||
{
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
setState(QMediaPlayer::PausedState);
|
||||
|
||||
if ((mState & (AndroidMediaPlayer::Started
|
||||
@@ -378,6 +418,8 @@ void QAndroidMediaPlayerControl::pause()
|
||||
|
||||
void QAndroidMediaPlayerControl::stop()
|
||||
{
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
setState(QMediaPlayer::StoppedState);
|
||||
|
||||
if ((mState & (AndroidMediaPlayer::Prepared
|
||||
@@ -395,6 +437,8 @@ void QAndroidMediaPlayerControl::stop()
|
||||
|
||||
void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
|
||||
{
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
Q_UNUSED(extra);
|
||||
switch (what) {
|
||||
case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN:
|
||||
@@ -426,6 +470,8 @@ void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
|
||||
|
||||
void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
|
||||
{
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
QString errorString;
|
||||
QMediaPlayer::Error error = QMediaPlayer::ResourceError;
|
||||
|
||||
@@ -478,6 +524,8 @@ void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
|
||||
|
||||
void QAndroidMediaPlayerControl::onBufferingChanged(qint32 percent)
|
||||
{
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
mBuffering = percent != 100;
|
||||
mBufferPercent = percent;
|
||||
|
||||
@@ -509,6 +557,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
|
||||
return;
|
||||
}
|
||||
|
||||
StateChangeNotifier notifier(this);
|
||||
|
||||
mState = state;
|
||||
switch (mState) {
|
||||
case AndroidMediaPlayer::Idle:
|
||||
@@ -516,7 +566,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
|
||||
case AndroidMediaPlayer::Initialized:
|
||||
break;
|
||||
case AndroidMediaPlayer::Preparing:
|
||||
setMediaStatus(QMediaPlayer::LoadingMedia);
|
||||
if (!mReloadingMedia)
|
||||
setMediaStatus(QMediaPlayer::LoadingMedia);
|
||||
break;
|
||||
case AndroidMediaPlayer::Prepared:
|
||||
setMediaStatus(QMediaPlayer::LoadedMedia);
|
||||
@@ -537,6 +588,7 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
|
||||
} else {
|
||||
setMediaStatus(QMediaPlayer::BufferedMedia);
|
||||
}
|
||||
Q_EMIT positionChanged(position());
|
||||
break;
|
||||
case AndroidMediaPlayer::Paused:
|
||||
setState(QMediaPlayer::PausedState);
|
||||
@@ -545,27 +597,32 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
|
||||
setState(QMediaPlayer::StoppedState);
|
||||
setMediaStatus(QMediaPlayer::UnknownMediaStatus);
|
||||
mMediaPlayer->release();
|
||||
Q_EMIT positionChanged(0);
|
||||
break;
|
||||
case AndroidMediaPlayer::Stopped:
|
||||
setState(QMediaPlayer::StoppedState);
|
||||
setMediaStatus(QMediaPlayer::LoadedMedia);
|
||||
setPosition(0);
|
||||
Q_EMIT positionChanged(0);
|
||||
break;
|
||||
case AndroidMediaPlayer::PlaybackCompleted:
|
||||
setState(QMediaPlayer::StoppedState);
|
||||
setPosition(0);
|
||||
setMediaStatus(QMediaPlayer::EndOfMedia);
|
||||
break;
|
||||
case AndroidMediaPlayer::Uninitialized:
|
||||
// reset some properties
|
||||
resetBufferingProgress();
|
||||
mPendingPosition = -1;
|
||||
mPendingSetMedia = false;
|
||||
mPendingState = -1;
|
||||
// reset some properties (unless we reload the same media)
|
||||
if (!mReloadingMedia) {
|
||||
resetBufferingProgress();
|
||||
mPendingPosition = -1;
|
||||
mPendingSetMedia = false;
|
||||
mPendingState = -1;
|
||||
|
||||
setAudioAvailable(false);
|
||||
setVideoAvailable(false);
|
||||
setSeekable(true);
|
||||
Q_EMIT durationChanged(0);
|
||||
Q_EMIT positionChanged(0);
|
||||
|
||||
setAudioAvailable(false);
|
||||
setVideoAvailable(false);
|
||||
setSeekable(true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -597,7 +654,6 @@ void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
|
||||
return;
|
||||
|
||||
mCurrentState = state;
|
||||
Q_EMIT stateChanged(mCurrentState);
|
||||
}
|
||||
|
||||
void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
|
||||
@@ -605,14 +661,13 @@ void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status
|
||||
if (mCurrentMediaStatus == status)
|
||||
return;
|
||||
|
||||
mCurrentMediaStatus = status;
|
||||
|
||||
if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
|
||||
Q_EMIT durationChanged(0);
|
||||
|
||||
if (status == QMediaPlayer::EndOfMedia)
|
||||
Q_EMIT durationChanged(duration());
|
||||
|
||||
mCurrentMediaStatus = status;
|
||||
Q_EMIT mediaStatusChanged(mCurrentMediaStatus);
|
||||
Q_EMIT positionChanged(position());
|
||||
|
||||
updateBufferStatus();
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void metaDataUpdated();
|
||||
void actualMediaLocationChanged(const QString &url);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setPosition(qint64 position) Q_DECL_OVERRIDE;
|
||||
@@ -110,7 +111,9 @@ private:
|
||||
bool mPendingSetMedia;
|
||||
int mPendingVolume;
|
||||
int mPendingMute;
|
||||
bool mReloadingMedia;
|
||||
QScopedPointer<QTemporaryFile> mTempFile;
|
||||
int mActiveStateChangeNotifiers;
|
||||
|
||||
void setState(QMediaPlayer::State state);
|
||||
void setMediaStatus(QMediaPlayer::MediaStatus status);
|
||||
@@ -121,6 +124,8 @@ private:
|
||||
void resetBufferingProgress();
|
||||
void flushPendingStates();
|
||||
void updateBufferStatus();
|
||||
|
||||
friend class StateChangeNotifier;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -45,8 +45,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent)
|
||||
{
|
||||
mMediaControl = new QAndroidMediaPlayerControl;
|
||||
mMetadataControl = new QAndroidMetaDataReaderControl;
|
||||
connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)),
|
||||
mMetadataControl, SLOT(onMediaChanged(QMediaContent)));
|
||||
connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)),
|
||||
mMetadataControl, SLOT(onMediaChanged(QString)));
|
||||
connect(mMediaControl, SIGNAL(metaDataUpdated()),
|
||||
mMetadataControl, SLOT(onUpdateMetaData()));
|
||||
}
|
||||
|
||||
@@ -93,18 +93,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const
|
||||
return m_metadata.keys();
|
||||
}
|
||||
|
||||
void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
|
||||
void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url)
|
||||
{
|
||||
if (!m_retriever)
|
||||
return;
|
||||
|
||||
m_mediaContent = media;
|
||||
m_mediaLocation = url;
|
||||
updateData();
|
||||
}
|
||||
|
||||
void QAndroidMetaDataReaderControl::onUpdateMetaData()
|
||||
{
|
||||
if (!m_retriever || m_mediaContent.isNull())
|
||||
if (!m_retriever || m_mediaLocation.isEmpty())
|
||||
return;
|
||||
|
||||
updateData();
|
||||
@@ -114,8 +114,8 @@ void QAndroidMetaDataReaderControl::updateData()
|
||||
{
|
||||
m_metadata.clear();
|
||||
|
||||
if (!m_mediaContent.isNull()) {
|
||||
if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) {
|
||||
if (!m_mediaLocation.isEmpty()) {
|
||||
if (m_retriever->setDataSource(m_mediaLocation)) {
|
||||
QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType);
|
||||
if (!mimeType.isNull())
|
||||
m_metadata.insert(QMediaMetaData::MediaType, mimeType);
|
||||
|
||||
@@ -54,13 +54,13 @@ public:
|
||||
QStringList availableMetaData() const Q_DECL_OVERRIDE;
|
||||
|
||||
public Q_SLOTS:
|
||||
void onMediaChanged(const QMediaContent &media);
|
||||
void onMediaChanged(const QString &url);
|
||||
void onUpdateMetaData();
|
||||
|
||||
private:
|
||||
void updateData();
|
||||
|
||||
QMediaContent m_mediaContent;
|
||||
QString m_mediaLocation;
|
||||
bool m_available;
|
||||
QVariantMap m_metadata;
|
||||
|
||||
|
||||
@@ -35,9 +35,24 @@
|
||||
|
||||
#include <QtCore/private/qjnihelpers_p.h>
|
||||
#include <QtCore/private/qjni_p.h>
|
||||
#include <QtCore/QUrl>
|
||||
#include <qdebug.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static bool exceptionCheckAndClear(JNIEnv *env)
|
||||
{
|
||||
if (Q_UNLIKELY(env->ExceptionCheck())) {
|
||||
#ifdef QT_DEBUG
|
||||
env->ExceptionDescribe();
|
||||
#endif // QT_DEBUG
|
||||
env->ExceptionClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever()
|
||||
{
|
||||
m_metadataRetriever = QJNIObjectPrivate("android/media/MediaMetadataRetriever");
|
||||
@@ -68,55 +83,105 @@ void AndroidMediaMetadataRetriever::release()
|
||||
m_metadataRetriever.callMethod<void>("release");
|
||||
}
|
||||
|
||||
bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
|
||||
bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString)
|
||||
{
|
||||
if (!m_metadataRetriever.isValid())
|
||||
return false;
|
||||
|
||||
QJNIEnvironmentPrivate env;
|
||||
QUrl url(urlString);
|
||||
|
||||
bool loaded = false;
|
||||
if (url.isLocalFile()) { // also includes qrc files (copied to a temp file)
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path());
|
||||
QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
|
||||
"(Ljava/lang/String;)V",
|
||||
string.object());
|
||||
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
|
||||
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
|
||||
"parse",
|
||||
"(Ljava/lang/String;)Landroid/net/Uri;",
|
||||
string.object());
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionClear();
|
||||
QJNIObjectPrivate fd = fileInputStream.callObjectMethod("getFD",
|
||||
"()Ljava/io/FileDescriptor;");
|
||||
if (exceptionCheckAndClear(env)) {
|
||||
fileInputStream.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/io/FileDescriptor;)V",
|
||||
fd.object());
|
||||
|
||||
bool ok = !exceptionCheckAndClear(env);
|
||||
|
||||
fileInputStream.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
} else if (url.scheme() == QLatin1String("assets")) {
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path().mid(1)); // remove first '/'
|
||||
QJNIObjectPrivate activity(QtAndroidPrivate::activity());
|
||||
QJNIObjectPrivate assetManager = activity.callObjectMethod("getAssets",
|
||||
"()Landroid/content/res/AssetManager;");
|
||||
QJNIObjectPrivate assetFd = assetManager.callObjectMethod("openFd",
|
||||
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;",
|
||||
string.object());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
|
||||
QJNIObjectPrivate fd = assetFd.callObjectMethod("getFileDescriptor",
|
||||
"()Ljava/io/FileDescriptor;");
|
||||
if (exceptionCheckAndClear(env)) {
|
||||
assetFd.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/io/FileDescriptor;JJ)V",
|
||||
fd.object(),
|
||||
assetFd.callMethod<jlong>("getStartOffset"),
|
||||
assetFd.callMethod<jlong>("getLength"));
|
||||
|
||||
bool ok = !exceptionCheckAndClear(env);
|
||||
|
||||
assetFd.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
} else if (QtAndroidPrivate::androidSdkVersion() >= 14) {
|
||||
// On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString);
|
||||
QJNIObjectPrivate hash("java/util/HashMap");
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/lang/String;Ljava/util/Map;)V",
|
||||
string.object(),
|
||||
hash.object());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
} else {
|
||||
// While on API levels < 14, only setDataSource(Context, Uri) is available and works for
|
||||
// remote media...
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString);
|
||||
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
|
||||
"parse",
|
||||
"(Ljava/lang/String;)Landroid/net/Uri;",
|
||||
string.object());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Landroid/content/Context;Landroid/net/Uri;)V",
|
||||
QtAndroidPrivate::activity(),
|
||||
uri.object());
|
||||
if (env->ExceptionCheck())
|
||||
env->ExceptionClear();
|
||||
else
|
||||
loaded = true;
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool AndroidMediaMetadataRetriever::setDataSource(const QString &path)
|
||||
{
|
||||
if (!m_metadataRetriever.isValid())
|
||||
return false;
|
||||
|
||||
QJNIEnvironmentPrivate env;
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/lang/String;)V",
|
||||
QJNIObjectPrivate::fromString(path).object());
|
||||
if (env->ExceptionCheck())
|
||||
env->ExceptionClear();
|
||||
else
|
||||
loaded = true;
|
||||
|
||||
return loaded;
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#define ANDROIDMEDIAMETADATARETRIEVER_H
|
||||
|
||||
#include <QtCore/private/qjni_p.h>
|
||||
#include <qurl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -73,8 +72,7 @@ public:
|
||||
|
||||
QString extractMetadata(MetadataKey key);
|
||||
void release();
|
||||
bool setDataSource(const QUrl &url);
|
||||
bool setDataSource(const QString &path);
|
||||
bool setDataSource(const QString &url);
|
||||
|
||||
private:
|
||||
QJNIObjectPrivate m_metadataRetriever;
|
||||
|
||||
@@ -142,9 +142,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
|
||||
void AVFCameraService::releaseControl(QMediaControl *control)
|
||||
{
|
||||
if (m_videoOutput == control) {
|
||||
m_videoOutput = 0;
|
||||
m_session->setVideoOutput(0);
|
||||
delete control;
|
||||
delete m_videoOutput;
|
||||
m_videoOutput = 0;
|
||||
}
|
||||
AVFMediaVideoProbeControl *videoProbe = qobject_cast<AVFMediaVideoProbeControl *>(control);
|
||||
if (videoProbe) {
|
||||
|
||||
@@ -118,14 +118,15 @@ void AVFMediaPlayerService::releaseControl(QMediaControl *control)
|
||||
#ifdef QT_DEBUG_AVF
|
||||
qDebug() << Q_FUNC_INFO << control;
|
||||
#endif
|
||||
#if defined(Q_OS_OSX)
|
||||
if (m_videoOutput == control) {
|
||||
#if defined(Q_OS_OSX)
|
||||
AVFVideoRendererControl *renderControl = qobject_cast<AVFVideoRendererControl*>(m_videoOutput);
|
||||
if (renderControl)
|
||||
renderControl->setSurface(0);
|
||||
#endif
|
||||
m_videoOutput = 0;
|
||||
m_session->setVideoOutput(0);
|
||||
|
||||
delete control;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -97,8 +97,6 @@ public Q_SLOTS:
|
||||
void processPositionChange();
|
||||
void processMediaLoadError();
|
||||
|
||||
void processCurrentItemChanged();
|
||||
|
||||
Q_SIGNALS:
|
||||
void positionChanged(qint64 position);
|
||||
void durationChanged(qint64 duration);
|
||||
@@ -148,6 +146,9 @@ private:
|
||||
QByteArray rawData;
|
||||
};
|
||||
|
||||
void setAudioAvailable(bool available);
|
||||
void setVideoAvailable(bool available);
|
||||
|
||||
AVFMediaPlayerService *m_service;
|
||||
AVFVideoOutput *m_videoOutput;
|
||||
|
||||
|
||||
@@ -70,15 +70,11 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
|
||||
AVPlayerItem *m_playerItem;
|
||||
AVPlayerLayer *m_playerLayer;
|
||||
NSURL *m_URL;
|
||||
bool m_audioAvailable;
|
||||
bool m_videoAvailable;
|
||||
}
|
||||
|
||||
@property (readonly, getter=player) AVPlayer* m_player;
|
||||
@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
|
||||
@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
|
||||
@property (readonly, getter=audioAvailable) bool m_audioAvailable;
|
||||
@property (readonly, getter=videoAvailable) bool m_videoAvailable;
|
||||
@property (readonly, getter=session) AVFMediaPlayerSession* m_session;
|
||||
|
||||
- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session;
|
||||
@@ -96,7 +92,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
|
||||
|
||||
@implementation AVFMediaPlayerSessionObserver
|
||||
|
||||
@synthesize m_player, m_playerItem, m_playerLayer, m_audioAvailable, m_videoAvailable, m_session;
|
||||
@synthesize m_player, m_playerItem, m_playerLayer, m_session;
|
||||
|
||||
- (AVFMediaPlayerSessionObserver *) initWithMediaPlayerSession:(AVFMediaPlayerSession *)session
|
||||
{
|
||||
@@ -186,18 +182,6 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
|
||||
return;
|
||||
}
|
||||
|
||||
m_audioAvailable = false;
|
||||
m_videoAvailable = false;
|
||||
|
||||
//Check each track of asset for audio and video content
|
||||
NSArray *tracks = [asset tracks];
|
||||
for (AVAssetTrack *track in tracks) {
|
||||
if ([track hasMediaCharacteristic:AVMediaCharacteristicAudible])
|
||||
m_audioAvailable = true;
|
||||
if ([track hasMediaCharacteristic:AVMediaCharacteristicVisual])
|
||||
m_videoAvailable = true;
|
||||
}
|
||||
|
||||
//At this point we're ready to set up for playback of the asset.
|
||||
//Stop observing our prior AVPlayerItem, if we have one.
|
||||
if (m_playerItem)
|
||||
@@ -258,18 +242,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
|
||||
m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:m_player];
|
||||
[m_playerLayer retain];
|
||||
m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
||||
|
||||
//Get the native size of the new item, and reset the bounds of the player layer
|
||||
AVAsset *asset = m_playerItem.asset;
|
||||
if (asset) {
|
||||
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
|
||||
if ([tracks count]) {
|
||||
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
|
||||
m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
|
||||
m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
//Observe the AVPlayer "currentItem" property to find out when any
|
||||
@@ -322,7 +295,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
|
||||
//AVPlayerItem "status" property value observer.
|
||||
if (context == AVFMediaPlayerSessionObserverStatusObservationContext)
|
||||
{
|
||||
AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
|
||||
AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
|
||||
switch (status)
|
||||
{
|
||||
//Indicates that the status of the player is not yet known because
|
||||
@@ -366,24 +339,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
|
||||
{
|
||||
AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
|
||||
if (m_playerItem != newPlayerItem)
|
||||
{
|
||||
m_playerItem = newPlayerItem;
|
||||
|
||||
//Get the native size of the new item, and reset the bounds of the player layer
|
||||
//AVAsset *asset = m_playerItem.asset;
|
||||
AVAsset *asset = [m_playerItem asset];
|
||||
if (asset) {
|
||||
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
|
||||
if ([tracks count]) {
|
||||
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
|
||||
m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
|
||||
m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, videoTrack.naturalSize.width, videoTrack.naturalSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (self.session)
|
||||
QMetaObject::invokeMethod(m_session, "processCurrentItemChanged", Qt::AutoConnection);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -513,6 +469,9 @@ void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *st
|
||||
m_resources = content;
|
||||
m_mediaStream = stream;
|
||||
|
||||
setAudioAvailable(false);
|
||||
setVideoAvailable(false);
|
||||
|
||||
QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
|
||||
|
||||
if (content.isNull() || content.canonicalUrl().isEmpty()) {
|
||||
@@ -582,14 +541,32 @@ bool AVFMediaPlayerSession::isMuted() const
|
||||
return m_muted;
|
||||
}
|
||||
|
||||
void AVFMediaPlayerSession::setAudioAvailable(bool available)
|
||||
{
|
||||
if (m_audioAvailable == available)
|
||||
return;
|
||||
|
||||
m_audioAvailable = available;
|
||||
Q_EMIT audioAvailableChanged(available);
|
||||
}
|
||||
|
||||
bool AVFMediaPlayerSession::isAudioAvailable() const
|
||||
{
|
||||
return [(AVFMediaPlayerSessionObserver*)m_observer audioAvailable];
|
||||
return m_audioAvailable;
|
||||
}
|
||||
|
||||
void AVFMediaPlayerSession::setVideoAvailable(bool available)
|
||||
{
|
||||
if (m_videoAvailable == available)
|
||||
return;
|
||||
|
||||
m_videoAvailable = available;
|
||||
Q_EMIT videoAvailableChanged(available);
|
||||
}
|
||||
|
||||
bool AVFMediaPlayerSession::isVideoAvailable() const
|
||||
{
|
||||
return [(AVFMediaPlayerSessionObserver*)m_observer videoAvailable];
|
||||
return m_videoAvailable;
|
||||
}
|
||||
|
||||
bool AVFMediaPlayerSession::isSeekable() const
|
||||
@@ -802,16 +779,41 @@ void AVFMediaPlayerSession::processLoadStateChange()
|
||||
bool isPlaying = (m_state != QMediaPlayer::StoppedState);
|
||||
|
||||
if (currentStatus == AVPlayerStatusReadyToPlay) {
|
||||
AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
|
||||
if (playerItem) {
|
||||
// Check each track for audio and video content
|
||||
AVAssetTrack *videoTrack = nil;
|
||||
NSArray *tracks = playerItem.tracks;
|
||||
for (AVPlayerItemTrack *track in tracks) {
|
||||
AVAssetTrack *assetTrack = track.assetTrack;
|
||||
if (assetTrack) {
|
||||
if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio])
|
||||
setAudioAvailable(true);
|
||||
if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
|
||||
setVideoAvailable(true);
|
||||
if (!videoTrack)
|
||||
videoTrack = assetTrack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the native size of the video, and reset the bounds of the player layer
|
||||
AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer];
|
||||
if (videoTrack && playerLayer) {
|
||||
playerLayer.bounds = CGRectMake(0.0f, 0.0f,
|
||||
videoTrack.naturalSize.width,
|
||||
videoTrack.naturalSize.height);
|
||||
|
||||
if (m_videoOutput && m_state != QMediaPlayer::StoppedState) {
|
||||
m_videoOutput->setLayer(playerLayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qint64 currentDuration = duration();
|
||||
if (m_duration != currentDuration)
|
||||
Q_EMIT durationChanged(m_duration = currentDuration);
|
||||
|
||||
if (m_audioAvailable != isAudioAvailable())
|
||||
Q_EMIT audioAvailableChanged(m_audioAvailable = !m_audioAvailable);
|
||||
|
||||
if (m_videoAvailable != isVideoAvailable())
|
||||
Q_EMIT videoAvailableChanged(m_videoAvailable = !m_videoAvailable);
|
||||
|
||||
newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia;
|
||||
|
||||
if (m_state == QMediaPlayer::PlayingState && [(AVFMediaPlayerSessionObserver*)m_observer player]) {
|
||||
@@ -835,17 +837,3 @@ void AVFMediaPlayerSession::processMediaLoadError()
|
||||
Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia);
|
||||
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
|
||||
}
|
||||
|
||||
void AVFMediaPlayerSession::processCurrentItemChanged()
|
||||
{
|
||||
#ifdef QT_DEBUG_AVF
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
#endif
|
||||
|
||||
AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer];
|
||||
|
||||
if (m_videoOutput && m_state != QMediaPlayer::StoppedState) {
|
||||
m_videoOutput->setLayer(playerLayer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,8 +64,10 @@ AVFVideoWidget::~AVFVideoWidget()
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
#endif
|
||||
|
||||
if (m_playerLayer)
|
||||
if (m_playerLayer) {
|
||||
[m_playerLayer removeFromSuperlayer];
|
||||
[m_playerLayer release];
|
||||
}
|
||||
}
|
||||
|
||||
QSize AVFVideoWidget::sizeHint() const
|
||||
|
||||
@@ -61,8 +61,10 @@ AVFVideoWindowControl::AVFVideoWindowControl(QObject *parent)
|
||||
|
||||
AVFVideoWindowControl::~AVFVideoWindowControl()
|
||||
{
|
||||
if (m_playerLayer)
|
||||
if (m_playerLayer) {
|
||||
[m_playerLayer removeFromSuperlayer];
|
||||
[m_playerLayer release];
|
||||
}
|
||||
}
|
||||
|
||||
WId AVFVideoWindowControl::winId() const
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
# include "coreaudiosessionmanager.h"
|
||||
#endif
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QSet>
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QWaitCondition>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#endif
|
||||
|
||||
#include <QtMultimedia/private/qaudiohelpers_p.h>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QWaitCondition>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "coreaudiodeviceinfo.h"
|
||||
#include "coreaudioutils.h"
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
@@ -698,14 +699,14 @@ void CoreAudioOutput::audioThreadStop()
|
||||
{
|
||||
stopTimers();
|
||||
if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
|
||||
m_threadFinished.wait(&m_mutex);
|
||||
m_threadFinished.wait(&m_mutex, 500);
|
||||
}
|
||||
|
||||
void CoreAudioOutput::audioThreadDrain()
|
||||
{
|
||||
stopTimers();
|
||||
if (m_audioThreadState.testAndSetAcquire(Running, Draining))
|
||||
m_threadFinished.wait(&m_mutex);
|
||||
m_threadFinished.wait(&m_mutex, 500);
|
||||
}
|
||||
|
||||
void CoreAudioOutput::audioDeviceStop()
|
||||
|
||||
@@ -117,4 +117,28 @@ GstEncodingProfile *CameraBinAudioEncoder::createProfile()
|
||||
return profile;
|
||||
}
|
||||
|
||||
void CameraBinAudioEncoder::applySettings(GstElement *encoder)
|
||||
{
|
||||
GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder);
|
||||
const char * const name = gst_plugin_feature_get_name(
|
||||
GST_PLUGIN_FEATURE(gst_element_get_factory(encoder)));
|
||||
|
||||
const bool isVorbis = qstrcmp(name, "vorbisenc") == 0;
|
||||
|
||||
const int bitRate = m_actualAudioSettings.bitRate();
|
||||
if (!isVorbis && bitRate == -1) {
|
||||
// Bit rate is invalid, don't evaluate the remaining conditions unless the encoder is
|
||||
// vorbisenc which is known to accept -1 as an unspecified bitrate.
|
||||
} else if (g_object_class_find_property(objectClass, "bitrate")) {
|
||||
g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL);
|
||||
} else if (g_object_class_find_property(objectClass, "target-bitrate")) {
|
||||
g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL);
|
||||
}
|
||||
|
||||
if (isVorbis) {
|
||||
static const double qualities[] = { 0.1, 0.3, 0.5, 0.7, 1.0 };
|
||||
g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualAudioSettings.quality()], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -78,6 +78,8 @@ public:
|
||||
|
||||
GstEncodingProfile *createProfile();
|
||||
|
||||
void applySettings(GstElement *element);
|
||||
|
||||
Q_SIGNALS:
|
||||
void settingsChanged();
|
||||
|
||||
|
||||
@@ -38,13 +38,9 @@
|
||||
|
||||
#include "camerabinserviceplugin.h"
|
||||
|
||||
|
||||
#include "camerabinservice.h"
|
||||
#include <private/qgstutils_p.h>
|
||||
|
||||
#include <private/qcore_unix_p.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename T, int N> static int lengthOf(const T(&)[N]) { return N; }
|
||||
|
||||
@@ -98,6 +98,8 @@
|
||||
#define CAPTURE_START "start-capture"
|
||||
#define CAPTURE_STOP "stop-capture"
|
||||
|
||||
#define FILESINK_BIN_NAME "videobin-filesink"
|
||||
|
||||
#define CAMERABIN_IMAGE_MODE 1
|
||||
#define CAMERABIN_VIDEO_MODE 2
|
||||
|
||||
@@ -133,6 +135,7 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa
|
||||
m_capsFilter(0),
|
||||
m_fileSink(0),
|
||||
m_audioEncoder(0),
|
||||
m_videoEncoder(0),
|
||||
m_muxer(0)
|
||||
{
|
||||
if (m_sourceFactory)
|
||||
@@ -140,6 +143,8 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa
|
||||
|
||||
m_camerabin = gst_element_factory_make("camerabin2", "camerabin2");
|
||||
g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this);
|
||||
g_signal_connect(G_OBJECT(m_camerabin), "element-added", G_CALLBACK(elementAdded), this);
|
||||
g_signal_connect(G_OBJECT(m_camerabin), "element-removed", G_CALLBACK(elementRemoved), this);
|
||||
qt_gst_object_ref_sink(m_camerabin);
|
||||
|
||||
m_bus = gst_element_get_bus(m_camerabin);
|
||||
@@ -344,6 +349,9 @@ void CameraBinSession::setupCaptureResolution()
|
||||
} else {
|
||||
g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, NULL, NULL);
|
||||
}
|
||||
|
||||
if (m_videoEncoder)
|
||||
m_videoEncodeControl->applySettings(m_videoEncoder);
|
||||
}
|
||||
|
||||
void CameraBinSession::setAudioCaptureCaps()
|
||||
@@ -370,6 +378,9 @@ void CameraBinSession::setAudioCaptureCaps()
|
||||
GstCaps *caps = gst_caps_new_full(structure, NULL);
|
||||
g_object_set(G_OBJECT(m_camerabin), AUDIO_CAPTURE_CAPS_PROPERTY, caps, NULL);
|
||||
gst_caps_unref(caps);
|
||||
|
||||
if (m_audioEncoder)
|
||||
m_audioEncodeControl->applySettings(m_audioEncoder);
|
||||
}
|
||||
|
||||
GstElement *CameraBinSession::buildCameraSource()
|
||||
@@ -712,13 +723,19 @@ void CameraBinSession::updateBusyStatus(GObject *o, GParamSpec *p, gpointer d)
|
||||
|
||||
qint64 CameraBinSession::duration() const
|
||||
{
|
||||
GstFormat format = GST_FORMAT_TIME;
|
||||
gint64 duration = 0;
|
||||
if (m_camerabin) {
|
||||
GstElement *fileSink = gst_bin_get_by_name(GST_BIN(m_camerabin), FILESINK_BIN_NAME);
|
||||
if (fileSink) {
|
||||
GstFormat format = GST_FORMAT_TIME;
|
||||
gint64 duration = 0;
|
||||
bool ret = gst_element_query_position(fileSink, &format, &duration);
|
||||
gst_object_unref(GST_OBJECT(fileSink));
|
||||
if (ret)
|
||||
return duration / 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_camerabin && gst_element_query_position(m_camerabin, &format, &duration))
|
||||
return duration / 1000000;
|
||||
else
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CameraBinSession::isMuted() const
|
||||
@@ -1293,4 +1310,32 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate,
|
||||
return res;
|
||||
}
|
||||
|
||||
void CameraBinSession::elementAdded(GstBin *, GstElement *element, CameraBinSession *session)
|
||||
{
|
||||
GstElementFactory *factory = gst_element_get_factory(element);
|
||||
|
||||
if (GST_IS_BIN(element)) {
|
||||
g_signal_connect(G_OBJECT(element), "element-added", G_CALLBACK(elementAdded), session);
|
||||
g_signal_connect(G_OBJECT(element), "element-removed", G_CALLBACK(elementRemoved), session);
|
||||
} else if (!factory) {
|
||||
// no-op
|
||||
} else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER)) {
|
||||
session->m_audioEncoder = element;
|
||||
|
||||
session->m_audioEncodeControl->applySettings(element);
|
||||
} else if (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER)) {
|
||||
session->m_videoEncoder = element;
|
||||
|
||||
session->m_videoEncodeControl->applySettings(element);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraBinSession::elementRemoved(GstBin *, GstElement *element, CameraBinSession *session)
|
||||
{
|
||||
if (element == session->m_audioEncoder)
|
||||
session->m_audioEncoder = 0;
|
||||
else if (element == session->m_videoEncoder)
|
||||
session->m_videoEncoder = 0;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -190,6 +190,9 @@ private:
|
||||
void setAudioCaptureCaps();
|
||||
static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d);
|
||||
|
||||
static void elementAdded(GstBin *bin, GstElement *element, CameraBinSession *session);
|
||||
static void elementRemoved(GstBin *bin, GstElement *element, CameraBinSession *session);
|
||||
|
||||
QUrl m_sink;
|
||||
QUrl m_actualSink;
|
||||
bool m_recordingActive;
|
||||
@@ -241,6 +244,7 @@ private:
|
||||
GstElement *m_capsFilter;
|
||||
GstElement *m_fileSink;
|
||||
GstElement *m_audioEncoder;
|
||||
GstElement *m_videoEncoder;
|
||||
GstElement *m_muxer;
|
||||
|
||||
public:
|
||||
|
||||
@@ -175,4 +175,46 @@ GstEncodingProfile *CameraBinVideoEncoder::createProfile()
|
||||
return (GstEncodingProfile *)profile;
|
||||
}
|
||||
|
||||
void CameraBinVideoEncoder::applySettings(GstElement *encoder)
|
||||
{
|
||||
GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder);
|
||||
const char * const name = gst_plugin_feature_get_name(
|
||||
GST_PLUGIN_FEATURE(gst_element_get_factory(encoder)));
|
||||
|
||||
const int bitRate = m_actualVideoSettings.bitRate();
|
||||
if (bitRate == -1) {
|
||||
// Bit rate is invalid, don't evaluate the remaining conditions.
|
||||
} else if (g_object_class_find_property(objectClass, "bitrate")) {
|
||||
g_object_set(G_OBJECT(encoder), "bitrate", bitRate, NULL);
|
||||
} else if (g_object_class_find_property(objectClass, "target-bitrate")) {
|
||||
g_object_set(G_OBJECT(encoder), "target-bitrate", bitRate, NULL);
|
||||
}
|
||||
|
||||
if (qstrcmp(name, "theoraenc") == 0) {
|
||||
static const int qualities[] = { 8, 16, 32, 45, 60 };
|
||||
g_object_set(G_OBJECT(encoder), "quality", qualities[m_actualVideoSettings.quality()], NULL);
|
||||
} else if (qstrncmp(name, "avenc_", 6) == 0) {
|
||||
if (g_object_class_find_property(objectClass, "pass")) {
|
||||
static const int modes[] = { 0, 2, 512, 1024 };
|
||||
g_object_set(G_OBJECT(encoder), "pass", modes[m_actualVideoSettings.encodingMode()], NULL);
|
||||
}
|
||||
if (g_object_class_find_property(objectClass, "quantizer")) {
|
||||
static const double qualities[] = { 20, 8.0, 3.0, 2.5, 2.0 };
|
||||
g_object_set(G_OBJECT(encoder), "quantizer", qualities[m_actualVideoSettings.quality()], NULL);
|
||||
}
|
||||
} else if (qstrncmp(name, "omx", 3) == 0) {
|
||||
if (!g_object_class_find_property(objectClass, "control-rate")) {
|
||||
} else switch (m_actualVideoSettings.encodingMode()) {
|
||||
case QMultimedia::ConstantBitRateEncoding:
|
||||
g_object_set(G_OBJECT(encoder), "control-rate", 2, NULL);
|
||||
break;
|
||||
case QMultimedia::AverageBitRateEncoding:
|
||||
g_object_set(G_OBJECT(encoder), "control-rate", 1, NULL);
|
||||
break;
|
||||
default:
|
||||
g_object_set(G_OBJECT(encoder), "control-rate", 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -76,6 +76,8 @@ public:
|
||||
|
||||
GstEncodingProfile *createProfile();
|
||||
|
||||
void applySettings(GstElement *encoder);
|
||||
|
||||
Q_SIGNALS:
|
||||
void settingsChanged();
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ HEADERS += $$PWD/qgstreamercaptureservice.h \
|
||||
$$PWD/qgstreamerrecordercontrol.h \
|
||||
$$PWD/qgstreamermediacontainercontrol.h \
|
||||
$$PWD/qgstreamercameracontrol.h \
|
||||
$$PWD/qgstreamerv4l2input.h \
|
||||
$$PWD/qgstreamercapturemetadatacontrol.h \
|
||||
$$PWD/qgstreamerimagecapturecontrol.h \
|
||||
$$PWD/qgstreamerimageencode.h \
|
||||
@@ -28,7 +27,6 @@ SOURCES += $$PWD/qgstreamercaptureservice.cpp \
|
||||
$$PWD/qgstreamerrecordercontrol.cpp \
|
||||
$$PWD/qgstreamermediacontainercontrol.cpp \
|
||||
$$PWD/qgstreamercameracontrol.cpp \
|
||||
$$PWD/qgstreamerv4l2input.cpp \
|
||||
$$PWD/qgstreamercapturemetadatacontrol.cpp \
|
||||
$$PWD/qgstreamerimagecapturecontrol.cpp \
|
||||
$$PWD/qgstreamerimageencode.cpp \
|
||||
@@ -37,13 +35,18 @@ SOURCES += $$PWD/qgstreamercaptureservice.cpp \
|
||||
# Camera usage with gstreamer needs to have
|
||||
#CONFIG += use_gstreamer_camera
|
||||
|
||||
use_gstreamer_camera {
|
||||
DEFINES += USE_GSTREAMER_CAMERA
|
||||
use_gstreamer_camera:config_linux_v4l {
|
||||
DEFINES += USE_GSTREAMER_CAMERA
|
||||
|
||||
OTHER_FILES += \
|
||||
mediacapturecamera.json
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qgstreamerv4l2input.h
|
||||
SOURCES += \
|
||||
$$PWD/qgstreamerv4l2input.cpp
|
||||
|
||||
OTHER_FILES += \
|
||||
mediacapturecamera.json
|
||||
} else {
|
||||
OTHER_FILES += \
|
||||
mediacapture.json
|
||||
OTHER_FILES += \
|
||||
mediacapture.json
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,12 @@
|
||||
#include "qgstreamerimageencode.h"
|
||||
#include "qgstreamercameracontrol.h"
|
||||
#include <private/qgstreamerbushelper_p.h>
|
||||
#include "qgstreamerv4l2input.h"
|
||||
#include "qgstreamercapturemetadatacontrol.h"
|
||||
|
||||
#if defined(USE_GSTREAMER_CAMERA)
|
||||
#include "qgstreamerv4l2input.h"
|
||||
#endif
|
||||
|
||||
#include "qgstreamerimagecapturecontrol.h"
|
||||
#include <private/qgstreameraudioinputselector_p.h>
|
||||
#include <private/qgstreamervideoinputdevicecontrol_p.h>
|
||||
@@ -66,7 +69,9 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje
|
||||
m_cameraControl = 0;
|
||||
m_metaDataControl = 0;
|
||||
|
||||
#if defined(USE_GSTREAMER_CAMERA)
|
||||
m_videoInput = 0;
|
||||
#endif
|
||||
m_audioInputSelector = 0;
|
||||
m_videoInputDevice = 0;
|
||||
|
||||
@@ -82,6 +87,7 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje
|
||||
m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this);
|
||||
}
|
||||
|
||||
#if defined(USE_GSTREAMER_CAMERA)
|
||||
if (service == Q_MEDIASERVICE_CAMERA) {
|
||||
m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::AudioAndVideo, this);
|
||||
m_cameraControl = new QGstreamerCameraControl(m_captureSession);
|
||||
@@ -103,6 +109,7 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje
|
||||
#endif
|
||||
m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_audioInputSelector = new QGstreamerAudioInputSelector(this);
|
||||
connect(m_audioInputSelector, SIGNAL(activeInputChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString)));
|
||||
|
||||
@@ -70,7 +70,9 @@ private:
|
||||
|
||||
QGstreamerCaptureSession *m_captureSession;
|
||||
QGstreamerCameraControl *m_cameraControl;
|
||||
#if defined(USE_GSTREAMER_CAMERA)
|
||||
QGstreamerV4L2Input *m_videoInput;
|
||||
#endif
|
||||
QGstreamerCaptureMetaDataControl *m_metaDataControl;
|
||||
|
||||
QAudioInputSelectorControl *m_audioInputSelector;
|
||||
|
||||
@@ -76,8 +76,10 @@ static const QGstreamerMetaDataKeyLookup *qt_gstreamerMetaDataKeys()
|
||||
|
||||
// Music
|
||||
metadataKeys->insert(GST_TAG_ALBUM, QMediaMetaData::AlbumTitle);
|
||||
metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::AlbumArtist);
|
||||
metadataKeys->insert(GST_TAG_PERFORMER, QMediaMetaData::ContributingArtist);
|
||||
#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 25)
|
||||
metadataKeys->insert(GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist);
|
||||
#endif
|
||||
metadataKeys->insert(GST_TAG_ARTIST, QMediaMetaData::ContributingArtist);
|
||||
#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19)
|
||||
metadataKeys->insert(GST_TAG_COMPOSER, QMediaMetaData::Composer);
|
||||
#endif
|
||||
@@ -164,6 +166,11 @@ void QGstreamerMetaDataProvider::updateTags()
|
||||
}
|
||||
}
|
||||
|
||||
if (oldTags.isEmpty() != m_tags.isEmpty()) {
|
||||
emit metaDataAvailableChanged(isMetaDataAvailable());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
emit metaDataChanged();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio
|
||||
, m_currentState(QMediaPlayer::StoppedState)
|
||||
, m_mediaStatus(QMediaPlayer::NoMedia)
|
||||
, m_bufferProgress(-1)
|
||||
, m_seekToStartPending(false)
|
||||
, m_pendingSeekPosition(-1)
|
||||
, m_setMediaPending(false)
|
||||
, m_stream(0)
|
||||
@@ -69,7 +68,7 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio
|
||||
Q_ASSERT(m_resources);
|
||||
|
||||
connect(m_session, SIGNAL(positionChanged(qint64)),
|
||||
this, SLOT(updatePosition(qint64)));
|
||||
this, SIGNAL(positionChanged(qint64)));
|
||||
connect(m_session, SIGNAL(durationChanged(qint64)),
|
||||
this, SIGNAL(durationChanged(qint64)));
|
||||
connect(m_session, SIGNAL(mutedStateChanged(bool)),
|
||||
@@ -94,8 +93,6 @@ QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *sessio
|
||||
this, SLOT(handleInvalidMedia()));
|
||||
connect(m_session, SIGNAL(playbackRateChanged(qreal)),
|
||||
this, SIGNAL(playbackRateChanged(qreal)));
|
||||
connect(m_session, SIGNAL(seekableChanged(bool)),
|
||||
this, SLOT(applyPendingSeek(bool)));
|
||||
|
||||
connect(m_resources, SIGNAL(resourcesGranted()), SLOT(handleResourcesGranted()));
|
||||
//denied signal should be queued to have correct state update process,
|
||||
@@ -117,7 +114,7 @@ QMediaPlayerResourceSetInterface* QGstreamerPlayerControl::resources() const
|
||||
|
||||
qint64 QGstreamerPlayerControl::position() const
|
||||
{
|
||||
return m_seekToStartPending ? 0 : m_session->position();
|
||||
return m_pendingSeekPosition != -1 ? m_pendingSeekPosition : m_session->position();
|
||||
}
|
||||
|
||||
qint64 QGstreamerPlayerControl::duration() const
|
||||
@@ -183,15 +180,21 @@ void QGstreamerPlayerControl::setPosition(qint64 pos)
|
||||
|
||||
if (m_mediaStatus == QMediaPlayer::EndOfMedia) {
|
||||
m_mediaStatus = QMediaPlayer::LoadedMedia;
|
||||
m_seekToStartPending = true;
|
||||
}
|
||||
|
||||
if (m_session->isSeekable() && m_session->seek(pos)) {
|
||||
m_seekToStartPending = false;
|
||||
} else {
|
||||
if (m_currentState == QMediaPlayer::StoppedState) {
|
||||
m_pendingSeekPosition = pos;
|
||||
//don't display the first video frame since it's not what user requested.
|
||||
m_session->showPrerollFrames(false);
|
||||
emit positionChanged(m_pendingSeekPosition);
|
||||
} else if (m_session->isSeekable()) {
|
||||
m_session->showPrerollFrames(true);
|
||||
m_session->seek(pos);
|
||||
m_pendingSeekPosition = -1;
|
||||
} else if (m_session->state() == QMediaPlayer::StoppedState) {
|
||||
m_pendingSeekPosition = pos;
|
||||
emit positionChanged(m_pendingSeekPosition);
|
||||
} else if (m_pendingSeekPosition != -1) {
|
||||
m_pendingSeekPosition = -1;
|
||||
emit positionChanged(m_pendingSeekPosition);
|
||||
}
|
||||
|
||||
popAndNotifyState();
|
||||
@@ -239,26 +242,30 @@ void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_mediaStatus == QMediaPlayer::EndOfMedia && m_pendingSeekPosition == -1) {
|
||||
m_pendingSeekPosition = 0;
|
||||
}
|
||||
|
||||
if (!m_resources->isGranted())
|
||||
m_resources->acquire();
|
||||
|
||||
if (m_resources->isGranted()) {
|
||||
if (m_seekToStartPending) {
|
||||
// show prerolled frame if switching from stopped state
|
||||
if (m_pendingSeekPosition == -1) {
|
||||
m_session->showPrerollFrames(true);
|
||||
} else if (m_session->state() == QMediaPlayer::StoppedState) {
|
||||
// Don't evaluate the next two conditions.
|
||||
} else if (m_session->isSeekable()) {
|
||||
m_session->pause();
|
||||
if (!m_session->seek(0)) {
|
||||
m_bufferProgress = -1;
|
||||
m_session->stop();
|
||||
m_mediaStatus = QMediaPlayer::LoadingMedia;
|
||||
}
|
||||
m_seekToStartPending = false;
|
||||
m_session->showPrerollFrames(true);
|
||||
m_session->seek(m_pendingSeekPosition);
|
||||
m_pendingSeekPosition = -1;
|
||||
} else {
|
||||
m_pendingSeekPosition = -1;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
|
||||
// show prerolled frame if switching from stopped state
|
||||
if (newState != QMediaPlayer::StoppedState && m_currentState == QMediaPlayer::StoppedState && m_pendingSeekPosition == -1)
|
||||
m_session->showPrerollFrames(true);
|
||||
|
||||
//To prevent displaying the first video frame when playback is resumed
|
||||
//the pipeline is paused instead of playing, seeked to requested position,
|
||||
//and after seeking is finished (position updated) playback is restarted
|
||||
@@ -305,7 +312,7 @@ void QGstreamerPlayerControl::stop()
|
||||
m_session->pause();
|
||||
|
||||
if (m_mediaStatus != QMediaPlayer::EndOfMedia) {
|
||||
m_seekToStartPending = true;
|
||||
m_pendingSeekPosition = 0;
|
||||
emit positionChanged(position());
|
||||
}
|
||||
}
|
||||
@@ -343,7 +350,7 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *
|
||||
|
||||
m_currentState = QMediaPlayer::StoppedState;
|
||||
QMediaContent oldMedia = m_currentResource;
|
||||
m_pendingSeekPosition = -1;
|
||||
m_pendingSeekPosition = 0;
|
||||
m_session->showPrerollFrames(false); // do not show prerolled frames until pause() or play() explicitly called
|
||||
m_setMediaPending = false;
|
||||
|
||||
@@ -390,7 +397,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *
|
||||
|
||||
m_currentResource = content;
|
||||
m_stream = stream;
|
||||
m_seekToStartPending = false;
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
@@ -462,8 +468,21 @@ void QGstreamerPlayerControl::updateSessionState(QMediaPlayer::State state)
|
||||
{
|
||||
pushState();
|
||||
|
||||
if (state == QMediaPlayer::StoppedState)
|
||||
if (state == QMediaPlayer::StoppedState) {
|
||||
m_session->showPrerollFrames(false);
|
||||
m_currentState = QMediaPlayer::StoppedState;
|
||||
}
|
||||
|
||||
if (state == QMediaPlayer::PausedState && m_currentState != QMediaPlayer::StoppedState) {
|
||||
if (m_pendingSeekPosition != -1 && m_session->isSeekable()) {
|
||||
m_session->showPrerollFrames(true);
|
||||
m_session->seek(m_pendingSeekPosition);
|
||||
}
|
||||
m_pendingSeekPosition = -1;
|
||||
|
||||
if (m_currentState == QMediaPlayer::PlayingState)
|
||||
m_session->play();
|
||||
}
|
||||
|
||||
updateMediaStatus();
|
||||
|
||||
@@ -512,7 +531,6 @@ void QGstreamerPlayerControl::processEOS()
|
||||
m_mediaStatus = QMediaPlayer::EndOfMedia;
|
||||
emit positionChanged(position());
|
||||
m_session->endOfMediaReset();
|
||||
m_setMediaPending = true;
|
||||
|
||||
if (m_currentState != QMediaPlayer::StoppedState) {
|
||||
m_currentState = QMediaPlayer::StoppedState;
|
||||
@@ -549,17 +567,12 @@ void QGstreamerPlayerControl::setBufferProgress(int progress)
|
||||
emit bufferStatusChanged(m_bufferProgress);
|
||||
}
|
||||
|
||||
void QGstreamerPlayerControl::applyPendingSeek(bool isSeekable)
|
||||
{
|
||||
if (isSeekable && m_pendingSeekPosition != -1)
|
||||
setPosition(m_pendingSeekPosition);
|
||||
}
|
||||
|
||||
void QGstreamerPlayerControl::handleInvalidMedia()
|
||||
{
|
||||
pushState();
|
||||
m_mediaStatus = QMediaPlayer::InvalidMedia;
|
||||
m_currentState = QMediaPlayer::StoppedState;
|
||||
m_setMediaPending = true;
|
||||
popAndNotifyState();
|
||||
}
|
||||
|
||||
@@ -636,24 +649,4 @@ void QGstreamerPlayerControl::popAndNotifyState()
|
||||
}
|
||||
}
|
||||
|
||||
void QGstreamerPlayerControl::updatePosition(qint64 pos)
|
||||
{
|
||||
#ifdef DEBUG_PLAYBIN
|
||||
qDebug() << Q_FUNC_INFO << pos/1000.0 << "pending:" << m_pendingSeekPosition/1000.0;
|
||||
#endif
|
||||
|
||||
if (m_pendingSeekPosition != -1) {
|
||||
//seek request is complete, it's safe to resume playback
|
||||
//with prerolled frame displayed
|
||||
m_pendingSeekPosition = -1;
|
||||
if (m_currentState != QMediaPlayer::StoppedState)
|
||||
m_session->showPrerollFrames(true);
|
||||
if (m_currentState == QMediaPlayer::PlayingState) {
|
||||
m_session->play();
|
||||
}
|
||||
}
|
||||
|
||||
emit positionChanged(pos);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -103,8 +103,6 @@ private Q_SLOTS:
|
||||
void updateMediaStatus();
|
||||
void processEOS();
|
||||
void setBufferProgress(int progress);
|
||||
void applyPendingSeek(bool isSeekable);
|
||||
void updatePosition(qint64 pos);
|
||||
|
||||
void handleInvalidMedia();
|
||||
|
||||
@@ -127,7 +125,6 @@ private:
|
||||
QStack<QMediaPlayer::MediaStatus> m_mediaStatusStack;
|
||||
|
||||
int m_bufferProgress;
|
||||
bool m_seekToStartPending;
|
||||
qint64 m_pendingSeekPosition;
|
||||
bool m_setMediaPending;
|
||||
QMediaContent m_currentResource;
|
||||
|
||||
@@ -474,7 +474,7 @@ qint64 QOpenSLESAudioInput::elapsedUSecs() const
|
||||
if (m_deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return m_clockStamp.elapsed() * 1000;
|
||||
return m_clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QOpenSLESAudioInput::setVolume(qreal vol)
|
||||
|
||||
@@ -70,7 +70,9 @@ QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device)
|
||||
m_periodSize(0),
|
||||
m_elapsedTime(0),
|
||||
m_processedBytes(0),
|
||||
m_availableBuffers(BUFFER_COUNT)
|
||||
m_availableBuffers(BUFFER_COUNT),
|
||||
m_eventMask(SL_PLAYEVENT_HEADATEND),
|
||||
m_startRequiresInit(true)
|
||||
{
|
||||
#ifndef ANDROID
|
||||
m_streamType = -1;
|
||||
@@ -98,13 +100,10 @@ QAudio::State QOpenSLESAudioOutput::state() const
|
||||
void QOpenSLESAudioOutput::start(QIODevice *device)
|
||||
{
|
||||
Q_ASSERT(device);
|
||||
destroyPlayer();
|
||||
|
||||
m_pullMode = true;
|
||||
|
||||
if (!preparePlayer())
|
||||
return;
|
||||
|
||||
m_pullMode = true;
|
||||
m_audioSource = device;
|
||||
setState(QAudio::ActiveState);
|
||||
setError(QAudio::NoError);
|
||||
@@ -125,29 +124,20 @@ void QOpenSLESAudioOutput::start(QIODevice *device)
|
||||
|
||||
// Change the state to playing.
|
||||
// We need to do this after filling the buffers or processedBytes might get corrupted.
|
||||
if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
|
||||
setError(QAudio::FatalError);
|
||||
destroyPlayer();
|
||||
}
|
||||
startPlayer();
|
||||
}
|
||||
|
||||
QIODevice *QOpenSLESAudioOutput::start()
|
||||
{
|
||||
destroyPlayer();
|
||||
|
||||
m_pullMode = false;
|
||||
|
||||
if (!preparePlayer())
|
||||
return Q_NULLPTR;
|
||||
|
||||
m_pullMode = false;
|
||||
m_audioSource = new SLIODevicePrivate(this);
|
||||
m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
|
||||
|
||||
// Change the state to playing
|
||||
if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
|
||||
setError(QAudio::FatalError);
|
||||
destroyPlayer();
|
||||
}
|
||||
startPlayer();
|
||||
|
||||
setState(QAudio::IdleState);
|
||||
return m_audioSource;
|
||||
@@ -158,7 +148,7 @@ void QOpenSLESAudioOutput::stop()
|
||||
if (m_state == QAudio::StoppedState)
|
||||
return;
|
||||
|
||||
destroyPlayer();
|
||||
stopPlayer();
|
||||
setError(QAudio::NoError);
|
||||
}
|
||||
|
||||
@@ -180,6 +170,7 @@ void QOpenSLESAudioOutput::setBufferSize(int value)
|
||||
if (m_state != QAudio::StoppedState)
|
||||
return;
|
||||
|
||||
m_startRequiresInit = true;
|
||||
m_bufferSize = value;
|
||||
}
|
||||
|
||||
@@ -190,7 +181,33 @@ int QOpenSLESAudioOutput::bufferSize() const
|
||||
|
||||
void QOpenSLESAudioOutput::setNotifyInterval(int ms)
|
||||
{
|
||||
m_notifyInterval = ms > 0 ? ms : 0;
|
||||
const int newInterval = ms > 0 ? ms : 0;
|
||||
|
||||
if (newInterval == m_notifyInterval)
|
||||
return;
|
||||
|
||||
const SLuint32 newEvenMask = newInterval == 0 ? m_eventMask & ~SL_PLAYEVENT_HEADATNEWPOS
|
||||
: m_eventMask & SL_PLAYEVENT_HEADATNEWPOS;
|
||||
|
||||
if (m_state == QAudio::StoppedState) {
|
||||
m_eventMask = newEvenMask;
|
||||
m_notifyInterval = newInterval;
|
||||
return;
|
||||
}
|
||||
|
||||
if (newEvenMask != m_eventMask
|
||||
&& SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, newEvenMask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_eventMask = newEvenMask;
|
||||
|
||||
if (newInterval && SL_RESULT_SUCCESS != (*m_playItf)->SetPositionUpdatePeriod(m_playItf,
|
||||
newInterval)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_notifyInterval = newInterval;
|
||||
}
|
||||
|
||||
int QOpenSLESAudioOutput::notifyInterval() const
|
||||
@@ -227,6 +244,7 @@ void QOpenSLESAudioOutput::resume()
|
||||
|
||||
void QOpenSLESAudioOutput::setFormat(const QAudioFormat &format)
|
||||
{
|
||||
m_startRequiresInit = true;
|
||||
m_format = format;
|
||||
}
|
||||
|
||||
@@ -255,7 +273,7 @@ qint64 QOpenSLESAudioOutput::elapsedUSecs() const
|
||||
if (m_state == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return m_clockStamp.elapsed() * 1000;
|
||||
return m_clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QOpenSLESAudioOutput::reset()
|
||||
@@ -298,6 +316,7 @@ void QOpenSLESAudioOutput::setCategory(const QString &category)
|
||||
return;
|
||||
}
|
||||
|
||||
m_startRequiresInit = true;
|
||||
m_streamType = streamType;
|
||||
m_category = category;
|
||||
#endif // ANDROID
|
||||
@@ -376,6 +395,11 @@ void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, voi
|
||||
|
||||
bool QOpenSLESAudioOutput::preparePlayer()
|
||||
{
|
||||
if (m_startRequiresInit)
|
||||
destroyPlayer();
|
||||
else
|
||||
return true;
|
||||
|
||||
SLEngineItf engine = QOpenSLESEngine::instance()->slEngine();
|
||||
if (!engine) {
|
||||
qWarning() << "No engine";
|
||||
@@ -480,13 +504,12 @@ bool QOpenSLESAudioOutput::preparePlayer()
|
||||
return false;
|
||||
}
|
||||
|
||||
SLuint32 mask = SL_PLAYEVENT_HEADATEND;
|
||||
if (m_notifyInterval && SL_RESULT_SUCCESS == (*m_playItf)->SetPositionUpdatePeriod(m_playItf,
|
||||
m_notifyInterval)) {
|
||||
mask |= SL_PLAYEVENT_HEADATNEWPOS;
|
||||
m_eventMask |= SL_PLAYEVENT_HEADATNEWPOS;
|
||||
}
|
||||
|
||||
if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, mask)) {
|
||||
if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, m_eventMask)) {
|
||||
setError(QAudio::FatalError);
|
||||
return false;
|
||||
}
|
||||
@@ -517,20 +540,15 @@ bool QOpenSLESAudioOutput::preparePlayer()
|
||||
|
||||
m_clockStamp.restart();
|
||||
setError(QAudio::NoError);
|
||||
m_startRequiresInit = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QOpenSLESAudioOutput::destroyPlayer()
|
||||
{
|
||||
setState(QAudio::StoppedState);
|
||||
|
||||
// We need to change the state manually...
|
||||
if (m_playItf)
|
||||
(*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);
|
||||
|
||||
if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf))
|
||||
qWarning() << "Unable to clear buffer";
|
||||
if (m_state != QAudio::StoppedState)
|
||||
stopPlayer();
|
||||
|
||||
if (m_playerObject) {
|
||||
(*m_playerObject)->Destroy(m_playerObject);
|
||||
@@ -556,6 +574,27 @@ void QOpenSLESAudioOutput::destroyPlayer()
|
||||
m_playItf = Q_NULLPTR;
|
||||
m_volumeItf = Q_NULLPTR;
|
||||
m_bufferQueueItf = Q_NULLPTR;
|
||||
m_startRequiresInit = true;
|
||||
}
|
||||
|
||||
void QOpenSLESAudioOutput::stopPlayer()
|
||||
{
|
||||
setState(QAudio::StoppedState);
|
||||
|
||||
// We need to change the state manually...
|
||||
if (m_playItf)
|
||||
(*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);
|
||||
|
||||
if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf))
|
||||
qWarning() << "Unable to clear buffer";
|
||||
}
|
||||
|
||||
void QOpenSLESAudioOutput::startPlayer()
|
||||
{
|
||||
if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) {
|
||||
setError(QAudio::FatalError);
|
||||
destroyPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len)
|
||||
|
||||
@@ -86,6 +86,8 @@ private:
|
||||
|
||||
bool preparePlayer();
|
||||
void destroyPlayer();
|
||||
void stopPlayer();
|
||||
void startPlayer();
|
||||
qint64 writeData(const char *data, qint64 len);
|
||||
|
||||
void setState(QAudio::State state);
|
||||
@@ -112,6 +114,8 @@ private:
|
||||
qint64 m_elapsedTime;
|
||||
qint64 m_processedBytes;
|
||||
QAtomicInt m_availableBuffers;
|
||||
SLuint32 m_eventMask;
|
||||
bool m_startRequiresInit;
|
||||
|
||||
qint32 m_streamType;
|
||||
QTime m_clockStamp;
|
||||
|
||||
@@ -44,15 +44,15 @@ unix:!mac:!android {
|
||||
config_alsa: SUBDIRS += alsa
|
||||
|
||||
# v4l is turned off because it is not supported in Qt 5
|
||||
# !maemo*:SUBDIRS += v4l
|
||||
# config_linux_v4l {
|
||||
# !maemo*:SUBDIRS += v4l
|
||||
# }
|
||||
}
|
||||
|
||||
mac:!simulator {
|
||||
SUBDIRS += audiocapture coreaudio
|
||||
|
||||
config_avfoundation: SUBDIRS += avfoundation
|
||||
|
||||
contains(QT_CONFIG, opengl.*):!ios: SUBDIRS += qt7
|
||||
}
|
||||
|
||||
config_resourcepolicy {
|
||||
|
||||
@@ -684,7 +684,7 @@ qint64 QPulseAudioInput::elapsedUSecs() const
|
||||
if (m_deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return m_clockStamp.elapsed() * 1000;
|
||||
return m_clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QPulseAudioInput::reset()
|
||||
|
||||
@@ -596,7 +596,7 @@ qint64 QPulseAudioOutput::elapsedUSecs() const
|
||||
if (m_deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return m_clockStamp.elapsed() * 1000;
|
||||
return m_clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QPulseAudioOutput::reset()
|
||||
|
||||
@@ -186,7 +186,7 @@ qint64 QnxAudioInput::elapsedUSecs() const
|
||||
if (m_state == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return m_clockStamp.elapsed() * 1000;
|
||||
return m_clockStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
QAudio::Error QnxAudioInput::error() const
|
||||
|
||||
@@ -172,7 +172,7 @@ qint64 QnxAudioOutput::elapsedUSecs() const
|
||||
if (m_state == QAudio::StoppedState)
|
||||
return 0;
|
||||
else
|
||||
return m_startTimeStamp.elapsed() * 1000;
|
||||
return m_startTimeStamp.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
QAudio::Error QnxAudioOutput::error() const
|
||||
|
||||
@@ -538,6 +538,7 @@ void MmRendererMediaPlayerControl::play()
|
||||
return;
|
||||
}
|
||||
|
||||
m_stopEventsToIgnore = 0; // once playing, stop events must be proccessed
|
||||
setState( QMediaPlayer::PlayingState);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
DEFINES += QMEDIA_QT7_PLAYER
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qt7playercontrol.h \
|
||||
$$PWD/qt7playermetadata.h \
|
||||
$$PWD/qt7playerservice.h \
|
||||
$$PWD/qt7playersession.h
|
||||
|
||||
OBJECTIVE_SOURCES += \
|
||||
$$PWD/qt7playercontrol.mm \
|
||||
$$PWD/qt7playermetadata.mm \
|
||||
$$PWD/qt7playerservice.mm \
|
||||
$$PWD/qt7playersession.mm
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7PLAYERCONTROL_H
|
||||
#define QT7PLAYERCONTROL_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
#include <qmediaplayercontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
class QMediaPlaylist;
|
||||
class QMediaPlaylistNavigator;
|
||||
|
||||
class QT7PlayerControl : public QMediaPlayerControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QT7PlayerControl(QObject *parent = 0);
|
||||
~QT7PlayerControl();
|
||||
|
||||
void setSession(QT7PlayerSession *session);
|
||||
|
||||
QMediaPlayer::State state() const;
|
||||
QMediaPlayer::MediaStatus mediaStatus() const;
|
||||
|
||||
QMediaContent media() const;
|
||||
const QIODevice *mediaStream() const;
|
||||
void setMedia(const QMediaContent &content, QIODevice *stream);
|
||||
|
||||
qint64 position() const;
|
||||
qint64 duration() const;
|
||||
|
||||
int bufferStatus() const;
|
||||
|
||||
int volume() const;
|
||||
bool isMuted() const;
|
||||
|
||||
bool isAudioAvailable() const;
|
||||
bool isVideoAvailable() const;
|
||||
|
||||
bool isSeekable() const;
|
||||
QMediaTimeRange availablePlaybackRanges() const;
|
||||
|
||||
qreal playbackRate() const;
|
||||
void setPlaybackRate(qreal rate);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setPosition(qint64 pos);
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
void stop();
|
||||
|
||||
void setVolume(int volume);
|
||||
void setMuted(bool muted);
|
||||
|
||||
private:
|
||||
QT7PlayerSession *m_session;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,191 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7playersession.h"
|
||||
|
||||
#include <private/qmediaplaylistnavigator_p.h>
|
||||
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
QT7PlayerControl::QT7PlayerControl(QObject *parent)
|
||||
: QMediaPlayerControl(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QT7PlayerControl::~QT7PlayerControl()
|
||||
{
|
||||
}
|
||||
|
||||
void QT7PlayerControl::setSession(QT7PlayerSession *session)
|
||||
{
|
||||
m_session = session;
|
||||
|
||||
connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
|
||||
connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
|
||||
connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)),
|
||||
this, SIGNAL(stateChanged(QMediaPlayer::State)));
|
||||
connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
|
||||
this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
|
||||
connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int)));
|
||||
connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
|
||||
connect(m_session, SIGNAL(audioAvailableChanged(bool)), this, SIGNAL(audioAvailableChanged(bool)));
|
||||
connect(m_session, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(videoAvailableChanged(bool)));
|
||||
connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
|
||||
}
|
||||
|
||||
qint64 QT7PlayerControl::position() const
|
||||
{
|
||||
return m_session->position();
|
||||
}
|
||||
|
||||
qint64 QT7PlayerControl::duration() const
|
||||
{
|
||||
return m_session->duration();
|
||||
}
|
||||
|
||||
QMediaPlayer::State QT7PlayerControl::state() const
|
||||
{
|
||||
return m_session->state();
|
||||
}
|
||||
|
||||
QMediaPlayer::MediaStatus QT7PlayerControl::mediaStatus() const
|
||||
{
|
||||
return m_session->mediaStatus();
|
||||
}
|
||||
|
||||
int QT7PlayerControl::bufferStatus() const
|
||||
{
|
||||
return m_session->bufferStatus();
|
||||
}
|
||||
|
||||
int QT7PlayerControl::volume() const
|
||||
{
|
||||
return m_session->volume();
|
||||
}
|
||||
|
||||
bool QT7PlayerControl::isMuted() const
|
||||
{
|
||||
return m_session->isMuted();
|
||||
}
|
||||
|
||||
bool QT7PlayerControl::isSeekable() const
|
||||
{
|
||||
return m_session->isSeekable();
|
||||
}
|
||||
|
||||
QMediaTimeRange QT7PlayerControl::availablePlaybackRanges() const
|
||||
{
|
||||
return m_session->availablePlaybackRanges();
|
||||
}
|
||||
|
||||
qreal QT7PlayerControl::playbackRate() const
|
||||
{
|
||||
return m_session->playbackRate();
|
||||
}
|
||||
|
||||
void QT7PlayerControl::setPlaybackRate(qreal rate)
|
||||
{
|
||||
m_session->setPlaybackRate(rate);
|
||||
}
|
||||
|
||||
void QT7PlayerControl::setPosition(qint64 pos)
|
||||
{
|
||||
m_session->setPosition(pos);
|
||||
}
|
||||
|
||||
void QT7PlayerControl::play()
|
||||
{
|
||||
m_session->play();
|
||||
}
|
||||
|
||||
void QT7PlayerControl::pause()
|
||||
{
|
||||
m_session->pause();
|
||||
}
|
||||
|
||||
void QT7PlayerControl::stop()
|
||||
{
|
||||
m_session->stop();
|
||||
}
|
||||
|
||||
void QT7PlayerControl::setVolume(int volume)
|
||||
{
|
||||
m_session->setVolume(volume);
|
||||
}
|
||||
|
||||
void QT7PlayerControl::setMuted(bool muted)
|
||||
{
|
||||
m_session->setMuted(muted);
|
||||
}
|
||||
|
||||
QMediaContent QT7PlayerControl::media() const
|
||||
{
|
||||
return m_session->media();
|
||||
}
|
||||
|
||||
const QIODevice *QT7PlayerControl::mediaStream() const
|
||||
{
|
||||
return m_session->mediaStream();
|
||||
}
|
||||
|
||||
void QT7PlayerControl::setMedia(const QMediaContent &content, QIODevice *stream)
|
||||
{
|
||||
m_session->setMedia(content, stream);
|
||||
|
||||
Q_EMIT mediaChanged(content);
|
||||
}
|
||||
|
||||
bool QT7PlayerControl::isAudioAvailable() const
|
||||
{
|
||||
return m_session->isAudioAvailable();
|
||||
}
|
||||
|
||||
bool QT7PlayerControl::isVideoAvailable() const
|
||||
{
|
||||
return m_session->isVideoAvailable();
|
||||
}
|
||||
|
||||
|
||||
#include "moc_qt7playercontrol.cpp"
|
||||
@@ -1,66 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7PLAYERMETADATACONTROL_H
|
||||
#define QT7PLAYERMETADATACONTROL_H
|
||||
|
||||
#include <qmetadatareadercontrol.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QT7PlayerSession;
|
||||
|
||||
class QT7PlayerMetaDataControl : public QMetaDataReaderControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent);
|
||||
virtual ~QT7PlayerMetaDataControl();
|
||||
|
||||
bool isMetaDataAvailable() const;
|
||||
bool isWritable() const;
|
||||
|
||||
QVariant metaData(const QString &key) const;
|
||||
QStringList availableMetaData() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateTags();
|
||||
|
||||
private:
|
||||
QT7PlayerSession *m_session;
|
||||
QMap<QString, QVariant> m_tags;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,250 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qt7backend.h"
|
||||
#include "qt7playermetadata.h"
|
||||
#include "qt7playersession.h"
|
||||
#include <QtCore/qvarlengtharray.h>
|
||||
#include <QtMultimedia/qmediametadata.h>
|
||||
|
||||
#import <QTKit/QTMovie.h>
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
#include <QuickTime/QuickTime.h>
|
||||
#undef check // avoid name clash;
|
||||
#endif
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
QT7PlayerMetaDataControl::QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent)
|
||||
:QMetaDataReaderControl(parent), m_session(session)
|
||||
{
|
||||
}
|
||||
|
||||
QT7PlayerMetaDataControl::~QT7PlayerMetaDataControl()
|
||||
{
|
||||
}
|
||||
|
||||
bool QT7PlayerMetaDataControl::isMetaDataAvailable() const
|
||||
{
|
||||
return !m_tags.isEmpty();
|
||||
}
|
||||
|
||||
bool QT7PlayerMetaDataControl::isWritable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant QT7PlayerMetaDataControl::metaData(const QString &key) const
|
||||
{
|
||||
return m_tags.value(key);
|
||||
}
|
||||
|
||||
QStringList QT7PlayerMetaDataControl::availableMetaData() const
|
||||
{
|
||||
return m_tags.keys();
|
||||
}
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
static QString stripCopyRightSymbol(const QString &key)
|
||||
{
|
||||
return key.right(key.length()-1);
|
||||
}
|
||||
|
||||
static QString convertQuickTimeKeyToUserKey(const QString &key)
|
||||
{
|
||||
if (key == QLatin1String("com.apple.quicktime.displayname"))
|
||||
return QLatin1String("nam");
|
||||
else if (key == QLatin1String("com.apple.quicktime.album"))
|
||||
return QLatin1String("alb");
|
||||
else if (key == QLatin1String("com.apple.quicktime.artist"))
|
||||
return QLatin1String("ART");
|
||||
else
|
||||
return QLatin1String("???");
|
||||
}
|
||||
|
||||
static OSStatus readMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, QTPropertyClass propClass,
|
||||
QTPropertyID id, QTPropertyValuePtr *value, ByteCount *size)
|
||||
{
|
||||
QTPropertyValueType type;
|
||||
ByteCount propSize;
|
||||
UInt32 propFlags;
|
||||
OSStatus err = QTMetaDataGetItemPropertyInfo(metaDataRef, item, propClass, id, &type, &propSize, &propFlags);
|
||||
|
||||
if (err == noErr) {
|
||||
*value = malloc(propSize);
|
||||
if (*value != 0) {
|
||||
err = QTMetaDataGetItemProperty(metaDataRef, item, propClass, id, propSize, *value, size);
|
||||
|
||||
if (err == noErr && (type == 'code' || type == 'itsk' || type == 'itlk')) {
|
||||
// convert from native endian to big endian
|
||||
OSTypePtr pType = (OSTypePtr)*value;
|
||||
*pType = EndianU32_NtoB(*pType);
|
||||
}
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static UInt32 getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item)
|
||||
{
|
||||
QTPropertyValuePtr value = 0;
|
||||
ByteCount ignore = 0;
|
||||
OSStatus err = readMetaValue(
|
||||
metaDataRef, item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_DataType, &value, &ignore);
|
||||
|
||||
if (err == noErr) {
|
||||
UInt32 type = *((UInt32 *) value);
|
||||
if (value)
|
||||
free(value);
|
||||
return type;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QString cFStringToQString(CFStringRef str)
|
||||
{
|
||||
if(!str)
|
||||
return QString();
|
||||
CFIndex length = CFStringGetLength(str);
|
||||
const UniChar *chars = CFStringGetCharactersPtr(str);
|
||||
if (chars)
|
||||
return QString(reinterpret_cast<const QChar *>(chars), length);
|
||||
|
||||
QVarLengthArray<UniChar> buffer(length);
|
||||
CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
|
||||
return QString(reinterpret_cast<const QChar *>(buffer.constData()), length);
|
||||
}
|
||||
|
||||
|
||||
static QString getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id)
|
||||
{
|
||||
QTPropertyValuePtr value = 0;
|
||||
ByteCount size = 0;
|
||||
OSStatus err = readMetaValue(metaDataRef, item, kPropertyClass_MetaDataItem, id, &value, &size);
|
||||
QString string;
|
||||
|
||||
if (err == noErr) {
|
||||
UInt32 dataType = getMetaType(metaDataRef, item);
|
||||
switch (dataType){
|
||||
case kQTMetaDataTypeUTF8:
|
||||
case kQTMetaDataTypeMacEncodedText:
|
||||
string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF8, false));
|
||||
break;
|
||||
case kQTMetaDataTypeUTF16BE:
|
||||
string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF16BE, false));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (value)
|
||||
free(value);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
static void readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result)
|
||||
{
|
||||
QTMetaDataItem item = kQTMetaDataItemUninitialized;
|
||||
OSStatus err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item);
|
||||
while (err == noErr){
|
||||
QString key = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Key);
|
||||
if (format == kQTMetaDataStorageFormatQuickTime)
|
||||
key = convertQuickTimeKeyToUserKey(key);
|
||||
else
|
||||
key = stripCopyRightSymbol(key);
|
||||
|
||||
if (!result.contains(key)){
|
||||
QString val = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Value);
|
||||
result.insert(key, val);
|
||||
}
|
||||
err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void QT7PlayerMetaDataControl::updateTags()
|
||||
{
|
||||
bool wasEmpty = m_tags.isEmpty();
|
||||
m_tags.clear();
|
||||
|
||||
QTMovie *movie = (QTMovie*)m_session->movie();
|
||||
|
||||
if (movie) {
|
||||
QMultiMap<QString, QString> metaMap;
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
QTMetaDataRef metaDataRef;
|
||||
OSStatus err = QTCopyMovieMetaData([movie quickTimeMovie], &metaDataRef);
|
||||
if (err == noErr) {
|
||||
readFormattedData(metaDataRef, kQTMetaDataStorageFormatUserData, metaMap);
|
||||
readFormattedData(metaDataRef, kQTMetaDataStorageFormatQuickTime, metaMap);
|
||||
readFormattedData(metaDataRef, kQTMetaDataStorageFormatiTunes, metaMap);
|
||||
}
|
||||
#else
|
||||
AutoReleasePool pool;
|
||||
NSString *name = [movie attributeForKey:@"QTMovieDisplayNameAttribute"];
|
||||
metaMap.insert(QLatin1String("nam"), QString::fromUtf8([name UTF8String]));
|
||||
#endif // QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
m_tags.insert(QMediaMetaData::AlbumArtist, metaMap.value(QLatin1String("ART")));
|
||||
m_tags.insert(QMediaMetaData::AlbumTitle, metaMap.value(QLatin1String("alb")));
|
||||
m_tags.insert(QMediaMetaData::Title, metaMap.value(QLatin1String("nam")));
|
||||
m_tags.insert(QMediaMetaData::Date, metaMap.value(QLatin1String("day")));
|
||||
m_tags.insert(QMediaMetaData::Genre, metaMap.value(QLatin1String("gnre")));
|
||||
m_tags.insert(QMediaMetaData::TrackNumber, metaMap.value(QLatin1String("trk")));
|
||||
m_tags.insert(QMediaMetaData::Description, metaMap.value(QLatin1String("des")));
|
||||
}
|
||||
|
||||
if (!wasEmpty || !m_tags.isEmpty())
|
||||
Q_EMIT metaDataChanged();
|
||||
}
|
||||
|
||||
#include "moc_qt7playermetadata.cpp"
|
||||
@@ -1,73 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7PLAYERSERVICE_H
|
||||
#define QT7PLAYERSERVICE_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qset.h>
|
||||
#include <qmediaservice.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMediaPlayerControl;
|
||||
class QMediaPlaylist;
|
||||
class QMediaPlaylistNavigator;
|
||||
class QT7PlayerControl;
|
||||
class QT7PlayerMetaDataControl;
|
||||
class QT7VideoWindowControl;
|
||||
class QT7VideoWidgetControl;
|
||||
class QT7VideoRendererControl;
|
||||
class QT7VideoOutput;
|
||||
class QT7PlayerSession;
|
||||
|
||||
class QT7PlayerService : public QMediaService
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QT7PlayerService(QObject *parent = 0);
|
||||
~QT7PlayerService();
|
||||
|
||||
QMediaControl* requestControl(const char *name);
|
||||
void releaseControl(QMediaControl *control);
|
||||
|
||||
private:
|
||||
QT7PlayerSession *m_session;
|
||||
QT7PlayerControl *m_control;
|
||||
QMediaControl * m_videoOutput;
|
||||
QT7PlayerMetaDataControl *m_playerMetaDataControl;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,128 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include "qt7backend.h"
|
||||
#include "qt7playerservice.h"
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7playersession.h"
|
||||
#include "qt7videooutput.h"
|
||||
#include "qt7movieviewoutput.h"
|
||||
#include "qt7movieviewrenderer.h"
|
||||
#include "qt7movierenderer.h"
|
||||
#ifndef QT_NO_WIDGETS
|
||||
#include "qt7movievideowidget.h"
|
||||
#endif
|
||||
#include "qt7playermetadata.h"
|
||||
|
||||
#include <private/qmediaplaylistnavigator_p.h>
|
||||
#include <qmediaplaylist.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
QT7PlayerService::QT7PlayerService(QObject *parent):
|
||||
QMediaService(parent),
|
||||
m_videoOutput(0)
|
||||
{
|
||||
m_session = new QT7PlayerSession(this);
|
||||
|
||||
m_control = new QT7PlayerControl(this);
|
||||
m_control->setSession(m_session);
|
||||
|
||||
m_playerMetaDataControl = new QT7PlayerMetaDataControl(m_session, this);
|
||||
connect(m_control, SIGNAL(mediaChanged(QMediaContent)), m_playerMetaDataControl, SLOT(updateTags()));
|
||||
}
|
||||
|
||||
QT7PlayerService::~QT7PlayerService()
|
||||
{
|
||||
}
|
||||
|
||||
QMediaControl *QT7PlayerService::requestControl(const char *name)
|
||||
{
|
||||
if (qstrcmp(name, QMediaPlayerControl_iid) == 0)
|
||||
return m_control;
|
||||
|
||||
if (qstrcmp(name, QMetaDataReaderControl_iid) == 0)
|
||||
return m_playerMetaDataControl;
|
||||
|
||||
#ifndef QT_NO_OPENGL
|
||||
if (!m_videoOutput) {
|
||||
if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
|
||||
m_videoOutput = new QT7MovieViewOutput(this);
|
||||
}
|
||||
|
||||
if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
|
||||
#ifndef QT_NO_WIDGETS
|
||||
m_videoOutput = new QT7MovieViewRenderer(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
m_videoOutput = new QT7MovieVideoWidget(this);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_videoOutput) {
|
||||
QT7VideoOutput *videoOutput = qobject_cast<QT7VideoOutput*>(m_videoOutput);
|
||||
m_session->setVideoOutput(videoOutput);
|
||||
return m_videoOutput;
|
||||
}
|
||||
}
|
||||
#endif // !defined(QT_NO_OPENGL)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QT7PlayerService::releaseControl(QMediaControl *control)
|
||||
{
|
||||
if (m_videoOutput == control) {
|
||||
m_videoOutput = 0;
|
||||
m_session->setVideoOutput(0);
|
||||
delete control;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_qt7playerservice.cpp"
|
||||
@@ -1,183 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7PLAYERSESSION_H
|
||||
#define QT7PLAYERSESSION_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qset.h>
|
||||
#include <QtCore/qresource.h>
|
||||
|
||||
#include <qmediaplayercontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QT7PlayerControl;
|
||||
class QMediaPlaylist;
|
||||
class QMediaPlaylistNavigator;
|
||||
class QT7VideoOutput;
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
|
||||
|
||||
class QT7PlayerSession : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QT7PlayerSession(QObject *parent = 0);
|
||||
~QT7PlayerSession();
|
||||
|
||||
void *movie() const;
|
||||
|
||||
void setControl(QT7PlayerControl *control);
|
||||
|
||||
void setVideoOutput(QT7VideoOutput *output);
|
||||
|
||||
QMediaPlayer::State state() const;
|
||||
QMediaPlayer::MediaStatus mediaStatus() const;
|
||||
|
||||
QMediaContent media() const;
|
||||
const QIODevice *mediaStream() const;
|
||||
void setMedia(const QMediaContent &content, QIODevice *stream);
|
||||
|
||||
qint64 position() const;
|
||||
qint64 duration() const;
|
||||
|
||||
int bufferStatus() const;
|
||||
|
||||
int volume() const;
|
||||
bool isMuted() const;
|
||||
|
||||
bool isAudioAvailable() const;
|
||||
bool isVideoAvailable() const;
|
||||
|
||||
bool isSeekable() const;
|
||||
QMediaTimeRange availablePlaybackRanges() const;
|
||||
|
||||
qreal playbackRate() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setPlaybackRate(qreal rate);
|
||||
|
||||
void setPosition(qint64 pos);
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
void stop();
|
||||
|
||||
void setVolume(int volume);
|
||||
void setMuted(bool muted);
|
||||
|
||||
void processEOS();
|
||||
void processLoadStateChange();
|
||||
void processVolumeChange();
|
||||
void processNaturalSizeChange();
|
||||
void processPositionChange();
|
||||
|
||||
Q_SIGNALS:
|
||||
void positionChanged(qint64 position);
|
||||
void durationChanged(qint64 duration);
|
||||
void stateChanged(QMediaPlayer::State newState);
|
||||
void mediaStatusChanged(QMediaPlayer::MediaStatus status);
|
||||
void volumeChanged(int volume);
|
||||
void mutedChanged(bool muted);
|
||||
void audioAvailableChanged(bool audioAvailable);
|
||||
void videoAvailableChanged(bool videoAvailable);
|
||||
void error(int error, const QString &errorString);
|
||||
|
||||
private:
|
||||
class ResourceHandler {
|
||||
public:
|
||||
ResourceHandler():resource(0) {}
|
||||
~ResourceHandler() { clear(); }
|
||||
void setResourceFile(const QString &file) {
|
||||
if (resource) {
|
||||
if (resource->fileName() == file)
|
||||
return;
|
||||
delete resource;
|
||||
rawData.clear();
|
||||
}
|
||||
resource = new QResource(file);
|
||||
}
|
||||
bool isValid() const { return resource && resource->isValid() && resource->data() != 0; }
|
||||
const uchar *data() {
|
||||
if (!isValid())
|
||||
return 0;
|
||||
if (resource->isCompressed()) {
|
||||
if (rawData.size() == 0)
|
||||
rawData = qUncompress(resource->data(), resource->size());
|
||||
return (const uchar *)rawData.constData();
|
||||
}
|
||||
return resource->data();
|
||||
}
|
||||
qint64 size() {
|
||||
if (data() == 0)
|
||||
return 0;
|
||||
return resource->isCompressed() ? rawData.size() : resource->size();
|
||||
}
|
||||
void clear() {
|
||||
delete resource;
|
||||
rawData.clear();
|
||||
}
|
||||
QResource *resource;
|
||||
QByteArray rawData;
|
||||
};
|
||||
|
||||
void openMovie(bool tryAsync);
|
||||
|
||||
void *m_QTMovie;
|
||||
void *m_movieObserver;
|
||||
|
||||
QMediaPlayer::State m_state;
|
||||
QMediaPlayer::MediaStatus m_mediaStatus;
|
||||
QIODevice *m_mediaStream;
|
||||
QMediaContent m_resources;
|
||||
ResourceHandler m_resourceHandler;
|
||||
|
||||
QT7VideoOutput * m_videoOutput;
|
||||
|
||||
bool m_muted;
|
||||
bool m_tryingAsync;
|
||||
int m_volume;
|
||||
qreal m_rate;
|
||||
|
||||
qint64 m_duration;
|
||||
bool m_videoAvailable;
|
||||
bool m_audioAvailable;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,751 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#import <QTKit/QTDataReference.h>
|
||||
#import <QTKit/QTMovie.h>
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#include "qt7playersession.h"
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7videooutput.h"
|
||||
|
||||
#include <QtNetwork/qnetworkcookie.h>
|
||||
#include <private/qmediaplaylistnavigator_p.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qurl.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
//#define QT_DEBUG_QT7
|
||||
|
||||
@interface QTMovieObserver : NSObject
|
||||
{
|
||||
@private
|
||||
QT7PlayerSession *m_session;
|
||||
QTMovie *m_movie;
|
||||
}
|
||||
|
||||
- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session;
|
||||
- (void) setMovie:(QTMovie *)movie;
|
||||
- (void) processEOS:(NSNotification *)notification;
|
||||
- (void) processLoadStateChange:(NSNotification *)notification;
|
||||
- (void) processVolumeChange:(NSNotification *)notification;
|
||||
- (void) processNaturalSizeChange :(NSNotification *)notification;
|
||||
- (void) processPositionChange :(NSNotification *)notification;
|
||||
@end
|
||||
|
||||
@implementation QTMovieObserver
|
||||
|
||||
- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
self->m_session = session;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setMovie:(QTMovie *)movie
|
||||
{
|
||||
if (m_movie == movie)
|
||||
return;
|
||||
|
||||
if (m_movie) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[m_movie release];
|
||||
}
|
||||
|
||||
m_movie = movie;
|
||||
|
||||
if (movie) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(processEOS:)
|
||||
name:QTMovieDidEndNotification
|
||||
object:m_movie];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(processLoadStateChange:)
|
||||
name:QTMovieLoadStateDidChangeNotification
|
||||
object:m_movie];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(processVolumeChange:)
|
||||
name:QTMovieVolumeDidChangeNotification
|
||||
object:m_movie];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(processPositionChange:)
|
||||
name:QTMovieTimeDidChangeNotification
|
||||
object:m_movie];
|
||||
|
||||
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(processNaturalSizeChange:)
|
||||
name:@"QTMovieNaturalSizeDidChangeNotification"
|
||||
object:m_movie];
|
||||
|
||||
}
|
||||
else {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(processNaturalSizeChange:)
|
||||
name:QTMovieEditedNotification
|
||||
object:m_movie];
|
||||
}
|
||||
|
||||
[movie retain];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) processEOS:(NSNotification *)notification
|
||||
{
|
||||
Q_UNUSED(notification);
|
||||
QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection);
|
||||
}
|
||||
|
||||
- (void) processLoadStateChange:(NSNotification *)notification
|
||||
{
|
||||
Q_UNUSED(notification);
|
||||
QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
|
||||
}
|
||||
|
||||
- (void) processVolumeChange:(NSNotification *)notification
|
||||
{
|
||||
Q_UNUSED(notification);
|
||||
QMetaObject::invokeMethod(m_session, "processVolumeChange", Qt::AutoConnection);
|
||||
}
|
||||
|
||||
- (void) processNaturalSizeChange :(NSNotification *)notification
|
||||
{
|
||||
Q_UNUSED(notification);
|
||||
QMetaObject::invokeMethod(m_session, "processNaturalSizeChange", Qt::AutoConnection);
|
||||
}
|
||||
|
||||
- (void) processPositionChange :(NSNotification *)notification
|
||||
{
|
||||
Q_UNUSED(notification);
|
||||
QMetaObject::invokeMethod(m_session, "processPositionChange", Qt::AutoConnection);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static inline NSString *qString2CFStringRef(const QString &string)
|
||||
{
|
||||
return [NSString stringWithCharacters:reinterpret_cast<const UniChar *>(string.unicode()) length:string.length()];
|
||||
}
|
||||
|
||||
QT7PlayerSession::QT7PlayerSession(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_QTMovie(0)
|
||||
, m_state(QMediaPlayer::StoppedState)
|
||||
, m_mediaStatus(QMediaPlayer::NoMedia)
|
||||
, m_mediaStream(0)
|
||||
, m_videoOutput(0)
|
||||
, m_muted(false)
|
||||
, m_tryingAsync(false)
|
||||
, m_volume(100)
|
||||
, m_rate(1.0)
|
||||
, m_duration(0)
|
||||
, m_videoAvailable(false)
|
||||
, m_audioAvailable(false)
|
||||
{
|
||||
m_movieObserver = [[QTMovieObserver alloc] initWithPlayerSession:this];
|
||||
}
|
||||
|
||||
QT7PlayerSession::~QT7PlayerSession()
|
||||
{
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(0);
|
||||
|
||||
[(QTMovieObserver*)m_movieObserver setMovie:nil];
|
||||
[(QTMovieObserver*)m_movieObserver release];
|
||||
[(QTMovie*)m_QTMovie release];
|
||||
}
|
||||
|
||||
void *QT7PlayerSession::movie() const
|
||||
{
|
||||
return m_QTMovie;
|
||||
}
|
||||
|
||||
void QT7PlayerSession::setVideoOutput(QT7VideoOutput *output)
|
||||
{
|
||||
if (m_videoOutput == output)
|
||||
return;
|
||||
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(0);
|
||||
|
||||
m_videoOutput = output;
|
||||
|
||||
if (m_videoOutput && m_state != QMediaPlayer::StoppedState)
|
||||
m_videoOutput->setMovie(m_QTMovie);
|
||||
}
|
||||
|
||||
qint64 QT7PlayerSession::position() const
|
||||
{
|
||||
if (!m_QTMovie)
|
||||
return 0;
|
||||
|
||||
QTTime qtTime = [(QTMovie*)m_QTMovie currentTime];
|
||||
|
||||
return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f);
|
||||
}
|
||||
|
||||
qint64 QT7PlayerSession::duration() const
|
||||
{
|
||||
if (!m_QTMovie)
|
||||
return 0;
|
||||
|
||||
QTTime qtTime = [(QTMovie*)m_QTMovie duration];
|
||||
|
||||
return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f);
|
||||
}
|
||||
|
||||
QMediaPlayer::State QT7PlayerSession::state() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
QMediaPlayer::MediaStatus QT7PlayerSession::mediaStatus() const
|
||||
{
|
||||
return m_mediaStatus;
|
||||
}
|
||||
|
||||
int QT7PlayerSession::bufferStatus() const
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
int QT7PlayerSession::volume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
bool QT7PlayerSession::isMuted() const
|
||||
{
|
||||
return m_muted;
|
||||
}
|
||||
|
||||
bool QT7PlayerSession::isSeekable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef QUICKTIME_C_API_AVAILABLE
|
||||
@interface QTMovie(QtExtensions)
|
||||
- (NSArray*)loadedRanges;
|
||||
- (QTTime)maxTimeLoaded;
|
||||
@end
|
||||
#endif
|
||||
|
||||
QMediaTimeRange QT7PlayerSession::availablePlaybackRanges() const
|
||||
{
|
||||
QTMovie *movie = (QTMovie*)m_QTMovie;
|
||||
#ifndef QUICKTIME_C_API_AVAILABLE
|
||||
AutoReleasePool pool;
|
||||
if ([movie respondsToSelector:@selector(loadedRanges)]) {
|
||||
QMediaTimeRange rc;
|
||||
NSArray *r = [movie loadedRanges];
|
||||
for (NSValue *tr in r) {
|
||||
QTTimeRange timeRange = [tr QTTimeRangeValue];
|
||||
qint64 startTime = qint64(float(timeRange.time.timeValue) / timeRange.time.timeScale * 1000.0);
|
||||
rc.addInterval(startTime, startTime + qint64(float(timeRange.duration.timeValue) / timeRange.duration.timeScale * 1000.0));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
else if ([movie respondsToSelector:@selector(maxTimeLoaded)]) {
|
||||
QTTime loadedTime = [movie maxTimeLoaded];
|
||||
return QMediaTimeRange(0, qint64(float(loadedTime.timeValue) / loadedTime.timeScale * 1000.0));
|
||||
}
|
||||
#else
|
||||
TimeValue loadedTime;
|
||||
TimeScale scale;
|
||||
Movie m = [movie quickTimeMovie];
|
||||
if (GetMaxLoadedTimeInMovie(m, &loadedTime) == noErr) {
|
||||
scale = GetMovieTimeScale(m);
|
||||
return QMediaTimeRange(0, qint64(float(loadedTime) / scale * 1000.0f));
|
||||
}
|
||||
#endif
|
||||
return QMediaTimeRange(0, duration());
|
||||
}
|
||||
|
||||
qreal QT7PlayerSession::playbackRate() const
|
||||
{
|
||||
return m_rate;
|
||||
}
|
||||
|
||||
void QT7PlayerSession::setPlaybackRate(qreal rate)
|
||||
{
|
||||
if (qFuzzyCompare(m_rate, rate))
|
||||
return;
|
||||
|
||||
m_rate = rate;
|
||||
|
||||
if (m_QTMovie != 0 && m_state == QMediaPlayer::PlayingState) {
|
||||
AutoReleasePool pool;
|
||||
float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue];
|
||||
[(QTMovie*)m_QTMovie setRate:preferredRate * m_rate];
|
||||
}
|
||||
}
|
||||
|
||||
void QT7PlayerSession::setPosition(qint64 pos)
|
||||
{
|
||||
if ( !isSeekable() || pos == position())
|
||||
return;
|
||||
|
||||
if (duration() > 0)
|
||||
pos = qMin(pos, duration());
|
||||
|
||||
QTTime newQTTime = [(QTMovie*)m_QTMovie currentTime];
|
||||
newQTTime.timeValue = (pos / 1000.0f) * newQTTime.timeScale;
|
||||
[(QTMovie*)m_QTMovie setCurrentTime:newQTTime];
|
||||
|
||||
//reset the EndOfMedia status position is changed after playback is finished
|
||||
if (m_mediaStatus == QMediaPlayer::EndOfMedia)
|
||||
processLoadStateChange();
|
||||
}
|
||||
|
||||
void QT7PlayerSession::play()
|
||||
{
|
||||
if (m_state == QMediaPlayer::PlayingState)
|
||||
return;
|
||||
|
||||
m_state = QMediaPlayer::PlayingState;
|
||||
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(m_QTMovie);
|
||||
|
||||
//reset the EndOfMedia status if the same file is played again
|
||||
if (m_mediaStatus == QMediaPlayer::EndOfMedia)
|
||||
processLoadStateChange();
|
||||
|
||||
AutoReleasePool pool;
|
||||
float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue];
|
||||
[(QTMovie*)m_QTMovie setRate:preferredRate * m_rate];
|
||||
|
||||
processLoadStateChange();
|
||||
Q_EMIT stateChanged(m_state);
|
||||
}
|
||||
|
||||
void QT7PlayerSession::pause()
|
||||
{
|
||||
if (m_state == QMediaPlayer::PausedState)
|
||||
return;
|
||||
|
||||
m_state = QMediaPlayer::PausedState;
|
||||
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(m_QTMovie);
|
||||
|
||||
//reset the EndOfMedia status if the same file is played again
|
||||
if (m_mediaStatus == QMediaPlayer::EndOfMedia)
|
||||
processLoadStateChange();
|
||||
|
||||
[(QTMovie*)m_QTMovie setRate:0];
|
||||
|
||||
processLoadStateChange();
|
||||
Q_EMIT stateChanged(m_state);
|
||||
}
|
||||
|
||||
void QT7PlayerSession::stop()
|
||||
{
|
||||
if (m_state == QMediaPlayer::StoppedState)
|
||||
return;
|
||||
|
||||
m_state = QMediaPlayer::StoppedState;
|
||||
|
||||
[(QTMovie*)m_QTMovie setRate:0];
|
||||
setPosition(0);
|
||||
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(0);
|
||||
|
||||
processLoadStateChange();
|
||||
Q_EMIT stateChanged(m_state);
|
||||
Q_EMIT positionChanged(position());
|
||||
}
|
||||
|
||||
void QT7PlayerSession::setVolume(int volume)
|
||||
{
|
||||
if (m_volume == volume)
|
||||
return;
|
||||
|
||||
m_volume = volume;
|
||||
|
||||
if (m_QTMovie != 0)
|
||||
[(QTMovie*)m_QTMovie setVolume:m_volume / 100.0f];
|
||||
|
||||
Q_EMIT volumeChanged(m_volume);
|
||||
}
|
||||
|
||||
void QT7PlayerSession::setMuted(bool muted)
|
||||
{
|
||||
if (m_muted == muted)
|
||||
return;
|
||||
|
||||
m_muted = muted;
|
||||
|
||||
if (m_QTMovie != 0)
|
||||
[(QTMovie*)m_QTMovie setMuted:m_muted];
|
||||
|
||||
Q_EMIT mutedChanged(muted);
|
||||
}
|
||||
|
||||
QMediaContent QT7PlayerSession::media() const
|
||||
{
|
||||
return m_resources;
|
||||
}
|
||||
|
||||
const QIODevice *QT7PlayerSession::mediaStream() const
|
||||
{
|
||||
return m_mediaStream;
|
||||
}
|
||||
|
||||
void QT7PlayerSession::setMedia(const QMediaContent &content, QIODevice *stream)
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << Q_FUNC_INFO << content.canonicalUrl();
|
||||
#endif
|
||||
|
||||
if (m_QTMovie) {
|
||||
[(QTMovieObserver*)m_movieObserver setMovie:nil];
|
||||
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(0);
|
||||
|
||||
[(QTMovie*)m_QTMovie release];
|
||||
m_QTMovie = 0;
|
||||
m_resourceHandler.clear();
|
||||
}
|
||||
|
||||
m_resources = content;
|
||||
m_mediaStream = stream;
|
||||
QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
|
||||
|
||||
if (content.isNull()) {
|
||||
m_mediaStatus = QMediaPlayer::NoMedia;
|
||||
if (m_state != QMediaPlayer::StoppedState)
|
||||
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
|
||||
|
||||
if (m_mediaStatus != oldMediaStatus)
|
||||
Q_EMIT mediaStatusChanged(m_mediaStatus);
|
||||
Q_EMIT positionChanged(position());
|
||||
return;
|
||||
}
|
||||
|
||||
m_mediaStatus = QMediaPlayer::LoadingMedia;
|
||||
if (m_mediaStatus != oldMediaStatus)
|
||||
Q_EMIT mediaStatusChanged(m_mediaStatus);
|
||||
|
||||
QNetworkRequest request = content.canonicalResource().request();
|
||||
|
||||
QVariant cookies = request.header(QNetworkRequest::CookieHeader);
|
||||
if (cookies.isValid()) {
|
||||
NSHTTPCookieStorage *store = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
||||
QList<QNetworkCookie> cookieList = cookies.value<QList<QNetworkCookie> >();
|
||||
|
||||
Q_FOREACH (const QNetworkCookie &requestCookie, cookieList) {
|
||||
NSMutableDictionary *p = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
qString2CFStringRef(requestCookie.name()), NSHTTPCookieName,
|
||||
qString2CFStringRef(requestCookie.value()), NSHTTPCookieValue,
|
||||
qString2CFStringRef(requestCookie.domain()), NSHTTPCookieDomain,
|
||||
qString2CFStringRef(requestCookie.path()), NSHTTPCookiePath,
|
||||
nil
|
||||
];
|
||||
if (requestCookie.isSessionCookie())
|
||||
[p setObject:[NSString stringWithUTF8String:"TRUE"] forKey:NSHTTPCookieDiscard];
|
||||
else
|
||||
[p setObject:[NSDate dateWithTimeIntervalSince1970:requestCookie.expirationDate().toTime_t()] forKey:NSHTTPCookieExpires];
|
||||
|
||||
[store setCookie:[NSHTTPCookie cookieWithProperties:p]];
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt multiple times to open the movie.
|
||||
// First try - attempt open in async mode
|
||||
openMovie(true);
|
||||
|
||||
Q_EMIT positionChanged(position());
|
||||
}
|
||||
|
||||
void QT7PlayerSession::openMovie(bool tryAsync)
|
||||
{
|
||||
QUrl requestUrl = m_resources.canonicalResource().request().url();
|
||||
if (requestUrl.scheme().isEmpty())
|
||||
requestUrl.setScheme(QLatin1String("file"));
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << Q_FUNC_INFO << requestUrl;
|
||||
#endif
|
||||
|
||||
NSError *err = 0;
|
||||
NSString *urlString = [NSString stringWithUTF8String:requestUrl.toEncoded().constData()];
|
||||
|
||||
NSMutableDictionary *attr = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,
|
||||
[NSNumber numberWithBool:YES], QTMovieIsActiveAttribute,
|
||||
[NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute,
|
||||
[NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute,
|
||||
nil];
|
||||
|
||||
|
||||
if (requestUrl.scheme() == QLatin1String("qrc")) {
|
||||
// Load from Qt resource
|
||||
m_resourceHandler.setResourceFile(QLatin1Char(':') + requestUrl.path());
|
||||
if (!m_resourceHandler.isValid()) {
|
||||
Q_EMIT error(QMediaPlayer::FormatError, tr("Attempting to play invalid Qt resource"));
|
||||
return;
|
||||
}
|
||||
|
||||
CFDataRef resourceData =
|
||||
CFDataCreateWithBytesNoCopy(0, m_resourceHandler.data(), m_resourceHandler.size(), kCFAllocatorNull);
|
||||
|
||||
QTDataReference *dataReference =
|
||||
[QTDataReference dataReferenceWithReferenceToData:(NSData*)resourceData
|
||||
name:qString2CFStringRef(requestUrl.path())
|
||||
MIMEType:nil];
|
||||
|
||||
[attr setObject:dataReference forKey:QTMovieDataReferenceAttribute];
|
||||
|
||||
CFRelease(resourceData);
|
||||
} else {
|
||||
[attr setObject:[NSURL URLWithString:urlString] forKey:QTMovieURLAttribute];
|
||||
}
|
||||
|
||||
if (tryAsync && QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) {
|
||||
[attr setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenAsyncRequiredAttribute"];
|
||||
// XXX: This is disabled for now. causes some problems with video playback for some formats
|
||||
// [attr setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"];
|
||||
m_tryingAsync = true;
|
||||
}
|
||||
else
|
||||
m_tryingAsync = false;
|
||||
|
||||
m_QTMovie = [QTMovie movieWithAttributes:attr error:&err];
|
||||
if (err != nil) {
|
||||
// First attempt to test for inability to perform async
|
||||
// if ([err code] == QTErrorMovieOpeningCannotBeAsynchronous) { XXX: error code unknown!
|
||||
if (m_tryingAsync) {
|
||||
m_tryingAsync = false;
|
||||
err = nil;
|
||||
[attr removeObjectForKey:@"QTMovieOpenAsyncRequiredAttribute"];
|
||||
m_QTMovie = [QTMovie movieWithAttributes:attr error:&err];
|
||||
}
|
||||
}
|
||||
|
||||
if (err != nil) {
|
||||
m_QTMovie = 0;
|
||||
QString description = QString::fromUtf8([[err localizedDescription] UTF8String]);
|
||||
Q_EMIT error(QMediaPlayer::FormatError, description);
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << Q_FUNC_INFO << description;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
[(QTMovie*)m_QTMovie retain];
|
||||
|
||||
[(QTMovieObserver*)m_movieObserver setMovie:(QTMovie*)m_QTMovie];
|
||||
|
||||
if (m_state != QMediaPlayer::StoppedState && m_videoOutput)
|
||||
m_videoOutput->setMovie(m_QTMovie);
|
||||
|
||||
processLoadStateChange();
|
||||
|
||||
[(QTMovie*)m_QTMovie setMuted:m_muted];
|
||||
[(QTMovie*)m_QTMovie setVolume:m_volume / 100.0f];
|
||||
}
|
||||
}
|
||||
|
||||
bool QT7PlayerSession::isAudioAvailable() const
|
||||
{
|
||||
if (!m_QTMovie)
|
||||
return false;
|
||||
|
||||
AutoReleasePool pool;
|
||||
return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES;
|
||||
}
|
||||
|
||||
bool QT7PlayerSession::isVideoAvailable() const
|
||||
{
|
||||
if (!m_QTMovie)
|
||||
return false;
|
||||
|
||||
AutoReleasePool pool;
|
||||
return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES;
|
||||
}
|
||||
|
||||
void QT7PlayerSession::processEOS()
|
||||
{
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
#endif
|
||||
Q_EMIT positionChanged(position());
|
||||
m_mediaStatus = QMediaPlayer::EndOfMedia;
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(0);
|
||||
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
|
||||
Q_EMIT mediaStatusChanged(m_mediaStatus);
|
||||
}
|
||||
|
||||
void QT7PlayerSession::processLoadStateChange()
|
||||
{
|
||||
if (!m_QTMovie)
|
||||
return;
|
||||
|
||||
AutoReleasePool pool;
|
||||
|
||||
long state = [[(QTMovie*)m_QTMovie attributeForKey:QTMovieLoadStateAttribute] longValue];
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << Q_FUNC_INFO << state;
|
||||
#endif
|
||||
|
||||
#ifndef QUICKTIME_C_API_AVAILABLE
|
||||
enum {
|
||||
kMovieLoadStateError = -1L,
|
||||
kMovieLoadStateLoading = 1000,
|
||||
kMovieLoadStateLoaded = 2000,
|
||||
kMovieLoadStatePlayable = 10000,
|
||||
kMovieLoadStatePlaythroughOK = 20000,
|
||||
kMovieLoadStateComplete = 100000
|
||||
};
|
||||
#endif
|
||||
|
||||
if (state == kMovieLoadStateError) {
|
||||
if (m_tryingAsync) {
|
||||
NSError *error = [(QTMovie*)m_QTMovie attributeForKey:@"QTMovieLoadStateErrorAttribute"];
|
||||
if ([error code] == componentNotThreadSafeErr) {
|
||||
// Last Async check, try again with no such flag
|
||||
openMovie(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->setMovie(0);
|
||||
|
||||
Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media"));
|
||||
Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia);
|
||||
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QMediaPlayer::MediaStatus newStatus = QMediaPlayer::NoMedia;
|
||||
bool isPlaying = (m_state != QMediaPlayer::StoppedState);
|
||||
|
||||
if (state >= kMovieLoadStatePlaythroughOK) {
|
||||
newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia;
|
||||
} else if (state >= kMovieLoadStatePlayable)
|
||||
newStatus = isPlaying ? QMediaPlayer::BufferingMedia : QMediaPlayer::LoadingMedia;
|
||||
else if (state >= kMovieLoadStateLoading) {
|
||||
if (!isPlaying)
|
||||
newStatus = QMediaPlayer::LoadingMedia;
|
||||
else if (m_mediaStatus >= QMediaPlayer::LoadedMedia)
|
||||
newStatus = QMediaPlayer::StalledMedia;
|
||||
else
|
||||
newStatus = QMediaPlayer::LoadingMedia;
|
||||
}
|
||||
|
||||
if (state >= kMovieLoadStatePlayable &&
|
||||
m_state == QMediaPlayer::PlayingState &&
|
||||
[(QTMovie*)m_QTMovie rate] == 0) {
|
||||
|
||||
float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue];
|
||||
|
||||
[(QTMovie*)m_QTMovie setRate:preferredRate * m_rate];
|
||||
}
|
||||
|
||||
if (state >= kMovieLoadStateLoaded) {
|
||||
qint64 currentDuration = duration();
|
||||
if (m_duration != currentDuration)
|
||||
Q_EMIT durationChanged(m_duration = currentDuration);
|
||||
|
||||
if (m_audioAvailable != isAudioAvailable())
|
||||
Q_EMIT audioAvailableChanged(m_audioAvailable = !m_audioAvailable);
|
||||
|
||||
if (m_videoAvailable != isVideoAvailable())
|
||||
Q_EMIT videoAvailableChanged(m_videoAvailable = !m_videoAvailable);
|
||||
}
|
||||
|
||||
if (newStatus != m_mediaStatus)
|
||||
Q_EMIT mediaStatusChanged(m_mediaStatus = newStatus);
|
||||
}
|
||||
|
||||
void QT7PlayerSession::processVolumeChange()
|
||||
{
|
||||
if (!m_QTMovie)
|
||||
return;
|
||||
|
||||
int newVolume = qRound(100.0f * [((QTMovie*)m_QTMovie) volume]);
|
||||
|
||||
if (newVolume != m_volume) {
|
||||
Q_EMIT volumeChanged(m_volume = newVolume);
|
||||
}
|
||||
}
|
||||
|
||||
void QT7PlayerSession::processNaturalSizeChange()
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
NSSize size = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue];
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << Q_FUNC_INFO << QSize(size.width, size.height);
|
||||
#endif
|
||||
|
||||
if (m_videoOutput)
|
||||
m_videoOutput->updateNaturalSize(QSize(size.width, size.height));
|
||||
}
|
||||
|
||||
void QT7PlayerSession::processPositionChange()
|
||||
{
|
||||
Q_EMIT positionChanged(position());
|
||||
}
|
||||
|
||||
#include "moc_qt7playersession.cpp"
|
||||
@@ -1,80 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCVDISPLAYLINK_H
|
||||
#define QCVDISPLAYLINK_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <qtmultimediadefs.h>
|
||||
|
||||
#include <QuartzCore/CVDisplayLink.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QCvDisplayLink : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QCvDisplayLink(QObject *parent = 0);
|
||||
virtual ~QCvDisplayLink();
|
||||
|
||||
bool isValid();
|
||||
bool isActive() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
Q_SIGNALS:
|
||||
void tick(const CVTimeStamp &ts);
|
||||
|
||||
public:
|
||||
void displayLinkEvent(const CVTimeStamp *);
|
||||
|
||||
protected:
|
||||
virtual bool event(QEvent *);
|
||||
|
||||
private:
|
||||
CVDisplayLinkRef m_displayLink;
|
||||
QMutex m_displayLinkMutex;
|
||||
bool m_pendingDisplayLinkEvent;
|
||||
bool m_isActive;
|
||||
CVTimeStamp m_frameTimeStamp;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qcvdisplaylink.h"
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink,
|
||||
const CVTimeStamp *inNow,
|
||||
const CVTimeStamp *inOutputTime,
|
||||
CVOptionFlags flagsIn,
|
||||
CVOptionFlags *flagsOut,
|
||||
void *displayLinkContext)
|
||||
{
|
||||
Q_UNUSED(displayLink);
|
||||
Q_UNUSED(inNow);
|
||||
Q_UNUSED(flagsIn);
|
||||
Q_UNUSED(flagsOut);
|
||||
|
||||
QCvDisplayLink *link = (QCvDisplayLink *)displayLinkContext;
|
||||
|
||||
link->displayLinkEvent(inOutputTime);
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
|
||||
QCvDisplayLink::QCvDisplayLink(QObject *parent)
|
||||
:QObject(parent),
|
||||
m_pendingDisplayLinkEvent(false),
|
||||
m_isActive(false)
|
||||
{
|
||||
// create display link for the main display
|
||||
CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink);
|
||||
if (m_displayLink) {
|
||||
// set the current display of a display link.
|
||||
CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay);
|
||||
|
||||
// set the renderer output callback function
|
||||
CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this);
|
||||
}
|
||||
}
|
||||
|
||||
QCvDisplayLink::~QCvDisplayLink()
|
||||
{
|
||||
if (m_displayLink) {
|
||||
CVDisplayLinkStop(m_displayLink);
|
||||
CVDisplayLinkRelease(m_displayLink);
|
||||
m_displayLink = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool QCvDisplayLink::isValid()
|
||||
{
|
||||
return m_displayLink != 0;
|
||||
}
|
||||
|
||||
bool QCvDisplayLink::isActive() const
|
||||
{
|
||||
return m_isActive;
|
||||
}
|
||||
|
||||
void QCvDisplayLink::start()
|
||||
{
|
||||
if (m_displayLink && !m_isActive) {
|
||||
CVDisplayLinkStart(m_displayLink);
|
||||
m_isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QCvDisplayLink::stop()
|
||||
{
|
||||
if (m_displayLink && m_isActive) {
|
||||
CVDisplayLinkStop(m_displayLink);
|
||||
m_isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void QCvDisplayLink::displayLinkEvent(const CVTimeStamp *ts)
|
||||
{
|
||||
// This function is called from a
|
||||
// thread != gui thread. So we post the event.
|
||||
// But we need to make sure that we don't post faster
|
||||
// than the event loop can eat:
|
||||
m_displayLinkMutex.lock();
|
||||
bool pending = m_pendingDisplayLinkEvent;
|
||||
m_pendingDisplayLinkEvent = true;
|
||||
m_frameTimeStamp = *ts;
|
||||
m_displayLinkMutex.unlock();
|
||||
|
||||
if (!pending)
|
||||
qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
|
||||
}
|
||||
|
||||
bool QCvDisplayLink::event(QEvent *event)
|
||||
{
|
||||
switch (event->type()){
|
||||
case QEvent::User: {
|
||||
m_displayLinkMutex.lock();
|
||||
m_pendingDisplayLinkEvent = false;
|
||||
CVTimeStamp ts = m_frameTimeStamp;
|
||||
m_displayLinkMutex.unlock();
|
||||
|
||||
Q_EMIT tick(ts);
|
||||
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QObject::event(event);
|
||||
}
|
||||
|
||||
#include "moc_qcvdisplaylink.cpp"
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"Keys": ["qt7"],
|
||||
"Services": ["org.qt-project.qt.mediaplayer"]
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
# Avoid clash with a variable named `slots' in a Quartz header
|
||||
CONFIG += no_keywords
|
||||
|
||||
TARGET = qqt7engine
|
||||
QT += multimedia-private network
|
||||
qtHaveModule(widgets) {
|
||||
QT += multimediawidgets-private widgets
|
||||
}
|
||||
|
||||
PLUGIN_TYPE = mediaservice
|
||||
PLUGIN_CLASS_NAME = QT7ServicePlugin
|
||||
load(qt_plugin)
|
||||
|
||||
!simulator {
|
||||
QT += opengl
|
||||
}
|
||||
|
||||
#DEFINES += QT_DEBUG_QT7
|
||||
|
||||
LIBS += -framework AppKit -framework AudioUnit \
|
||||
-framework AudioToolbox -framework CoreAudio \
|
||||
-framework QuartzCore -framework QTKit
|
||||
|
||||
# QUICKTIME_C_API_AVAILABLE is true only on i386
|
||||
# so make sure to link QuickTime
|
||||
contains(QT_ARCH, i386) {
|
||||
LIBS += -framework QuickTime
|
||||
}
|
||||
|
||||
HEADERS += \
|
||||
qt7backend.h \
|
||||
qt7videooutput.h \
|
||||
qt7serviceplugin.h
|
||||
|
||||
OBJECTIVE_SOURCES += \
|
||||
qt7backend.mm \
|
||||
qt7serviceplugin.mm
|
||||
|
||||
!simulator {
|
||||
HEADERS += \
|
||||
qt7movieviewoutput.h \
|
||||
qt7movierenderer.h \
|
||||
qt7ciimagevideobuffer.h \
|
||||
qcvdisplaylink.h
|
||||
|
||||
OBJECTIVE_SOURCES += \
|
||||
qt7movieviewoutput.mm \
|
||||
qt7movierenderer.mm \
|
||||
qt7videooutput.mm \
|
||||
qt7ciimagevideobuffer.mm \
|
||||
qcvdisplaylink.mm
|
||||
|
||||
qtHaveModule(widgets) {
|
||||
HEADERS += \
|
||||
qt7movieviewrenderer.h \
|
||||
qt7movievideowidget.h
|
||||
|
||||
OBJECTIVE_SOURCES += \
|
||||
qt7movieviewrenderer.mm \
|
||||
qt7movievideowidget.mm
|
||||
}
|
||||
}
|
||||
|
||||
include(mediaplayer/mediaplayer.pri)
|
||||
|
||||
OTHER_FILES += \
|
||||
qt7.json
|
||||
@@ -1,60 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7BACKEND_H
|
||||
#define QT7BACKEND_H
|
||||
|
||||
#include "qtmultimediadefs.h"
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
#ifndef Q_OS_SIMULATOR
|
||||
#ifndef Q_OS_MAC64
|
||||
#define QUICKTIME_C_API_AVAILABLE
|
||||
#endif
|
||||
#endif // !defined(Q_WS_SIMULATOR)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class AutoReleasePool
|
||||
{
|
||||
private:
|
||||
void *pool;
|
||||
public:
|
||||
AutoReleasePool();
|
||||
~AutoReleasePool();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,78 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7CIIMAGEVIDEOBUFFER_H
|
||||
#define QT7CIIMAGEVIDEOBUFFER_H
|
||||
|
||||
#include "qt7backend.h"
|
||||
#import <QTKit/QTKit.h>
|
||||
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <qabstractvideobuffer.h>
|
||||
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QT7CIImageVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
public:
|
||||
QT7CIImageVideoBuffer(CIImage *image);
|
||||
|
||||
virtual ~QT7CIImageVideoBuffer();
|
||||
|
||||
MapMode mapMode() const;
|
||||
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine);
|
||||
void unmap();
|
||||
QVariant handle() const;
|
||||
|
||||
private:
|
||||
CIImage *m_image;
|
||||
NSBitmapImageRep *m_buffer;
|
||||
MapMode m_mode;
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,107 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qt7ciimagevideobuffer.h"
|
||||
|
||||
#include <QuartzCore/CIFilter.h>
|
||||
#include <QuartzCore/CIVector.h>
|
||||
|
||||
QT7CIImageVideoBuffer::QT7CIImageVideoBuffer(CIImage *image)
|
||||
: QAbstractVideoBuffer(CoreImageHandle)
|
||||
, m_image(image)
|
||||
, m_buffer(0)
|
||||
, m_mode(NotMapped)
|
||||
{
|
||||
[m_image retain];
|
||||
}
|
||||
|
||||
QT7CIImageVideoBuffer::~QT7CIImageVideoBuffer()
|
||||
{
|
||||
[m_image release];
|
||||
[m_buffer release];
|
||||
}
|
||||
|
||||
QAbstractVideoBuffer::MapMode QT7CIImageVideoBuffer::mapMode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
uchar *QT7CIImageVideoBuffer::map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
{
|
||||
if (mode == NotMapped || m_mode != NotMapped || !m_image)
|
||||
return 0;
|
||||
|
||||
if (!m_buffer) {
|
||||
//swap R and B channels
|
||||
CIFilter *colorSwapFilter = [CIFilter filterWithName: @"CIColorMatrix" keysAndValues:
|
||||
@"inputImage", m_image,
|
||||
@"inputRVector", [CIVector vectorWithX: 0 Y: 0 Z: 1 W: 0],
|
||||
@"inputGVector", [CIVector vectorWithX: 0 Y: 1 Z: 0 W: 0],
|
||||
@"inputBVector", [CIVector vectorWithX: 1 Y: 0 Z: 0 W: 0],
|
||||
@"inputAVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 1],
|
||||
@"inputBiasVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 0],
|
||||
nil];
|
||||
CIImage *img = [colorSwapFilter valueForKey: @"outputImage"];
|
||||
|
||||
m_buffer = [[NSBitmapImageRep alloc] initWithCIImage:img];
|
||||
}
|
||||
|
||||
if (numBytes)
|
||||
*numBytes = [m_buffer bytesPerPlane];
|
||||
|
||||
if (bytesPerLine)
|
||||
*bytesPerLine = [m_buffer bytesPerRow];
|
||||
|
||||
m_mode = mode;
|
||||
|
||||
return [m_buffer bitmapData];
|
||||
}
|
||||
|
||||
void QT7CIImageVideoBuffer::unmap()
|
||||
{
|
||||
m_mode = NotMapped;
|
||||
}
|
||||
|
||||
QVariant QT7CIImageVideoBuffer::handle() const
|
||||
{
|
||||
return QVariant::fromValue<void*>(m_image);
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7MOVIERENDERER_H
|
||||
#define QT7MOVIERENDERER_H
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <qvideorenderercontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
#include "qt7videooutput.h"
|
||||
|
||||
#include <QuartzCore/CVOpenGLTexture.h>
|
||||
#include <QuickTime/QuickTime.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QGLContext;
|
||||
|
||||
class QCvDisplayLink;
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
|
||||
class QT7MovieRenderer : public QT7VideoRendererControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QT7MovieRenderer(QObject *parent = 0);
|
||||
virtual ~QT7MovieRenderer();
|
||||
|
||||
void setMovie(void *movie);
|
||||
void updateNaturalSize(const QSize &newSize);
|
||||
|
||||
QAbstractVideoSurface *surface() const;
|
||||
void setSurface(QAbstractVideoSurface *surface);
|
||||
|
||||
QSize nativeSize() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateVideoFrame(const CVTimeStamp &ts);
|
||||
|
||||
private:
|
||||
void setupVideoOutput();
|
||||
bool createPixelBufferVisualContext();
|
||||
bool createGLVisualContext();
|
||||
|
||||
void *m_movie;
|
||||
|
||||
QMutex m_mutex;
|
||||
|
||||
QCvDisplayLink *m_displayLink;
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
QTVisualContextRef m_visualContext;
|
||||
bool m_usingGLContext;
|
||||
const QGLContext *m_currentGLContext;
|
||||
QSize m_pixelBufferContextGeometry;
|
||||
#endif
|
||||
QAbstractVideoSurface *m_surface;
|
||||
QSize m_nativeSize;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,481 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#import <QTKit/QTKit.h>
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7movierenderer.h"
|
||||
#include "qt7playersession.h"
|
||||
#include "qt7ciimagevideobuffer.h"
|
||||
#include "qcvdisplaylink.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
|
||||
#include <qabstractvideobuffer.h>
|
||||
#include <qabstractvideosurface.h>
|
||||
#include <qvideosurfaceformat.h>
|
||||
|
||||
#include <QtOpenGL/QGLContext>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
//#define USE_MAIN_MONITOR_COLOR_SPACE 1
|
||||
|
||||
class CVGLTextureVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
public:
|
||||
CVGLTextureVideoBuffer(CVOpenGLTextureRef buffer)
|
||||
: QAbstractVideoBuffer(GLTextureHandle)
|
||||
, m_buffer(buffer)
|
||||
, m_mode(NotMapped)
|
||||
{
|
||||
CVOpenGLTextureRetain(m_buffer);
|
||||
}
|
||||
|
||||
virtual ~CVGLTextureVideoBuffer()
|
||||
{
|
||||
CVOpenGLTextureRelease(m_buffer);
|
||||
}
|
||||
|
||||
QVariant handle() const
|
||||
{
|
||||
GLuint id = CVOpenGLTextureGetName(m_buffer);
|
||||
return QVariant(int(id));
|
||||
}
|
||||
|
||||
MapMode mapMode() const { return m_mode; }
|
||||
|
||||
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
{
|
||||
if (numBytes)
|
||||
*numBytes = 0;
|
||||
|
||||
if (bytesPerLine)
|
||||
*bytesPerLine = 0;
|
||||
|
||||
m_mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unmap() { m_mode = NotMapped; }
|
||||
|
||||
private:
|
||||
CVOpenGLTextureRef m_buffer;
|
||||
MapMode m_mode;
|
||||
};
|
||||
|
||||
|
||||
class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
public:
|
||||
CVPixelBufferVideoBuffer(CVPixelBufferRef buffer)
|
||||
: QAbstractVideoBuffer(NoHandle)
|
||||
, m_buffer(buffer)
|
||||
, m_mode(NotMapped)
|
||||
{
|
||||
CVPixelBufferRetain(m_buffer);
|
||||
}
|
||||
|
||||
virtual ~CVPixelBufferVideoBuffer()
|
||||
{
|
||||
CVPixelBufferRelease(m_buffer);
|
||||
}
|
||||
|
||||
MapMode mapMode() const { return m_mode; }
|
||||
|
||||
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
{
|
||||
if (mode != NotMapped && m_mode == NotMapped) {
|
||||
CVPixelBufferLockBaseAddress(m_buffer, 0);
|
||||
|
||||
if (numBytes)
|
||||
*numBytes = CVPixelBufferGetDataSize(m_buffer);
|
||||
|
||||
if (bytesPerLine)
|
||||
*bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer);
|
||||
|
||||
m_mode = mode;
|
||||
|
||||
return (uchar*)CVPixelBufferGetBaseAddress(m_buffer);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void unmap()
|
||||
{
|
||||
if (m_mode != NotMapped) {
|
||||
m_mode = NotMapped;
|
||||
CVPixelBufferUnlockBaseAddress(m_buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CVPixelBufferRef m_buffer;
|
||||
MapMode m_mode;
|
||||
};
|
||||
|
||||
|
||||
|
||||
QT7MovieRenderer::QT7MovieRenderer(QObject *parent)
|
||||
:QT7VideoRendererControl(parent),
|
||||
m_movie(0),
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
m_visualContext(0),
|
||||
m_usingGLContext(false),
|
||||
m_currentGLContext(0),
|
||||
#endif
|
||||
m_surface(0)
|
||||
{
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieRenderer";
|
||||
#endif
|
||||
m_displayLink = new QCvDisplayLink(this);
|
||||
connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp)));
|
||||
}
|
||||
|
||||
|
||||
bool QT7MovieRenderer::createGLVisualContext()
|
||||
{
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
AutoReleasePool pool;
|
||||
CGLContextObj cglContext = CGLGetCurrentContext();
|
||||
NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat];
|
||||
CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]);
|
||||
|
||||
OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext,
|
||||
cglPixelFormat, NULL, &m_visualContext);
|
||||
if (err != noErr)
|
||||
qWarning() << "Could not create visual context (OpenGL)";
|
||||
|
||||
return (err == noErr);
|
||||
#endif // QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
static bool DictionarySetValue(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value)
|
||||
{
|
||||
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
|
||||
|
||||
if (number) {
|
||||
CFDictionarySetValue( dict, key, number );
|
||||
CFRelease( number );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
bool QT7MovieRenderer::createPixelBufferVisualContext()
|
||||
{
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
if (m_visualContext) {
|
||||
QTVisualContextRelease(m_visualContext);
|
||||
m_visualContext = 0;
|
||||
}
|
||||
|
||||
m_pixelBufferContextGeometry = m_nativeSize;
|
||||
|
||||
CFMutableDictionaryRef pixelBufferOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
//DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32ARGBPixelFormat );
|
||||
DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32BGRAPixelFormat );
|
||||
DictionarySetValue(pixelBufferOptions, kCVPixelBufferWidthKey, m_nativeSize.width() );
|
||||
DictionarySetValue(pixelBufferOptions, kCVPixelBufferHeightKey, m_nativeSize.height() );
|
||||
DictionarySetValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, 16);
|
||||
//CFDictionarySetValue(pixelBufferOptions, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
|
||||
|
||||
CFMutableDictionaryRef visualContextOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions);
|
||||
|
||||
CGColorSpaceRef colorSpace = NULL;
|
||||
|
||||
#if USE_MAIN_MONITOR_COLOR_SPACE
|
||||
CMProfileRef sysprof = NULL;
|
||||
|
||||
// Get the Systems Profile for the main display
|
||||
if (CMGetSystemProfile(&sysprof) == noErr) {
|
||||
// Create a colorspace with the systems profile
|
||||
colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof);
|
||||
CMCloseProfile(sysprof);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!colorSpace)
|
||||
colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
CFDictionarySetValue(visualContextOptions, kQTVisualContextOutputColorSpaceKey, colorSpace);
|
||||
|
||||
OSStatus err = QTPixelBufferContextCreate(kCFAllocatorDefault,
|
||||
visualContextOptions,
|
||||
&m_visualContext);
|
||||
CFRelease(pixelBufferOptions);
|
||||
CFRelease(visualContextOptions);
|
||||
|
||||
if (err != noErr) {
|
||||
qWarning() << "Could not create visual context (PixelBuffer)";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif // QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QT7MovieRenderer::~QT7MovieRenderer()
|
||||
{
|
||||
m_displayLink->stop();
|
||||
}
|
||||
|
||||
void QT7MovieRenderer::setupVideoOutput()
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieRenderer::setupVideoOutput" << m_movie;
|
||||
#endif
|
||||
|
||||
if (m_movie == 0 || m_surface == 0) {
|
||||
m_displayLink->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue];
|
||||
m_nativeSize = QSize(size.width, size.height);
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
bool usedGLContext = m_usingGLContext;
|
||||
|
||||
if (!m_nativeSize.isEmpty()) {
|
||||
|
||||
bool glSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty();
|
||||
|
||||
//Try rendering using opengl textures first:
|
||||
if (glSupported) {
|
||||
QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle);
|
||||
|
||||
if (m_surface->isActive())
|
||||
m_surface->stop();
|
||||
|
||||
if (!m_surface->start(format)) {
|
||||
qWarning() << "failed to start video surface" << m_surface->error();
|
||||
qWarning() << "Surface format:" << format;
|
||||
glSupported = false;
|
||||
} else {
|
||||
m_usingGLContext = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!glSupported) {
|
||||
m_usingGLContext = false;
|
||||
QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32);
|
||||
|
||||
if (m_surface->isActive() && m_surface->surfaceFormat() != format) {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Surface format was changed, stop the surface.";
|
||||
#endif
|
||||
m_surface->stop();
|
||||
}
|
||||
|
||||
if (!m_surface->isActive()) {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Starting the surface with format" << format;
|
||||
#endif
|
||||
if (!m_surface->start(format)) {
|
||||
qWarning() << "failed to start video surface" << m_surface->error();
|
||||
qWarning() << "Surface format:" << format;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_visualContext) {
|
||||
//check if the visual context still can be reused
|
||||
if (usedGLContext != m_usingGLContext ||
|
||||
(m_usingGLContext && (m_currentGLContext != QGLContext::currentContext())) ||
|
||||
(!m_usingGLContext && (m_pixelBufferContextGeometry != m_nativeSize))) {
|
||||
QTVisualContextRelease(m_visualContext);
|
||||
m_pixelBufferContextGeometry = QSize();
|
||||
m_visualContext = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_nativeSize.isEmpty()) {
|
||||
if (!m_visualContext) {
|
||||
if (m_usingGLContext) {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Building OpenGL visual context" << m_nativeSize;
|
||||
#endif
|
||||
m_currentGLContext = QGLContext::currentContext();
|
||||
if (!createGLVisualContext()) {
|
||||
qWarning() << "QT7MovieRenderer: failed to create visual context";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Building Pixel Buffer visual context" << m_nativeSize;
|
||||
#endif
|
||||
if (!createPixelBufferVisualContext()) {
|
||||
qWarning() << "QT7MovieRenderer: failed to create visual context";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// targets a Movie to render into a visual context
|
||||
SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext);
|
||||
|
||||
m_displayLink->start();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void QT7MovieRenderer::setMovie(void *movie)
|
||||
{
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieRenderer::setMovie" << movie;
|
||||
#endif
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_movie != movie) {
|
||||
if (m_movie) {
|
||||
//ensure the old movie doesn't hold the visual context, otherwise it can't be reused
|
||||
SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil);
|
||||
[(QTMovie*)m_movie release];
|
||||
}
|
||||
|
||||
m_movie = movie;
|
||||
[(QTMovie*)m_movie retain];
|
||||
|
||||
setupVideoOutput();
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(movie);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QT7MovieRenderer::updateNaturalSize(const QSize &newSize)
|
||||
{
|
||||
if (m_nativeSize != newSize) {
|
||||
m_nativeSize = newSize;
|
||||
setupVideoOutput();
|
||||
}
|
||||
}
|
||||
|
||||
QAbstractVideoSurface *QT7MovieRenderer::surface() const
|
||||
{
|
||||
return m_surface;
|
||||
}
|
||||
|
||||
void QT7MovieRenderer::setSurface(QAbstractVideoSurface *surface)
|
||||
{
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Set video surface" << surface;
|
||||
#endif
|
||||
|
||||
if (surface == m_surface)
|
||||
return;
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_surface && m_surface->isActive())
|
||||
m_surface->stop();
|
||||
|
||||
m_surface = surface;
|
||||
setupVideoOutput();
|
||||
}
|
||||
|
||||
|
||||
QSize QT7MovieRenderer::nativeSize() const
|
||||
{
|
||||
return m_nativeSize;
|
||||
}
|
||||
|
||||
void QT7MovieRenderer::updateVideoFrame(const CVTimeStamp &ts)
|
||||
{
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_surface && m_surface->isActive() &&
|
||||
m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) {
|
||||
|
||||
CVImageBufferRef imageBuffer = NULL;
|
||||
|
||||
OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, &imageBuffer);
|
||||
|
||||
if (status == noErr && imageBuffer) {
|
||||
QAbstractVideoBuffer *buffer = 0;
|
||||
|
||||
if (m_usingGLContext) {
|
||||
buffer = new QT7CIImageVideoBuffer([CIImage imageWithCVImageBuffer:imageBuffer]);
|
||||
CVOpenGLTextureRelease((CVOpenGLTextureRef)imageBuffer);
|
||||
} else {
|
||||
buffer = new CVPixelBufferVideoBuffer((CVPixelBufferRef)imageBuffer);
|
||||
//buffer = new QT7CIImageVideoBuffer( [CIImage imageWithCVImageBuffer:imageBuffer] );
|
||||
CVPixelBufferRelease((CVPixelBufferRef)imageBuffer);
|
||||
}
|
||||
|
||||
QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_RGB32);
|
||||
m_surface->present(frame);
|
||||
QTVisualContextTask(m_visualContext);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "moc_qt7movierenderer.cpp"
|
||||
@@ -1,117 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7MOVIEVIDEOWIDGET_H
|
||||
#define QT7MOVIEVIDEOWIDGET_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <qvideowindowcontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
#include "qt7videooutput.h"
|
||||
|
||||
#include <QuartzCore/CVOpenGLTexture.h>
|
||||
#include <QuickTime/QuickTime.h>
|
||||
|
||||
class GLVideoWidget;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QCvDisplayLink;
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
|
||||
class QT7MovieVideoWidget : public QT7VideoWidgetControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QT7MovieVideoWidget(QObject *parent = 0);
|
||||
virtual ~QT7MovieVideoWidget();
|
||||
|
||||
void setMovie(void *movie);
|
||||
void updateNaturalSize(const QSize &newSize);
|
||||
|
||||
QWidget *videoWidget();
|
||||
|
||||
bool isFullScreen() const;
|
||||
void setFullScreen(bool fullScreen);
|
||||
|
||||
QSize nativeSize() const;
|
||||
|
||||
Qt::AspectRatioMode aspectRatioMode() const;
|
||||
void setAspectRatioMode(Qt::AspectRatioMode mode);
|
||||
|
||||
int brightness() const;
|
||||
void setBrightness(int brightness);
|
||||
|
||||
int contrast() const;
|
||||
void setContrast(int contrast);
|
||||
|
||||
int hue() const;
|
||||
void setHue(int hue);
|
||||
|
||||
int saturation() const;
|
||||
void setSaturation(int saturation);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateVideoFrame(const CVTimeStamp &ts);
|
||||
|
||||
private:
|
||||
void setupVideoOutput();
|
||||
bool createVisualContext();
|
||||
|
||||
void updateColors();
|
||||
|
||||
void *m_movie;
|
||||
GLVideoWidget *m_videoWidget;
|
||||
|
||||
QCvDisplayLink *m_displayLink;
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
QTVisualContextRef m_visualContext;
|
||||
#endif
|
||||
|
||||
bool m_fullscreen;
|
||||
QSize m_nativeSize;
|
||||
Qt::AspectRatioMode m_aspectRatioMode;
|
||||
int m_brightness;
|
||||
int m_contrast;
|
||||
int m_hue;
|
||||
int m_saturation;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,437 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#import <QTKit/QTDataReference.h>
|
||||
#import <QTKit/QTMovie.h>
|
||||
#import <QTKit/QTMovieView.h>
|
||||
#import <QTKit/QTMovieLayer.h>
|
||||
#import <AppKit/NSImage.h>
|
||||
#import <OpenGL/glu.h>
|
||||
|
||||
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7movievideowidget.h"
|
||||
#include "qt7playersession.h"
|
||||
#include "qcvdisplaylink.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
|
||||
#include <QGLWidget>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include "math.h"
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class GLVideoWidget : public QGLWidget
|
||||
{
|
||||
public:
|
||||
|
||||
GLVideoWidget(QWidget *parent, const QGLFormat &format)
|
||||
: QGLWidget(format, parent),
|
||||
m_texRef(0),
|
||||
m_nativeSize(640,480),
|
||||
m_aspectRatioMode(Qt::KeepAspectRatio)
|
||||
{
|
||||
setAutoFillBackground(false);
|
||||
}
|
||||
|
||||
void initializeGL()
|
||||
{
|
||||
QColor bgColor = palette().color(QPalette::Background);
|
||||
glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), bgColor.alphaF());
|
||||
}
|
||||
|
||||
void resizeGL(int w, int h)
|
||||
{
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glViewport(0, 0, GLsizei(w), GLsizei(h));
|
||||
gluOrtho2D(0, GLsizei(w), 0, GLsizei(h));
|
||||
updateGL();
|
||||
}
|
||||
|
||||
void paintGL()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (!m_texRef)
|
||||
return;
|
||||
|
||||
glPushMatrix();
|
||||
glDisable(GL_CULL_FACE);
|
||||
GLenum target = CVOpenGLTextureGetTarget(m_texRef);
|
||||
glEnable(target);
|
||||
|
||||
glBindTexture(target, CVOpenGLTextureGetName(m_texRef));
|
||||
glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2];
|
||||
CVOpenGLTextureGetCleanTexCoords(m_texRef, lowerLeft, lowerRight, upperRight, upperLeft);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
QRect rect = displayRect();
|
||||
glTexCoord2f(lowerLeft[0], lowerLeft[1]);
|
||||
glVertex2i(rect.topLeft().x(), rect.topLeft().y());
|
||||
glTexCoord2f(lowerRight[0], lowerRight[1]);
|
||||
glVertex2i(rect.topRight().x() + 1, rect.topRight().y());
|
||||
glTexCoord2f(upperRight[0], upperRight[1]);
|
||||
glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1);
|
||||
glTexCoord2f(upperLeft[0], upperLeft[1]);
|
||||
glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1);
|
||||
glEnd();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void setCVTexture(CVOpenGLTextureRef texRef)
|
||||
{
|
||||
if (m_texRef)
|
||||
CVOpenGLTextureRelease(m_texRef);
|
||||
|
||||
m_texRef = texRef;
|
||||
|
||||
if (m_texRef)
|
||||
CVOpenGLTextureRetain(m_texRef);
|
||||
|
||||
if (isVisible()) {
|
||||
makeCurrent();
|
||||
paintGL();
|
||||
swapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
QSize sizeHint() const
|
||||
{
|
||||
return m_nativeSize;
|
||||
}
|
||||
|
||||
void setNativeSize(const QSize &size)
|
||||
{
|
||||
m_nativeSize = size;
|
||||
}
|
||||
|
||||
void setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
if (m_aspectRatioMode != mode) {
|
||||
m_aspectRatioMode = mode;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QRect displayRect() const
|
||||
{
|
||||
QRect displayRect = rect();
|
||||
|
||||
if (m_aspectRatioMode == Qt::KeepAspectRatio) {
|
||||
QSize size = m_nativeSize;
|
||||
size.scale(displayRect.size(), Qt::KeepAspectRatio);
|
||||
|
||||
displayRect = QRect(QPoint(0, 0), size);
|
||||
displayRect.moveCenter(rect().center());
|
||||
}
|
||||
return displayRect;
|
||||
}
|
||||
|
||||
CVOpenGLTextureRef m_texRef;
|
||||
QSize m_nativeSize;
|
||||
Qt::AspectRatioMode m_aspectRatioMode;
|
||||
};
|
||||
|
||||
QT7MovieVideoWidget::QT7MovieVideoWidget(QObject *parent)
|
||||
:QT7VideoWidgetControl(parent),
|
||||
m_movie(0),
|
||||
m_videoWidget(0),
|
||||
m_fullscreen(false),
|
||||
m_aspectRatioMode(Qt::KeepAspectRatio),
|
||||
m_brightness(0),
|
||||
m_contrast(0),
|
||||
m_hue(0),
|
||||
m_saturation(0)
|
||||
{
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieVideoWidget";
|
||||
#endif
|
||||
|
||||
QGLFormat format = QGLFormat::defaultFormat();
|
||||
format.setSwapInterval(1); // Vertical sync (avoid tearing)
|
||||
m_videoWidget = new GLVideoWidget(0, format);
|
||||
|
||||
m_displayLink = new QCvDisplayLink(this);
|
||||
|
||||
connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp)));
|
||||
|
||||
if (!createVisualContext()) {
|
||||
qWarning() << "QT7MovieVideoWidget: failed to create visual context";
|
||||
}
|
||||
}
|
||||
|
||||
bool QT7MovieVideoWidget::createVisualContext()
|
||||
{
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
m_videoWidget->makeCurrent();
|
||||
|
||||
AutoReleasePool pool;
|
||||
CGLContextObj cglContext = CGLGetCurrentContext();
|
||||
NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat];
|
||||
CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]);
|
||||
|
||||
CFTypeRef keys[] = { kQTVisualContextOutputColorSpaceKey };
|
||||
CGColorSpaceRef colorSpace = NULL;
|
||||
CMProfileRef sysprof = NULL;
|
||||
|
||||
// Get the Systems Profile for the main display
|
||||
if (CMGetSystemProfile(&sysprof) == noErr) {
|
||||
// Create a colorspace with the systems profile
|
||||
colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof);
|
||||
CMCloseProfile(sysprof);
|
||||
}
|
||||
|
||||
if (!colorSpace)
|
||||
colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault,
|
||||
(const void **)keys,
|
||||
(const void **)&colorSpace, 1,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault,
|
||||
cglContext,
|
||||
cglPixelFormat,
|
||||
textureContextAttributes,
|
||||
&m_visualContext);
|
||||
if (err != noErr)
|
||||
qWarning() << "Could not create visual context (OpenGL)";
|
||||
|
||||
|
||||
return (err == noErr);
|
||||
#endif // QUICKTIME_C_API_AVAILABLE
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QT7MovieVideoWidget::~QT7MovieVideoWidget()
|
||||
{
|
||||
m_displayLink->stop();
|
||||
[(QTMovie*)m_movie release];
|
||||
delete m_videoWidget;
|
||||
}
|
||||
|
||||
QWidget *QT7MovieVideoWidget::videoWidget()
|
||||
{
|
||||
return m_videoWidget;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setupVideoOutput()
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieVideoWidget::setupVideoOutput" << m_movie;
|
||||
#endif
|
||||
|
||||
if (m_movie == 0) {
|
||||
m_displayLink->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue];
|
||||
m_nativeSize = QSize(size.width, size.height);
|
||||
m_videoWidget->setNativeSize(m_nativeSize);
|
||||
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
// targets a Movie to render into a visual context
|
||||
SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext);
|
||||
#endif
|
||||
|
||||
m_displayLink->start();
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setMovie(void *movie)
|
||||
{
|
||||
if (m_movie == movie)
|
||||
return;
|
||||
|
||||
if (m_movie) {
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil);
|
||||
#endif
|
||||
[(QTMovie*)m_movie release];
|
||||
}
|
||||
|
||||
m_movie = movie;
|
||||
[(QTMovie*)m_movie retain];
|
||||
|
||||
setupVideoOutput();
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::updateNaturalSize(const QSize &newSize)
|
||||
{
|
||||
if (m_nativeSize != newSize) {
|
||||
m_nativeSize = newSize;
|
||||
setupVideoOutput();
|
||||
}
|
||||
}
|
||||
|
||||
bool QT7MovieVideoWidget::isFullScreen() const
|
||||
{
|
||||
return m_fullscreen;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setFullScreen(bool fullScreen)
|
||||
{
|
||||
m_fullscreen = fullScreen;
|
||||
}
|
||||
|
||||
QSize QT7MovieVideoWidget::nativeSize() const
|
||||
{
|
||||
return m_nativeSize;
|
||||
}
|
||||
|
||||
Qt::AspectRatioMode QT7MovieVideoWidget::aspectRatioMode() const
|
||||
{
|
||||
return m_aspectRatioMode;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
m_aspectRatioMode = mode;
|
||||
m_videoWidget->setAspectRatioMode(mode);
|
||||
}
|
||||
|
||||
int QT7MovieVideoWidget::brightness() const
|
||||
{
|
||||
return m_brightness;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setBrightness(int brightness)
|
||||
{
|
||||
m_brightness = brightness;
|
||||
updateColors();
|
||||
}
|
||||
|
||||
int QT7MovieVideoWidget::contrast() const
|
||||
{
|
||||
return m_contrast;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setContrast(int contrast)
|
||||
{
|
||||
m_contrast = contrast;
|
||||
updateColors();
|
||||
}
|
||||
|
||||
int QT7MovieVideoWidget::hue() const
|
||||
{
|
||||
return m_hue;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setHue(int hue)
|
||||
{
|
||||
m_hue = hue;
|
||||
updateColors();
|
||||
}
|
||||
|
||||
int QT7MovieVideoWidget::saturation() const
|
||||
{
|
||||
return m_saturation;
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::setSaturation(int saturation)
|
||||
{
|
||||
m_saturation = saturation;
|
||||
updateColors();
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::updateColors()
|
||||
{
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
if (m_movie) {
|
||||
QTMovie *movie = (QTMovie*)m_movie;
|
||||
|
||||
Float32 value;
|
||||
value = m_brightness/100.0;
|
||||
SetMovieVisualBrightness([movie quickTimeMovie], value, 0);
|
||||
value = pow(2, m_contrast/50.0);
|
||||
SetMovieVisualContrast([movie quickTimeMovie], value, 0);
|
||||
value = m_hue/100.0;
|
||||
SetMovieVisualHue([movie quickTimeMovie], value, 0);
|
||||
value = 1.0+m_saturation/100.0;
|
||||
SetMovieVisualSaturation([movie quickTimeMovie], value, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void QT7MovieVideoWidget::updateVideoFrame(const CVTimeStamp &ts)
|
||||
{
|
||||
#ifdef QUICKTIME_C_API_AVAILABLE
|
||||
AutoReleasePool pool;
|
||||
// check for new frame
|
||||
if (m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) {
|
||||
CVOpenGLTextureRef currentFrame = NULL;
|
||||
|
||||
// get a "frame" (image buffer) from the Visual Context, indexed by the provided time
|
||||
OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, ¤tFrame);
|
||||
|
||||
// the above call may produce a null frame so check for this first
|
||||
// if we have a frame, then draw it
|
||||
if (status == noErr && currentFrame) {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "render video frame";
|
||||
#endif
|
||||
m_videoWidget->setCVTexture(currentFrame);
|
||||
CVOpenGLTextureRelease(currentFrame);
|
||||
}
|
||||
QTVisualContextTask(m_visualContext);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "moc_qt7movievideowidget.cpp"
|
||||
@@ -1,107 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7MOVIEVIEWOUTPUT_H
|
||||
#define QT7MOVIEVIEWOUTPUT_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
#include <qvideowindowcontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
#include "qt7videooutput.h"
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
|
||||
class QT7MovieViewOutput : public QT7VideoWindowControl
|
||||
{
|
||||
public:
|
||||
QT7MovieViewOutput(QObject *parent = 0);
|
||||
~QT7MovieViewOutput();
|
||||
|
||||
void setMovie(void *movie);
|
||||
void updateNaturalSize(const QSize &newSize);
|
||||
|
||||
WId winId() const;
|
||||
void setWinId(WId id);
|
||||
|
||||
QRect displayRect() const;
|
||||
void setDisplayRect(const QRect &rect);
|
||||
|
||||
bool isFullScreen() const;
|
||||
void setFullScreen(bool fullScreen);
|
||||
|
||||
void repaint();
|
||||
|
||||
QSize nativeSize() const;
|
||||
|
||||
Qt::AspectRatioMode aspectRatioMode() const;
|
||||
void setAspectRatioMode(Qt::AspectRatioMode mode);
|
||||
|
||||
int brightness() const;
|
||||
void setBrightness(int brightness);
|
||||
|
||||
int contrast() const;
|
||||
void setContrast(int contrast);
|
||||
|
||||
int hue() const;
|
||||
void setHue(int hue);
|
||||
|
||||
int saturation() const;
|
||||
void setSaturation(int saturation);
|
||||
|
||||
private:
|
||||
void setupVideoOutput();
|
||||
|
||||
void *m_movie;
|
||||
void *m_movieView;
|
||||
bool m_layouted;
|
||||
|
||||
WId m_winId;
|
||||
QRect m_displayRect;
|
||||
bool m_fullscreen;
|
||||
QSize m_nativeSize;
|
||||
Qt::AspectRatioMode m_aspectRatioMode;
|
||||
int m_brightness;
|
||||
int m_contrast;
|
||||
int m_hue;
|
||||
int m_saturation;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,339 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#import <QTKit/QTKit.h>
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7movieviewoutput.h"
|
||||
#include "qt7playersession.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include <QuartzCore/CIFilter.h>
|
||||
#include <QuartzCore/CIVector.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];}
|
||||
|
||||
@interface TransparentQTMovieView : QTMovieView
|
||||
{
|
||||
@private
|
||||
QRect *m_drawRect;
|
||||
qreal m_brightness, m_contrast, m_saturation, m_hue;
|
||||
}
|
||||
|
||||
- (TransparentQTMovieView *) init;
|
||||
- (void) setDrawRect:(QRect &)rect;
|
||||
- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img;
|
||||
- (void) setContrast:(qreal) contrast;
|
||||
@end
|
||||
|
||||
@implementation TransparentQTMovieView
|
||||
|
||||
- (TransparentQTMovieView *) init
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self) {
|
||||
[self setControllerVisible:NO];
|
||||
[self setContrast:1.0];
|
||||
[self setDelegate:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setContrast:(qreal) contrast
|
||||
{
|
||||
m_hue = 0.0;
|
||||
m_brightness = 0.0;
|
||||
m_contrast = contrast;
|
||||
m_saturation = 1.0;
|
||||
}
|
||||
|
||||
|
||||
- (void) setDrawRect:(QRect &)rect
|
||||
{
|
||||
*m_drawRect = rect;
|
||||
|
||||
NSRect nsrect;
|
||||
nsrect.origin.x = m_drawRect->x();
|
||||
nsrect.origin.y = m_drawRect->y();
|
||||
nsrect.size.width = m_drawRect->width();
|
||||
nsrect.size.height = m_drawRect->height();
|
||||
[self setFrame:nsrect];
|
||||
}
|
||||
|
||||
- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img
|
||||
{
|
||||
// This method is called from QTMovieView just
|
||||
// before the image will be drawn.
|
||||
Q_UNUSED(view);
|
||||
|
||||
if ( !qFuzzyCompare(m_brightness, 0.0) ||
|
||||
!qFuzzyCompare(m_contrast, 1.0) ||
|
||||
!qFuzzyCompare(m_saturation, 1.0)){
|
||||
CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"];
|
||||
[colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"];
|
||||
[colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"];
|
||||
[colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"];
|
||||
[colorFilter setValue:img forKey:@"inputImage"];
|
||||
img = [colorFilter valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
/*if (m_hue){
|
||||
CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"];
|
||||
[colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"];
|
||||
[colorFilter setValue:img forKey:@"inputImage"];
|
||||
img = [colorFilter valueForKey:@"outputImage"];
|
||||
}*/
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
VIDEO_TRANSPARENT(mouseDown);
|
||||
VIDEO_TRANSPARENT(mouseDragged);
|
||||
VIDEO_TRANSPARENT(mouseUp);
|
||||
VIDEO_TRANSPARENT(mouseMoved);
|
||||
VIDEO_TRANSPARENT(mouseEntered);
|
||||
VIDEO_TRANSPARENT(mouseExited);
|
||||
VIDEO_TRANSPARENT(rightMouseDown);
|
||||
VIDEO_TRANSPARENT(rightMouseDragged);
|
||||
VIDEO_TRANSPARENT(rightMouseUp);
|
||||
VIDEO_TRANSPARENT(otherMouseDown);
|
||||
VIDEO_TRANSPARENT(otherMouseDragged);
|
||||
VIDEO_TRANSPARENT(otherMouseUp);
|
||||
VIDEO_TRANSPARENT(keyDown);
|
||||
VIDEO_TRANSPARENT(keyUp);
|
||||
VIDEO_TRANSPARENT(scrollWheel)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
QT7MovieViewOutput::QT7MovieViewOutput(QObject *parent)
|
||||
:QT7VideoWindowControl(parent),
|
||||
m_movie(0),
|
||||
m_movieView(0),
|
||||
m_layouted(false),
|
||||
m_winId(0),
|
||||
m_fullscreen(false),
|
||||
m_aspectRatioMode(Qt::KeepAspectRatio),
|
||||
m_brightness(0),
|
||||
m_contrast(0),
|
||||
m_hue(0),
|
||||
m_saturation(0)
|
||||
{
|
||||
}
|
||||
|
||||
QT7MovieViewOutput::~QT7MovieViewOutput()
|
||||
{
|
||||
[(QTMovieView*)m_movieView release];
|
||||
[(QTMovie*)m_movie release];
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setupVideoOutput()
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieViewOutput::setupVideoOutput" << m_movie << m_winId;
|
||||
#endif
|
||||
if (m_movie == 0 || m_winId <= 0)
|
||||
return;
|
||||
|
||||
NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue];
|
||||
m_nativeSize = QSize(size.width, size.height);
|
||||
|
||||
if (!m_movieView)
|
||||
m_movieView = [[TransparentQTMovieView alloc] init];
|
||||
|
||||
[(QTMovieView*)m_movieView setControllerVisible:NO];
|
||||
[(QTMovieView*)m_movieView setMovie:(QTMovie*)m_movie];
|
||||
|
||||
[(NSView *)m_winId addSubview:(QTMovieView*)m_movieView];
|
||||
m_layouted = true;
|
||||
|
||||
setDisplayRect(m_displayRect);
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setMovie(void *movie)
|
||||
{
|
||||
if (m_movie != movie) {
|
||||
if (m_movie) {
|
||||
if (m_movieView)
|
||||
[(QTMovieView*)m_movieView setMovie:nil];
|
||||
|
||||
[(QTMovie*)m_movie release];
|
||||
}
|
||||
|
||||
m_movie = movie;
|
||||
|
||||
if (m_movie)
|
||||
[(QTMovie*)m_movie retain];
|
||||
|
||||
setupVideoOutput();
|
||||
}
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::updateNaturalSize(const QSize &newSize)
|
||||
{
|
||||
if (m_nativeSize != newSize) {
|
||||
m_nativeSize = newSize;
|
||||
Q_EMIT nativeSizeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
WId QT7MovieViewOutput::winId() const
|
||||
{
|
||||
return m_winId;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setWinId(WId id)
|
||||
{
|
||||
if (m_winId != id) {
|
||||
if (m_movieView && m_layouted) {
|
||||
[(QTMovieView*)m_movieView removeFromSuperview];
|
||||
m_layouted = false;
|
||||
}
|
||||
|
||||
m_winId = id;
|
||||
setupVideoOutput();
|
||||
}
|
||||
}
|
||||
|
||||
QRect QT7MovieViewOutput::displayRect() const
|
||||
{
|
||||
return m_displayRect;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setDisplayRect(const QRect &rect)
|
||||
{
|
||||
m_displayRect = rect;
|
||||
|
||||
if (m_movieView) {
|
||||
AutoReleasePool pool;
|
||||
[(QTMovieView*)m_movieView setPreservesAspectRatio:(m_aspectRatioMode == Qt::KeepAspectRatio ? YES : NO)];
|
||||
[(QTMovieView*)m_movieView setFrame:NSMakeRect(m_displayRect.x(),
|
||||
m_displayRect.y(),
|
||||
m_displayRect.width(),
|
||||
m_displayRect.height())];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool QT7MovieViewOutput::isFullScreen() const
|
||||
{
|
||||
return m_fullscreen;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setFullScreen(bool fullScreen)
|
||||
{
|
||||
m_fullscreen = fullScreen;
|
||||
setDisplayRect(m_displayRect);
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::repaint()
|
||||
{
|
||||
}
|
||||
|
||||
QSize QT7MovieViewOutput::nativeSize() const
|
||||
{
|
||||
return m_nativeSize;
|
||||
}
|
||||
|
||||
Qt::AspectRatioMode QT7MovieViewOutput::aspectRatioMode() const
|
||||
{
|
||||
return m_aspectRatioMode;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
m_aspectRatioMode = mode;
|
||||
setDisplayRect(m_displayRect);
|
||||
}
|
||||
|
||||
int QT7MovieViewOutput::brightness() const
|
||||
{
|
||||
return m_brightness;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setBrightness(int brightness)
|
||||
{
|
||||
m_brightness = brightness;
|
||||
}
|
||||
|
||||
int QT7MovieViewOutput::contrast() const
|
||||
{
|
||||
return m_contrast;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setContrast(int contrast)
|
||||
{
|
||||
m_contrast = contrast;
|
||||
[(TransparentQTMovieView*)m_movieView setContrast:(contrast/100.0+1.0)];
|
||||
}
|
||||
|
||||
int QT7MovieViewOutput::hue() const
|
||||
{
|
||||
return m_hue;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setHue(int hue)
|
||||
{
|
||||
m_hue = hue;
|
||||
}
|
||||
|
||||
int QT7MovieViewOutput::saturation() const
|
||||
{
|
||||
return m_saturation;
|
||||
}
|
||||
|
||||
void QT7MovieViewOutput::setSaturation(int saturation)
|
||||
{
|
||||
m_saturation = saturation;
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7MOVIEVIEWRENDERER_H
|
||||
#define QT7MOVIEVIEWRENDERER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <qvideowindowcontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
#include "qt7videooutput.h"
|
||||
#include <qvideoframe.h>
|
||||
|
||||
#include <QuartzCore/CIContext.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVideoFrame;
|
||||
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
class QGLWidget;
|
||||
class QGLFramebufferObject;
|
||||
class QWindow;
|
||||
class QOpenGLContext;
|
||||
|
||||
class QT7MovieViewRenderer : public QT7VideoRendererControl
|
||||
{
|
||||
public:
|
||||
QT7MovieViewRenderer(QObject *parent = 0);
|
||||
~QT7MovieViewRenderer();
|
||||
|
||||
void setMovie(void *movie);
|
||||
void updateNaturalSize(const QSize &newSize);
|
||||
|
||||
QAbstractVideoSurface *surface() const;
|
||||
void setSurface(QAbstractVideoSurface *surface);
|
||||
|
||||
void renderFrame(const QVideoFrame &);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event);
|
||||
|
||||
private:
|
||||
void setupVideoOutput();
|
||||
QVideoFrame convertCIImageToGLTexture(const QVideoFrame &frame);
|
||||
|
||||
void *m_movie;
|
||||
void *m_movieView;
|
||||
QSize m_nativeSize;
|
||||
QAbstractVideoSurface *m_surface;
|
||||
QVideoFrame m_currentFrame;
|
||||
QWindow *m_window;
|
||||
QOpenGLContext *m_context;
|
||||
QGLFramebufferObject *m_fbo;
|
||||
CIContext *m_ciContext;
|
||||
|
||||
bool m_pendingRenderEvent;
|
||||
QMutex m_mutex;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -1,509 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#import <QTKit/QTKit.h>
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7movieviewrenderer.h"
|
||||
#include "qt7playersession.h"
|
||||
#include "qt7ciimagevideobuffer.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qcoreevent.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtGui/qopenglcontext.h>
|
||||
#include <QtOpenGL/qgl.h>
|
||||
#include <QtOpenGL/qglframebufferobject.h>
|
||||
|
||||
#include <QtCore/qreadwritelock.h>
|
||||
|
||||
#include <qabstractvideobuffer.h>
|
||||
#include <qabstractvideosurface.h>
|
||||
#include <qvideosurfaceformat.h>
|
||||
|
||||
#include <QuartzCore/CIFilter.h>
|
||||
#include <QuartzCore/CIVector.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class NSBitmapVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
public:
|
||||
NSBitmapVideoBuffer(NSBitmapImageRep *buffer)
|
||||
: QAbstractVideoBuffer(NoHandle)
|
||||
, m_buffer(buffer)
|
||||
, m_mode(NotMapped)
|
||||
{
|
||||
[m_buffer retain];
|
||||
}
|
||||
|
||||
virtual ~NSBitmapVideoBuffer()
|
||||
{
|
||||
[m_buffer release];
|
||||
}
|
||||
|
||||
MapMode mapMode() const { return m_mode; }
|
||||
|
||||
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
{
|
||||
if (mode != NotMapped && m_mode == NotMapped) {
|
||||
if (numBytes)
|
||||
*numBytes = [m_buffer bytesPerPlane];
|
||||
|
||||
if (bytesPerLine)
|
||||
*bytesPerLine = [m_buffer bytesPerRow];
|
||||
|
||||
m_mode = mode;
|
||||
|
||||
return [m_buffer bitmapData];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void unmap() { m_mode = NotMapped; }
|
||||
|
||||
private:
|
||||
NSBitmapImageRep *m_buffer;
|
||||
MapMode m_mode;
|
||||
};
|
||||
|
||||
class TextureVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
public:
|
||||
TextureVideoBuffer(GLuint textureId)
|
||||
: QAbstractVideoBuffer(GLTextureHandle)
|
||||
, m_textureId(textureId)
|
||||
{}
|
||||
|
||||
virtual ~TextureVideoBuffer() {}
|
||||
|
||||
MapMode mapMode() const { return NotMapped; }
|
||||
uchar *map(MapMode, int*, int*) { return 0; }
|
||||
void unmap() {}
|
||||
|
||||
QVariant handle() const
|
||||
{
|
||||
return QVariant::fromValue<unsigned int>(m_textureId);
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint m_textureId;
|
||||
};
|
||||
|
||||
|
||||
#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];}
|
||||
|
||||
@interface HiddenQTMovieView : QTMovieView
|
||||
{
|
||||
@private
|
||||
QWindow *m_window;
|
||||
QT7MovieViewRenderer *m_renderer;
|
||||
QReadWriteLock m_rendererLock;
|
||||
}
|
||||
|
||||
- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer;
|
||||
- (void) setRenderer:(QT7MovieViewRenderer *)renderer;
|
||||
- (void) setDrawRect:(const QRect &)rect;
|
||||
- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img;
|
||||
@end
|
||||
|
||||
@implementation HiddenQTMovieView
|
||||
|
||||
- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self) {
|
||||
[self setControllerVisible:NO];
|
||||
[self setDelegate:self];
|
||||
|
||||
QWriteLocker lock(&self->m_rendererLock);
|
||||
self->m_renderer = renderer;
|
||||
|
||||
self->m_window = new QWindow;
|
||||
self->m_window->setOpacity(0.0);
|
||||
self->m_window->setGeometry(0,0,1,1);
|
||||
self->m_window->create();
|
||||
|
||||
[(NSView *)(self->m_window->winId()) addSubview:self];
|
||||
[self setDrawRect:QRect(0,0,1,1)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
self->m_window->deleteLater();
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setRenderer:(QT7MovieViewRenderer *)renderer
|
||||
{
|
||||
QWriteLocker lock(&m_rendererLock);
|
||||
m_renderer = renderer;
|
||||
}
|
||||
|
||||
- (void) setDrawRect:(const QRect &)rect
|
||||
{
|
||||
NSRect nsrect;
|
||||
nsrect.origin.x = rect.x();
|
||||
nsrect.origin.y = rect.y();
|
||||
nsrect.size.width = rect.width();
|
||||
nsrect.size.height = rect.height();
|
||||
[self setFrame:nsrect];
|
||||
}
|
||||
|
||||
- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img
|
||||
{
|
||||
// This method is called from QTMovieView just
|
||||
// before the image will be drawn.
|
||||
Q_UNUSED(view);
|
||||
QReadLocker lock(&m_rendererLock);
|
||||
AutoReleasePool pool;
|
||||
|
||||
if (m_renderer) {
|
||||
CGRect bounds = [img extent];
|
||||
int w = bounds.size.width;
|
||||
int h = bounds.size.height;
|
||||
|
||||
QVideoFrame frame;
|
||||
|
||||
QAbstractVideoSurface *surface = m_renderer->surface();
|
||||
if (!surface || !surface->isActive())
|
||||
return img;
|
||||
|
||||
if (surface->surfaceFormat().handleType() == QAbstractVideoBuffer::CoreImageHandle ||
|
||||
surface->surfaceFormat().handleType() == QAbstractVideoBuffer::GLTextureHandle) {
|
||||
//surface supports rendering of opengl based CIImage
|
||||
frame = QVideoFrame(new QT7CIImageVideoBuffer(img), QSize(w,h), QVideoFrame::Format_RGB32 );
|
||||
} else {
|
||||
//Swap R and B colors
|
||||
CIFilter *colorSwapFilter = [CIFilter filterWithName: @"CIColorMatrix" keysAndValues:
|
||||
@"inputImage", img,
|
||||
@"inputRVector", [CIVector vectorWithX: 0 Y: 0 Z: 1 W: 0],
|
||||
@"inputGVector", [CIVector vectorWithX: 0 Y: 1 Z: 0 W: 0],
|
||||
@"inputBVector", [CIVector vectorWithX: 1 Y: 0 Z: 0 W: 0],
|
||||
@"inputAVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 1],
|
||||
@"inputBiasVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 0],
|
||||
nil];
|
||||
CIImage *img = [colorSwapFilter valueForKey: @"outputImage"];
|
||||
NSBitmapImageRep *bitmap =[[NSBitmapImageRep alloc] initWithCIImage:img];
|
||||
//requesting the bitmap data is slow,
|
||||
//but it's better to do it here to avoid blocking the main thread for a long.
|
||||
[bitmap bitmapData];
|
||||
frame = QVideoFrame(new NSBitmapVideoBuffer(bitmap), QSize(w,h), QVideoFrame::Format_RGB32 );
|
||||
[bitmap release];
|
||||
}
|
||||
|
||||
m_renderer->renderFrame(frame);
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
// Override this method so that the movie doesn't stop if
|
||||
// the window becomes invisible
|
||||
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
|
||||
{
|
||||
Q_UNUSED(newWindow);
|
||||
}
|
||||
|
||||
|
||||
VIDEO_TRANSPARENT(mouseDown);
|
||||
VIDEO_TRANSPARENT(mouseDragged);
|
||||
VIDEO_TRANSPARENT(mouseUp);
|
||||
VIDEO_TRANSPARENT(mouseMoved);
|
||||
VIDEO_TRANSPARENT(mouseEntered);
|
||||
VIDEO_TRANSPARENT(mouseExited);
|
||||
VIDEO_TRANSPARENT(rightMouseDown);
|
||||
VIDEO_TRANSPARENT(rightMouseDragged);
|
||||
VIDEO_TRANSPARENT(rightMouseUp);
|
||||
VIDEO_TRANSPARENT(otherMouseDown);
|
||||
VIDEO_TRANSPARENT(otherMouseDragged);
|
||||
VIDEO_TRANSPARENT(otherMouseUp);
|
||||
VIDEO_TRANSPARENT(keyDown);
|
||||
VIDEO_TRANSPARENT(keyUp);
|
||||
VIDEO_TRANSPARENT(scrollWheel)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
QT7MovieViewRenderer::QT7MovieViewRenderer(QObject *parent)
|
||||
:QT7VideoRendererControl(parent),
|
||||
m_movie(0),
|
||||
m_movieView(0),
|
||||
m_surface(0),
|
||||
m_window(0),
|
||||
m_context(0),
|
||||
m_fbo(0),
|
||||
m_ciContext(0),
|
||||
m_pendingRenderEvent(false)
|
||||
{
|
||||
}
|
||||
|
||||
QT7MovieViewRenderer::~QT7MovieViewRenderer()
|
||||
{
|
||||
[(HiddenQTMovieView*)m_movieView setRenderer:0];
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_currentFrame = QVideoFrame();
|
||||
[(HiddenQTMovieView*)m_movieView release];
|
||||
[m_ciContext release];
|
||||
delete m_fbo;
|
||||
delete m_window;
|
||||
}
|
||||
|
||||
void QT7MovieViewRenderer::setupVideoOutput()
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7MovieViewRenderer::setupVideoOutput" << m_movie << m_surface;
|
||||
#endif
|
||||
|
||||
HiddenQTMovieView *movieView = (HiddenQTMovieView*)m_movieView;
|
||||
|
||||
if (movieView && !m_movie) {
|
||||
[movieView setMovie:nil];
|
||||
}
|
||||
|
||||
if (m_movie) {
|
||||
NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue];
|
||||
|
||||
m_nativeSize = QSize(size.width, size.height);
|
||||
|
||||
if (!movieView) {
|
||||
movieView = [[HiddenQTMovieView alloc] initWithRenderer:this];
|
||||
m_movieView = movieView;
|
||||
[movieView setControllerVisible:NO];
|
||||
}
|
||||
|
||||
[movieView setMovie:(QTMovie*)m_movie];
|
||||
[movieView setDrawRect:QRect(QPoint(0,0), m_nativeSize)];
|
||||
} else {
|
||||
m_nativeSize = QSize();
|
||||
}
|
||||
|
||||
if (m_surface && !m_nativeSize.isEmpty()) {
|
||||
bool coreImageFrameSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::CoreImageHandle).isEmpty();
|
||||
bool glTextureSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty();
|
||||
|
||||
QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle;
|
||||
QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_RGB32;
|
||||
|
||||
if (coreImageFrameSupported) {
|
||||
handleType = QAbstractVideoBuffer::CoreImageHandle;
|
||||
} else if (glTextureSupported) {
|
||||
handleType = QAbstractVideoBuffer::GLTextureHandle;
|
||||
pixelFormat = QVideoFrame::Format_BGR32;
|
||||
}
|
||||
|
||||
QVideoSurfaceFormat format(m_nativeSize, pixelFormat, handleType);
|
||||
|
||||
if (m_surface->isActive() && m_surface->surfaceFormat() != format) {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Surface format was changed, stop the surface.";
|
||||
#endif
|
||||
m_surface->stop();
|
||||
}
|
||||
|
||||
if (!m_surface->isActive()) {
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "Starting the surface with format" << format;
|
||||
#endif
|
||||
if (!m_surface->start(format))
|
||||
qWarning() << "failed to start video surface" << m_surface->error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Render the CIImage based video frame to FBO and return the video frame with resulting texture
|
||||
*/
|
||||
QVideoFrame QT7MovieViewRenderer::convertCIImageToGLTexture(const QVideoFrame &frame)
|
||||
{
|
||||
if (frame.handleType() != QAbstractVideoBuffer::CoreImageHandle)
|
||||
return QVideoFrame();
|
||||
|
||||
if (!m_window) {
|
||||
QOpenGLContext *qGlContext = 0;
|
||||
|
||||
if (m_surface)
|
||||
qGlContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
|
||||
|
||||
if (qGlContext) {
|
||||
m_window = new QWindow();
|
||||
|
||||
QSurfaceFormat format(qGlContext->format());
|
||||
|
||||
m_context = new QOpenGLContext(m_window);
|
||||
m_context->setShareContext(qGlContext);
|
||||
m_context->setFormat(format);
|
||||
m_context->create();
|
||||
|
||||
m_window->setFormat(format);
|
||||
m_window->setGeometry(0, 0, 1, 1);
|
||||
m_window->setSurfaceType(QWindow::OpenGLSurface);
|
||||
m_window->create();
|
||||
} else {
|
||||
return QVideoFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_context)
|
||||
return QVideoFrame();
|
||||
|
||||
m_context->makeCurrent(m_window);
|
||||
|
||||
if (!m_fbo || m_fbo->size() != frame.size()) {
|
||||
delete m_fbo;
|
||||
m_fbo = new QGLFramebufferObject(frame.size());
|
||||
}
|
||||
|
||||
CIImage *ciImg = (CIImage*)(frame.handle().value<void*>());
|
||||
if (ciImg) {
|
||||
AutoReleasePool pool;
|
||||
|
||||
QPainter p(m_fbo);
|
||||
p.beginNativePainting();
|
||||
CGLContextObj cglContext = CGLGetCurrentContext();
|
||||
if (cglContext) {
|
||||
if (!m_ciContext) {
|
||||
NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat];
|
||||
CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]);
|
||||
|
||||
m_ciContext = [CIContext contextWithCGLContext:cglContext
|
||||
pixelFormat:cglPixelFormat
|
||||
colorSpace:nil
|
||||
options:nil];
|
||||
|
||||
[m_ciContext retain];
|
||||
}
|
||||
|
||||
QRect viewport = QRect(0, 0, frame.width(), frame.height());
|
||||
CGRect sRect = CGRectMake(viewport.x(), viewport.y(), viewport.width(), viewport.height());
|
||||
CGRect dRect = CGRectMake(viewport.x(), viewport.y(), viewport.width(), viewport.height());
|
||||
|
||||
[m_ciContext drawImage:ciImg inRect:dRect fromRect:sRect];
|
||||
}
|
||||
|
||||
p.endNativePainting();
|
||||
|
||||
QAbstractVideoBuffer *buffer = new TextureVideoBuffer(m_fbo->texture());
|
||||
return QVideoFrame(buffer, frame.size(), QVideoFrame::Format_BGR32);
|
||||
}
|
||||
|
||||
return QVideoFrame();
|
||||
}
|
||||
|
||||
void QT7MovieViewRenderer::setMovie(void *movie)
|
||||
{
|
||||
if (movie == m_movie)
|
||||
return;
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_movie = movie;
|
||||
setupVideoOutput();
|
||||
}
|
||||
|
||||
void QT7MovieViewRenderer::updateNaturalSize(const QSize &newSize)
|
||||
{
|
||||
if (m_nativeSize != newSize) {
|
||||
m_nativeSize = newSize;
|
||||
setupVideoOutput();
|
||||
}
|
||||
}
|
||||
|
||||
QAbstractVideoSurface *QT7MovieViewRenderer::surface() const
|
||||
{
|
||||
return m_surface;
|
||||
}
|
||||
|
||||
void QT7MovieViewRenderer::setSurface(QAbstractVideoSurface *surface)
|
||||
{
|
||||
if (surface == m_surface)
|
||||
return;
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
if (m_surface && m_surface->isActive())
|
||||
m_surface->stop();
|
||||
|
||||
m_surface = surface;
|
||||
setupVideoOutput();
|
||||
}
|
||||
|
||||
void QT7MovieViewRenderer::renderFrame(const QVideoFrame &frame)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_currentFrame = frame;
|
||||
|
||||
if (!m_pendingRenderEvent)
|
||||
qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
|
||||
|
||||
m_pendingRenderEvent = true;
|
||||
}
|
||||
|
||||
bool QT7MovieViewRenderer::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::User) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_pendingRenderEvent = false;
|
||||
|
||||
if (m_surface->isActive()) {
|
||||
//For GL texture frames, render in the main thread CIImage based buffers
|
||||
//to FBO shared with video surface shared context
|
||||
if (m_surface->surfaceFormat().handleType() == QAbstractVideoBuffer::GLTextureHandle) {
|
||||
m_currentFrame = convertCIImageToGLTexture(m_currentFrame);
|
||||
if (m_currentFrame.isValid())
|
||||
m_surface->present(m_currentFrame);
|
||||
} else {
|
||||
m_surface->present(m_currentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
m_currentFrame = QVideoFrame();
|
||||
}
|
||||
|
||||
return QT7VideoRendererControl::event(event);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QT7SERVICEPLUGIN_H
|
||||
#define QT7SERVICEPLUGIN_H
|
||||
|
||||
#include <qmediaserviceproviderplugin.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QT7ServicePlugin
|
||||
: public QMediaServiceProviderPlugin
|
||||
, public QMediaServiceSupportedFormatsInterface
|
||||
, public QMediaServiceFeaturesInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QMediaServiceFeaturesInterface)
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "qt7.json")
|
||||
|
||||
public:
|
||||
QT7ServicePlugin();
|
||||
|
||||
QMediaService* create(QString const& key);
|
||||
void release(QMediaService *service);
|
||||
|
||||
QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const;
|
||||
QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const;
|
||||
QStringList supportedMimeTypes() const;
|
||||
|
||||
private:
|
||||
void buildSupportedTypes();
|
||||
|
||||
QStringList m_supportedMimeTypes;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QGSTREAMERSERVICEPLUGIN_H
|
||||
@@ -1,109 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL21$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 or version 3 as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
** following information to ensure the GNU Lesser General Public License
|
||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QT7VIDEOOUTPUTCONTROL_H
|
||||
#define QT7VIDEOOUTPUTCONTROL_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qsize.h>
|
||||
|
||||
#include <qvideowindowcontrol.h>
|
||||
#ifndef QT_NO_WIDGETS
|
||||
#include <qvideowidgetcontrol.h>
|
||||
#endif
|
||||
#include <qvideorenderercontrol.h>
|
||||
#include <qmediaplayer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMediaPlaylist;
|
||||
class QMediaPlaylistNavigator;
|
||||
class QT7PlayerSession;
|
||||
class QT7PlayerService;
|
||||
|
||||
|
||||
class QT7VideoOutput {
|
||||
public:
|
||||
virtual ~QT7VideoOutput() {}
|
||||
virtual void setMovie(void *movie) = 0;
|
||||
virtual void updateNaturalSize(const QSize &newSize) = 0;
|
||||
};
|
||||
|
||||
#define QT7VideoOutput_iid \
|
||||
"org.qt-project.qt.QT7VideoOutput/5.0"
|
||||
Q_DECLARE_INTERFACE(QT7VideoOutput, QT7VideoOutput_iid)
|
||||
|
||||
class QT7VideoWindowControl : public QVideoWindowControl, public QT7VideoOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QT7VideoOutput)
|
||||
public:
|
||||
virtual ~QT7VideoWindowControl() {}
|
||||
|
||||
protected:
|
||||
QT7VideoWindowControl(QObject *parent)
|
||||
:QVideoWindowControl(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
class QT7VideoRendererControl : public QVideoRendererControl, public QT7VideoOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QT7VideoOutput)
|
||||
public:
|
||||
virtual ~QT7VideoRendererControl() {}
|
||||
|
||||
protected:
|
||||
QT7VideoRendererControl(QObject *parent)
|
||||
:QVideoRendererControl(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
class QT7VideoWidgetControl : public QVideoWidgetControl, public QT7VideoOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QT7VideoOutput)
|
||||
public:
|
||||
virtual ~QT7VideoWidgetControl() {}
|
||||
|
||||
protected:
|
||||
QT7VideoWidgetControl(QObject *parent)
|
||||
:QVideoWidgetControl(parent)
|
||||
{}
|
||||
};
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
@@ -404,6 +404,7 @@ QList<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode)
|
||||
Q_UNUSED(mode)
|
||||
|
||||
QList<QByteArray> devices;
|
||||
#ifndef Q_OS_WINCE
|
||||
//enumerate device fullnames through directshow api
|
||||
CoInitialize(NULL);
|
||||
ICreateDevEnum *pDevEnum = NULL;
|
||||
@@ -455,6 +456,35 @@ QList<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode)
|
||||
}
|
||||
}
|
||||
CoUninitialize();
|
||||
#else // Q_OS_WINCE
|
||||
if (mode == QAudio::AudioOutput) {
|
||||
WAVEOUTCAPS woc;
|
||||
unsigned long iNumDevs,i;
|
||||
iNumDevs = waveOutGetNumDevs();
|
||||
for (i=0;i<iNumDevs;i++) {
|
||||
if (waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
QByteArray device;
|
||||
QDataStream ds(&device, QIODevice::WriteOnly);
|
||||
ds << quint32(i) << QString::fromWCharArray(woc.szPname);
|
||||
devices.append(device);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WAVEINCAPS woc;
|
||||
unsigned long iNumDevs,i;
|
||||
iNumDevs = waveInGetNumDevs();
|
||||
for (i=0;i<iNumDevs;i++) {
|
||||
if (waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
QByteArray device;
|
||||
QDataStream ds(&device, QIODevice::WriteOnly);
|
||||
ds << quint32(i) << QString::fromWCharArray(woc.szPname);
|
||||
devices.append(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !Q_OS_WINCE
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
@@ -698,7 +698,7 @@ qint64 QWindowsAudioInput::elapsedUSecs() const
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return timeStampOpened.elapsed()*1000;
|
||||
return timeStampOpened.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
void QWindowsAudioInput::reset()
|
||||
|
||||
@@ -674,7 +674,7 @@ qint64 QWindowsAudioOutput::elapsedUSecs() const
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return timeStampOpened.elapsed()*1000;
|
||||
return timeStampOpened.elapsed() * qint64(1000);
|
||||
}
|
||||
|
||||
QAudio::Error QWindowsAudioOutput::error() const
|
||||
|
||||
@@ -5,7 +5,8 @@ PLUGIN_TYPE = audio
|
||||
PLUGIN_CLASS_NAME = QWindowsAudioPlugin
|
||||
load(qt_plugin)
|
||||
|
||||
LIBS += -lwinmm -lstrmiids -lole32 -loleaut32
|
||||
LIBS += -lstrmiids -lole32 -loleaut32
|
||||
!wince*:LIBS += -lwinmm
|
||||
|
||||
HEADERS += \
|
||||
qwindowsaudioplugin.h \
|
||||
|
||||
783
src/plugins/winrt/qwinrtcameracontrol.cpp
Normal file
783
src/plugins/winrt/qwinrtcameracontrol.cpp
Normal file
@@ -0,0 +1,783 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtcameracontrol.h"
|
||||
#include "qwinrtcameravideorenderercontrol.h"
|
||||
#include "qwinrtvideodeviceselectorcontrol.h"
|
||||
#include "qwinrtcameraimagecapturecontrol.h"
|
||||
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mferror.h>
|
||||
#include <mfidl.h>
|
||||
#include <wrl.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
#include <windows.media.capture.h>
|
||||
#include <windows.storage.streams.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace ABI::Windows::Devices::Enumeration;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::Foundation::Collections;
|
||||
using namespace ABI::Windows::Media;
|
||||
using namespace ABI::Windows::Media::Capture;
|
||||
using namespace ABI::Windows::Media::Devices;
|
||||
using namespace ABI::Windows::Media::MediaProperties;
|
||||
using namespace ABI::Windows::Storage::Streams;
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
#define RETURN_VOID_AND_EMIT_ERROR(msg) \
|
||||
if (FAILED(hr)) { \
|
||||
emit error(QCamera::CameraError, qt_error_string(hr)); \
|
||||
RETURN_VOID_IF_FAILED(msg); \
|
||||
}
|
||||
|
||||
class CriticalSectionLocker
|
||||
{
|
||||
public:
|
||||
CriticalSectionLocker(CRITICAL_SECTION *section)
|
||||
: m_section(section)
|
||||
{
|
||||
EnterCriticalSection(m_section);
|
||||
}
|
||||
~CriticalSectionLocker()
|
||||
{
|
||||
LeaveCriticalSection(m_section);
|
||||
}
|
||||
private:
|
||||
CRITICAL_SECTION *m_section;
|
||||
};
|
||||
|
||||
class MediaStream : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMFStreamSink, IMFMediaEventGenerator, IMFMediaTypeHandler>
|
||||
{
|
||||
public:
|
||||
MediaStream(IMFMediaType *type, IMFMediaSink *mediaSink, QWinRTCameraVideoRendererControl *videoRenderer)
|
||||
: m_type(type), m_sink(mediaSink), m_videoRenderer(videoRenderer)
|
||||
{
|
||||
Q_ASSERT(m_videoRenderer);
|
||||
|
||||
InitializeCriticalSectionEx(&m_mutex, 0, 0);
|
||||
|
||||
HRESULT hr;
|
||||
hr = MFCreateEventQueue(&m_eventQueue);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = MFAllocateSerialWorkQueue(MFASYNC_CALLBACK_QUEUE_STANDARD, &m_workQueueId);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
~MediaStream()
|
||||
{
|
||||
CriticalSectionLocker locker(&m_mutex);
|
||||
m_eventQueue->Shutdown();
|
||||
DeleteCriticalSection(&m_mutex);
|
||||
}
|
||||
|
||||
HRESULT RequestSample()
|
||||
{
|
||||
if (m_pendingSamples.load() < 3) {
|
||||
m_pendingSamples.ref();
|
||||
return QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, Q_NULLPTR);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetEvent(DWORD flags, IMFMediaEvent **event) Q_DECL_OVERRIDE
|
||||
{
|
||||
EnterCriticalSection(&m_mutex);
|
||||
// Create an extra reference to avoid deadlock
|
||||
ComPtr<IMFMediaEventQueue> eventQueue = m_eventQueue;
|
||||
LeaveCriticalSection(&m_mutex);
|
||||
|
||||
return eventQueue->GetEvent(flags, event);
|
||||
}
|
||||
|
||||
HRESULT __stdcall BeginGetEvent(IMFAsyncCallback *callback, IUnknown *state) Q_DECL_OVERRIDE
|
||||
{
|
||||
CriticalSectionLocker locker(&m_mutex);
|
||||
HRESULT hr = m_eventQueue->BeginGetEvent(callback, state);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT __stdcall EndGetEvent(IMFAsyncResult *result, IMFMediaEvent **event) Q_DECL_OVERRIDE
|
||||
{
|
||||
CriticalSectionLocker locker(&m_mutex);
|
||||
return m_eventQueue->EndGetEvent(result, event);
|
||||
}
|
||||
|
||||
HRESULT __stdcall QueueEvent(MediaEventType eventType, const GUID &extendedType, HRESULT status, const PROPVARIANT *value) Q_DECL_OVERRIDE
|
||||
{
|
||||
CriticalSectionLocker locker(&m_mutex);
|
||||
return m_eventQueue->QueueEventParamVar(eventType, extendedType, status, value);
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetMediaSink(IMFMediaSink **mediaSink) Q_DECL_OVERRIDE
|
||||
{
|
||||
*mediaSink = m_sink;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetIdentifier(DWORD *identifier) Q_DECL_OVERRIDE
|
||||
{
|
||||
*identifier = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetMediaTypeHandler(IMFMediaTypeHandler **handler) Q_DECL_OVERRIDE
|
||||
{
|
||||
return QueryInterface(IID_PPV_ARGS(handler));
|
||||
}
|
||||
|
||||
HRESULT __stdcall ProcessSample(IMFSample *sample) Q_DECL_OVERRIDE
|
||||
{
|
||||
ComPtr<IMFMediaBuffer> buffer;
|
||||
HRESULT hr = sample->GetBufferByIndex(0, &buffer);
|
||||
RETURN_HR_IF_FAILED("Failed to get buffer from camera sample");
|
||||
ComPtr<IMF2DBuffer> buffer2d;
|
||||
hr = buffer.As(&buffer2d);
|
||||
RETURN_HR_IF_FAILED("Failed to cast camera sample buffer to 2D buffer");
|
||||
|
||||
m_pendingSamples.deref();
|
||||
m_videoRenderer->queueBuffer(buffer2d.Get());
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT __stdcall PlaceMarker(MFSTREAMSINK_MARKER_TYPE type, const PROPVARIANT *value, const PROPVARIANT *context) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(type);
|
||||
Q_UNUSED(value);
|
||||
QueueEvent(MEStreamSinkMarker, GUID_NULL, S_OK, context);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall Flush() Q_DECL_OVERRIDE
|
||||
{
|
||||
m_videoRenderer->discardBuffers();
|
||||
m_pendingSamples.store(0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall IsMediaTypeSupported(IMFMediaType *type, IMFMediaType **) Q_DECL_OVERRIDE
|
||||
{
|
||||
HRESULT hr;
|
||||
GUID majorType;
|
||||
hr = type->GetMajorType(&majorType);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (!IsEqualGUID(majorType, MFMediaType_Video))
|
||||
return MF_E_INVALIDMEDIATYPE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetMediaTypeCount(DWORD *typeCount) Q_DECL_OVERRIDE
|
||||
{
|
||||
*typeCount = 1;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetMediaTypeByIndex(DWORD index, IMFMediaType **type) Q_DECL_OVERRIDE
|
||||
{
|
||||
if (index == 0)
|
||||
return m_type.CopyTo(type);
|
||||
return E_BOUNDS;
|
||||
}
|
||||
|
||||
HRESULT __stdcall SetCurrentMediaType(IMFMediaType *type) Q_DECL_OVERRIDE
|
||||
{
|
||||
if (FAILED(IsMediaTypeSupported(type, Q_NULLPTR)))
|
||||
return MF_E_INVALIDREQUEST;
|
||||
|
||||
m_type = type;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetCurrentMediaType(IMFMediaType **type) Q_DECL_OVERRIDE
|
||||
{
|
||||
return m_type.CopyTo(type);
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetMajorType(GUID *majorType) Q_DECL_OVERRIDE
|
||||
{
|
||||
return m_type->GetMajorType(majorType);
|
||||
}
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION m_mutex;
|
||||
ComPtr<IMFMediaType> m_type;
|
||||
IMFMediaSink *m_sink;
|
||||
ComPtr<IMFMediaEventQueue> m_eventQueue;
|
||||
DWORD m_workQueueId;
|
||||
|
||||
QWinRTCameraVideoRendererControl *m_videoRenderer;
|
||||
QAtomicInt m_pendingSamples;
|
||||
};
|
||||
|
||||
class MediaSink : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMediaExtension, IMFMediaSink, IMFClockStateSink>
|
||||
{
|
||||
public:
|
||||
MediaSink(IMediaEncodingProfile *encodingProfile, QWinRTCameraVideoRendererControl *videoRenderer)
|
||||
: m_videoRenderer(videoRenderer)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IVideoEncodingProperties> videoProperties;
|
||||
hr = encodingProfile->get_Video(&videoProperties);
|
||||
RETURN_VOID_IF_FAILED("Failed to get video properties");
|
||||
ComPtr<IMFMediaType> videoType;
|
||||
hr = MFCreateMediaTypeFromProperties(videoProperties.Get(), &videoType);
|
||||
RETURN_VOID_IF_FAILED("Failed to create video type");
|
||||
m_stream = Make<MediaStream>(videoType.Get(), this, videoRenderer);
|
||||
}
|
||||
|
||||
~MediaSink()
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT RequestSample()
|
||||
{
|
||||
return m_stream->RequestSample();
|
||||
}
|
||||
|
||||
HRESULT __stdcall SetProperties(Collections::IPropertySet *configuration) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(configuration);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetCharacteristics(DWORD *characteristics) Q_DECL_OVERRIDE
|
||||
{
|
||||
*characteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall AddStreamSink(DWORD streamSinkIdentifier, IMFMediaType *mediaType, IMFStreamSink **streamSink) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(streamSinkIdentifier);
|
||||
Q_UNUSED(mediaType);
|
||||
Q_UNUSED(streamSink);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT __stdcall RemoveStreamSink(DWORD streamSinkIdentifier) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(streamSinkIdentifier);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetStreamSinkCount(DWORD *streamSinkCount) Q_DECL_OVERRIDE
|
||||
{
|
||||
*streamSinkCount = 1;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetStreamSinkByIndex(DWORD index, IMFStreamSink **streamSink) Q_DECL_OVERRIDE
|
||||
{
|
||||
if (index == 0)
|
||||
return m_stream.CopyTo(streamSink);
|
||||
return MF_E_INVALIDINDEX;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetStreamSinkById(DWORD streamSinkIdentifier, IMFStreamSink **streamSink) Q_DECL_OVERRIDE
|
||||
{
|
||||
// ID and index are always 0
|
||||
HRESULT hr = GetStreamSinkByIndex(streamSinkIdentifier, streamSink);
|
||||
return hr == MF_E_INVALIDINDEX ? MF_E_INVALIDSTREAMNUMBER : hr;
|
||||
}
|
||||
|
||||
HRESULT __stdcall SetPresentationClock(IMFPresentationClock *presentationClock) Q_DECL_OVERRIDE
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
m_presentationClock = presentationClock;
|
||||
if (m_presentationClock)
|
||||
hr = m_presentationClock->AddClockStateSink(this);
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT __stdcall GetPresentationClock(IMFPresentationClock **presentationClock) Q_DECL_OVERRIDE
|
||||
{
|
||||
return m_presentationClock.CopyTo(presentationClock);
|
||||
}
|
||||
|
||||
HRESULT __stdcall Shutdown() Q_DECL_OVERRIDE
|
||||
{
|
||||
m_stream->Flush();
|
||||
m_videoRenderer->setActive(false);
|
||||
return m_presentationClock->Stop();
|
||||
}
|
||||
|
||||
HRESULT __stdcall OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(systemTime);
|
||||
Q_UNUSED(clockStartOffset);
|
||||
|
||||
m_videoRenderer->setActive(true);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT __stdcall OnClockStop(MFTIME systemTime) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(systemTime);
|
||||
|
||||
m_videoRenderer->setActive(false);
|
||||
|
||||
return m_stream->QueueEvent(MEStreamSinkStopped, GUID_NULL, S_OK, Q_NULLPTR);
|
||||
}
|
||||
|
||||
HRESULT __stdcall OnClockPause(MFTIME systemTime) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(systemTime);
|
||||
|
||||
m_videoRenderer->setActive(false);
|
||||
|
||||
return m_stream->QueueEvent(MEStreamSinkPaused, GUID_NULL, S_OK, Q_NULLPTR);
|
||||
}
|
||||
|
||||
HRESULT __stdcall OnClockRestart(MFTIME systemTime) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(systemTime);
|
||||
|
||||
m_videoRenderer->setActive(true);
|
||||
|
||||
return m_stream->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, Q_NULLPTR);
|
||||
}
|
||||
|
||||
HRESULT __stdcall OnClockSetRate(MFTIME systemTime, float rate) Q_DECL_OVERRIDE
|
||||
{
|
||||
Q_UNUSED(systemTime);
|
||||
Q_UNUSED(rate);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
private:
|
||||
ComPtr<MediaStream> m_stream;
|
||||
ComPtr<IMFPresentationClock> m_presentationClock;
|
||||
|
||||
QWinRTCameraVideoRendererControl *m_videoRenderer;
|
||||
};
|
||||
|
||||
class QWinRTCameraControlPrivate
|
||||
{
|
||||
public:
|
||||
QCamera::State state;
|
||||
QCamera::Status status;
|
||||
QCamera::CaptureModes captureMode;
|
||||
|
||||
ComPtr<IMediaCapture> capture;
|
||||
ComPtr<IMediaCaptureVideoPreview> capturePreview;
|
||||
EventRegistrationToken captureFailedCookie;
|
||||
EventRegistrationToken recordLimitationCookie;
|
||||
|
||||
ComPtr<IMediaEncodingProfileStatics> encodingProfileFactory;
|
||||
|
||||
ComPtr<IMediaEncodingProfile> encodingProfile;
|
||||
ComPtr<MediaSink> mediaSink;
|
||||
|
||||
QSize size;
|
||||
QPointer<QWinRTCameraVideoRendererControl> videoRenderer;
|
||||
QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector;
|
||||
QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl;
|
||||
};
|
||||
|
||||
QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
|
||||
: QCameraControl(parent), d_ptr(new QWinRTCameraControlPrivate)
|
||||
{
|
||||
Q_D(QWinRTCameraControl);
|
||||
|
||||
d->state = QCamera::UnloadedState;
|
||||
d->status = QCamera::UnloadedStatus;
|
||||
d->captureMode = QCamera::CaptureStillImage;
|
||||
d->captureFailedCookie.value = 0;
|
||||
d->recordLimitationCookie.value = 0;
|
||||
d->videoRenderer = new QWinRTCameraVideoRendererControl(d->size, this);
|
||||
connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested,
|
||||
this, &QWinRTCameraControl::onBufferRequested);
|
||||
d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this);
|
||||
d->imageCaptureControl = new QWinRTCameraImageCaptureControl(this);
|
||||
}
|
||||
|
||||
QWinRTCameraControl::~QWinRTCameraControl()
|
||||
{
|
||||
setState(QCamera::UnloadedState);
|
||||
}
|
||||
|
||||
QCamera::State QWinRTCameraControl::state() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->state;
|
||||
}
|
||||
|
||||
void QWinRTCameraControl::setState(QCamera::State state)
|
||||
{
|
||||
Q_D(QWinRTCameraControl);
|
||||
|
||||
if (d->state == state)
|
||||
return;
|
||||
|
||||
HRESULT hr;
|
||||
switch (state) {
|
||||
case QCamera::ActiveState: {
|
||||
// Capture has not been created or initialized
|
||||
if (d->state == QCamera::UnloadedState) {
|
||||
hr = initialize();
|
||||
RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture");
|
||||
}
|
||||
Q_ASSERT(d->state == QCamera::LoadedState);
|
||||
|
||||
d->mediaSink = Make<MediaSink>(d->encodingProfile.Get(), d->videoRenderer);
|
||||
ComPtr<IAsyncAction> op;
|
||||
hr = d->capturePreview->StartPreviewToCustomSinkAsync(d->encodingProfile.Get(), d->mediaSink.Get(), &op);
|
||||
RETURN_VOID_AND_EMIT_ERROR("Failed to initiate capture");
|
||||
if (d->status != QCamera::StartingStatus) {
|
||||
d->status = QCamera::StartingStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
|
||||
hr = QWinRTFunctions::await(op);
|
||||
if (FAILED(hr)) {
|
||||
emit error(QCamera::CameraError, qt_error_string(hr));
|
||||
setState(QCamera::UnloadedState); // Unload everything, as initialize() will need be called again
|
||||
return;
|
||||
}
|
||||
|
||||
d->state = QCamera::ActiveState;
|
||||
emit stateChanged(d->state);
|
||||
d->status = QCamera::ActiveStatus;
|
||||
emit statusChanged(d->status);
|
||||
break;
|
||||
}
|
||||
case QCamera::LoadedState: {
|
||||
// If moving from unloaded, initialize the camera
|
||||
if (d->state == QCamera::UnloadedState) {
|
||||
hr = initialize();
|
||||
RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture");
|
||||
}
|
||||
// fall through
|
||||
}
|
||||
case QCamera::UnloadedState: {
|
||||
// Stop the camera if it is running (transition to LoadedState)
|
||||
if (d->status == QCamera::ActiveStatus) {
|
||||
ComPtr<IAsyncAction> op;
|
||||
hr = d->capturePreview->StopPreviewAsync(&op);
|
||||
RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview");
|
||||
if (d->status != QCamera::StoppingStatus) {
|
||||
d->status = QCamera::StoppingStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = QWinRTFunctions::await(op); // Synchronize unloading
|
||||
if (FAILED(hr))
|
||||
emit error(QCamera::InvalidRequestError, qt_error_string(hr));
|
||||
|
||||
d->mediaSink->Shutdown();
|
||||
d->mediaSink.Reset();
|
||||
|
||||
d->state = QCamera::LoadedState;
|
||||
emit stateChanged(d->state);
|
||||
|
||||
d->status = QCamera::LoadedStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
// Completely unload if needed
|
||||
if (state == QCamera::UnloadedState) {
|
||||
if (!d->capture) // Already unloaded
|
||||
break;
|
||||
|
||||
if (d->status != QCamera::UnloadingStatus) {
|
||||
d->status = QCamera::UnloadingStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
|
||||
if (d->capture && d->captureFailedCookie.value) {
|
||||
hr = d->capture->remove_Failed(d->captureFailedCookie);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
d->captureFailedCookie.value = 0;
|
||||
}
|
||||
if (d->capture && d->recordLimitationCookie.value) {
|
||||
d->capture->remove_RecordLimitationExceeded(d->recordLimitationCookie);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
d->recordLimitationCookie.value = 0;
|
||||
}
|
||||
ComPtr<IClosable> capture;
|
||||
hr = d->capture.As(&capture);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = capture->Close();
|
||||
RETURN_VOID_AND_EMIT_ERROR("Failed to close the capture manger");
|
||||
d->capture.Reset();
|
||||
if (d->state != QCamera::UnloadedState) {
|
||||
d->state = QCamera::UnloadedState;
|
||||
emit stateChanged(d->state);
|
||||
}
|
||||
if (d->status != QCamera::UnloadedStatus) {
|
||||
d->status = QCamera::UnloadedStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QCamera::Status QWinRTCameraControl::status() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->status;
|
||||
}
|
||||
|
||||
QCamera::CaptureModes QWinRTCameraControl::captureMode() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->captureMode;
|
||||
}
|
||||
|
||||
void QWinRTCameraControl::setCaptureMode(QCamera::CaptureModes mode)
|
||||
{
|
||||
Q_D(QWinRTCameraControl);
|
||||
|
||||
if (d->captureMode == mode)
|
||||
return;
|
||||
|
||||
if (!isCaptureModeSupported(mode)) {
|
||||
qWarning("Unsupported capture mode: %d", mode);
|
||||
return;
|
||||
}
|
||||
|
||||
d->captureMode = mode;
|
||||
emit captureModeChanged(d->captureMode);
|
||||
}
|
||||
|
||||
bool QWinRTCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
|
||||
{
|
||||
return mode >= QCamera::CaptureViewfinder && mode <= QCamera::CaptureStillImage;
|
||||
}
|
||||
|
||||
bool QWinRTCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const
|
||||
{
|
||||
Q_UNUSED(changeType);
|
||||
|
||||
return status == QCamera::UnloadedStatus; // For now, assume shutdown is required for all property changes
|
||||
}
|
||||
|
||||
QVideoRendererControl *QWinRTCameraControl::videoRenderer() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->videoRenderer;
|
||||
}
|
||||
|
||||
QVideoDeviceSelectorControl *QWinRTCameraControl::videoDeviceSelector() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->videoDeviceSelector;
|
||||
}
|
||||
|
||||
QCameraImageCaptureControl *QWinRTCameraControl::imageCaptureControl() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->imageCaptureControl;
|
||||
}
|
||||
|
||||
IMediaCapture *QWinRTCameraControl::handle() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->capture.Get();
|
||||
}
|
||||
|
||||
QSize QWinRTCameraControl::imageSize() const
|
||||
{
|
||||
Q_D(const QWinRTCameraControl);
|
||||
return d->size;
|
||||
}
|
||||
|
||||
void QWinRTCameraControl::onBufferRequested()
|
||||
{
|
||||
Q_D(QWinRTCameraControl);
|
||||
|
||||
if (d->mediaSink)
|
||||
d->mediaSink->RequestSample();
|
||||
}
|
||||
|
||||
HRESULT QWinRTCameraControl::initialize()
|
||||
{
|
||||
Q_D(QWinRTCameraControl);
|
||||
|
||||
if (d->status != QCamera::LoadingStatus) {
|
||||
d->status = QCamera::LoadingStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IInspectable> capture;
|
||||
hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(),
|
||||
&capture);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = capture.As(&d->capture);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = d->capture.As(&d->capturePreview);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = d->capture->add_Failed(Callback<IMediaCaptureFailedEventHandler>(this, &QWinRTCameraControl::onCaptureFailed).Get(),
|
||||
&d->captureFailedCookie);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = d->capture->add_RecordLimitationExceeded(Callback<IRecordLimitationExceededEventHandler>(this, &QWinRTCameraControl::onRecordLimitationExceeded).Get(),
|
||||
&d->recordLimitationCookie);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
|
||||
IID_PPV_ARGS(&d->encodingProfileFactory));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
int deviceIndex = d->videoDeviceSelector->selectedDevice();
|
||||
if (deviceIndex < 0)
|
||||
deviceIndex = d->videoDeviceSelector->defaultDevice();
|
||||
|
||||
const QString deviceName = d->videoDeviceSelector->deviceName(deviceIndex);
|
||||
if (deviceName.isEmpty()) {
|
||||
qWarning("No video device available or selected.");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
ComPtr<IMediaCaptureInitializationSettings> settings;
|
||||
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings).Get(),
|
||||
&settings);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
HStringReference deviceId(reinterpret_cast<LPCWSTR>(deviceName.utf16()), deviceName.length());
|
||||
hr = settings->put_VideoDeviceId(deviceId.Get());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = settings->put_StreamingCaptureMode(StreamingCaptureMode_Video);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = settings->put_PhotoCaptureSource(PhotoCaptureSource_Auto);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IAsyncAction> op;
|
||||
hr = d->capture->InitializeWithSettingsAsync(settings.Get(), &op);
|
||||
RETURN_HR_IF_FAILED("Failed to begin initialization of media capture manager");
|
||||
hr = QWinRTFunctions::await(op, QWinRTFunctions::ProcessThreadEvents);
|
||||
if (hr == E_ACCESSDENIED) {
|
||||
qWarning("Access denied when initializing the media capture manager. "
|
||||
"Check your manifest settings for microphone and webcam access.");
|
||||
}
|
||||
RETURN_HR_IF_FAILED("Failed to initialize media capture manager");
|
||||
|
||||
ComPtr<IVideoDeviceController> videoDeviceController;
|
||||
hr = d->capture->get_VideoDeviceController(&videoDeviceController);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IMediaDeviceController> deviceController;
|
||||
hr = videoDeviceController.As(&deviceController);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IVectorView<IMediaEncodingProperties *>> encodingPropertiesList;
|
||||
hr = deviceController->GetAvailableMediaStreamProperties(MediaStreamType_Photo, &encodingPropertiesList);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
d->size = QSize();
|
||||
ComPtr<IVideoEncodingProperties> videoEncodingProperties;
|
||||
quint32 encodingPropertiesListSize;
|
||||
hr = encodingPropertiesList->get_Size(&encodingPropertiesListSize);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
for (quint32 i = 0; i < encodingPropertiesListSize; ++i) {
|
||||
ComPtr<IMediaEncodingProperties> properties;
|
||||
hr = encodingPropertiesList->GetAt(i, &properties);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IVideoEncodingProperties> videoProperties;
|
||||
hr = properties.As(&videoEncodingProperties);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
UINT32 width, height;
|
||||
hr = videoEncodingProperties->get_Width(&width);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = videoEncodingProperties->get_Height(&height);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
// Choose the highest-quality format
|
||||
if (int(width * height) > d->size.width() * d->size.height()) {
|
||||
d->size = QSize(width, height);
|
||||
videoEncodingProperties = videoProperties;
|
||||
}
|
||||
}
|
||||
|
||||
if (!videoEncodingProperties || d->size.isEmpty()) {
|
||||
hr = MF_E_INVALID_FORMAT;
|
||||
RETURN_HR_IF_FAILED("Failed to find a suitable video format");
|
||||
}
|
||||
|
||||
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
|
||||
&d->encodingProfile);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = d->encodingProfile->put_Video(videoEncodingProperties.Get());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (d->videoRenderer)
|
||||
d->videoRenderer->setSize(d->size);
|
||||
|
||||
if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) {
|
||||
d->state = QCamera::LoadedState;
|
||||
emit stateChanged(d->state);
|
||||
}
|
||||
if (SUCCEEDED(hr) && d->status != QCamera::LoadedStatus) {
|
||||
d->status = QCamera::LoadedStatus;
|
||||
emit statusChanged(d->status);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT QWinRTCameraControl::onCaptureFailed(IMediaCapture *, IMediaCaptureFailedEventArgs *args)
|
||||
{
|
||||
HRESULT hr;
|
||||
UINT32 code;
|
||||
hr = args->get_Code(&code);
|
||||
RETURN_HR_IF_FAILED("Failed to get error code");
|
||||
HString message;
|
||||
args->get_Message(message.GetAddressOf());
|
||||
RETURN_HR_IF_FAILED("Failed to get error message");
|
||||
quint32 messageLength;
|
||||
const wchar_t *messageBuffer = message.GetRawBuffer(&messageLength);
|
||||
emit error(QCamera::CameraError, QString::fromWCharArray(messageBuffer, messageLength));
|
||||
setState(QCamera::LoadedState);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT QWinRTCameraControl::onRecordLimitationExceeded(IMediaCapture *)
|
||||
{
|
||||
emit error(QCamera::CameraError, QStringLiteral("Recording limit exceeded."));
|
||||
setState(QCamera::LoadedState);
|
||||
return S_OK;
|
||||
}
|
||||
111
src/plugins/winrt/qwinrtcameracontrol.h
Normal file
111
src/plugins/winrt/qwinrtcameracontrol.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINRTCAMERACONTROL_H
|
||||
#define QWINRTCAMERACONTROL_H
|
||||
|
||||
#include <QtMultimedia/QCameraControl>
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
namespace ABI {
|
||||
namespace Windows {
|
||||
namespace Media {
|
||||
namespace Capture {
|
||||
struct IMediaCapture;
|
||||
struct IMediaCaptureFailedEventArgs;
|
||||
}
|
||||
}
|
||||
namespace Foundation {
|
||||
struct IAsyncAction;
|
||||
enum class AsyncStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVideoRendererControl;
|
||||
class QVideoDeviceSelectorControl;
|
||||
class QCameraImageCaptureControl;
|
||||
|
||||
class QWinRTCameraControlPrivate;
|
||||
class QWinRTCameraControl : public QCameraControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTCameraControl(QObject *parent = 0);
|
||||
~QWinRTCameraControl();
|
||||
|
||||
QCamera::State state() const Q_DECL_OVERRIDE;
|
||||
void setState(QCamera::State state) Q_DECL_OVERRIDE;
|
||||
|
||||
QCamera::Status status() const Q_DECL_OVERRIDE;
|
||||
|
||||
QCamera::CaptureModes captureMode() const Q_DECL_OVERRIDE;
|
||||
void setCaptureMode(QCamera::CaptureModes mode) Q_DECL_OVERRIDE;
|
||||
bool isCaptureModeSupported(QCamera::CaptureModes mode) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const Q_DECL_OVERRIDE;
|
||||
|
||||
QVideoRendererControl *videoRenderer() const;
|
||||
QVideoDeviceSelectorControl *videoDeviceSelector() const;
|
||||
QCameraImageCaptureControl *imageCaptureControl() const;
|
||||
|
||||
ABI::Windows::Media::Capture::IMediaCapture *handle() const;
|
||||
QSize imageSize() const;
|
||||
|
||||
private slots:
|
||||
void onBufferRequested();
|
||||
|
||||
private:
|
||||
HRESULT enumerateDevices();
|
||||
HRESULT initialize();
|
||||
HRESULT onCaptureFailed(ABI::Windows::Media::Capture::IMediaCapture *,
|
||||
ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs *);
|
||||
HRESULT onRecordLimitationExceeded(ABI::Windows::Media::Capture::IMediaCapture *);
|
||||
|
||||
QScopedPointer<QWinRTCameraControlPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTCameraControl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTCAMERACONTROL_H
|
||||
280
src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp
Normal file
280
src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtcameraimagecapturecontrol.h"
|
||||
#include "qwinrtcameracontrol.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QGlobalStatic>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtMultimedia/private/qmediastoragelocation_p.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <windows.media.capture.h>
|
||||
#include <windows.media.devices.h>
|
||||
#include <windows.media.mediaproperties.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <windows.graphics.imaging.h>
|
||||
#include <robuffer.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::Media::Capture;
|
||||
using namespace ABI::Windows::Media::Devices;
|
||||
using namespace ABI::Windows::Media::MediaProperties;
|
||||
using namespace ABI::Windows::Storage::Streams;
|
||||
using namespace ABI::Windows::Graphics::Imaging;
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
#define wchar(str) reinterpret_cast<const wchar_t *>(str.utf16())
|
||||
|
||||
struct QWinRTCameraImageCaptureControlGlobal
|
||||
{
|
||||
QWinRTCameraImageCaptureControlGlobal()
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_ImageEncodingProperties).Get(),
|
||||
&encodingPropertiesFactory);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
|
||||
&bufferFactory);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(),
|
||||
&dataReaderFactory);
|
||||
}
|
||||
|
||||
ComPtr<IImageEncodingPropertiesStatics2> encodingPropertiesFactory;
|
||||
ComPtr<IBufferFactory> bufferFactory;
|
||||
ComPtr<IDataReaderFactory> dataReaderFactory;
|
||||
};
|
||||
Q_GLOBAL_STATIC(QWinRTCameraImageCaptureControlGlobal, g)
|
||||
|
||||
struct CaptureRequest
|
||||
{
|
||||
quint16 id;
|
||||
QString fileName;
|
||||
ComPtr<IImageEncodingProperties> imageFormat;
|
||||
ComPtr<IRandomAccessStream> stream;
|
||||
ComPtr<IAsyncAction> op;
|
||||
};
|
||||
|
||||
class QWinRTCameraImageCaptureControlPrivate
|
||||
{
|
||||
public:
|
||||
QPointer<QWinRTCameraControl> cameraControl;
|
||||
QHash<IAsyncAction *, CaptureRequest> requests;
|
||||
quint16 currentCaptureId;
|
||||
QMediaStorageLocation location;
|
||||
|
||||
void onCameraStateChanged()
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
QWinRTCameraImageCaptureControl::QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent)
|
||||
: QCameraImageCaptureControl(parent), d_ptr(new QWinRTCameraImageCaptureControlPrivate)
|
||||
{
|
||||
Q_D(QWinRTCameraImageCaptureControl);
|
||||
|
||||
d->cameraControl = parent;
|
||||
connect(d->cameraControl, &QCameraControl::stateChanged,
|
||||
this, &QWinRTCameraImageCaptureControl::readyForCaptureChanged);
|
||||
d->currentCaptureId = 0;
|
||||
}
|
||||
|
||||
bool QWinRTCameraImageCaptureControl::isReadyForCapture() const
|
||||
{
|
||||
Q_D(const QWinRTCameraImageCaptureControl);
|
||||
return d->cameraControl->state() == QCamera::ActiveState;
|
||||
}
|
||||
|
||||
QCameraImageCapture::DriveMode QWinRTCameraImageCaptureControl::driveMode() const
|
||||
{
|
||||
return QCameraImageCapture::SingleImageCapture;
|
||||
}
|
||||
|
||||
void QWinRTCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
|
||||
{
|
||||
Q_UNUSED(mode);
|
||||
}
|
||||
|
||||
int QWinRTCameraImageCaptureControl::capture(const QString &fileName)
|
||||
{
|
||||
Q_D(QWinRTCameraImageCaptureControl);
|
||||
|
||||
++d->currentCaptureId;
|
||||
IMediaCapture *capture = d->cameraControl->handle();
|
||||
if (!capture) {
|
||||
emit error(d->currentCaptureId, QCameraImageCapture::NotReadyError, tr("Camera not ready"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
CaptureRequest request = {
|
||||
d->currentCaptureId,
|
||||
d->location.generateFileName(fileName, QMediaStorageLocation::Pictures, QStringLiteral("IMG_"),
|
||||
fileName.isEmpty() ? QStringLiteral("jpg") : QFileInfo(fileName).suffix())
|
||||
};
|
||||
|
||||
HRESULT hr;
|
||||
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(),
|
||||
&request.stream);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = g->encodingPropertiesFactory->CreateBmp(&request.imageFormat);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
const QSize imageSize = d->cameraControl->imageSize();
|
||||
hr = request.imageFormat->put_Width(imageSize.width());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = request.imageFormat->put_Height(imageSize.height());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = capture->CapturePhotoToStreamAsync(request.imageFormat.Get(), request.stream.Get(), &request.op);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
d->requests.insert(request.op.Get(), request);
|
||||
|
||||
hr = request.op->put_Completed(Callback<IAsyncActionCompletedHandler>(
|
||||
this, &QWinRTCameraImageCaptureControl::onCaptureCompleted).Get());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
return request.id;
|
||||
}
|
||||
|
||||
void QWinRTCameraImageCaptureControl::cancelCapture()
|
||||
{
|
||||
Q_D(QWinRTCameraImageCaptureControl);
|
||||
|
||||
QHash<IAsyncAction *, CaptureRequest>::iterator it = d->requests.begin();
|
||||
while (it != d->requests.end()) {
|
||||
ComPtr<IAsyncInfo> info;
|
||||
it->op.As(&info);
|
||||
info->Cancel();
|
||||
it = d->requests.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT QWinRTCameraImageCaptureControl::onCaptureCompleted(IAsyncAction *asyncInfo, AsyncStatus status)
|
||||
{
|
||||
Q_D(QWinRTCameraImageCaptureControl);
|
||||
|
||||
if (status == Canceled || !d->requests.contains(asyncInfo))
|
||||
return S_OK;
|
||||
|
||||
CaptureRequest request = d->requests.take(asyncInfo);
|
||||
|
||||
HRESULT hr;
|
||||
if (status == Error) {
|
||||
hr = asyncInfo->GetResults();
|
||||
emit error(request.id, QCameraImageCapture::ResourceError, qt_error_string(hr));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
quint64 dataLength;
|
||||
hr = request.stream->get_Size(&dataLength);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (dataLength == 0 || dataLength > INT_MAX) {
|
||||
emit error(request.id, QCameraImageCapture::FormatError, tr("Invalid photo data length."));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ComPtr<IBitmapDecoderStatics> bitmapFactory;
|
||||
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapDecoder).Get(),
|
||||
&bitmapFactory);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IAsyncOperation<BitmapDecoder *>> op;
|
||||
hr = bitmapFactory->CreateAsync(request.stream.Get(), &op);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IBitmapDecoder> decoder;
|
||||
hr = QWinRTFunctions::await(op, decoder.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IAsyncOperation<BitmapFrame *>> op2;
|
||||
hr = decoder->GetFrameAsync(0, &op2);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IBitmapFrame> frame;
|
||||
hr = QWinRTFunctions::await(op2, frame.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IBitmapTransform> transform;
|
||||
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapTransform).Get(),
|
||||
&transform);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IAsyncOperation<PixelDataProvider *>> op3;
|
||||
hr = frame->GetPixelDataTransformedAsync(BitmapPixelFormat_Rgba8, BitmapAlphaMode_Straight,
|
||||
transform.Get(), ExifOrientationMode_IgnoreExifOrientation,
|
||||
ColorManagementMode_DoNotColorManage, &op3);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IPixelDataProvider> pixelDataProvider;
|
||||
hr = QWinRTFunctions::await(op3, pixelDataProvider.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
UINT32 pixelDataSize;
|
||||
BYTE *pixelData;
|
||||
hr = pixelDataProvider->DetachPixelData(&pixelDataSize, &pixelData);
|
||||
|
||||
UINT32 pixelHeight;
|
||||
hr = frame->get_PixelHeight(&pixelHeight);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
UINT32 pixelWidth;
|
||||
hr = frame->get_PixelWidth(&pixelWidth);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
const QImage image(pixelData, pixelWidth, pixelHeight, QImage::Format_RGBA8888,
|
||||
reinterpret_cast<QImageCleanupFunction>(&CoTaskMemFree), pixelData);
|
||||
emit imageCaptured(request.id, image);
|
||||
if (image.save(request.fileName))
|
||||
emit imageSaved(request.id, request.fileName);
|
||||
else
|
||||
emit error(request.id, QCameraImageCapture::ResourceError, tr("Image saving failed"));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
@@ -39,80 +39,48 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <QTKit/QTKit.h>
|
||||
#ifndef QWINRTCAMERAIMAGECAPTURECONTROL_H
|
||||
#define QWINRTCAMERAIMAGECAPTURECONTROL_H
|
||||
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtMultimedia/QCameraImageCaptureControl>
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
#include "qt7backend.h"
|
||||
#include "qt7serviceplugin.h"
|
||||
#include "qt7playerservice.h"
|
||||
|
||||
#include <qmediaserviceproviderplugin.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
QT7ServicePlugin::QT7ServicePlugin()
|
||||
{
|
||||
buildSupportedTypes();
|
||||
}
|
||||
|
||||
QMediaService* QT7ServicePlugin::create(QString const& key)
|
||||
{
|
||||
#ifdef QT_DEBUG_QT7
|
||||
qDebug() << "QT7ServicePlugin::create" << key;
|
||||
#endif
|
||||
#ifdef QMEDIA_QT7_PLAYER
|
||||
if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
|
||||
return new QT7PlayerService;
|
||||
#endif
|
||||
qWarning() << "unsupported key:" << key;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QT7ServicePlugin::release(QMediaService *service)
|
||||
{
|
||||
delete service;
|
||||
}
|
||||
|
||||
QMediaServiceProviderHint::Features QT7ServicePlugin::supportedFeatures(
|
||||
const QByteArray &service) const
|
||||
{
|
||||
if (service == Q_MEDIASERVICE_MEDIAPLAYER)
|
||||
return QMediaServiceProviderHint::VideoSurface;
|
||||
else
|
||||
return QMediaServiceProviderHint::Features();
|
||||
}
|
||||
|
||||
QMultimedia::SupportEstimate QT7ServicePlugin::hasSupport(const QString &mimeType, const QStringList& codecs) const
|
||||
{
|
||||
Q_UNUSED(codecs);
|
||||
|
||||
if (m_supportedMimeTypes.contains(mimeType))
|
||||
return QMultimedia::ProbablySupported;
|
||||
|
||||
return QMultimedia::MaybeSupported;
|
||||
}
|
||||
|
||||
QStringList QT7ServicePlugin::supportedMimeTypes() const
|
||||
{
|
||||
return m_supportedMimeTypes;
|
||||
}
|
||||
|
||||
void QT7ServicePlugin::buildSupportedTypes()
|
||||
{
|
||||
AutoReleasePool pool;
|
||||
NSArray *utis = [QTMovie movieTypesWithOptions:QTIncludeCommonTypes];
|
||||
for (NSString *uti in utis) {
|
||||
NSString* mimeType = (NSString*)UTTypeCopyPreferredTagWithClass((CFStringRef)uti, kUTTagClassMIMEType);
|
||||
if (mimeType != 0) {
|
||||
m_supportedMimeTypes.append(QString::fromUtf8([mimeType UTF8String]));
|
||||
[mimeType release];
|
||||
namespace ABI {
|
||||
namespace Windows {
|
||||
namespace Foundation {
|
||||
struct IAsyncAction;
|
||||
enum class AsyncStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWinRTCameraControl;
|
||||
|
||||
class QWinRTCameraImageCaptureControlPrivate;
|
||||
class QWinRTCameraImageCaptureControl : public QCameraImageCaptureControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent);
|
||||
|
||||
bool isReadyForCapture() const Q_DECL_OVERRIDE;
|
||||
|
||||
QCameraImageCapture::DriveMode driveMode() const Q_DECL_OVERRIDE;
|
||||
void setDriveMode(QCameraImageCapture::DriveMode mode) Q_DECL_OVERRIDE;
|
||||
|
||||
int capture(const QString &fileName) Q_DECL_OVERRIDE;
|
||||
void cancelCapture() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
HRESULT onCaptureCompleted(ABI::Windows::Foundation::IAsyncAction *,
|
||||
ABI::Windows::Foundation::AsyncStatus);
|
||||
|
||||
QScopedPointer<QWinRTCameraImageCaptureControlPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTCameraImageCaptureControl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTCAMERAIMAGECAPTURECONTROL_H
|
||||
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
@@ -39,53 +39,22 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qt7playercontrol.h"
|
||||
#include "qt7playersession.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include "qwinrtcamerainfocontrol.h"
|
||||
#include "qwinrtvideodeviceselectorcontrol.h"
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
/*
|
||||
QT7VideoOutputControl::QT7VideoOutputControl(QObject *parent)
|
||||
:QVideoOutputControl(parent),
|
||||
m_session(0),
|
||||
m_output(QVideoOutputControl::NoOutput)
|
||||
QWinRTCameraInfoControl::QWinRTCameraInfoControl(QObject *parent)
|
||||
: QCameraInfoControl(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QT7VideoOutputControl::~QT7VideoOutputControl()
|
||||
QCamera::Position QWinRTCameraInfoControl::cameraPosition(const QString &deviceName) const
|
||||
{
|
||||
return QWinRTVideoDeviceSelectorControl::cameraPosition(deviceName);
|
||||
}
|
||||
|
||||
void QT7VideoOutputControl::setSession(QT7PlayerSession *session)
|
||||
int QWinRTCameraInfoControl::cameraOrientation(const QString &deviceName) const
|
||||
{
|
||||
m_session = session;
|
||||
return QWinRTVideoDeviceSelectorControl::cameraOrientation(deviceName);
|
||||
}
|
||||
|
||||
QList<QVideoOutputControl::Output> QT7VideoOutputControl::availableOutputs() const
|
||||
{
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
void QT7VideoOutputControl::enableOutput(QVideoOutputControl::Output output)
|
||||
{
|
||||
if (!m_outputs.contains(output))
|
||||
m_outputs.append(output);
|
||||
}
|
||||
|
||||
QVideoOutputControl::Output QT7VideoOutputControl::output() const
|
||||
{
|
||||
return m_output;
|
||||
}
|
||||
|
||||
void QT7VideoOutputControl::setOutput(Output output)
|
||||
{
|
||||
if (m_output != output) {
|
||||
m_output = output;
|
||||
Q_EMIT videoOutputChanged(m_output);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_qt7videooutputcontrol.cpp"
|
||||
|
||||
*/
|
||||
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
@@ -39,22 +39,23 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qt7backend.h"
|
||||
|
||||
#import <Foundation/NSAutoreleasePool.h>
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#ifndef QWINRTCAMERAINFOCONTROL_H
|
||||
#define QWINRTCAMERAINFOCONTROL_H
|
||||
|
||||
#include <QtMultimedia/QCameraInfoControl>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
AutoReleasePool::AutoReleasePool()
|
||||
class QWinRTCameraInfoControl : public QCameraInfoControl
|
||||
{
|
||||
pool = (void*)[[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTCameraInfoControl(QObject *parent = 0);
|
||||
|
||||
AutoReleasePool::~AutoReleasePool()
|
||||
{
|
||||
[(NSAutoreleasePool*)pool release];
|
||||
}
|
||||
QCamera::Position cameraPosition(const QString &deviceName) const Q_DECL_OVERRIDE;
|
||||
int cameraOrientation(const QString &deviceName) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTCAMERAINFOCONTROL_H
|
||||
104
src/plugins/winrt/qwinrtcameraservice.cpp
Normal file
104
src/plugins/winrt/qwinrtcameraservice.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtcameraservice.h"
|
||||
#include "qwinrtcameracontrol.h"
|
||||
#include "qwinrtcamerainfocontrol.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtMultimedia/QCameraImageCaptureControl>
|
||||
#include <QtMultimedia/QVideoRendererControl>
|
||||
#include <QtMultimedia/QVideoDeviceSelectorControl>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class QWinRTCameraServicePrivate
|
||||
{
|
||||
public:
|
||||
QPointer<QWinRTCameraControl> cameraControl;
|
||||
QPointer<QWinRTCameraInfoControl> cameraInfoControl;
|
||||
};
|
||||
|
||||
QWinRTCameraService::QWinRTCameraService(QObject *parent)
|
||||
: QMediaService(parent), d_ptr(new QWinRTCameraServicePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
QMediaControl *QWinRTCameraService::requestControl(const char *name)
|
||||
{
|
||||
Q_D(QWinRTCameraService);
|
||||
|
||||
if (qstrcmp(name, QCameraControl_iid) == 0) {
|
||||
if (!d->cameraControl)
|
||||
d->cameraControl = new QWinRTCameraControl(this);
|
||||
return d->cameraControl;
|
||||
}
|
||||
|
||||
if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
|
||||
if (d->cameraControl)
|
||||
return d->cameraControl->videoRenderer();
|
||||
}
|
||||
|
||||
if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0) {
|
||||
if (d->cameraControl)
|
||||
return d->cameraControl->videoDeviceSelector();
|
||||
}
|
||||
|
||||
if (qstrcmp(name, QCameraInfoControl_iid) == 0) {
|
||||
if (!d->cameraInfoControl)
|
||||
d->cameraInfoControl = new QWinRTCameraInfoControl(this);
|
||||
return d->cameraInfoControl;
|
||||
}
|
||||
|
||||
if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) {
|
||||
if (d->cameraControl)
|
||||
return d->cameraControl->imageCaptureControl();
|
||||
}
|
||||
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
void QWinRTCameraService::releaseControl(QMediaControl *control)
|
||||
{
|
||||
Q_UNUSED(control);
|
||||
}
|
||||
66
src/plugins/winrt/qwinrtcameraservice.h
Normal file
66
src/plugins/winrt/qwinrtcameraservice.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINRTCAMERASERVICE_H
|
||||
#define QWINRTCAMERASERVICE_H
|
||||
|
||||
#include <QtMultimedia/QMediaService>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWinRTCameraServicePrivate;
|
||||
class QWinRTCameraService : public QMediaService
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTCameraService(QObject *parent = 0);
|
||||
|
||||
QMediaControl *requestControl(const char *name) Q_DECL_OVERRIDE;
|
||||
void releaseControl(QMediaControl *control) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QScopedPointer<QWinRTCameraServicePrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTCameraService)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTCAMERASERVICE_H
|
||||
204
src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
Normal file
204
src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtcameravideorenderercontrol.h"
|
||||
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtCore/QSize>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <mfapi.h>
|
||||
#include <wrl.h>
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class D3DVideoBlitter
|
||||
{
|
||||
public:
|
||||
D3DVideoBlitter(ID3D11Device *device, ID3D11Texture2D *target)
|
||||
: m_d3dDevice(device), m_target(target)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IDXGIResource> targetResource;
|
||||
hr = target->QueryInterface(IID_PPV_ARGS(&targetResource));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
HANDLE sharedHandle;
|
||||
hr = targetResource->GetSharedHandle(&sharedHandle);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = m_d3dDevice->OpenSharedResource(sharedHandle, IID_PPV_ARGS(&m_targetTexture));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = m_d3dDevice.As(&m_videoDevice);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
ID3D11Device *device() const
|
||||
{
|
||||
return m_d3dDevice.Get();
|
||||
}
|
||||
|
||||
ID3D11Texture2D *target() const
|
||||
{
|
||||
return m_target;
|
||||
}
|
||||
|
||||
void blit(ID3D11Texture2D *texture)
|
||||
{
|
||||
HRESULT hr;
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
texture->GetDesc(&desc);
|
||||
if (!m_videoEnumerator) {
|
||||
D3D11_VIDEO_PROCESSOR_CONTENT_DESC videoProcessorDesc = {
|
||||
D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE,
|
||||
{ 0 }, desc.Width, desc.Height,
|
||||
{ 0 }, desc.Width, desc.Height,
|
||||
D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
|
||||
};
|
||||
hr = m_videoDevice->CreateVideoProcessorEnumerator(&videoProcessorDesc, &m_videoEnumerator);
|
||||
RETURN_VOID_IF_FAILED("Failed to create video enumerator");
|
||||
}
|
||||
|
||||
if (!m_videoProcessor) {
|
||||
hr = m_videoDevice->CreateVideoProcessor(m_videoEnumerator.Get(), 0, &m_videoProcessor);
|
||||
RETURN_VOID_IF_FAILED("Failed to create video processor");
|
||||
}
|
||||
|
||||
if (!m_outputView) {
|
||||
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = { D3D11_VPOV_DIMENSION_TEXTURE2D };
|
||||
hr = m_videoDevice->CreateVideoProcessorOutputView(
|
||||
m_targetTexture.Get(), m_videoEnumerator.Get(), &outputDesc, &m_outputView);
|
||||
RETURN_VOID_IF_FAILED("Failed to create video output view");
|
||||
}
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputViewDesc = {
|
||||
0, D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, 0 }
|
||||
};
|
||||
ComPtr<ID3D11VideoProcessorInputView> inputView;
|
||||
hr = m_videoDevice->CreateVideoProcessorInputView(
|
||||
texture, m_videoEnumerator.Get(), &inputViewDesc, &inputView);
|
||||
RETURN_VOID_IF_FAILED("Failed to create video input view");
|
||||
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
ComPtr<ID3D11VideoContext> videoContext;
|
||||
m_d3dDevice->GetImmediateContext(&context);
|
||||
hr = context.As(&videoContext);
|
||||
RETURN_VOID_IF_FAILED("Failed to get video context");
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_STREAM stream = { TRUE };
|
||||
stream.pInputSurface = inputView.Get();
|
||||
hr = videoContext->VideoProcessorBlt(
|
||||
m_videoProcessor.Get(), m_outputView.Get(), 0, 1, &stream);
|
||||
RETURN_VOID_IF_FAILED("Failed to get blit video frame");
|
||||
}
|
||||
|
||||
private:
|
||||
ComPtr<ID3D11Device> m_d3dDevice;
|
||||
ComPtr<ID3D11Texture2D> m_targetTexture;
|
||||
ID3D11Texture2D *m_target;
|
||||
ComPtr<ID3D11VideoDevice> m_videoDevice;
|
||||
ComPtr<ID3D11VideoProcessorEnumerator> m_videoEnumerator;
|
||||
ComPtr<ID3D11VideoProcessor> m_videoProcessor;
|
||||
ComPtr<ID3D11VideoProcessorOutputView> m_outputView;
|
||||
};
|
||||
|
||||
class QWinRTCameraVideoRendererControlPrivate
|
||||
{
|
||||
public:
|
||||
QScopedPointer<D3DVideoBlitter> blitter;
|
||||
QVector<ComPtr<IMF2DBuffer>> buffers;
|
||||
};
|
||||
|
||||
QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent)
|
||||
: QWinRTAbstractVideoRendererControl(size, parent), d_ptr(new QWinRTCameraVideoRendererControlPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
QWinRTCameraVideoRendererControl::~QWinRTCameraVideoRendererControl()
|
||||
{
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool QWinRTCameraVideoRendererControl::render(ID3D11Texture2D *target)
|
||||
{
|
||||
Q_D(QWinRTCameraVideoRendererControl);
|
||||
|
||||
if (d->buffers.isEmpty()) {
|
||||
emit bufferRequested();
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IMF2DBuffer> buffer = d->buffers.takeFirst();
|
||||
|
||||
ComPtr<ID3D11Texture2D> sourceTexture;
|
||||
ComPtr<IMFDXGIBuffer> dxgiBuffer;
|
||||
hr = buffer.As(&dxgiBuffer);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&sourceTexture));
|
||||
if (FAILED(hr)) {
|
||||
qErrnoWarning(hr, "The video frame does not support texture output; aborting rendering.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<ID3D11Device> device;
|
||||
sourceTexture->GetDevice(&device);
|
||||
if (!d->blitter || d->blitter->device() != device.Get() || d->blitter->target() != target)
|
||||
d->blitter.reset(new D3DVideoBlitter(device.Get(), target));
|
||||
|
||||
d->blitter->blit(sourceTexture.Get());
|
||||
|
||||
emit bufferRequested();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QWinRTCameraVideoRendererControl::queueBuffer(IMF2DBuffer *buffer)
|
||||
{
|
||||
Q_D(QWinRTCameraVideoRendererControl);
|
||||
Q_ASSERT(buffer);
|
||||
d->buffers.append(buffer);
|
||||
}
|
||||
|
||||
void QWinRTCameraVideoRendererControl::discardBuffers()
|
||||
{
|
||||
Q_D(QWinRTCameraVideoRendererControl);
|
||||
d->buffers.clear();
|
||||
}
|
||||
74
src/plugins/winrt/qwinrtcameravideorenderercontrol.h
Normal file
74
src/plugins/winrt/qwinrtcameravideorenderercontrol.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINRTCAMERAVIDEORENDERERCONTROL_H
|
||||
#define QWINRTCAMERAVIDEORENDERERCONTROL_H
|
||||
|
||||
#include "qwinrtabstractvideorenderercontrol.h"
|
||||
|
||||
struct IMF2DBuffer;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVideoSurfaceFormat;
|
||||
class QWinRTCameraVideoRendererControlPrivate;
|
||||
class QWinRTCameraVideoRendererControl : public QWinRTAbstractVideoRendererControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent);
|
||||
~QWinRTCameraVideoRendererControl();
|
||||
|
||||
bool render(ID3D11Texture2D *texture) Q_DECL_OVERRIDE;
|
||||
void queueBuffer(IMF2DBuffer *buffer);
|
||||
void discardBuffers();
|
||||
|
||||
signals:
|
||||
void bufferRequested();
|
||||
|
||||
private:
|
||||
QScopedPointer<QWinRTCameraVideoRendererControlPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTCameraVideoRendererControl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTCAMERAVIDEORENDERERCONTROL_H
|
||||
@@ -44,6 +44,8 @@
|
||||
|
||||
#include "qwinrtserviceplugin.h"
|
||||
#include "qwinrtmediaplayerservice.h"
|
||||
#include "qwinrtcameraservice.h"
|
||||
#include "qwinrtvideodeviceselectorcontrol.h"
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
@@ -52,6 +54,9 @@ QMediaService *QWinRTServicePlugin::create(QString const &key)
|
||||
if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
|
||||
return new QWinRTMediaPlayerService(this);
|
||||
|
||||
if (key == QLatin1String(Q_MEDIASERVICE_CAMERA))
|
||||
return new QWinRTCameraService(this);
|
||||
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@@ -68,3 +73,37 @@ QMediaServiceProviderHint::Features QWinRTServicePlugin::supportedFeatures(
|
||||
|
||||
return QMediaServiceProviderHint::Features();
|
||||
}
|
||||
|
||||
QCamera::Position QWinRTServicePlugin::cameraPosition(const QByteArray &device) const
|
||||
{
|
||||
return QWinRTVideoDeviceSelectorControl::cameraPosition(device);
|
||||
}
|
||||
|
||||
int QWinRTServicePlugin::cameraOrientation(const QByteArray &device) const
|
||||
{
|
||||
return QWinRTVideoDeviceSelectorControl::cameraOrientation(device);
|
||||
}
|
||||
|
||||
QList<QByteArray> QWinRTServicePlugin::devices(const QByteArray &service) const
|
||||
{
|
||||
if (service == Q_MEDIASERVICE_CAMERA)
|
||||
return QWinRTVideoDeviceSelectorControl::deviceNames();
|
||||
|
||||
return QList<QByteArray>();
|
||||
}
|
||||
|
||||
QString QWinRTServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
|
||||
{
|
||||
if (service == Q_MEDIASERVICE_CAMERA)
|
||||
return QWinRTVideoDeviceSelectorControl::deviceDescription(device);
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QByteArray QWinRTServicePlugin::defaultDevice(const QByteArray &service) const
|
||||
{
|
||||
if (service == Q_MEDIASERVICE_CAMERA)
|
||||
return QWinRTVideoDeviceSelectorControl::defaultDeviceName();
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
@@ -48,15 +48,29 @@ QT_USE_NAMESPACE
|
||||
|
||||
class QWinRTServicePlugin : public QMediaServiceProviderPlugin
|
||||
, public QMediaServiceFeaturesInterface
|
||||
, public QMediaServiceCameraInfoInterface
|
||||
, public QMediaServiceSupportedDevicesInterface
|
||||
, public QMediaServiceDefaultDeviceInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QMediaServiceFeaturesInterface)
|
||||
Q_INTERFACES(QMediaServiceCameraInfoInterface)
|
||||
Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
|
||||
Q_INTERFACES(QMediaServiceDefaultDeviceInterface)
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "winrt.json")
|
||||
public:
|
||||
QMediaService *create(QString const &key);
|
||||
void release(QMediaService *service);
|
||||
|
||||
QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const;
|
||||
|
||||
QCamera::Position cameraPosition(const QByteArray &device) const Q_DECL_OVERRIDE;
|
||||
int cameraOrientation(const QByteArray &device) const Q_DECL_OVERRIDE;
|
||||
|
||||
QList<QByteArray> devices(const QByteArray &service) const Q_DECL_OVERRIDE;
|
||||
QString deviceDescription(const QByteArray &service, const QByteArray &device) Q_DECL_OVERRIDE;
|
||||
|
||||
QByteArray defaultDevice(const QByteArray &service) const Q_DECL_OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // QWINRTSERVICEPLUGIN_H
|
||||
|
||||
383
src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp
Normal file
383
src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp
Normal file
@@ -0,0 +1,383 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtvideodeviceselectorcontrol.h"
|
||||
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QGlobalStatic>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
|
||||
using namespace ABI::Windows::Devices::Enumeration;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::Foundation::Collections;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
typedef ITypedEventHandler<DeviceWatcher *, DeviceInformation *> DeviceInformationHandler;
|
||||
typedef ITypedEventHandler<DeviceWatcher *, DeviceInformationUpdate *> DeviceInformationUpdateHandler;
|
||||
typedef ITypedEventHandler<DeviceWatcher *, IInspectable *> DeviceEnumerationCompletedHandler;
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
static QString deviceName(IDeviceInformation *device)
|
||||
{
|
||||
HRESULT hr;
|
||||
HString id;
|
||||
hr = device->get_Id(id.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
quint32 length;
|
||||
const wchar_t *buffer = id.GetRawBuffer(&length);
|
||||
return QString::fromWCharArray(buffer, length);
|
||||
}
|
||||
|
||||
static QString deviceDescription(IDeviceInformation *device)
|
||||
{
|
||||
HRESULT hr;
|
||||
HString name;
|
||||
hr = device->get_Name(name.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
quint32 length;
|
||||
const wchar_t *buffer = name.GetRawBuffer(&length);
|
||||
return QString::fromWCharArray(buffer, length);
|
||||
}
|
||||
|
||||
struct QWinRTVideoDeviceSelectorControlGlobal
|
||||
{
|
||||
QWinRTVideoDeviceSelectorControlGlobal()
|
||||
: defaultDeviceIndex(-1)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IDeviceInformationStatics> deviceWatcherFactory;
|
||||
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(),
|
||||
IID_PPV_ARGS(&deviceWatcherFactory));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = deviceWatcherFactory->CreateWatcherDeviceClass(DeviceClass_VideoCapture, &deviceWatcher);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = deviceWatcher->add_Added(
|
||||
Callback<DeviceInformationHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceAdded).Get(),
|
||||
&deviceAddedToken);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = deviceWatcher->add_Removed(
|
||||
Callback<DeviceInformationUpdateHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceRemoved).Get(),
|
||||
&deviceRemovedToken);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = deviceWatcher->add_Updated(
|
||||
Callback<DeviceInformationUpdateHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceUpdated).Get(),
|
||||
&deviceUpdatedToken);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
// Synchronously populate the devices on construction
|
||||
ComPtr<IAsyncOperation<DeviceInformationCollection *>> op;
|
||||
hr = deviceWatcherFactory->FindAllAsyncDeviceClass(DeviceClass_VideoCapture, &op);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IVectorView<DeviceInformation *>> deviceList;
|
||||
hr = QWinRTFunctions::await(op, deviceList.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
quint32 deviceCount;
|
||||
hr = deviceList->get_Size(&deviceCount);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
for (quint32 i = 0; i < deviceCount; ++i) {
|
||||
IDeviceInformation *device;
|
||||
hr = deviceList->GetAt(i, &device);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
onDeviceAdded(Q_NULLPTR, device);
|
||||
}
|
||||
|
||||
// If there is no default device provided by the API, choose the first one
|
||||
if (!devices.isEmpty() && defaultDeviceIndex < 0)
|
||||
defaultDeviceIndex = 0;
|
||||
}
|
||||
|
||||
~QWinRTVideoDeviceSelectorControlGlobal()
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = deviceWatcher->remove_Added(deviceAddedToken);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = deviceWatcher->remove_Removed(deviceRemovedToken);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = deviceWatcher->remove_Updated(deviceUpdatedToken);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
private:
|
||||
HRESULT onDeviceAdded(IDeviceWatcher *, IDeviceInformation *device)
|
||||
{
|
||||
const QString name = deviceName(device);
|
||||
if (deviceIndex.contains(name))
|
||||
return S_OK;
|
||||
|
||||
devices.append(device);
|
||||
const int index = devices.size() - 1;
|
||||
deviceIndex.insert(name, index);
|
||||
|
||||
HRESULT hr;
|
||||
boolean isDefault;
|
||||
hr = device->get_IsDefault(&isDefault);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (isDefault)
|
||||
defaultDeviceIndex = index;
|
||||
|
||||
foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers)
|
||||
emit watcher->devicesChanged();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT onDeviceRemoved(IDeviceWatcher *, IDeviceInformationUpdate *device)
|
||||
{
|
||||
HRESULT hr;
|
||||
HString id;
|
||||
hr = device->get_Id(id.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
HString name;
|
||||
hr = device->get_Id(name.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
quint32 nameLength;
|
||||
const wchar_t *nameString = name.GetRawBuffer(&nameLength);
|
||||
const int index = deviceIndex.take(QString::fromWCharArray(nameString, nameLength));
|
||||
if (index >= 0)
|
||||
devices.remove(index);
|
||||
|
||||
foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers)
|
||||
emit watcher->devicesChanged();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT onDeviceUpdated(IDeviceWatcher *, IDeviceInformationUpdate *)
|
||||
{
|
||||
// A name or description may have changed, so emit devicesChanged
|
||||
foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers)
|
||||
emit watcher->devicesChanged();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
public:
|
||||
void addWatcher(QWinRTVideoDeviceSelectorControl *control)
|
||||
{
|
||||
watchers.append(control);
|
||||
|
||||
HRESULT hr;
|
||||
DeviceWatcherStatus status;
|
||||
hr = deviceWatcher->get_Status(&status);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (status != DeviceWatcherStatus_Started) {
|
||||
// We can't immediately Start() if we have just called Stop()
|
||||
while (status == DeviceWatcherStatus_Stopping) {
|
||||
QThread::yieldCurrentThread();
|
||||
hr = deviceWatcher->get_Status(&status);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
}
|
||||
hr = deviceWatcher->Start();
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
}
|
||||
}
|
||||
|
||||
void removeWatcher(QWinRTVideoDeviceSelectorControl *control)
|
||||
{
|
||||
watchers.removeAll(control);
|
||||
|
||||
if (!watchers.isEmpty())
|
||||
return;
|
||||
|
||||
HRESULT hr;
|
||||
DeviceWatcherStatus status;
|
||||
hr = deviceWatcher->get_Status(&status);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (status == DeviceWatcherStatus_Stopped || status == DeviceWatcherStatus_Stopping)
|
||||
return;
|
||||
|
||||
hr = deviceWatcher->Stop();
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
QVector<ComPtr<IDeviceInformation>> devices;
|
||||
QHash<QString, int> deviceIndex;
|
||||
int defaultDeviceIndex;
|
||||
|
||||
private:
|
||||
ComPtr<IDeviceWatcher> deviceWatcher;
|
||||
QList<QWinRTVideoDeviceSelectorControl *> watchers;
|
||||
EventRegistrationToken deviceAddedToken;
|
||||
EventRegistrationToken deviceRemovedToken;
|
||||
EventRegistrationToken deviceUpdatedToken;
|
||||
};
|
||||
Q_GLOBAL_STATIC(QWinRTVideoDeviceSelectorControlGlobal, g)
|
||||
|
||||
class QWinRTVideoDeviceSelectorControlPrivate
|
||||
{
|
||||
public:
|
||||
int selectedDevice;
|
||||
};
|
||||
|
||||
QWinRTVideoDeviceSelectorControl::QWinRTVideoDeviceSelectorControl(QObject *parent)
|
||||
: QVideoDeviceSelectorControl(parent), d_ptr(new QWinRTVideoDeviceSelectorControlPrivate)
|
||||
{
|
||||
Q_D(QWinRTVideoDeviceSelectorControl);
|
||||
d->selectedDevice = -1;
|
||||
g->addWatcher(this);
|
||||
}
|
||||
|
||||
QWinRTVideoDeviceSelectorControl::~QWinRTVideoDeviceSelectorControl()
|
||||
{
|
||||
if (g.isDestroyed())
|
||||
return;
|
||||
|
||||
g->removeWatcher(this);
|
||||
}
|
||||
|
||||
int QWinRTVideoDeviceSelectorControl::deviceCount() const
|
||||
{
|
||||
return g->devices.size();
|
||||
}
|
||||
|
||||
QString QWinRTVideoDeviceSelectorControl::deviceName(int index) const
|
||||
{
|
||||
if (index < 0 || index >= g->devices.size())
|
||||
return QString();
|
||||
|
||||
return ::deviceName(g->devices.at(index).Get());
|
||||
}
|
||||
|
||||
QString QWinRTVideoDeviceSelectorControl::deviceDescription(int index) const
|
||||
{
|
||||
if (index < 0 || index >= g->devices.size())
|
||||
return QString();
|
||||
|
||||
return ::deviceDescription(g->devices.at(index).Get());
|
||||
}
|
||||
|
||||
int QWinRTVideoDeviceSelectorControl::defaultDevice() const
|
||||
{
|
||||
return g->defaultDeviceIndex;
|
||||
}
|
||||
|
||||
int QWinRTVideoDeviceSelectorControl::selectedDevice() const
|
||||
{
|
||||
Q_D(const QWinRTVideoDeviceSelectorControl);
|
||||
return d->selectedDevice;
|
||||
}
|
||||
|
||||
QCamera::Position QWinRTVideoDeviceSelectorControl::cameraPosition(const QString &deviceName)
|
||||
{
|
||||
int deviceIndex = g->deviceIndex.value(deviceName);
|
||||
IDeviceInformation *deviceInfo = g->devices.value(deviceIndex).Get();
|
||||
if (!deviceInfo)
|
||||
return QCamera::UnspecifiedPosition;
|
||||
|
||||
ComPtr<IEnclosureLocation> enclosureLocation;
|
||||
HRESULT hr;
|
||||
hr = deviceInfo->get_EnclosureLocation(&enclosureLocation);
|
||||
RETURN_IF_FAILED("Failed to get camera enclosure location", return QCamera::UnspecifiedPosition);
|
||||
if (!enclosureLocation)
|
||||
return QCamera::UnspecifiedPosition;
|
||||
|
||||
Panel panel;
|
||||
hr = enclosureLocation->get_Panel(&panel);
|
||||
RETURN_IF_FAILED("Failed to get camera panel location", return QCamera::UnspecifiedPosition);
|
||||
|
||||
switch (panel) {
|
||||
case Panel_Front:
|
||||
return QCamera::FrontFace;
|
||||
case Panel_Back:
|
||||
return QCamera::BackFace;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QCamera::UnspecifiedPosition;
|
||||
}
|
||||
|
||||
int QWinRTVideoDeviceSelectorControl::cameraOrientation(const QString &deviceName)
|
||||
{
|
||||
Q_UNUSED(deviceName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
QList<QByteArray> QWinRTVideoDeviceSelectorControl::deviceNames()
|
||||
{
|
||||
QList<QByteArray> devices;
|
||||
foreach (const QString &device, g->deviceIndex.keys())
|
||||
devices.append(device.toUtf8());
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
QByteArray QWinRTVideoDeviceSelectorControl::deviceDescription(const QByteArray &deviceName)
|
||||
{
|
||||
int deviceIndex = g->deviceIndex.value(QString::fromUtf8(deviceName), -1);
|
||||
if (deviceIndex < 0)
|
||||
return QByteArray();
|
||||
|
||||
return ::deviceDescription(g->devices.value(deviceIndex).Get()).toUtf8();
|
||||
}
|
||||
|
||||
QByteArray QWinRTVideoDeviceSelectorControl::defaultDeviceName()
|
||||
{
|
||||
if (g->defaultDeviceIndex < 0)
|
||||
return QByteArray();
|
||||
|
||||
return ::deviceName(g->devices.value(g->defaultDeviceIndex).Get()).toUtf8();
|
||||
}
|
||||
|
||||
void QWinRTVideoDeviceSelectorControl::setSelectedDevice(int index)
|
||||
{
|
||||
Q_D(QWinRTVideoDeviceSelectorControl);
|
||||
|
||||
int selectedDevice = index;
|
||||
if (index < 0 || index >= g->devices.size())
|
||||
selectedDevice = -1;
|
||||
|
||||
if (d->selectedDevice != selectedDevice) {
|
||||
d->selectedDevice = selectedDevice;
|
||||
emit selectedDeviceChanged(d->selectedDevice);
|
||||
emit selectedDeviceChanged(deviceName(d->selectedDevice));
|
||||
}
|
||||
}
|
||||
94
src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h
Normal file
94
src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINRTVIDEODEVICESELECTORCONTROL_H
|
||||
#define QWINRTVIDEODEVICESELECTORCONTROL_H
|
||||
|
||||
#include <QtMultimedia/QVideoDeviceSelectorControl>
|
||||
#include <QtMultimedia/QCameraInfoControl>
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
struct IInspectable;
|
||||
namespace ABI {
|
||||
namespace Windows {
|
||||
namespace Devices {
|
||||
namespace Enumeration {
|
||||
struct IDeviceInformation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWinRTVideoDeviceSelectorControlPrivate;
|
||||
class QWinRTVideoDeviceSelectorControl : public QVideoDeviceSelectorControl
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTVideoDeviceSelectorControl(QObject *parent = 0);
|
||||
~QWinRTVideoDeviceSelectorControl();
|
||||
|
||||
int deviceCount() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString deviceName(int index) const Q_DECL_OVERRIDE;
|
||||
QString deviceDescription(int index) const Q_DECL_OVERRIDE;
|
||||
|
||||
int defaultDevice() const Q_DECL_OVERRIDE;
|
||||
int selectedDevice() const Q_DECL_OVERRIDE;
|
||||
|
||||
static QCamera::Position cameraPosition(const QString &deviceName);
|
||||
static int cameraOrientation(const QString &deviceName);
|
||||
static QList<QByteArray> deviceNames();
|
||||
static QByteArray deviceDescription(const QByteArray &deviceName);
|
||||
static QByteArray defaultDeviceName();
|
||||
|
||||
public slots:
|
||||
void setSelectedDevice(int index) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QScopedPointer<QWinRTVideoDeviceSelectorControlPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTVideoDeviceSelectorControl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTVIDEODEVICESELECTORCONTROL_H
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"Keys": ["winrt"],
|
||||
"Services": ["org.qt-project.qt.mediaplayer"]
|
||||
"Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.camera"]
|
||||
}
|
||||
|
||||
@@ -5,21 +5,33 @@ PLUGIN_TYPE=mediaservice
|
||||
PLUGIN_CLASS_NAME = WinRTServicePlugin
|
||||
load(qt_plugin)
|
||||
|
||||
LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11
|
||||
LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11 -lruntimeobject
|
||||
|
||||
HEADERS += \
|
||||
qwinrtabstractvideorenderercontrol.h \
|
||||
qwinrtcameracontrol.h \
|
||||
qwinrtcamerainfocontrol.h \
|
||||
qwinrtcameraimagecapturecontrol.h \
|
||||
qwinrtcameraservice.h \
|
||||
qwinrtcameravideorenderercontrol.h \
|
||||
qwinrtmediaplayercontrol.h \
|
||||
qwinrtmediaplayerservice.h \
|
||||
qwinrtplayerrenderercontrol.h \
|
||||
qwinrtserviceplugin.h
|
||||
qwinrtserviceplugin.h \
|
||||
qwinrtvideodeviceselectorcontrol.h
|
||||
|
||||
SOURCES += \
|
||||
qwinrtabstractvideorenderercontrol.cpp \
|
||||
qwinrtcameracontrol.cpp \
|
||||
qwinrtcamerainfocontrol.cpp \
|
||||
qwinrtcameraimagecapturecontrol.cpp \
|
||||
qwinrtcameraservice.cpp \
|
||||
qwinrtcameravideorenderercontrol.cpp \
|
||||
qwinrtmediaplayercontrol.cpp \
|
||||
qwinrtmediaplayerservice.cpp \
|
||||
qwinrtplayerrenderercontrol.cpp \
|
||||
qwinrtserviceplugin.cpp
|
||||
qwinrtserviceplugin.cpp \
|
||||
qwinrtvideodeviceselectorcontrol.cpp
|
||||
|
||||
OTHER_FILES += \
|
||||
winrt.json
|
||||
|
||||
@@ -236,7 +236,6 @@ void MFAudioDecoderControl::handleMediaSourceReady()
|
||||
}
|
||||
|
||||
if (m_sourceResolver->mediaSource()) {
|
||||
IMFPresentationDescriptor *pd = 0;
|
||||
if (mediaType && m_resampler) {
|
||||
HRESULT hr = S_OK;
|
||||
hr = m_resampler->SetInputType(m_mfInputStreamID, mediaType, 0);
|
||||
@@ -246,9 +245,11 @@ void MFAudioDecoderControl::handleMediaSourceReady()
|
||||
qWarning() << "MFAudioDecoderControl: failed to SetInputType of resampler" << hr;
|
||||
}
|
||||
}
|
||||
IMFPresentationDescriptor *pd;
|
||||
if (SUCCEEDED(m_sourceResolver->mediaSource()->CreatePresentationDescriptor(&pd))) {
|
||||
UINT64 duration = 0;
|
||||
pd->GetUINT64(MF_PD_DURATION, &duration);
|
||||
pd->Release();
|
||||
duration /= 10000;
|
||||
if (m_duration != qint64(duration)) {
|
||||
m_duration = qint64(duration);
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <d3d9.h>
|
||||
#include <dxva2api.h>
|
||||
#include <WinUser.h>
|
||||
@@ -331,34 +330,36 @@ void D3DPresentEngine::presentSample(void *opaque, qint64)
|
||||
IMFMediaBuffer* buffer = NULL;
|
||||
IDirect3DSurface9* surface = NULL;
|
||||
|
||||
if (sample) {
|
||||
// Get the buffer from the sample.
|
||||
hr = sample->GetBufferByIndex(0, &buffer);
|
||||
if (FAILED(hr))
|
||||
goto done;
|
||||
if (m_surface && m_surface->isActive()) {
|
||||
if (sample) {
|
||||
// Get the buffer from the sample.
|
||||
hr = sample->GetBufferByIndex(0, &buffer);
|
||||
if (FAILED(hr))
|
||||
goto done;
|
||||
|
||||
// Get the surface from the buffer.
|
||||
hr = MFGetService(buffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&surface));
|
||||
if (FAILED(hr))
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (surface && updateTexture(surface)) {
|
||||
QVideoFrame frame = QVideoFrame(new TextureVideoBuffer(m_glTexture),
|
||||
m_surfaceFormat.frameSize(),
|
||||
m_surfaceFormat.pixelFormat());
|
||||
|
||||
// WMF uses 100-nanosecond units, Qt uses microseconds
|
||||
LONGLONG startTime = -1;
|
||||
if (SUCCEEDED(sample->GetSampleTime(&startTime))) {
|
||||
frame.setStartTime(startTime * 0.1);
|
||||
|
||||
LONGLONG duration = -1;
|
||||
if (SUCCEEDED(sample->GetSampleDuration(&duration)))
|
||||
frame.setEndTime((startTime + duration) * 0.1);
|
||||
// Get the surface from the buffer.
|
||||
hr = MFGetService(buffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&surface));
|
||||
if (FAILED(hr))
|
||||
goto done;
|
||||
}
|
||||
|
||||
m_surface->present(frame);
|
||||
if (surface && updateTexture(surface)) {
|
||||
QVideoFrame frame = QVideoFrame(new TextureVideoBuffer(m_glTexture),
|
||||
m_surfaceFormat.frameSize(),
|
||||
m_surfaceFormat.pixelFormat());
|
||||
|
||||
// WMF uses 100-nanosecond units, Qt uses microseconds
|
||||
LONGLONG startTime = -1;
|
||||
if (SUCCEEDED(sample->GetSampleTime(&startTime))) {
|
||||
frame.setStartTime(startTime * 0.1);
|
||||
|
||||
LONGLONG duration = -1;
|
||||
if (SUCCEEDED(sample->GetSampleDuration(&duration)))
|
||||
frame.setEndTime((startTime + duration) * 0.1);
|
||||
}
|
||||
|
||||
m_surface->present(frame);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
@@ -420,18 +421,19 @@ void D3DPresentEngine::createOffscreenTexture()
|
||||
QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
|
||||
m_eglDisplay = static_cast<EGLDisplay*>(
|
||||
nativeInterface->nativeResourceForContext("eglDisplay", currentContext));
|
||||
m_eglConfig = static_cast<EGLDisplay*>(
|
||||
m_eglConfig = static_cast<EGLConfig*>(
|
||||
nativeInterface->nativeResourceForContext("eglConfig", currentContext));
|
||||
|
||||
currentContext->functions()->glGenTextures(1, &m_glTexture);
|
||||
|
||||
int w = m_surfaceFormat.frameWidth();
|
||||
int h = m_surfaceFormat.frameHeight();
|
||||
bool hasAlpha = currentContext->format().hasAlpha();
|
||||
|
||||
EGLint attribs[] = {
|
||||
EGL_WIDTH, w,
|
||||
EGL_HEIGHT, h,
|
||||
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
|
||||
EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
|
||||
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
|
||||
EGL_NONE
|
||||
};
|
||||
@@ -450,7 +452,7 @@ void D3DPresentEngine::createOffscreenTexture()
|
||||
|
||||
m_device->CreateTexture(w, h, 1,
|
||||
D3DUSAGE_RENDERTARGET,
|
||||
D3DFMT_X8R8G8B8,
|
||||
hasAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
|
||||
D3DPOOL_DEFAULT,
|
||||
&m_texture,
|
||||
&share_handle);
|
||||
|
||||
@@ -258,6 +258,7 @@ void MFPlayerSession::handleMediaSourceReady()
|
||||
//convert from 100 nanosecond to milisecond
|
||||
emit durationUpdate(qint64(m_duration / 10000));
|
||||
setupPlaybackTopology(mediaSource, sourcePD);
|
||||
sourcePD->Release();
|
||||
} else {
|
||||
changeStatus(QMediaPlayer::InvalidMedia);
|
||||
emit error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true);
|
||||
@@ -415,12 +416,15 @@ IMFTopologyNode* MFPlayerSession::addOutputNode(IMFStreamDescriptor *streamDesc,
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = node->SetUINT32(MF_TOPONODE_STREAMID, sinkID);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (SUCCEEDED(topology->AddNode(node)))
|
||||
if (SUCCEEDED(topology->AddNode(node))) {
|
||||
handler->Release();
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handler->Release();
|
||||
}
|
||||
node->Release();
|
||||
return NULL;
|
||||
@@ -609,42 +613,39 @@ HRESULT BindOutputNode(IMFTopologyNode *pNode)
|
||||
// Sets the IMFStreamSink pointers on all of the output nodes in a topology.
|
||||
HRESULT BindOutputNodes(IMFTopology *pTopology)
|
||||
{
|
||||
DWORD cNodes = 0;
|
||||
|
||||
IMFCollection *collection = NULL;
|
||||
IUnknown *element = NULL;
|
||||
IMFTopologyNode *node = NULL;
|
||||
IMFCollection *collection;
|
||||
|
||||
// Get the collection of output nodes.
|
||||
HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
|
||||
|
||||
// Enumerate all of the nodes in the collection.
|
||||
if (SUCCEEDED(hr))
|
||||
if (SUCCEEDED(hr)) {
|
||||
DWORD cNodes;
|
||||
hr = collection->GetElementCount(&cNodes);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
for (DWORD i = 0; i < cNodes; i++) {
|
||||
hr = collection->GetElement(i, &element);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
if (SUCCEEDED(hr)) {
|
||||
for (DWORD i = 0; i < cNodes; i++) {
|
||||
IUnknown *element;
|
||||
hr = collection->GetElement(i, &element);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
|
||||
hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
IMFTopologyNode *node;
|
||||
hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node);
|
||||
element->Release();
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
|
||||
// Bind this node.
|
||||
hr = BindOutputNode(node);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
// Bind this node.
|
||||
hr = BindOutputNode(node);
|
||||
node->Release();
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
}
|
||||
}
|
||||
collection->Release();
|
||||
}
|
||||
|
||||
if (collection)
|
||||
collection->Release();
|
||||
if (element)
|
||||
element->Release();
|
||||
if (node)
|
||||
node->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -1395,14 +1396,17 @@ int MFPlayerSession::bufferStatus()
|
||||
if (!m_netsourceStatistics)
|
||||
return 0;
|
||||
PROPVARIANT var;
|
||||
PropVariantInit(&var);
|
||||
PROPERTYKEY key;
|
||||
key.fmtid = MFNETSOURCE_STATISTICS;
|
||||
key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
|
||||
int progress = -1;
|
||||
if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) {
|
||||
// GetValue returns S_FALSE if the property is not available, which has
|
||||
// a value > 0. We therefore can't use the SUCCEEDED macro here.
|
||||
if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
|
||||
progress = var.lVal;
|
||||
PropVariantClear(&var);
|
||||
}
|
||||
PropVariantClear(&var);
|
||||
|
||||
#ifdef DEBUG_MEDIAFOUNDATION
|
||||
qDebug() << "bufferStatus: progress = " << progress;
|
||||
@@ -1413,22 +1417,30 @@ int MFPlayerSession::bufferStatus()
|
||||
|
||||
QMediaTimeRange MFPlayerSession::availablePlaybackRanges()
|
||||
{
|
||||
if (!m_netsourceStatistics)
|
||||
return QMediaTimeRange();
|
||||
// defaults to the whole media
|
||||
qint64 start = 0;
|
||||
qint64 end = qint64(m_duration / 10000);
|
||||
|
||||
qint64 start = 0, end = 0;
|
||||
PROPVARIANT var;
|
||||
PROPERTYKEY key;
|
||||
key.fmtid = MFNETSOURCE_STATISTICS;
|
||||
key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
|
||||
if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) {
|
||||
start = qint64(var.uhVal.QuadPart / 10000);
|
||||
key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
|
||||
if (SUCCEEDED(m_netsourceStatistics->GetValue(key, &var))) {
|
||||
end = qint64(var.uhVal.QuadPart / 10000);
|
||||
if (m_netsourceStatistics) {
|
||||
PROPVARIANT var;
|
||||
PropVariantInit(&var);
|
||||
PROPERTYKEY key;
|
||||
key.fmtid = MFNETSOURCE_STATISTICS;
|
||||
key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
|
||||
// GetValue returns S_FALSE if the property is not available, which has
|
||||
// a value > 0. We therefore can't use the SUCCEEDED macro here.
|
||||
if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
|
||||
start = qint64(var.uhVal.QuadPart / 10000);
|
||||
PropVariantClear(&var);
|
||||
PropVariantInit(&var);
|
||||
key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
|
||||
if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
|
||||
end = qint64(var.uhVal.QuadPart / 10000);
|
||||
PropVariantClear(&var);
|
||||
}
|
||||
}
|
||||
}
|
||||
PropVariantClear(&var);
|
||||
|
||||
return QMediaTimeRange(start, end);
|
||||
}
|
||||
|
||||
@@ -1491,8 +1503,11 @@ HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult)
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_closing)
|
||||
if (!m_closing) {
|
||||
emit sessionEvent(pEvent);
|
||||
} else {
|
||||
pEvent->Release();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -1615,9 +1630,6 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl))))
|
||||
setVolumeInternal(m_muted ? 0 : m_volume);
|
||||
|
||||
DWORD dwCharacteristics = 0;
|
||||
m_sourceResolver->mediaSource()->GetCharacteristics(&dwCharacteristics);
|
||||
emit seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
|
||||
@@ -1688,6 +1700,9 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
|
||||
}
|
||||
}
|
||||
MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics));
|
||||
|
||||
if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl))))
|
||||
setVolumeInternal(m_muted ? 0 : m_volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,9 @@ STDMETHODIMP AudioSampleGrabberCallback::OnProcessSample(REFGUID guidMajorMediaT
|
||||
if (llSampleTime == _I64_MAX) {
|
||||
// Set default QAudioBuffer start time
|
||||
llSampleTime = -1;
|
||||
} else {
|
||||
// WMF uses 100-nanosecond units, Qt uses microseconds
|
||||
llSampleTime /= 10;
|
||||
}
|
||||
|
||||
foreach (MFAudioProbeControl* probe, m_audioProbes)
|
||||
|
||||
Reference in New Issue
Block a user