From 4bae7697257e5294721211fdd0b9bad5731f70c9 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 20 Sep 2016 19:28:40 +0300 Subject: PulseAudio: make code more robust Some asynchronous operations return a pa_operation pointer, which can be null if the operation fails. In some cases we were not checking that the returned object was non null, leading to some asserts being raised in pa_operation_unref. Change-Id: Iff1cc67b7f79b758fa81d79e658debb1d737b29f Reviewed-by: Christian Stromme --- src/plugins/pulseaudio/qpulseaudioengine.cpp | 65 ++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 17 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/pulseaudio/qpulseaudioengine.cpp b/src/plugins/pulseaudio/qpulseaudioengine.cpp index e9510fd6a..b3d5044b7 100644 --- a/src/plugins/pulseaudio/qpulseaudioengine.cpp +++ b/src/plugins/pulseaudio/qpulseaudioengine.cpp @@ -170,15 +170,30 @@ static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32 case PA_SUBSCRIPTION_EVENT_NEW: case PA_SUBSCRIPTION_EVENT_CHANGE: switch (facility) { - case PA_SUBSCRIPTION_EVENT_SERVER: - pa_operation_unref(pa_context_get_server_info(context, serverInfoCallback, userdata)); + case PA_SUBSCRIPTION_EVENT_SERVER: { + pa_operation *op = pa_context_get_server_info(context, serverInfoCallback, userdata); + if (op) + pa_operation_unref(op); + else + qWarning("PulseAudioService: failed to get server info"); break; - case PA_SUBSCRIPTION_EVENT_SINK: - pa_operation_unref(pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata)); + } + case PA_SUBSCRIPTION_EVENT_SINK: { + pa_operation *op = pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata); + if (op) + pa_operation_unref(op); + else + qWarning("PulseAudioService: failed to get sink info"); break; - case PA_SUBSCRIPTION_EVENT_SOURCE: - pa_operation_unref(pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata)); + } + case PA_SUBSCRIPTION_EVENT_SOURCE: { + pa_operation *op = pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata); + if (op) + pa_operation_unref(op); + else + qWarning("PulseAudioService: failed to get source info"); break; + } default: break; } @@ -328,11 +343,15 @@ void QPulseAudioEngine::prepare() pa_context_set_state_callback(m_context, contextStateCallback, this); pa_context_set_subscribe_callback(m_context, event_cb, this); - pa_operation_unref(pa_context_subscribe(m_context, + pa_operation *op = pa_context_subscribe(m_context, pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER), - NULL, NULL)); + NULL, NULL); + if (op) + pa_operation_unref(op); + else + qWarning("PulseAudioService: failed to subscribe to context notifications"); } else { pa_context_unref(m_context); m_context = 0; @@ -376,21 +395,33 @@ void QPulseAudioEngine::updateDevices() // Get default input and output devices pa_operation *operation = pa_context_get_server_info(m_context, serverInfoCallback, this); - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - pa_operation_unref(operation); + if (operation) { + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(m_mainLoop); + pa_operation_unref(operation); + } else { + qWarning("PulseAudioService: failed to get server info"); + } // Get output devices operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this); - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - pa_operation_unref(operation); + if (operation) { + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(m_mainLoop); + pa_operation_unref(operation); + } else { + qWarning("PulseAudioService: failed to get sink info"); + } // Get input devices operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this); - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - pa_operation_unref(operation); + if (operation) { + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(m_mainLoop); + pa_operation_unref(operation); + } else { + qWarning("PulseAudioService: failed to get source info"); + } unlock(); } -- cgit v1.2.3 From 6b94b3ca91df6ee047e99c63061dc33b1fb1c0f7 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 21 Sep 2016 12:01:23 +0300 Subject: GStreamer: print a warning when the camerabin plugin is missing Don't create the camera service when the camerabin plugin is not available on the system and print a warning. Task-number: QTBUG-50775 Change-Id: I56c7e6297bebe4b90596cb3c0323f1d38231bceb Reviewed-by: Christian Stromme --- src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/plugins') diff --git a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp b/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp index bee81a953..0658b0af0 100644 --- a/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinserviceplugin.cpp @@ -60,8 +60,19 @@ QMediaService* CameraBinServicePlugin::create(const QString &key) { QGstUtils::initializeGst(); - if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) { + if (!CameraBinService::isCameraBinAvailable()) { + guint major, minor, micro, nano; + gst_version(&major, &minor, µ, &nano); + qWarning("Error: cannot create camera service, the 'camerabin' plugin is missing for " + "GStreamer %u.%u." + "\nPlease install the 'bad' GStreamer plugin package.", + major, minor); + return Q_NULLPTR; + } + return new CameraBinService(sourceFactory()); + } qWarning() << "Gstreamer camerabin service plugin: unsupported key:" << key; return 0; -- cgit v1.2.3 From 81a48edb548a9ad86414b3a115d718c932c11058 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 17 Aug 2016 13:55:10 +0200 Subject: AVFoundation: fix metadata control Correctly emit metaDataAvailableChanged and metaDataChanged signals. Task-number: QTBUG-46368 Change-Id: Ic79acd519b47f4d864fa335c8f75758fe9185c39 Reviewed-by: Christian Stromme --- .../mediaplayer/avfmediaplayermetadatacontrol.h | 2 +- .../mediaplayer/avfmediaplayermetadatacontrol.mm | 110 +++++++++++---------- 2 files changed, 59 insertions(+), 53 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h index d40e70c2a..e28fc2936 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.h @@ -58,7 +58,7 @@ private Q_SLOTS: private: AVFMediaPlayerSession *m_session; - QMap m_tags; + QVariantMap m_tags; void *m_asset; }; diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm index 336f8637c..b84ff5952 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayermetadatacontrol.mm @@ -75,6 +75,41 @@ QStringList AVFMediaPlayerMetaDataControl::availableMetaData() const return m_tags.keys(); } +static QString itemKey(AVMetadataItem *item) +{ + NSString *keyString = [item commonKey]; + + if (keyString.length != 0) { + if ([keyString isEqualToString:AVMetadataCommonKeyTitle]) { + return QMediaMetaData::Title; + } else if ([keyString isEqualToString: AVMetadataCommonKeySubject]) { + return QMediaMetaData::SubTitle; + } else if ([keyString isEqualToString: AVMetadataCommonKeyDescription]) { + return QMediaMetaData::Description; + } else if ([keyString isEqualToString: AVMetadataCommonKeyPublisher]) { + return QMediaMetaData::Publisher; + } else if ([keyString isEqualToString: AVMetadataCommonKeyCreationDate]) { + return QMediaMetaData::Date; + } else if ([keyString isEqualToString: AVMetadataCommonKeyType]) { + return QMediaMetaData::MediaType; + } else if ([keyString isEqualToString: AVMetadataCommonKeyLanguage]) { + return QMediaMetaData::Language; + } else if ([keyString isEqualToString: AVMetadataCommonKeyCopyrights]) { + return QMediaMetaData::Copyright; + } else if ([keyString isEqualToString: AVMetadataCommonKeyAlbumName]) { + return QMediaMetaData::AlbumTitle; + } else if ([keyString isEqualToString: AVMetadataCommonKeyAuthor]) { + return QMediaMetaData::Author; + } else if ([keyString isEqualToString: AVMetadataCommonKeyArtist]) { + return QMediaMetaData::ContributingArtist; + } else if ([keyString isEqualToString: AVMetadataCommonKeyArtwork]) { + return QMediaMetaData::PosterUrl; + } + } + + return QString(); +} + void AVFMediaPlayerMetaDataControl::updateTags() { #ifdef QT_DEBUG_AVF @@ -83,67 +118,38 @@ void AVFMediaPlayerMetaDataControl::updateTags() AVAsset *currentAsset = (AVAsset*)m_session->currentAssetHandle(); //Don't read the tags from the same asset more than once - if (currentAsset == m_asset) { + if (currentAsset == m_asset) return; - } m_asset = currentAsset; + QVariantMap oldTags = m_tags; //Since we've changed assets, clear old tags m_tags.clear(); - - NSArray *metadataFormats = [currentAsset availableMetadataFormats]; - for ( NSString *format in metadataFormats) { -#ifdef QT_DEBUG_AVF - qDebug() << "format: " << [format UTF8String]; -#endif - NSArray *metadataItems = [currentAsset metadataForFormat:format]; - for (AVMetadataItem* item in metadataItems) { - NSString *keyString = [item commonKey]; - NSString *value = [item stringValue]; - - if (keyString.length != 0) { - //Process "commonMetadata" tags here: - if ([keyString isEqualToString:AVMetadataCommonKeyTitle]) { - m_tags.insert(QMediaMetaData::Title, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyCreator]) { - m_tags.insert(QMediaMetaData::Author, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeySubject]) { - m_tags.insert(QMediaMetaData::SubTitle, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyDescription]) { - m_tags.insert(QMediaMetaData::Description, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyPublisher]) { - m_tags.insert(QMediaMetaData::Publisher, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyContributor]) { - m_tags.insert(QMediaMetaData::ContributingArtist, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyCreationDate]) { - m_tags.insert(QMediaMetaData::Date, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyType]) { - m_tags.insert(QMediaMetaData::MediaType, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyLanguage]) { - m_tags.insert(QMediaMetaData::Language, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyCopyrights]) { - m_tags.insert(QMediaMetaData::Copyright, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyAlbumName]) { - m_tags.insert(QMediaMetaData::AlbumTitle, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyAuthor]) { - m_tags.insert(QMediaMetaData::Author, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyArtist]) { - m_tags.insert(QMediaMetaData::AlbumArtist, QString([value UTF8String])); - } else if ([keyString isEqualToString: AVMetadataCommonKeyArtwork]) { - m_tags.insert(QMediaMetaData::PosterUrl, QString([value UTF8String])); + bool changed = false; + + // TODO: also process ID3, iTunes and QuickTime metadata + + NSArray *metadataItems = [currentAsset commonMetadata]; + for (AVMetadataItem* item in metadataItems) { + const QString key = itemKey(item); + if (!key.isEmpty()) { + const QString value = QString::fromNSString([item stringValue]); + if (!value.isNull()) { + m_tags.insert(key, value); + if (value != oldTags.value(key)) { + changed = true; + Q_EMIT metaDataChanged(key, value); } } - - if ([format isEqualToString:AVMetadataFormatID3Metadata]) { - //TODO: Process ID3 metadata - } else if ([format isEqualToString:AVMetadataFormatiTunesMetadata]) { - //TODO: Process iTunes metadata - } else if ([format isEqualToString:AVMetadataFormatQuickTimeUserData]) { - //TODO: Process QuickTime metadata - } } } - Q_EMIT metaDataChanged(); + if (oldTags.isEmpty() != m_tags.isEmpty()) { + Q_EMIT metaDataAvailableChanged(!m_tags.isEmpty()); + changed = true; + } + + if (changed) + Q_EMIT metaDataChanged(); } -- cgit v1.2.3