Merge remote-tracking branch 'origin/5.4' into dev

Change-Id: I8b9177d90afac8b834d333efc6c22b6b35dceaf8
This commit is contained in:
Frederik Gladhorn
2014-10-09 18:04:44 +02:00
166 changed files with 3766 additions and 5995 deletions

View File

@@ -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()

View File

@@ -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()

View File

@@ -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());
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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()));
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -45,6 +45,7 @@
# include "coreaudiosessionmanager.h"
#endif
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
#include <QtCore/QSet>

View File

@@ -38,6 +38,7 @@
#include <CoreAudio/CoreAudioTypes.h>
#include <AudioToolbox/AudioToolbox.h>
#include <QtCore/QIODevice>
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>
#include <QtCore/QTimer>

View File

@@ -52,6 +52,7 @@
#endif
#include <QtMultimedia/private/qaudiohelpers_p.h>
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
QT_BEGIN_NAMESPACE

View File

@@ -41,6 +41,7 @@
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudioTypes.h>
#include <QtCore/QIODevice>
#include <QtCore/QWaitCondition>
#include <QtCore/QMutex>

View File

@@ -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()

View File

@@ -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

View File

@@ -78,6 +78,8 @@ public:
GstEncodingProfile *createProfile();
void applySettings(GstElement *element);
Q_SIGNALS:
void settingsChanged();

View File

@@ -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; }

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -76,6 +76,8 @@ public:
GstEncodingProfile *createProfile();
void applySettings(GstElement *encoder);
Q_SIGNALS:
void settingsChanged();

View File

@@ -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
}

View File

@@ -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)));

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -538,6 +538,7 @@ void MmRendererMediaPlayerControl::play()
return;
}
m_stopEventsToIgnore = 0; // once playing, stop events must be proccessed
setState( QMediaPlayer::PlayingState);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -1,4 +0,0 @@
{
"Keys": ["qt7"],
"Services": ["org.qt-project.qt.mediaplayer"]
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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, &currentFrame);
// 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"

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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 \

View 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;
}

View 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

View 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;
}

View File

@@ -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

View File

@@ -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"
*/

View File

@@ -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

View 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);
}

View 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

View 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();
}

View 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

View File

@@ -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();
}

View File

@@ -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

View 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));
}
}

View 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

View File

@@ -1,4 +1,4 @@
{
"Keys": ["winrt"],
"Services": ["org.qt-project.qt.mediaplayer"]
"Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.camera"]
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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)