diff --git a/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp
index 57057ebd7602f748e1128e21a5605cd602c08e79..351e6ba404b7bab02fe66544046c25ad2aa20809 100644
--- a/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcameraexposurecontrol.cpp
@@ -63,6 +63,9 @@ QAndroidCameraExposureControl::QAndroidCameraExposureControl(QAndroidCameraSessi
 
 bool QAndroidCameraExposureControl::isParameterSupported(ExposureParameter parameter) const
 {
+    if (!m_session->camera())
+        return false;
+
     switch (parameter) {
     case QCameraExposureControl::ISO:
         return false;
@@ -71,7 +74,7 @@ bool QAndroidCameraExposureControl::isParameterSupported(ExposureParameter param
     case QCameraExposureControl::ShutterSpeed:
         return false;
     case QCameraExposureControl::ExposureCompensation:
-        return true;
+        return !m_supportedExposureCompensations.isEmpty();
     case QCameraExposureControl::FlashPower:
         return false;
     case QCameraExposureControl::FlashCompensation:
@@ -81,7 +84,7 @@ bool QAndroidCameraExposureControl::isParameterSupported(ExposureParameter param
     case QCameraExposureControl::SpotMeteringPoint:
         return false;
     case QCameraExposureControl::ExposureMode:
-        return true;
+        return !m_supportedExposureModes.isEmpty();
     case QCameraExposureControl::MeteringMode:
         return false;
     default:
@@ -127,27 +130,41 @@ QVariant QAndroidCameraExposureControl::actualValue(ExposureParameter parameter)
 
 bool QAndroidCameraExposureControl::setValue(ExposureParameter parameter, const QVariant& value)
 {
-    if (!m_session->camera() || !value.isValid())
+    if (!value.isValid())
         return false;
 
     if (parameter == QCameraExposureControl::ExposureCompensation) {
-        m_requestedExposureCompensation = value.toReal();
-        emit requestedValueChanged(QCameraExposureControl::ExposureCompensation);
+        qreal expComp = value.toReal();
+        if (!qFuzzyCompare(m_requestedExposureCompensation, expComp)) {
+            m_requestedExposureCompensation = expComp;
+            emit requestedValueChanged(QCameraExposureControl::ExposureCompensation);
+        }
+
+        if (!m_session->camera())
+            return true;
 
         int expCompIndex = qRound(m_requestedExposureCompensation / m_exposureCompensationStep);
         if (expCompIndex >= m_minExposureCompensationIndex
                 && expCompIndex <= m_maxExposureCompensationIndex) {
+            qreal comp = expCompIndex * m_exposureCompensationStep;
             m_session->camera()->setExposureCompensation(expCompIndex);
-
-            m_actualExposureCompensation = expCompIndex * m_exposureCompensationStep;
-            emit actualValueChanged(QCameraExposureControl::ExposureCompensation);
+            if (!qFuzzyCompare(m_actualExposureCompensation, comp)) {
+                m_actualExposureCompensation = expCompIndex * m_exposureCompensationStep;
+                emit actualValueChanged(QCameraExposureControl::ExposureCompensation);
+            }
 
             return true;
         }
 
     } else if (parameter == QCameraExposureControl::ExposureMode) {
-        m_requestedExposureMode = value.value<QCameraExposure::ExposureMode>();
-        emit requestedValueChanged(QCameraExposureControl::ExposureMode);
+        QCameraExposure::ExposureMode expMode = value.value<QCameraExposure::ExposureMode>();
+        if (m_requestedExposureMode != expMode) {
+            m_requestedExposureMode = expMode;
+            emit requestedValueChanged(QCameraExposureControl::ExposureMode);
+        }
+
+        if (!m_session->camera())
+            return true;
 
         if (!m_supportedExposureModes.isEmpty()) {
             m_actualExposureMode = m_requestedExposureMode;
@@ -190,38 +207,39 @@ bool QAndroidCameraExposureControl::setValue(ExposureParameter parameter, const
 
 void QAndroidCameraExposureControl::onCameraOpened()
 {
-    m_requestedExposureCompensation = m_actualExposureCompensation = 0.0;
-    m_requestedExposureMode = m_actualExposureMode = QCameraExposure::ExposureAuto;
-    emit requestedValueChanged(QCameraExposureControl::ExposureCompensation);
-    emit actualValueChanged(QCameraExposureControl::ExposureCompensation);
-    emit requestedValueChanged(QCameraExposureControl::ExposureMode);
-    emit actualValueChanged(QCameraExposureControl::ExposureMode);
-
+    m_supportedExposureCompensations.clear();
     m_minExposureCompensationIndex = m_session->camera()->getMinExposureCompensation();
     m_maxExposureCompensationIndex = m_session->camera()->getMaxExposureCompensation();
     m_exposureCompensationStep = m_session->camera()->getExposureCompensationStep();
-    for (int i = m_minExposureCompensationIndex; i <= m_maxExposureCompensationIndex; ++i)
-        m_supportedExposureCompensations.append(i * m_exposureCompensationStep);
-    emit parameterRangeChanged(QCameraExposureControl::ExposureCompensation);
+    if (m_minExposureCompensationIndex != 0 || m_maxExposureCompensationIndex != 0) {
+        for (int i = m_minExposureCompensationIndex; i <= m_maxExposureCompensationIndex; ++i)
+            m_supportedExposureCompensations.append(i * m_exposureCompensationStep);
+        emit parameterRangeChanged(QCameraExposureControl::ExposureCompensation);
+    }
 
     m_supportedExposureModes.clear();
     QStringList sceneModes = m_session->camera()->getSupportedSceneModes();
-    for (int i = 0; i < sceneModes.size(); ++i) {
-        const QString &sceneMode = sceneModes.at(i);
-        if (sceneMode == QLatin1String("auto"))
-            m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureAuto);
-        else if (sceneMode == QLatin1String("beach"))
-            m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureBeach);
-        else if (sceneMode == QLatin1String("night"))
-            m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureNight);
-        else if (sceneMode == QLatin1String("portrait"))
-            m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposurePortrait);
-        else if (sceneMode == QLatin1String("snow"))
-            m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSnow);
-        else if (sceneMode == QLatin1String("sports"))
-            m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSports);
+    if (!sceneModes.isEmpty()) {
+        for (int i = 0; i < sceneModes.size(); ++i) {
+            const QString &sceneMode = sceneModes.at(i);
+            if (sceneMode == QLatin1String("auto"))
+                m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureAuto);
+            else if (sceneMode == QLatin1String("beach"))
+                m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureBeach);
+            else if (sceneMode == QLatin1String("night"))
+                m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureNight);
+            else if (sceneMode == QLatin1String("portrait"))
+                m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposurePortrait);
+            else if (sceneMode == QLatin1String("snow"))
+                m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSnow);
+            else if (sceneMode == QLatin1String("sports"))
+                m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSports);
+        }
+        emit parameterRangeChanged(QCameraExposureControl::ExposureMode);
     }
-    emit parameterRangeChanged(QCameraExposureControl::ExposureMode);
+
+    setValue(QCameraExposureControl::ExposureCompensation, QVariant::fromValue(m_requestedExposureCompensation));
+    setValue(QCameraExposureControl::ExposureMode, QVariant::fromValue(m_requestedExposureMode));
 }
 
 QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp
index de8e521ee9ba9bd7115b274999d64db1193a53ae..20aece378d3f2bb825d18dad75f17907faa0e736 100644
--- a/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcameraflashcontrol.cpp
@@ -62,7 +62,12 @@ QCameraExposure::FlashModes QAndroidCameraFlashControl::flashMode() const
 
 void QAndroidCameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode)
 {
-    if (m_flashMode == mode || !m_session->camera() || !isFlashModeSupported(mode))
+    if (!m_session->camera()) {
+        m_flashMode = mode;
+        return;
+    }
+
+    if (!isFlashModeSupported(mode))
         return;
 
     // if torch was enabled, it first needs to be turned off before setting another mode
@@ -88,7 +93,7 @@ void QAndroidCameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode)
 
 bool QAndroidCameraFlashControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
 {
-    return m_supportedFlashModes.contains(mode);
+    return m_session->camera() ? m_supportedFlashModes.contains(mode) : false;
 }
 
 bool QAndroidCameraFlashControl::isFlashReady() const
@@ -115,6 +120,11 @@ void QAndroidCameraFlashControl::onCameraOpened()
         else if (flashMode == QLatin1String("torch"))
             m_supportedFlashModes << QCameraExposure::FlashVideoLight;
     }
+
+    if (!m_supportedFlashModes.contains(m_flashMode))
+        m_flashMode = QCameraExposure::FlashOff;
+
+    setFlashMode(m_flashMode);
 }
 
 QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp
index c4e0ea1a88da750412d1330cb7efc50229dc0371..dc636ccaf952acb5912f83cf47508bda29cf1384 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.cpp
@@ -80,42 +80,45 @@ QCameraFocus::FocusModes QAndroidCameraFocusControl::focusMode() const
 
 void QAndroidCameraFocusControl::setFocusMode(QCameraFocus::FocusModes mode)
 {
-    if (m_focusMode == mode || !m_session->camera() || !isFocusModeSupported(mode))
+    if (!m_session->camera()) {
+        setFocusModeHelper(mode);
         return;
+    }
 
-    QString focusMode = QLatin1String("fixed");
-
-    if (mode.testFlag(QCameraFocus::HyperfocalFocus)) {
-        focusMode = QLatin1String("edof");
-    } else if (mode.testFlag(QCameraFocus::ManualFocus)) {
-        focusMode = QLatin1String("fixed");
-    } else if (mode.testFlag(QCameraFocus::AutoFocus)) {
-        focusMode = QLatin1String("auto");
-    } else if (mode.testFlag(QCameraFocus::MacroFocus)) {
-        focusMode = QLatin1String("macro");
-    } else if (mode.testFlag(QCameraFocus::ContinuousFocus)) {
-        if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
-                || !m_continuousPictureFocusSupported) {
-            focusMode = QLatin1String("continuous-video");
-        } else {
-            focusMode = QLatin1String("continuous-picture");
+    if (isFocusModeSupported(mode)) {
+        QString focusMode = QLatin1String("fixed");
+
+        if (mode.testFlag(QCameraFocus::HyperfocalFocus)) {
+            focusMode = QLatin1String("edof");
+        } else if (mode.testFlag(QCameraFocus::ManualFocus)) {
+            focusMode = QLatin1String("fixed");
+        } else if (mode.testFlag(QCameraFocus::AutoFocus)) {
+            focusMode = QLatin1String("auto");
+        } else if (mode.testFlag(QCameraFocus::MacroFocus)) {
+            focusMode = QLatin1String("macro");
+        } else if (mode.testFlag(QCameraFocus::ContinuousFocus)) {
+            if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
+                    || !m_continuousPictureFocusSupported) {
+                focusMode = QLatin1String("continuous-video");
+            } else {
+                focusMode = QLatin1String("continuous-picture");
+            }
+        } else if (mode.testFlag(QCameraFocus::InfinityFocus)) {
+            focusMode = QLatin1String("infinity");
         }
-    } else if (mode.testFlag(QCameraFocus::InfinityFocus)) {
-        focusMode = QLatin1String("infinity");
-    }
 
-    m_session->camera()->setFocusMode(focusMode);
+        m_session->camera()->setFocusMode(focusMode);
 
-    // reset focus position
-    m_session->camera()->cancelAutoFocus();
+        // reset focus position
+        m_session->camera()->cancelAutoFocus();
 
-    m_focusMode = mode;
-    emit focusModeChanged(m_focusMode);
+        setFocusModeHelper(mode);
+    }
 }
 
 bool QAndroidCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes mode) const
 {
-    return m_supportedFocusModes.contains(mode);
+    return m_session->camera() ? m_supportedFocusModes.contains(mode) : false;
 }
 
 QCameraFocus::FocusPointMode QAndroidCameraFocusControl::focusPointMode() const
@@ -125,29 +128,31 @@ QCameraFocus::FocusPointMode QAndroidCameraFocusControl::focusPointMode() const
 
 void QAndroidCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode)
 {
-    if (!m_session->camera() || m_focusPointMode == mode || !isFocusPointModeSupported(mode))
+    if (!m_session->camera()) {
+        setFocusPointModeHelper(mode);
         return;
-
-    m_focusPointMode = mode;
-
-    if (mode == QCameraFocus::FocusPointCustom) {
-        m_actualFocusPoint = m_customFocusPoint;
-    } else {
-        // FocusPointAuto | FocusPointCenter
-        // note: there is no way to know the actual focus point in FocusPointAuto mode,
-        // so just report the focus point to be at the center of the frame
-        m_actualFocusPoint = QPointF(0.5, 0.5);
     }
 
-    updateFocusZones();
-    setCameraFocusArea();
+    if (isFocusPointModeSupported(mode)) {
+        if (mode == QCameraFocus::FocusPointCustom) {
+            m_actualFocusPoint = m_customFocusPoint;
+        } else {
+            // FocusPointAuto | FocusPointCenter
+            // note: there is no way to know the actual focus point in FocusPointAuto mode,
+            // so just report the focus point to be at the center of the frame
+            m_actualFocusPoint = QPointF(0.5, 0.5);
+        }
 
-    emit focusPointModeChanged(mode);
+        setFocusPointModeHelper(mode);
+
+        updateFocusZones();
+        setCameraFocusArea();
+    }
 }
 
 bool QAndroidCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
 {
-    return m_supportedFocusPointModes.contains(mode);
+    return m_session->camera() ? m_supportedFocusPointModes.contains(mode) : false;
 }
 
 QPointF QAndroidCameraFocusControl::customFocusPoint() const
@@ -157,13 +162,12 @@ QPointF QAndroidCameraFocusControl::customFocusPoint() const
 
 void QAndroidCameraFocusControl::setCustomFocusPoint(const QPointF &point)
 {
-    if (m_customFocusPoint == point)
-        return;
-
-    m_customFocusPoint = point;
-    emit customFocusPointChanged(m_customFocusPoint);
+    if (m_customFocusPoint != point) {
+        m_customFocusPoint = point;
+        emit customFocusPointChanged(m_customFocusPoint);
+    }
 
-    if (m_focusPointMode == QCameraFocus::FocusPointCustom) {
+    if (m_session->camera() && m_focusPointMode == QCameraFocus::FocusPointCustom) {
         m_actualFocusPoint = m_customFocusPoint;
         updateFocusZones();
         setCameraFocusArea();
@@ -187,12 +191,7 @@ void QAndroidCameraFocusControl::onCameraOpened()
     m_supportedFocusModes.clear();
     m_continuousPictureFocusSupported = false;
     m_continuousVideoFocusSupported = false;
-
-    m_focusPointMode = QCameraFocus::FocusPointAuto;
-    m_actualFocusPoint = QPointF(0.5, 0.5);
-    m_customFocusPoint = QPointF();
     m_supportedFocusPointModes.clear();
-    m_focusZones.clear();
 
     QStringList focusModes = m_session->camera()->getSupportedFocusModes();
     for (int i = 0; i < focusModes.size(); ++i) {
@@ -220,10 +219,14 @@ void QAndroidCameraFocusControl::onCameraOpened()
     if (m_session->camera()->getMaxNumFocusAreas() > 0)
         m_supportedFocusPointModes << QCameraFocus::FocusPointCenter << QCameraFocus::FocusPointCustom;
 
-    emit focusModeChanged(focusMode());
-    emit focusPointModeChanged(m_focusPointMode);
-    emit customFocusPointChanged(m_customFocusPoint);
-    emit focusZonesChanged();
+    if (!m_supportedFocusModes.contains(m_focusMode))
+        setFocusModeHelper(QCameraFocus::AutoFocus);
+    if (!m_supportedFocusPointModes.contains(m_focusPointMode))
+        setFocusPointModeHelper(QCameraFocus::FocusPointAuto);
+
+    setFocusMode(m_focusMode);
+    setCustomFocusPoint(m_customFocusPoint);
+    setFocusPointMode(m_focusPointMode);
 }
 
 void QAndroidCameraFocusControl::updateFocusZones(QCameraFocusZone::FocusZoneStatus status)
@@ -276,11 +279,12 @@ void QAndroidCameraFocusControl::onViewportSizeChanged()
     if (!m_focusZones.isEmpty())
         status = m_focusZones.at(0).status();
     updateFocusZones(status);
+    setCameraFocusArea();
 }
 
 void QAndroidCameraFocusControl::onCameraCaptureModeChanged()
 {
-    if (m_focusMode == QCameraFocus::ContinuousFocus) {
+    if (m_session->camera() && m_focusMode == QCameraFocus::ContinuousFocus) {
         QString focusMode;
         if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
                 || !m_continuousPictureFocusSupported) {
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h
index 4311e78ba3464c5809ab8e34159ddb0a5ea2ee77..997a313af0e4a2dc10e21b22f7d089aa68f1a5f9 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h
+++ b/src/plugins/android/src/mediacapture/qandroidcamerafocuscontrol.h
@@ -72,6 +72,22 @@ private Q_SLOTS:
     void onAutoFocusComplete(bool success);
 
 private:
+    inline void setFocusModeHelper(QCameraFocus::FocusModes mode)
+    {
+        if (m_focusMode != mode) {
+            m_focusMode = mode;
+            emit focusModeChanged(mode);
+        }
+    }
+
+    inline void setFocusPointModeHelper(QCameraFocus::FocusPointMode mode)
+    {
+        if (m_focusPointMode != mode) {
+            m_focusPointMode = mode;
+            emit focusPointModeChanged(mode);
+        }
+    }
+
     void updateFocusZones(QCameraFocusZone::FocusZoneStatus status = QCameraFocusZone::Selected);
     void setCameraFocusArea();
 
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp
index 4e4a416ddbc0315656c5d14332c1fe2f02be7982..7d06504209abfa3a8b14b534a3d570deb01dbaf5 100644
--- a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.cpp
@@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE
 QAndroidCameraImageProcessingControl::QAndroidCameraImageProcessingControl(QAndroidCameraSession *session)
     : QCameraImageProcessingControl()
     , m_session(session)
+    , m_whiteBalanceMode(QCameraImageProcessing::WhiteBalanceAuto)
 {
     connect(m_session, SIGNAL(opened()),
             this, SLOT(onCameraOpened()));
@@ -56,19 +57,17 @@ QAndroidCameraImageProcessingControl::QAndroidCameraImageProcessingControl(QAndr
 
 bool QAndroidCameraImageProcessingControl::isParameterSupported(ProcessingParameter parameter) const
 {
-    return (parameter == QCameraImageProcessingControl::WhiteBalancePreset);
+    return parameter == QCameraImageProcessingControl::WhiteBalancePreset
+            && m_session->camera()
+            && !m_supportedWhiteBalanceModes.isEmpty();
 }
 
 bool QAndroidCameraImageProcessingControl::isParameterValueSupported(ProcessingParameter parameter,
                                                                      const QVariant &value) const
 {
-    if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
-        return false;
-
-    if (!m_session->camera())
-        return false;
-
-    return m_supportedWhiteBalanceModes.contains(value.value<QCameraImageProcessing::WhiteBalanceMode>());
+    return parameter == QCameraImageProcessingControl::WhiteBalancePreset
+            && m_session->camera()
+            && m_supportedWhiteBalanceModes.contains(value.value<QCameraImageProcessing::WhiteBalanceMode>());
 }
 
 QVariant QAndroidCameraImageProcessingControl::parameter(ProcessingParameter parameter) const
@@ -76,13 +75,7 @@ QVariant QAndroidCameraImageProcessingControl::parameter(ProcessingParameter par
     if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
         return QVariant();
 
-    if (!m_session->camera())
-        return QVariant();
-
-    QString wb = m_session->camera()->getWhiteBalance();
-    QCameraImageProcessing::WhiteBalanceMode mode = m_supportedWhiteBalanceModes.key(wb, QCameraImageProcessing::WhiteBalanceAuto);
-
-    return QVariant::fromValue(mode);
+    return QVariant::fromValue(m_whiteBalanceMode);
 }
 
 void QAndroidCameraImageProcessingControl::setParameter(ProcessingParameter parameter, const QVariant &value)
@@ -90,12 +83,21 @@ void QAndroidCameraImageProcessingControl::setParameter(ProcessingParameter para
     if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
         return;
 
-    if (!m_session->camera())
-        return;
+    QCameraImageProcessing::WhiteBalanceMode mode = value.value<QCameraImageProcessing::WhiteBalanceMode>();
 
-    QString wb = m_supportedWhiteBalanceModes.value(value.value<QCameraImageProcessing::WhiteBalanceMode>(), QString());
-    if (!wb.isEmpty())
+    if (m_session->camera())
+        setWhiteBalanceModeHelper(mode);
+    else
+        m_whiteBalanceMode = mode;
+}
+
+void QAndroidCameraImageProcessingControl::setWhiteBalanceModeHelper(QCameraImageProcessing::WhiteBalanceMode mode)
+{
+    QString wb = m_supportedWhiteBalanceModes.value(mode, QString());
+    if (!wb.isEmpty()) {
         m_session->camera()->setWhiteBalance(wb);
+        m_whiteBalanceMode = mode;
+    }
 }
 
 void QAndroidCameraImageProcessingControl::onCameraOpened()
@@ -130,6 +132,11 @@ void QAndroidCameraImageProcessingControl::onCameraOpened()
                                                 QStringLiteral("warm-fluorescent"));
         }
     }
+
+    if (!m_supportedWhiteBalanceModes.contains(m_whiteBalanceMode))
+        m_whiteBalanceMode = QCameraImageProcessing::WhiteBalanceAuto;
+
+    setWhiteBalanceModeHelper(m_whiteBalanceMode);
 }
 
 QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h b/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h
index 3ef3c31443a4991fbfdc942af0a8a8b0c61ac1ea..0554dbf46535317daca3370331d308b42db3ec9a 100644
--- a/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h
+++ b/src/plugins/android/src/mediacapture/qandroidcameraimageprocessingcontrol.h
@@ -63,9 +63,13 @@ private Q_SLOTS:
     void onCameraOpened();
 
 private:
+    void setWhiteBalanceModeHelper(QCameraImageProcessing::WhiteBalanceMode mode);
+
     QAndroidCameraSession *m_session;
 
-    QHash<QCameraImageProcessing::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
+    QCameraImageProcessing::WhiteBalanceMode m_whiteBalanceMode;
+
+    QMap<QCameraImageProcessing::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp
index bfb48d06e988ea65043f2c9354e04ff6ac2c4638..3cf4209eb37ce401c11daaf135ce560f2d84e5bc 100644
--- a/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcameralockscontrol.cpp
@@ -66,13 +66,13 @@ QAndroidCameraLocksControl::QAndroidCameraLocksControl(QAndroidCameraSession *se
 
 QCamera::LockTypes QAndroidCameraLocksControl::supportedLocks() const
 {
-    return (QCamera::LockExposure | QCamera::LockWhiteBalance | QCamera::LockFocus);
+    return m_supportedLocks;
 }
 
 QCamera::LockStatus QAndroidCameraLocksControl::lockStatus(QCamera::LockType lock) const
 {
     if (!m_supportedLocks.testFlag(lock) || !m_session->camera())
-        return QCamera::Locked;
+        return QCamera::Unlocked;
 
     if (lock == QCamera::LockFocus)
         return m_focusLockStatus;
@@ -83,7 +83,7 @@ QCamera::LockStatus QAndroidCameraLocksControl::lockStatus(QCamera::LockType loc
     if (lock == QCamera::LockWhiteBalance)
         return m_whiteBalanceLockStatus;
 
-    return QCamera::Locked;
+    return QCamera::Unlocked;
 }
 
 void QAndroidCameraLocksControl::searchAndLock(QCamera::LockTypes locks)
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
index 963952294c66f5f6f691e0fe0f38d5d0a01ae0d1..90d223c34cee22b4e6351ed304f1ac341b7474a5 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
@@ -331,11 +331,12 @@ bool QAndroidCameraSession::startPreview()
     if (m_previewStarted)
         return true;
 
-    if (m_videoOutput->isReady())
-        m_camera->setPreviewTexture(m_videoOutput->surfaceTexture());
-    else
+    if (!m_videoOutput->isReady())
         return true; // delay starting until the video output is ready
 
+    if (!m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
+        return false;
+
     m_status = QCamera::StartingStatus;
     emit statusChanged(m_status);
 
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp
index 385cd942ae57888b06cbfa8f8ee8b062ce04adf5..3b6645fa8fddbc2303728d0082bd7130c1bc80ff 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcamerazoomcontrol.cpp
@@ -96,32 +96,25 @@ void QAndroidCameraZoomControl::zoomTo(qreal optical, qreal digital)
 {
     Q_UNUSED(optical);
 
-    if (!m_cameraSession->camera() ||
-            qFuzzyCompare(m_requestedZoom, digital) ||
-            qFuzzyCompare(m_maximumZoom, qreal(1))) {
-        return;
+    if (!qFuzzyCompare(m_requestedZoom, digital)) {
+        m_requestedZoom = digital;
+        emit requestedDigitalZoomChanged(m_requestedZoom);
     }
 
-    m_requestedZoom = digital;
-    emit requestedDigitalZoomChanged(m_requestedZoom);
-
-    digital = qBound(qreal(1), digital, m_maximumZoom);
-    int validZoomIndex = qt_findClosestValue(m_zoomRatios, qRound(digital * 100));
-    qreal newZoom = m_zoomRatios.at(validZoomIndex) / qreal(100);
-    if (!qFuzzyCompare(m_currentZoom, newZoom)) {
-        m_cameraSession->camera()->setZoom(validZoomIndex);
-        m_currentZoom = newZoom;
-        emit currentDigitalZoomChanged(m_currentZoom);
+    if (m_cameraSession->camera()) {
+        digital = qBound(qreal(1), digital, m_maximumZoom);
+        int validZoomIndex = qt_findClosestValue(m_zoomRatios, qRound(digital * 100));
+        qreal newZoom = m_zoomRatios.at(validZoomIndex) / qreal(100);
+        if (!qFuzzyCompare(m_currentZoom, newZoom)) {
+            m_cameraSession->camera()->setZoom(validZoomIndex);
+            m_currentZoom = newZoom;
+            emit currentDigitalZoomChanged(m_currentZoom);
+        }
     }
 }
 
 void QAndroidCameraZoomControl::onCameraOpened()
 {
-    m_requestedZoom = 1.0;
-    m_currentZoom = 1.0;
-    emit requestedDigitalZoomChanged(m_requestedZoom);
-    emit currentDigitalZoomChanged(m_currentZoom);
-
     if (m_cameraSession->camera()->isZoomSupported()) {
         m_zoomRatios = m_cameraSession->camera()->getZoomRatios();
         qreal maxZoom = m_zoomRatios.last() / qreal(100);
@@ -129,6 +122,7 @@ void QAndroidCameraZoomControl::onCameraOpened()
             m_maximumZoom = maxZoom;
             emit maximumDigitalZoomChanged(m_maximumZoom);
         }
+        zoomTo(1, m_requestedZoom);
     } else {
         m_zoomRatios.clear();
         if (!qFuzzyCompare(m_maximumZoom, qreal(1))) {
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
index c917bf2b732787422a2e248d687cfdebfc880479..32fc4bbaf8f83627773d6e9280ed206af7cf9dcd 100644
--- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
@@ -56,6 +56,19 @@ static QMutex g_cameraMapMutex;
 typedef QMap<int, AndroidCamera *> CameraMap;
 Q_GLOBAL_STATIC(CameraMap, g_cameraMap)
 
+static inline bool exceptionCheckAndClear(JNIEnv *env)
+{
+    if (Q_UNLIKELY(env->ExceptionCheck())) {
+#ifdef QT_DEBUG
+        env->ExceptionDescribe();
+#endif // QT_DEBUG
+        env->ExceptionClear();
+        return true;
+    }
+
+    return false;
+}
+
 static QRect areaToRect(jobject areaObj)
 {
     QJNIObjectPrivate area(areaObj);
@@ -132,9 +145,9 @@ public:
     Q_INVOKABLE bool init(int cameraId);
 
     Q_INVOKABLE void release();
-    Q_INVOKABLE void lock();
-    Q_INVOKABLE void unlock();
-    Q_INVOKABLE void reconnect();
+    Q_INVOKABLE bool lock();
+    Q_INVOKABLE bool unlock();
+    Q_INVOKABLE bool reconnect();
 
     Q_INVOKABLE AndroidCamera::CameraFacing getFacing();
     Q_INVOKABLE int getNativeOrientation();
@@ -147,7 +160,7 @@ public:
 
     Q_INVOKABLE QSize previewSize() const { return m_previewSize; }
     Q_INVOKABLE void updatePreviewSize();
-    Q_INVOKABLE void setPreviewTexture(void *surfaceTexture);
+    Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
 
     Q_INVOKABLE bool isZoomSupported();
     Q_INVOKABLE int getMaxZoom();
@@ -266,7 +279,7 @@ AndroidCamera *AndroidCamera::open(int cameraId)
     worker->start();
     d->moveToThread(worker);
     connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater);
-    bool ok = false;
+    bool ok = true;
     QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId));
     if (!ok) {
         worker->quit();
@@ -289,22 +302,28 @@ int AndroidCamera::cameraId() const
     return d->m_cameraId;
 }
 
-void AndroidCamera::lock()
+bool AndroidCamera::lock()
 {
     Q_D(AndroidCamera);
-    QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection);
+    bool ok = true;
+    QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+    return ok;
 }
 
-void AndroidCamera::unlock()
+bool AndroidCamera::unlock()
 {
     Q_D(AndroidCamera);
-    QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection);
+    bool ok = true;
+    QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+    return ok;
 }
 
-void AndroidCamera::reconnect()
+bool AndroidCamera::reconnect()
 {
     Q_D(AndroidCamera);
-    QMetaObject::invokeMethod(d, "reconnect");
+    bool ok = true;
+    QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+    return ok;
 }
 
 void AndroidCamera::release()
@@ -368,13 +387,16 @@ void AndroidCamera::setPreviewSize(const QSize &size)
     QMetaObject::invokeMethod(d, "updatePreviewSize");
 }
 
-void AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
+bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
 {
     Q_D(AndroidCamera);
+    bool ok = true;
     QMetaObject::invokeMethod(d,
                               "setPreviewTexture",
                               Qt::BlockingQueuedConnection,
+                              Q_RETURN_ARG(bool, ok),
                               Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0));
+    return ok;
 }
 
 bool AndroidCamera::isZoomSupported()
@@ -698,12 +720,12 @@ AndroidCameraPrivate::~AndroidCameraPrivate()
 bool AndroidCameraPrivate::init(int cameraId)
 {
     m_cameraId = cameraId;
+    QJNIEnvironmentPrivate env;
     m_camera = QJNIObjectPrivate::callStaticObjectMethod("android/hardware/Camera",
                                                          "open",
                                                          "(I)Landroid/hardware/Camera;",
                                                          cameraId);
-
-    if (!m_camera.isValid())
+    if (exceptionCheckAndClear(env) || !m_camera.isValid())
         return false;
 
     m_cameraListener = QJNIObjectPrivate(g_qtCameraListenerClass, "(I)V", m_cameraId);
@@ -731,26 +753,25 @@ void AndroidCameraPrivate::release()
         m_camera.callMethod<void>("release");
 }
 
-void AndroidCameraPrivate::lock()
+bool AndroidCameraPrivate::lock()
 {
+    QJNIEnvironmentPrivate env;
     m_camera.callMethod<void>("lock");
+    return !exceptionCheckAndClear(env);
 }
 
-void AndroidCameraPrivate::unlock()
+bool AndroidCameraPrivate::unlock()
 {
+    QJNIEnvironmentPrivate env;
     m_camera.callMethod<void>("unlock");
+    return !exceptionCheckAndClear(env);
 }
 
-void AndroidCameraPrivate::reconnect()
+bool AndroidCameraPrivate::reconnect()
 {
     QJNIEnvironmentPrivate env;
     m_camera.callMethod<void>("reconnect");
-    if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
-        env->ExceptionDescribe();
-#endif // QT_DEBUG
-        env->ExceptionDescribe();
-    }
+    return !exceptionCheckAndClear(env);
 }
 
 AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing()
@@ -832,11 +853,13 @@ void AndroidCameraPrivate::updatePreviewSize()
     emit previewSizeChanged();
 }
 
-void AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
+bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
 {
+    QJNIEnvironmentPrivate env;
     m_camera.callMethod<void>("setPreviewTexture",
                               "(Landroid/graphics/SurfaceTexture;)V",
                               static_cast<jobject>(surfaceTexture));
+    return !exceptionCheckAndClear(env);
 }
 
 bool AndroidCameraPrivate::isZoomSupported()
@@ -1020,8 +1043,7 @@ void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
             arrayList.callMethod<jboolean>("add",
                                            "(Ljava/lang/Object;)Z",
                                            rectToArea(areas.at(i)).object());
-            if (env->ExceptionCheck())
-                env->ExceptionClear();
+            exceptionCheckAndClear(env);
         }
         list = arrayList;
     }
@@ -1347,9 +1369,11 @@ void AndroidCameraPrivate::fetchLastPreviewFrame()
 
 void AndroidCameraPrivate::applyParameters()
 {
+    QJNIEnvironmentPrivate env;
     m_camera.callMethod<void>("setParameters",
                               "(Landroid/hardware/Camera$Parameters;)V",
                               m_parameters.object());
+    exceptionCheckAndClear(env);
 }
 
 QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName)
@@ -1386,10 +1410,8 @@ static JNINativeMethod methods[] = {
 bool AndroidCamera::initJNI(JNIEnv *env)
 {
     jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtCameraListener");
-    if (env->ExceptionCheck())
-        env->ExceptionClear();
 
-    if (clazz) {
+    if (!exceptionCheckAndClear(env) && clazz) {
         g_qtCameraListenerClass = static_cast<jclass>(env->NewGlobalRef(clazz));
         if (env->RegisterNatives(g_qtCameraListenerClass,
                                  methods,
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.h b/src/plugins/android/src/wrappers/jni/androidcamera.h
index 2ea69b7e3ac28563fe95546221d06c87c9925723..010f089fb9cfb429a5285684b859c6be3a145670 100644
--- a/src/plugins/android/src/wrappers/jni/androidcamera.h
+++ b/src/plugins/android/src/wrappers/jni/androidcamera.h
@@ -90,9 +90,9 @@ public:
 
     int cameraId() const;
 
-    void lock();
-    void unlock();
-    void reconnect();
+    bool lock();
+    bool unlock();
+    bool reconnect();
     void release();
 
     CameraFacing getFacing();
@@ -106,7 +106,7 @@ public:
 
     QSize previewSize() const;
     void setPreviewSize(const QSize &size);
-    void setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
+    bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
 
     bool isZoomSupported();
     int getMaxZoom();
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
index 4e82ada1b1ee58998e254f78ccfe9c4100485b77..d65d238d900ea1f95c0a4e973d41df652f6e223e 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
@@ -250,10 +250,8 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
 
             qDebugCamera() << "Video capture location:" << actualLocation.toString();
 
-            NSString *urlString = [NSString stringWithUTF8String:actualLocation.toString().toUtf8().constData()];
-            NSURL *fileURL = [NSURL URLWithString:urlString];
-
-            [m_movieOutput startRecordingToOutputFileURL:fileURL recordingDelegate:m_recorderDelagate];
+            [m_movieOutput startRecordingToOutputFileURL:actualLocation.toNSURL()
+                           recordingDelegate:m_recorderDelagate];
 
             Q_EMIT actualLocationChanged(actualLocation);
         } else {
diff --git a/src/plugins/coreaudio/coreaudiosessionmanager.mm b/src/plugins/coreaudio/coreaudiosessionmanager.mm
index 04c8b6ed4a0d50bf6c8924467f177077b71e7be6..6a8e97f4a6b0e26aed81f11cb744b3757890ff1b 100644
--- a/src/plugins/coreaudio/coreaudiosessionmanager.mm
+++ b/src/plugins/coreaudio/coreaudiosessionmanager.mm
@@ -216,8 +216,9 @@ CoreAudioSessionManager::CoreAudioSessionManager() :
 {
     m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this];
     setActive(true);
-    //set default category to just Playback and only switch if we need more permissions
-    setCategory(CoreAudioSessionManager::Playback, CoreAudioSessionManager::MixWithOthers);
+    // Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing
+    // if the screen is locked or if the Silent switch is toggled.
+    setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None);
 }
 
 CoreAudioSessionManager::~CoreAudioSessionManager()
diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri
index 2c0fe55b113001b757694c3bd8a2045bf674d5f6..75fca4aadbc184ce4baa3043d84ee956c7d2e1a1 100644
--- a/src/plugins/directshow/camera/camera.pri
+++ b/src/plugins/directshow/camera/camera.pri
@@ -23,10 +23,5 @@ SOURCES += \
     $$PWD/dsimagecapturecontrol.cpp \
     $$PWD/dscamerasession.cpp
 
-qtHaveModule(widgets) {
-    HEADERS += $$PWD/dsvideowidgetcontrol.h
-    SOURCES += $$PWD/dsvideowidgetcontrol.cpp
-}
-
 *-msvc*:INCLUDEPATH += $$(DXSDK_DIR)/include
 LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32
diff --git a/src/plugins/directshow/camera/dscameracontrol.cpp b/src/plugins/directshow/camera/dscameracontrol.cpp
index 07035f5dd87bbfd43b05a4cd4acea875520364d9..15c669f41037be68601cdce86f64000bf9f0c3c8 100644
--- a/src/plugins/directshow/camera/dscameracontrol.cpp
+++ b/src/plugins/directshow/camera/dscameracontrol.cpp
@@ -48,10 +48,13 @@
 QT_BEGIN_NAMESPACE
 
 DSCameraControl::DSCameraControl(QObject *parent)
-    :QCameraControl(parent), m_captureMode(QCamera::CaptureStillImage)
+    : QCameraControl(parent)
+    , m_state(QCamera::UnloadedState)
+    , m_captureMode(QCamera::CaptureStillImage)
 {
     m_session = qobject_cast<DSCameraSession*>(parent);
-    connect(m_session, SIGNAL(stateChanged(QCamera::State)),this, SIGNAL(stateChanged(QCamera::State)));
+    connect(m_session, SIGNAL(statusChanged(QCamera::Status)),
+            this, SIGNAL(statusChanged(QCamera::Status)));
 }
 
 DSCameraControl::~DSCameraControl()
@@ -60,14 +63,30 @@ DSCameraControl::~DSCameraControl()
 
 void DSCameraControl::setState(QCamera::State state)
 {
+    if (m_state == state)
+        return;
+
+    bool succeeded = false;
     switch (state) {
-        case QCamera::ActiveState:
-            start();
-            break;
-        case QCamera::UnloadedState: /* fall through */
-        case QCamera::LoadedState:
-            stop();
-            break;
+    case QCamera::UnloadedState:
+        succeeded = m_session->unload();
+        break;
+    case QCamera::LoadedState:
+    case QCamera::ActiveState:
+        if (m_state == QCamera::UnloadedState && !m_session->load())
+            return;
+
+        if (state == QCamera::ActiveState)
+            succeeded = m_session->startPreview();
+        else
+            succeeded = m_session->stopPreview();
+
+        break;
+    }
+
+    if (succeeded) {
+        m_state = state;
+        emit stateChanged(m_state);
     }
 }
 
@@ -85,19 +104,17 @@ bool DSCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
     return bCaptureSupported;
 }
 
-void DSCameraControl::start()
+void DSCameraControl::setCaptureMode(QCamera::CaptureModes mode)
 {
-    m_session->record();
-}
-
-void DSCameraControl::stop()
-{
-    m_session->stop();
+    if (m_captureMode != mode && isCaptureModeSupported(mode)) {
+        m_captureMode = mode;
+        emit captureModeChanged(mode);
+    }
 }
 
-QCamera::State DSCameraControl::state() const
+QCamera::Status DSCameraControl::status() const
 {
-    return (QCamera::State)m_session->state();
+    return m_session->status();
 }
 
 QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameracontrol.h b/src/plugins/directshow/camera/dscameracontrol.h
index 5c7661643061e4868926e11bb66dd85a41259c44..36ab515bf45c5d3089aa3a433f2128e47dbf8765 100644
--- a/src/plugins/directshow/camera/dscameracontrol.h
+++ b/src/plugins/directshow/camera/dscameracontrol.h
@@ -58,28 +58,21 @@ public:
     DSCameraControl(QObject *parent = 0);
     ~DSCameraControl();
 
-    void start();
-    void stop();
-    QCamera::State state() const;
+    QCamera::State state() const { return m_state; }
 
     QCamera::CaptureModes captureMode() const { return m_captureMode; }
-    void setCaptureMode(QCamera::CaptureModes mode)
-    {
-        if (m_captureMode != mode) {
-            m_captureMode = mode;
-            emit captureModeChanged(mode);
-        }
-    }
+    void setCaptureMode(QCamera::CaptureModes mode);
 
     void setState(QCamera::State state);
 
-    QCamera::Status status() const { return QCamera::UnavailableStatus; }
+    QCamera::Status status() const;
     bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
     bool canChangeProperty(PropertyChangeType /* changeType */, QCamera::Status /* status */) const {return false; }
 
 private:
     DSCameraSession *m_session;
     DSCameraService *m_service;
+    QCamera::State m_state;
     QCamera::CaptureModes m_captureMode;
 };
 
diff --git a/src/plugins/directshow/camera/dscameraservice.cpp b/src/plugins/directshow/camera/dscameraservice.cpp
index 9d99c6dd60168e41eab932f334dbf5e8bea237f4..b8a9b4aa7f01a8b71bb02cbe8cfb5fce71c3afd2 100644
--- a/src/plugins/directshow/camera/dscameraservice.cpp
+++ b/src/plugins/directshow/camera/dscameraservice.cpp
@@ -42,11 +42,6 @@
 #include <QtCore/qvariant.h>
 #include <QtCore/qdebug.h>
 
-#if defined(HAVE_WIDGETS)
-#include <QtWidgets/qwidget.h>
-#include <QVideoWidgetControl>
-#endif
-
 #include "dscameraservice.h"
 #include "dscameracontrol.h"
 #include "dscamerasession.h"
@@ -54,28 +49,16 @@
 #include "dsvideodevicecontrol.h"
 #include "dsimagecapturecontrol.h"
 
-#if defined(HAVE_WIDGETS)
-#include "dsvideowidgetcontrol.h"
-#endif
-
 QT_BEGIN_NAMESPACE
 
 DSCameraService::DSCameraService(QObject *parent):
     QMediaService(parent)
-#if defined(HAVE_WIDGETS)
-  , m_viewFinderWidget(0)
-  #endif
   , m_videoRenderer(0)
 {
     m_session = new DSCameraSession(this);
-
     m_control = new DSCameraControl(m_session);
-
     m_videoDevice = new DSVideoDeviceControl(m_session);
-
     m_imageCapture = new DSImageCaptureControl(m_session);
-
-    m_device = QByteArray("default");
 }
 
 DSCameraService::~DSCameraService()
@@ -84,9 +67,6 @@ DSCameraService::~DSCameraService()
     delete m_videoDevice;
     delete m_videoRenderer;
     delete m_imageCapture;
-#if defined(HAVE_WIDGETS)
-    delete m_viewFinderWidget;
-#endif
     delete m_session;
 }
 
@@ -98,21 +78,8 @@ QMediaControl* DSCameraService::requestControl(const char *name)
     if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
         return m_imageCapture;
 
-#if defined(HAVE_WIDGETS)
-    if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
-        if (!m_viewFinderWidget && !m_videoRenderer) {
-            m_viewFinderWidget = new DSVideoWidgetControl(m_session);
-            return m_viewFinderWidget;
-        }
-    }
-#endif
-
     if (qstrcmp(name,QVideoRendererControl_iid) == 0) {
-#if defined(HAVE_WIDGETS)
-        if (!m_videoRenderer && !m_viewFinderWidget) {
-#else
         if (!m_videoRenderer) {
-#endif
             m_videoRenderer = new DSVideoRendererControl(m_session, this);
             return m_videoRenderer;
         }
@@ -131,14 +98,6 @@ void DSCameraService::releaseControl(QMediaControl *control)
         m_videoRenderer = 0;
         return;
     }
-
-#if defined(HAVE_WIDGETS)
-    if (control == m_viewFinderWidget) {
-        delete m_viewFinderWidget;
-        m_viewFinderWidget = 0;
-        return;
-    }
-#endif
 }
 
 QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscameraservice.h b/src/plugins/directshow/camera/dscameraservice.h
index fb8b7886a04a12aad3396c770ed082af9c9f3702..ceb786414a7147e622af71c925c5cc5bdcb181f7 100644
--- a/src/plugins/directshow/camera/dscameraservice.h
+++ b/src/plugins/directshow/camera/dscameraservice.h
@@ -70,13 +70,9 @@ private:
     DSCameraControl        *m_control;
     DSCameraSession        *m_session;
     DSVideoOutputControl   *m_videoOutput;
-#if defined(HAVE_WIDGETS)
-    QMediaControl          *m_viewFinderWidget;
-#endif
     DSVideoDeviceControl   *m_videoDevice;
     QMediaControl          *m_videoRenderer;
     DSImageCaptureControl  *m_imageCapture;
-    QByteArray m_device;
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp
index 1ecc368e7390312562983458b8ac659f30e4b8ae..a8c85e5c664ff1d78223f7398c891fde0a6723a2 100644
--- a/src/plugins/directshow/camera/dscamerasession.cpp
+++ b/src/plugins/directshow/camera/dscamerasession.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -42,8 +42,11 @@
 #include <QtCore/qdebug.h>
 #include <QWidget>
 #include <QFile>
+#include <QtConcurrent/QtConcurrentRun>
 #include <QtMultimedia/qabstractvideobuffer.h>
 #include <QtMultimedia/qvideosurfaceformat.h>
+#include <QtMultimedia/qcameraimagecapture.h>
+#include <private/qmemoryvideobuffer_p.h>
 
 #include "dscamerasession.h"
 #include "dsvideorenderer.h"
@@ -51,12 +54,23 @@
 
 QT_BEGIN_NAMESPACE
 
-// If frames come in quicker than we display them, we allow the queue to build
-// up to this number before we start dropping them.
-const int LIMIT_FRAME = 5;
 
 namespace {
 // DirectShow helper implementation
+void _CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource)
+{
+    *pmtTarget = *pmtSource;
+    if (pmtTarget->cbFormat != 0) {
+        pmtTarget->pbFormat = reinterpret_cast<BYTE *>(CoTaskMemAlloc(pmtTarget->cbFormat));
+        if (pmtTarget->pbFormat)
+            memcpy(pmtTarget->pbFormat, pmtSource->pbFormat, pmtTarget->cbFormat);
+    }
+    if (pmtTarget->pUnk != NULL) {
+        // pUnk should not be used.
+        pmtTarget->pUnk->AddRef();
+    }
+}
+
 void _FreeMediaType(AM_MEDIA_TYPE& mt)
 {
     if (mt.cbFormat != 0) {
@@ -70,14 +84,36 @@ void _FreeMediaType(AM_MEDIA_TYPE& mt)
         mt.pUnk = NULL;
     }
 }
-
 } // end namespace
 
+typedef QList<QSize> SizeList;
+Q_GLOBAL_STATIC(SizeList, commonPreviewResolutions)
+
+static HRESULT getPin(IBaseFilter *filter, PIN_DIRECTION pinDir, IPin **pin);
+
+
 class SampleGrabberCallbackPrivate : public ISampleGrabberCB
 {
 public:
-    STDMETHODIMP_(ULONG) AddRef() { return 1; }
-    STDMETHODIMP_(ULONG) Release() { return 2; }
+    explicit SampleGrabberCallbackPrivate(DSCameraSession *session)
+        : m_ref(1)
+        , m_session(session)
+    { }
+
+    virtual ~SampleGrabberCallbackPrivate() { }
+
+    STDMETHODIMP_(ULONG) AddRef()
+    {
+        return InterlockedIncrement(&m_ref);
+    }
+
+    STDMETHODIMP_(ULONG) Release()
+    {
+        ULONG ref = InterlockedDecrement(&m_ref);
+        if (ref == 0)
+            delete this;
+        return ref;
+    }
 
     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
     {
@@ -101,143 +137,53 @@ public:
         return E_NOTIMPL;
     }
 
-    STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
+    STDMETHODIMP BufferCB(double time, BYTE *pBuffer, long bufferLen)
     {
-        if (!cs || active) {
-            return S_OK;
-        }
-
-        if ((cs->StillMediaType.majortype != MEDIATYPE_Video) ||
-                (cs->StillMediaType.formattype != FORMAT_VideoInfo) ||
-                (cs->StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER))) {
-            return VFW_E_INVALIDMEDIATYPE;
-        }
-
-        active = true;
-
-        if(toggle == true) {
-            toggle = false;
-        }
-        else {
-            toggle = true;
-        }
-
-        if(toggle) {
-            active = false;
-            return S_OK;
-        }
-
-        bool check = false;
-        cs->mutex.lock();
+        // We display frames as they arrive, the presentation time is
+        // irrelevant
+        Q_UNUSED(time);
 
-        if (cs->frames.size() > LIMIT_FRAME) {
-            check = true;
+        if (m_session) {
+           m_session->onFrameAvailable(reinterpret_cast<const char *>(pBuffer),
+                                       bufferLen);
         }
 
-        if (check) {
-            cs->mutex.unlock();
-            // Frames building up. We're going to drop some here
-            Sleep(100);
-            active = false;
-            return S_OK;
-        }
-        cs->mutex.unlock();
-
-        unsigned char* vidData = new unsigned char[BufferLen];
-        memcpy(vidData, pBuffer, BufferLen);
-
-        cs->mutex.lock();
-
-        video_buffer* buf = new video_buffer;
-        buf->buffer = vidData;
-        buf->length = BufferLen;
-        buf->time   = (qint64)Time;
-
-        cs->frames.append(buf);
-
-        cs->mutex.unlock();
-
-        QMetaObject::invokeMethod(cs, "captureFrame", Qt::QueuedConnection);
-
-        active = false;
-
         return S_OK;
     }
 
-    DSCameraSession* cs;
-    bool active;
-    bool toggle;
+private:
+    ULONG m_ref;
+    DSCameraSession *m_session;
 };
 
 
 DSCameraSession::DSCameraSession(QObject *parent)
     : QObject(parent)
-      ,m_currentImageId(0)
-    , needsHorizontalMirroring(false)
-    , needsVerticalMirroring(true)
-{
-    pBuild = NULL;
-    pGraph = NULL;
-    pCap = NULL;
-    pSG_Filter = NULL;
-    pSG = NULL;
-
-    opened = false;
-    available = false;
-    resolutions.clear();
-    m_state = QCamera::UnloadedState;
-    m_device = "default";
-
-    StillCapCB = new SampleGrabberCallbackPrivate;
-    StillCapCB->cs = this;
-    StillCapCB->active = false;
-    StillCapCB->toggle = false;
-
-    m_output = 0;
-    m_surface = 0;
-    pixelF = QVideoFrame::Format_Invalid;
-
-    graph = false;
-    active = false;
-
-    ::CoInitialize(NULL);
+    , m_graphBuilder(Q_NULLPTR)
+    , m_filterGraph(Q_NULLPTR)
+    , m_sourceDeviceName(QLatin1String("default"))
+    , m_sourceFilter(Q_NULLPTR)
+    , m_needsHorizontalMirroring(false)
+    , m_previewFilter(Q_NULLPTR)
+    , m_previewSampleGrabber(Q_NULLPTR)
+    , m_nullRendererFilter(Q_NULLPTR)
+    , m_previewStarted(false)
+    , m_surface(Q_NULLPTR)
+    , m_previewPixelFormat(QVideoFrame::Format_Invalid)
+    , m_readyForCapture(false)
+    , m_imageIdCounter(0)
+    , m_currentImageId(-1)
+    , m_status(QCamera::UnloadedStatus)
+{
+    ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
+
+    connect(this, SIGNAL(statusChanged(QCamera::Status)),
+            this, SLOT(updateReadyForCapture()));
 }
 
 DSCameraSession::~DSCameraSession()
 {
-    if (opened) {
-        closeStream();
-    }
-
-    CoUninitialize();
-
-    SAFE_RELEASE(pCap);
-    SAFE_RELEASE(pSG_Filter);
-    SAFE_RELEASE(pGraph);
-    SAFE_RELEASE(pBuild);
-
-    if (StillCapCB) {
-        delete StillCapCB;
-    }
-}
-
-int DSCameraSession::captureImage(const QString &fileName)
-{
-    emit readyForCaptureChanged(false);
-
-    // We're going to do this in one big synchronous call
-    m_currentImageId++;
-    if (fileName.isEmpty()) {
-        m_snapshot = "img.jpg";
-    } else {
-        m_snapshot = fileName;
-    }
-
-    if (!active) {
-        startStream();
-    }
-
-    return m_currentImageId;
+    unload();
 }
 
 void DSCameraSession::setSurface(QAbstractVideoSurface* surface)
@@ -245,415 +191,260 @@ void DSCameraSession::setSurface(QAbstractVideoSurface* surface)
     m_surface = surface;
 }
 
-bool DSCameraSession::deviceReady()
-{
-    return available;
-}
-
-bool DSCameraSession::pictureInProgress()
+void DSCameraSession::setDevice(const QString &device)
 {
-    return m_snapshot.isEmpty();
+    m_sourceDeviceName = device;
 }
 
-int DSCameraSession::framerate() const
+bool DSCameraSession::load()
 {
-    return -1;
-}
+    unload();
 
-void DSCameraSession::setFrameRate(int rate)
-{
-    Q_UNUSED(rate)
-}
+    setStatus(QCamera::LoadingStatus);
 
-int DSCameraSession::brightness() const
-{
-    return -1;
-}
+    bool succeeded = createFilterGraph();
+    if (succeeded)
+        setStatus(QCamera::LoadedStatus);
+    else
+        setStatus(QCamera::UnavailableStatus);
 
-void DSCameraSession::setBrightness(int b)
-{
-    Q_UNUSED(b)
+    return succeeded;
 }
 
-int DSCameraSession::contrast() const
+bool DSCameraSession::unload()
 {
-    return -1;
-}
-
-void DSCameraSession::setContrast(int c)
-{
-    Q_UNUSED(c)
-}
+    if (!m_graphBuilder)
+        return false;
 
-int DSCameraSession::saturation() const
-{
-    return -1;
-}
+    if (!stopPreview())
+        return false;
 
-void DSCameraSession::setSaturation(int s)
-{
-    Q_UNUSED(s)
-}
+    setStatus(QCamera::UnloadingStatus);
 
-int DSCameraSession::hue() const
-{
-    return -1;
-}
+    m_needsHorizontalMirroring = false;
+    m_sourcePreferredResolution = QSize();
+    _FreeMediaType(m_sourcePreferredFormat);
+    ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
+    SAFE_RELEASE(m_sourceFilter);
+    SAFE_RELEASE(m_previewSampleGrabber);
+    SAFE_RELEASE(m_previewFilter);
+    SAFE_RELEASE(m_nullRendererFilter);
+    SAFE_RELEASE(m_filterGraph);
+    SAFE_RELEASE(m_graphBuilder);
 
-void DSCameraSession::setHue(int h)
-{
-    Q_UNUSED(h)
-}
+    setStatus(QCamera::UnloadedStatus);
 
-int DSCameraSession::sharpness() const
-{
-    return -1;
-}
-
-void DSCameraSession::setSharpness(int s)
-{
-    Q_UNUSED(s)
+    return true;
 }
 
-int DSCameraSession::zoom() const
+bool DSCameraSession::startPreview()
 {
-    return -1;
-}
+    if (m_previewStarted)
+        return true;
 
-void DSCameraSession::setZoom(int z)
-{
-    Q_UNUSED(z)
-}
+    if (!m_graphBuilder)
+        return false;
 
-bool DSCameraSession::backlightCompensation() const
-{
-    return false;
-}
+    setStatus(QCamera::StartingStatus);
 
-void DSCameraSession::setBacklightCompensation(bool b)
-{
-    Q_UNUSED(b)
-}
+    HRESULT hr = S_OK;
+    IMediaControl* pControl = 0;
 
-int DSCameraSession::whitelevel() const
-{
-    return -1;
-}
+    if (!configurePreviewFormat()) {
+        qWarning() << "Failed to configure preview format";
+        goto failed;
+    }
 
-void DSCameraSession::setWhitelevel(int w)
-{
-    Q_UNUSED(w)
-}
+    if (!connectGraph())
+        goto failed;
 
-int DSCameraSession::rotation() const
-{
-    return 0;
-}
+    if (m_surface)
+        m_surface->start(m_previewSurfaceFormat);
 
-void DSCameraSession::setRotation(int r)
-{
-    Q_UNUSED(r)
-}
+    hr = m_filterGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
+    if (FAILED(hr)) {
+        qWarning() << "failed to get stream control";
+        goto failed;
+    }
+    hr = pControl->Run();
+    pControl->Release();
 
-bool DSCameraSession::flash() const
-{
-    return false;
-}
+    if (FAILED(hr)) {
+        qWarning() << "failed to start";
+        goto failed;
+    }
 
-void DSCameraSession::setFlash(bool f)
-{
-    Q_UNUSED(f)
-}
+    setStatus(QCamera::ActiveStatus);
+    m_previewStarted = true;
+    return true;
 
-bool DSCameraSession::autofocus() const
-{
+failed:
+    // go back to a clean state
+    if (m_surface && m_surface->isActive())
+        m_surface->stop();
+    disconnectGraph();
+    setStatus(QCamera::LoadedStatus);
     return false;
 }
 
-void DSCameraSession::setAutofocus(bool f)
+bool DSCameraSession::stopPreview()
 {
-    Q_UNUSED(f)
-}
-
-QSize DSCameraSession::frameSize() const
-{
-    return m_windowSize;
-}
-
-void DSCameraSession::setFrameSize(const QSize& s)
-{
-    if (supportedResolutions(pixelF).contains(s))
-        m_windowSize = s;
-    else
-        qWarning() << "frame size if not supported for current pixel format, no change";
-}
-
-void DSCameraSession::setDevice(const QString &device)
-{
-    if(opened)
-        stopStream();
-
-    if(graph) {
-        SAFE_RELEASE(pCap);
-        SAFE_RELEASE(pSG_Filter);
-        SAFE_RELEASE(pGraph);
-        SAFE_RELEASE(pBuild);
-    }
-
-    available = false;
-    m_state = QCamera::LoadedState;
-
-    CoInitialize(NULL);
-
-    ICreateDevEnum* pDevEnum = NULL;
-    IEnumMoniker* pEnum = NULL;
+    if (!m_previewStarted)
+        return true;
 
-    // Create the System device enumerator
-    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
-                                  CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
-                                  reinterpret_cast<void**>(&pDevEnum));
-    if(SUCCEEDED(hr)) {
-        // Create the enumerator for the video capture category
-        hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
-        if (S_OK == hr) {
-            pEnum->Reset();
-            // go through and find all video capture devices
-            IMoniker* pMoniker = NULL;
-            IMalloc *mallocInterface = 0;
-            CoGetMalloc(1, (LPMALLOC*)&mallocInterface);
-            while(pEnum->Next(1, &pMoniker, NULL) == S_OK) {
+    setStatus(QCamera::StoppingStatus);
 
-                BSTR strName = 0;
-                hr = pMoniker->GetDisplayName(NULL, NULL, &strName);
-                if (SUCCEEDED(hr)) {
-                    QString temp(QString::fromWCharArray(strName));
-                    mallocInterface->Free(strName);
-                    if(temp.contains(device)) {
-                        available = true;
-                    }
-                }
-
-                pMoniker->Release();
-            }
-            mallocInterface->Release();
-            pEnum->Release();
-        }
-        pDevEnum->Release();
+    IMediaControl* pControl = 0;
+    HRESULT hr = m_filterGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
+    if (FAILED(hr)) {
+        qWarning() << "failed to get stream control";
+        goto failed;
     }
-    CoUninitialize();
 
-    if(available) {
-        m_device = QByteArray(device.toUtf8().constData());
-        graph = createFilterGraph();
-        if(!graph)
-            available = false;
+    hr = pControl->Stop();
+    pControl->Release();
+    if (FAILED(hr)) {
+        qWarning() << "failed to stop";
+        goto failed;
     }
-}
 
-QList<QVideoFrame::PixelFormat> DSCameraSession::supportedPixelFormats()
-{
-    return types;
-}
-
-QVideoFrame::PixelFormat DSCameraSession::pixelFormat() const
-{
-    return pixelF;
-}
+    disconnectGraph();
 
-void DSCameraSession::setPixelFormat(QVideoFrame::PixelFormat fmt)
-{
-    pixelF = fmt;
-}
+    m_previewStarted = false;
+    setStatus(QCamera::LoadedStatus);
+    return true;
 
-QList<QSize> DSCameraSession::supportedResolutions(QVideoFrame::PixelFormat format)
-{
-    if (!resolutions.contains(format))
-        return QList<QSize>();
-    return resolutions.value(format);
+failed:
+    setStatus(QCamera::ActiveStatus);
+    return false;
 }
 
-bool DSCameraSession::setOutputLocation(const QUrl &sink)
+void DSCameraSession::setStatus(QCamera::Status status)
 {
-    m_sink = sink;
+    if (m_status == status)
+        return;
 
-    return true;
+    m_status = status;
+    emit statusChanged(m_status);
 }
 
-QUrl DSCameraSession::outputLocation() const
+bool DSCameraSession::isReadyForCapture()
 {
-    return m_sink;
+    return m_readyForCapture;
 }
 
-qint64 DSCameraSession::position() const
+void DSCameraSession::updateReadyForCapture()
 {
-    return timeStamp.elapsed();
+    bool isReady = (m_status == QCamera::ActiveStatus && m_imageCaptureFileName.isEmpty());
+    if (isReady != m_readyForCapture) {
+        m_readyForCapture = isReady;
+        emit readyForCaptureChanged(isReady);
+    }
 }
 
-int DSCameraSession::state() const
+int DSCameraSession::captureImage(const QString &fileName)
 {
-    return int(m_state);
-}
+    ++m_imageIdCounter;
 
-void DSCameraSession::record()
-{
-    if(opened) {
-        return;
+    if (!m_readyForCapture) {
+        emit captureError(m_imageIdCounter, QCameraImageCapture::NotReadyError,
+                          tr("Camera not ready for capture"));
+        return m_imageIdCounter;
     }
 
-    if(m_surface) {
+    m_imageCaptureFileName = m_fileNameGenerator.generateFileName(fileName,
+                                                         QMediaStorageLocation::Pictures,
+                                                         QLatin1String("IMG_"),
+                                                         QLatin1String("jpg"));
 
-        if (!graph)
-            graph = createFilterGraph();
+    updateReadyForCapture();
 
-        if (types.isEmpty()) {
-            if (pixelF == QVideoFrame::Format_Invalid)
-                pixelF = QVideoFrame::Format_RGB32;
-            if (!m_windowSize.isValid())
-                m_windowSize = QSize(320, 240);
-        }
-        actualFormat = QVideoSurfaceFormat(m_windowSize, pixelF);
-
-        if (!m_surface->isFormatSupported(actualFormat) && !types.isEmpty()) {
-            // enumerate through camera formats
-            QList<QVideoFrame::PixelFormat> fmts = m_surface->supportedPixelFormats();
-            foreach(QVideoFrame::PixelFormat f, types) {
-                if (fmts.contains(f)) {
-                    pixelF = f;
-                    if (!resolutions[pixelF].contains(m_windowSize)) {
-                        Q_ASSERT(!resolutions[pixelF].isEmpty());
-                        m_windowSize = resolutions[pixelF].first();
-                    }
-                    actualFormat = QVideoSurfaceFormat(m_windowSize, pixelF);
-                    break;
-                }
-            }
-        }
+    m_captureMutex.lock();
+    m_currentImageId = m_imageIdCounter;
+    m_captureMutex.unlock();
 
-        if (m_surface->isFormatSupported(actualFormat)) {
-            m_surface->start(actualFormat);
-            m_state = QCamera::ActiveState;
-            emit stateChanged(QCamera::ActiveState);
-        } else {
-            qWarning() << "surface doesn't support camera format, cant start";
-            m_state = QCamera::LoadedState;
-            emit stateChanged(QCamera::LoadedState);
-            return;
-        }
-    } else {
-        qWarning() << "no video surface, cant start";
-        m_state = QCamera::LoadedState;
-        emit stateChanged(QCamera::LoadedState);
-        return;
-    }
-
-    opened = startStream();
-
-    if (!opened) {
-        qWarning() << "Stream did not open";
-        m_state = QCamera::LoadedState;
-        emit stateChanged(QCamera::LoadedState);
-    }
+    return m_imageIdCounter;
 }
 
-void DSCameraSession::pause()
+void DSCameraSession::onFrameAvailable(const char *frameData, long len)
 {
-    suspendStream();
-}
+    // !!! Not called on the main thread
 
-void DSCameraSession::stop()
-{
-    if(!opened) {
-        return;
-    }
+    // Deep copy, the data might be modified or freed after the callback returns
+    QByteArray data(frameData, len);
 
-    stopStream();
-    opened = false;
-    m_state = QCamera::LoadedState;
-    emit stateChanged(QCamera::LoadedState);
-}
+    m_presentMutex.lock();
 
-void DSCameraSession::captureFrame()
-{
-    if(m_surface && frames.count() > 0) {
+    // (We should be getting only RGB32 data)
+    int stride = m_previewSize.width() * 4;
 
-        QImage image;
+    // In case the source produces frames faster than we can display them,
+    // only keep the most recent one
+    m_currentFrame = QVideoFrame(new QMemoryVideoBuffer(data, stride),
+                                 m_previewSize,
+                                 m_previewPixelFormat);
 
-        if(pixelF == QVideoFrame::Format_RGB24) {
+    m_presentMutex.unlock();
 
-            mutex.lock();
+    // Image capture
+    QMutexLocker locker(&m_captureMutex);
+    if (m_currentImageId != -1 && !m_capturedFrame.isValid()) {
+        m_capturedFrame = m_currentFrame;
+        emit imageExposed(m_currentImageId);
+    }
 
-            image = QImage(frames.at(0)->buffer,m_windowSize.width(),m_windowSize.height(),
-                    QImage::Format_RGB888).rgbSwapped().mirrored(needsHorizontalMirroring, needsVerticalMirroring);
+    QMetaObject::invokeMethod(this, "presentFrame", Qt::QueuedConnection);
+}
 
-            QVideoFrame frame(image);
-            frame.setStartTime(frames.at(0)->time);
+void DSCameraSession::presentFrame()
+{
+    m_presentMutex.lock();
 
-            mutex.unlock();
+    if (m_currentFrame.isValid() && m_surface) {
+        m_surface->present(m_currentFrame);
+        m_currentFrame = QVideoFrame();
+    }
 
-            m_surface->present(frame);
+    m_presentMutex.unlock();
 
-        } else if (pixelF == QVideoFrame::Format_RGB32) {
+    m_captureMutex.lock();
 
-            mutex.lock();
+    if (m_capturedFrame.isValid()) {
+        Q_ASSERT(m_previewPixelFormat == QVideoFrame::Format_RGB32);
 
-            image = QImage(frames.at(0)->buffer,m_windowSize.width(),m_windowSize.height(),
-                    QImage::Format_RGB32).mirrored(needsHorizontalMirroring, needsVerticalMirroring);
+        m_capturedFrame.map(QAbstractVideoBuffer::ReadOnly);
 
-            QVideoFrame frame(image);
-            frame.setStartTime(frames.at(0)->time);
+        QImage image = QImage(m_capturedFrame.bits(),
+                              m_previewSize.width(), m_previewSize.height(),
+                              QImage::Format_RGB32);
 
-            mutex.unlock();
+        image = image.mirrored(m_needsHorizontalMirroring); // also causes a deep copy of the data
 
-            m_surface->present(frame);
+        m_capturedFrame.unmap();
 
-        } else {
-            qWarning() << "TODO:captureFrame() format =" << pixelF;
-        }
+        emit imageCaptured(m_currentImageId, image);
 
-        if (m_snapshot.length() > 0) {
-            emit imageCaptured(m_currentImageId, image);
-            image.save(m_snapshot,"JPG");
-            emit imageSaved(m_currentImageId, m_snapshot);
-            m_snapshot.clear();
-            emit readyForCaptureChanged(true);
-        }
+        QtConcurrent::run(this, &DSCameraSession::saveCapturedImage,
+                          m_currentImageId, image, m_imageCaptureFileName);
 
-        mutex.lock();
-        if (frames.isEmpty()) {
-            qWarning() << "Frames over-run";
-        }
+        m_imageCaptureFileName.clear();
+        m_currentImageId = -1;
+        updateReadyForCapture();
 
-        video_buffer* buf = frames.takeFirst();
-        delete buf->buffer;
-        delete buf;
-        mutex.unlock();
+        m_capturedFrame = QVideoFrame();
     }
+
+    m_captureMutex.unlock();
 }
 
-HRESULT DSCameraSession::getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
+void DSCameraSession::saveCapturedImage(int id, const QImage &image, const QString &path)
 {
-    *ppPin = 0;
-    IEnumPins *pEnum = 0;
-    IPin *pPin = 0;
-
-    HRESULT hr = pFilter->EnumPins(&pEnum);
-    if(FAILED(hr)) {
-        return hr;
-    }
-
-    pEnum->Reset();
-    while(pEnum->Next(1, &pPin, NULL) == S_OK) {
-        PIN_DIRECTION ThisPinDir;
-        pPin->QueryDirection(&ThisPinDir);
-        if(ThisPinDir == PinDir) {
-            pEnum->Release();
-            *ppPin = pPin;
-            return S_OK;
-        }
-        pEnum->Release();
+    if (image.save(path, "JPG")) {
+        emit imageSaved(id, path);
+    } else {
+        emit captureError(id, QCameraImageCapture::ResourceError,
+                          tr("Could not save image to file."));
     }
-    pEnum->Release();
-    return E_FAIL;
 }
 
 bool DSCameraSession::createFilterGraph()
@@ -661,35 +452,34 @@ bool DSCameraSession::createFilterGraph()
     // Previously containered in <qedit.h>.
     static const IID iID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
     static const CLSID cLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
+    static const CLSID cLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
 
     HRESULT hr;
     IMoniker* pMoniker = NULL;
     ICreateDevEnum* pDevEnum = NULL;
     IEnumMoniker* pEnum = NULL;
 
-    CoInitialize(NULL);
-
     // Create the filter graph
     hr = CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC,
-            IID_IGraphBuilder, (void**)&pGraph);
+            IID_IGraphBuilder, (void**)&m_filterGraph);
     if (FAILED(hr)) {
-        qWarning()<<"failed to create filter graph";
-        return false;
+        qWarning() << "failed to create filter graph";
+        goto failed;
     }
 
     // Create the capture graph builder
     hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
-                          IID_ICaptureGraphBuilder2, (void**)&pBuild);
+                          IID_ICaptureGraphBuilder2, (void**)&m_graphBuilder);
     if (FAILED(hr)) {
-        qWarning()<<"failed to create graph builder";
-        return false;
+        qWarning() << "failed to create graph builder";
+        goto failed;
     }
 
     // Attach the filter graph to the capture graph
-    hr = pBuild->SetFiltergraph(pGraph);
+    hr = m_graphBuilder->SetFiltergraph(m_filterGraph);
     if (FAILED(hr)) {
-        qWarning()<<"failed to connect capture graph and filter graph";
-        return false;
+        qWarning() << "failed to connect capture graph and filter graph";
+        goto failed;
     }
 
     // Find the Capture device
@@ -712,8 +502,8 @@ bool DSCameraSession::createFilterGraph()
                 if (SUCCEEDED(hr)) {
                     QString output = QString::fromWCharArray(strName);
                     mallocInterface->Free(strName);
-                    if (m_device.contains(output.toUtf8().constData())) {
-                        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
+                    if (m_sourceDeviceName.contains(output)) {
+                        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_sourceFilter);
                         if (SUCCEEDED(hr)) {
                             pMoniker->Release();
                             break;
@@ -723,9 +513,9 @@ bool DSCameraSession::createFilterGraph()
                 pMoniker->Release();
             }
             mallocInterface->Release();
-            if (NULL == pCap)
+            if (NULL == m_sourceFilter)
             {
-                if (m_device.contains("default"))
+                if (m_sourceDeviceName.contains(QLatin1String("default")))
                 {
                     pEnum->Reset();
                     // still have to loop to discard bind to storage failure case
@@ -740,7 +530,7 @@ bool DSCameraSession::createFilterGraph()
 
                         // No need to get the description, just grab it
 
-                        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
+                        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_sourceFilter);
                         pPropBag->Release();
                         pMoniker->Release();
                         if (SUCCEEDED(hr)) {
@@ -757,443 +547,314 @@ bool DSCameraSession::createFilterGraph()
         }
     }
 
+    if (!m_sourceFilter) {
+        qWarning() << "No capture device found";
+        goto failed;
+    }
+
     // Sample grabber filter
     hr = CoCreateInstance(cLSID_SampleGrabber, NULL,CLSCTX_INPROC,
-                          IID_IBaseFilter, (void**)&pSG_Filter);
+                          IID_IBaseFilter, (void**)&m_previewFilter);
     if (FAILED(hr)) {
         qWarning() << "failed to create sample grabber";
-        return false;
+        goto failed;
     }
 
-    hr = pSG_Filter->QueryInterface(iID_ISampleGrabber, (void**)&pSG);
+    hr = m_previewFilter->QueryInterface(iID_ISampleGrabber, (void**)&m_previewSampleGrabber);
     if (FAILED(hr)) {
         qWarning() << "failed to get sample grabber";
-        return false;
+        goto failed;
     }
-    pSG->SetOneShot(FALSE);
-    pSG->SetBufferSamples(TRUE);
-    pSG->SetCallback(StillCapCB, 1);
-
-    updateProperties();
-    CoUninitialize();
-    return true;
-}
-
-void DSCameraSession::updateProperties()
-{
-    HRESULT hr;
-    AM_MEDIA_TYPE *pmt = NULL;
-    VIDEOINFOHEADER *pvi = NULL;
-    VIDEO_STREAM_CONFIG_CAPS scc;
-    IAMStreamConfig* pConfig = 0;
 
-    hr = pBuild->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,pCap,
-                               IID_IAMStreamConfig, (void**)&pConfig);
-    if (FAILED(hr)) {
-        qWarning()<<"failed to get config on capture device";
-        return;
+    {
+        SampleGrabberCallbackPrivate *callback = new SampleGrabberCallbackPrivate(this);
+        m_previewSampleGrabber->SetCallback(callback, 1);
+        m_previewSampleGrabber->SetOneShot(FALSE);
+        m_previewSampleGrabber->SetBufferSamples(FALSE);
+        callback->Release();
     }
 
-    int iCount;
-    int iSize;
-    hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
+    // Null renderer. Input connected to the sample grabber's output. Simply
+    // discard the samples it receives.
+    hr = CoCreateInstance(cLSID_NullRenderer, NULL, CLSCTX_INPROC,
+                          IID_IBaseFilter, (void**)&m_nullRendererFilter);
     if (FAILED(hr)) {
-        qWarning()<<"failed to get capabilities";
-        return;
+        qWarning() << "failed to create null renderer";
+        goto failed;
     }
 
-    QList<QSize> sizes;
-    QVideoFrame::PixelFormat f = QVideoFrame::Format_Invalid;
+    updateSourceCapabilities();
 
-    types.clear();
-    resolutions.clear();
-    IAMVideoControl *pVideoControl = 0;
-    hr = pBuild->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,pCap,
-                               IID_IAMVideoControl, (void**)&pVideoControl);
-    if (FAILED(hr)) {
-        qWarning() << "Failed to get the video control";
-    } else {
-        IPin *pPin = 0;
-        if (pCap) {
-            hr = getPin(pCap, PINDIR_OUTPUT, &pPin);
-            if (FAILED(hr)) {
-                qWarning() << "Failed to get the pin for the video control";
-            } else {
-                long supportedModes;
-                hr = pVideoControl->GetCaps(pPin, &supportedModes);
-                if (FAILED(hr)) {
-                    qWarning() << "Failed to get the supported modes of the video control";
-                } else if (supportedModes & VideoControlFlag_FlipHorizontal || supportedModes & VideoControlFlag_FlipVertical) {
-                    long mode;
-                    hr = pVideoControl->GetMode(pPin, &mode);
-                    if (FAILED(hr)) {
-                        qWarning() << "Failed to get the mode of the video control";
-                    } else {
-                        if (supportedModes & VideoControlFlag_FlipHorizontal)
-                            needsHorizontalMirroring = (mode & VideoControlFlag_FlipHorizontal);
-                        if (supportedModes & VideoControlFlag_FlipVertical)
-                            needsVerticalMirroring = (mode & VideoControlFlag_FlipVertical);
-                    }
-                }
-                pPin->Release();
-            }
-        }
-        pVideoControl->Release();
-    }
-    for (int iIndex = 0; iIndex < iCount; iIndex++) {
-        hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc));
-        if (hr == S_OK) {
-            pvi = (VIDEOINFOHEADER*)pmt->pbFormat;
-            if ((pmt->majortype == MEDIATYPE_Video) &&
-                    (pmt->formattype == FORMAT_VideoInfo)) {
-                // Add types
-                if (pmt->subtype == MEDIASUBTYPE_RGB24) {
-                    if (!types.contains(QVideoFrame::Format_RGB24)) {
-                        types.append(QVideoFrame::Format_RGB24);
-                        f = QVideoFrame::Format_RGB24;
-                    }
-                } else if (pmt->subtype == MEDIASUBTYPE_RGB32) {
-                    if (!types.contains(QVideoFrame::Format_RGB32)) {
-                        types.append(QVideoFrame::Format_RGB32);
-                        f = QVideoFrame::Format_RGB32;
-                    }
-                } else if (pmt->subtype == MEDIASUBTYPE_YUY2) {
-                    if (!types.contains(QVideoFrame::Format_YUYV)) {
-                        types.append(QVideoFrame::Format_YUYV);
-                        f = QVideoFrame::Format_YUYV;
-                    }
-                } else if (pmt->subtype == MEDIASUBTYPE_MJPG) {
-                } else if (pmt->subtype == MEDIASUBTYPE_I420) {
-                    if (!types.contains(QVideoFrame::Format_YUV420P)) {
-                        types.append(QVideoFrame::Format_YUV420P);
-                        f = QVideoFrame::Format_YUV420P;
-                    }
-                } else if (pmt->subtype == MEDIASUBTYPE_RGB555) {
-                    if (!types.contains(QVideoFrame::Format_RGB555)) {
-                        types.append(QVideoFrame::Format_RGB555);
-                        f = QVideoFrame::Format_RGB555;
-                    }
-                } else if (pmt->subtype == MEDIASUBTYPE_YVU9) {
-                } else if (pmt->subtype == MEDIASUBTYPE_UYVY) {
-                    if (!types.contains(QVideoFrame::Format_UYVY)) {
-                        types.append(QVideoFrame::Format_UYVY);
-                        f = QVideoFrame::Format_UYVY;
-                    }
-                } else {
-                    qWarning() << "UNKNOWN FORMAT: " << pmt->subtype.Data1;
-                }
-                // Add resolutions
-                QSize res(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight);
-                if (!resolutions.contains(f)) {
-                    sizes.clear();
-                    resolutions.insert(f,sizes);
-                }
-                resolutions[f].append(res);
-            }
-        }
-    }
-    pConfig->Release();
+    return true;
 
-    if (!types.isEmpty()) {
-        // Add RGB formats and let directshow do color space conversion if required.
-        if (!types.contains(QVideoFrame::Format_RGB24)) {
-            types.append(QVideoFrame::Format_RGB24);
-            resolutions.insert(QVideoFrame::Format_RGB24, resolutions[types.first()]);
-        }
-        if (!types.contains(QVideoFrame::Format_RGB32)) {
-            types.append(QVideoFrame::Format_RGB32);
-            resolutions.insert(QVideoFrame::Format_RGB32, resolutions[types.first()]);
-        }
-    }
+failed:
+    m_needsHorizontalMirroring = false;
+    m_sourcePreferredResolution = QSize();
+    _FreeMediaType(m_sourcePreferredFormat);
+    ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
+    SAFE_RELEASE(m_sourceFilter);
+    SAFE_RELEASE(m_previewSampleGrabber);
+    SAFE_RELEASE(m_previewFilter);
+    SAFE_RELEASE(m_nullRendererFilter);
+    SAFE_RELEASE(m_filterGraph);
+    SAFE_RELEASE(m_graphBuilder);
+
+    return false;
 }
 
-bool DSCameraSession::setProperties()
+bool DSCameraSession::configurePreviewFormat()
 {
-    CoInitialize(NULL);
+    // We only support RGB32, if the capture source doesn't support
+    // that format, the graph builder will automatically insert a
+    // converter.
 
-    HRESULT hr;
-    AM_MEDIA_TYPE am_media_type;
-    AM_MEDIA_TYPE *pmt = NULL;
-    VIDEOINFOHEADER *pvi = NULL;
-    VIDEO_STREAM_CONFIG_CAPS scc;
-
-    IAMStreamConfig* pConfig = 0;
-    hr = pBuild->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap,
-                               IID_IAMStreamConfig, (void**)&pConfig);
-    if(FAILED(hr)) {
-        qWarning()<<"failed to get config on capture device";
+    if (m_surface && !m_surface->supportedPixelFormats(QAbstractVideoBuffer::NoHandle)
+            .contains(QVideoFrame::Format_RGB32)) {
+        qWarning() << "Video surface needs to support RGB32 pixel format";
         return false;
     }
 
-    int iCount;
-    int iSize;
-    hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
-    if(FAILED(hr)) {
-        qWarning()<<"failed to get capabilities";
+    m_previewPixelFormat = QVideoFrame::Format_RGB32;
+    m_previewSize = m_sourcePreferredResolution;
+    m_previewSurfaceFormat = QVideoSurfaceFormat(m_previewSize,
+                                                 m_previewPixelFormat,
+                                                 QAbstractVideoBuffer::NoHandle);
+    m_previewSurfaceFormat.setScanLineDirection(QVideoSurfaceFormat::BottomToTop);
+
+    HRESULT hr;
+    IAMStreamConfig* pConfig = 0;
+    hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
+                                       &MEDIATYPE_Video,
+                                       m_sourceFilter,
+                                       IID_IAMStreamConfig, (void**)&pConfig);
+    if (FAILED(hr)) {
+        qWarning() << "Failed to get config for capture device";
         return false;
     }
 
-    bool setFormatOK = false;
-    for (int iIndex = 0; iIndex < iCount; iIndex++) {
-        hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc));
-        if (hr == S_OK) {
-            pvi = (VIDEOINFOHEADER*)pmt->pbFormat;
+    hr = pConfig->SetFormat(&m_sourcePreferredFormat);
 
-            if ((pmt->majortype == MEDIATYPE_Video) &&
-                (pmt->formattype == FORMAT_VideoInfo)) {
-                if ((actualFormat.frameWidth() == pvi->bmiHeader.biWidth) &&
-                    (actualFormat.frameHeight() == pvi->bmiHeader.biHeight)) {
-                    hr = pConfig->SetFormat(pmt);
-                    _FreeMediaType(*pmt);
-                    if(FAILED(hr)) {
-                        qWarning()<<"failed to set format:" << hr;
-                        qWarning()<<"but going to continue";
-                        continue; // We going to continue
-                    } else {
-                        setFormatOK = true;
-                        break;
-                    }
-                }
-            }
-        }
-    }
     pConfig->Release();
 
-    if (!setFormatOK) {
-            qWarning() << "unable to set any format for camera";
-            return false;
-    }
-
-    // Set Sample Grabber config to match capture
-    ZeroMemory(&am_media_type, sizeof(am_media_type));
-    am_media_type.majortype = MEDIATYPE_Video;
-
-    if (actualFormat.pixelFormat() == QVideoFrame::Format_RGB32)
-        am_media_type.subtype = MEDIASUBTYPE_RGB32;
-    else if (actualFormat.pixelFormat() == QVideoFrame::Format_RGB24)
-        am_media_type.subtype = MEDIASUBTYPE_RGB24;
-    else if (actualFormat.pixelFormat() == QVideoFrame::Format_YUYV)
-        am_media_type.subtype = MEDIASUBTYPE_YUY2;
-    else if (actualFormat.pixelFormat() == QVideoFrame::Format_YUV420P)
-        am_media_type.subtype = MEDIASUBTYPE_I420;
-    else if (actualFormat.pixelFormat() == QVideoFrame::Format_RGB555)
-        am_media_type.subtype = MEDIASUBTYPE_RGB555;
-    else if (actualFormat.pixelFormat() == QVideoFrame::Format_UYVY)
-        am_media_type.subtype = MEDIASUBTYPE_UYVY;
-    else {
-        qWarning()<<"unknown format? for SG";
+    if (FAILED(hr)) {
+        qWarning() << "Unable to set video format on capture device";
         return false;
     }
 
-    am_media_type.formattype = FORMAT_VideoInfo;
-    hr = pSG->SetMediaType(&am_media_type);
+    // Set sample grabber format (always RGB32)
+    AM_MEDIA_TYPE grabberFormat;
+    ZeroMemory(&grabberFormat, sizeof(grabberFormat));
+    grabberFormat.majortype = MEDIATYPE_Video;
+    grabberFormat.subtype = MEDIASUBTYPE_RGB32;
+    grabberFormat.formattype = FORMAT_VideoInfo;
+    hr = m_previewSampleGrabber->SetMediaType(&grabberFormat);
     if (FAILED(hr)) {
-        qWarning()<<"failed to set video format on grabber";
+        qWarning() << "Failed to set video format on grabber";
         return false;
     }
 
-    pSG->GetConnectedMediaType(&StillMediaType);
-
-    CoUninitialize();
-
     return true;
 }
 
-bool DSCameraSession::openStream()
+bool DSCameraSession::connectGraph()
 {
-    //Opens the stream for reading and allocates any necessary resources needed
-    //Return true if success, false otherwise
-
-    if (opened) {
-        return true;
-    }
-
-    if (!graph) {
-        graph = createFilterGraph();
-        if(!graph) {
-            qWarning()<<"failed to create filter graph in openStream";
-            return false;
-        }
+    HRESULT hr = m_filterGraph->AddFilter(m_sourceFilter, L"Capture Filter");
+    if (FAILED(hr)) {
+        qWarning() << "failed to add capture filter to graph";
+        return false;
     }
 
-    CoInitialize(NULL);
-
-    HRESULT hr;
-
-    hr = pGraph->AddFilter(pCap, L"Capture Filter");
+    hr = m_filterGraph->AddFilter(m_previewFilter, L"Sample Grabber");
     if (FAILED(hr)) {
-        qWarning()<<"failed to create capture filter";
+        qWarning() << "failed to add sample grabber to graph";
         return false;
     }
 
-    hr = pGraph->AddFilter(pSG_Filter, L"Sample Grabber");
+    hr = m_filterGraph->AddFilter(m_nullRendererFilter, L"Null Renderer");
     if (FAILED(hr)) {
-        qWarning()<<"failed to add sample grabber";
+        qWarning() << "failed to add null renderer to graph";
         return false;
     }
 
-    hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
-                              pCap, NULL, pSG_Filter);
+    hr = m_graphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
+                                      m_sourceFilter,
+                                      m_previewFilter,
+                                      m_nullRendererFilter);
     if (FAILED(hr)) {
-        qWarning() << "failed to renderstream" << hr;
+        qWarning() << "Graph failed to connect filters" << hr;
         return false;
     }
-    pSG->GetConnectedMediaType(&StillMediaType);
-    pSG_Filter->Release();
-
-    CoUninitialize();
 
     return true;
 }
 
-void DSCameraSession::closeStream()
+void DSCameraSession::disconnectGraph()
 {
-    // Closes the stream and internally frees any resources used
-    HRESULT hr;
-    IMediaControl* pControl = 0;
-
-    hr = pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
-    if (FAILED(hr)) {
-        qWarning()<<"failed to get stream control";
-        return;
+    IPin *pPin = 0;
+    HRESULT hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin);
+    if (SUCCEEDED(hr)) {
+        m_filterGraph->Disconnect(pPin);
+        pPin->Release();
+        pPin = NULL;
     }
 
-    hr = pControl->StopWhenReady();
-    if (FAILED(hr)) {
-        qWarning()<<"failed to stop";
-        pControl->Release();
-        return;
+    hr = getPin(m_previewFilter, PINDIR_INPUT, &pPin);
+    if (SUCCEEDED(hr)) {
+        m_filterGraph->Disconnect(pPin);
+        pPin->Release();
+        pPin = NULL;
     }
 
-    pControl->Release();
-
-    opened = false;
-    IPin *pPin = 0;
-
-    if (pCap)
-    {
-        hr = getPin(pCap, PINDIR_OUTPUT, &pPin);
-        if(FAILED(hr)) {
-            qWarning()<<"failed to disconnect capture filter";
-            return;
-        }
+    hr = getPin(m_previewFilter, PINDIR_OUTPUT, &pPin);
+    if (SUCCEEDED(hr)) {
+        m_filterGraph->Disconnect(pPin);
+        pPin->Release();
+        pPin = NULL;
     }
 
-    pGraph->Disconnect(pPin);
-    if (FAILED(hr)) {
-        qWarning()<<"failed to disconnect grabber filter";
-        return;
+    hr = getPin(m_nullRendererFilter, PINDIR_INPUT, &pPin);
+    if (SUCCEEDED(hr)) {
+        m_filterGraph->Disconnect(pPin);
+        pPin->Release();
+        pPin = NULL;
     }
 
-    hr = getPin(pSG_Filter,PINDIR_INPUT,&pPin);
-    pGraph->Disconnect(pPin);
-    pGraph->RemoveFilter(pSG_Filter);
-    pGraph->RemoveFilter(pCap);
-
-    SAFE_RELEASE(pCap);
-    SAFE_RELEASE(pSG_Filter);
-    SAFE_RELEASE(pGraph);
-    SAFE_RELEASE(pBuild);
-
-    graph = false;
+    m_filterGraph->RemoveFilter(m_nullRendererFilter);
+    m_filterGraph->RemoveFilter(m_previewFilter);
+    m_filterGraph->RemoveFilter(m_sourceFilter);
 }
 
-bool DSCameraSession::startStream()
+void DSCameraSession::updateSourceCapabilities()
 {
-    // Starts the stream, by emitting either QVideoPackets
-    // or QvideoFrames, depending on Format chosen
-    if (!graph)
-        graph = createFilterGraph();
-
-    if (!setProperties()) {
-        qWarning() << "Couldn't set properties (retrying)";
-        closeStream();
-        if (!openStream()) {
-            qWarning() << "Retry to open strean failed";
-            return false;
-        }
-    }
-
-    if (!opened) {
-        opened = openStream();
-        if (!opened) {
-            qWarning() << "failed to openStream()";
-            return false;
-        }
-    }
-
     HRESULT hr;
-    IMediaControl* pControl = 0;
-
-    hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
-    if (FAILED(hr)) {
-        qWarning() << "failed to get stream control";
-        return false;
-    }
+    AM_MEDIA_TYPE *pmt = NULL;
+    VIDEOINFOHEADER *pvi = NULL;
+    VIDEO_STREAM_CONFIG_CAPS scc;
+    IAMStreamConfig* pConfig = 0;
 
-    hr = pControl->Run();
-    pControl->Release();
+    m_needsHorizontalMirroring = false;
+    m_sourcePreferredResolution = QSize();
+    _FreeMediaType(m_sourcePreferredFormat);
+    ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
 
+    IAMVideoControl *pVideoControl = 0;
+    hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
+                                       m_sourceFilter,
+                                       IID_IAMVideoControl, (void**)&pVideoControl);
     if (FAILED(hr)) {
-        qWarning() << "failed to start";
-        return false;
+        qWarning() << "Failed to get the video control";
+    } else {
+        IPin *pPin = 0;
+        hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin);
+        if (FAILED(hr)) {
+            qWarning() << "Failed to get the pin for the video control";
+        } else {
+            long supportedModes;
+            hr = pVideoControl->GetCaps(pPin, &supportedModes);
+            if (FAILED(hr)) {
+                qWarning() << "Failed to get the supported modes of the video control";
+            } else if (supportedModes & VideoControlFlag_FlipHorizontal) {
+                long mode;
+                hr = pVideoControl->GetMode(pPin, &mode);
+                if (FAILED(hr))
+                    qWarning() << "Failed to get the mode of the video control";
+                else if (supportedModes & VideoControlFlag_FlipHorizontal)
+                    m_needsHorizontalMirroring = (mode & VideoControlFlag_FlipHorizontal);
+            }
+            pPin->Release();
+        }
+        pVideoControl->Release();
     }
-    active = true;
-    return true;
-}
 
-void DSCameraSession::stopStream()
-{
-    // Stops the stream from emitting packets
-    HRESULT hr;
-
-    IMediaControl* pControl = 0;
-    hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
+    hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
+                                       m_sourceFilter,
+                                       IID_IAMStreamConfig, (void**)&pConfig);
     if (FAILED(hr)) {
-        qWarning() << "failed to get stream control";
+        qWarning() << "failed to get config on capture device";
         return;
     }
 
-    hr = pControl->Stop();
-    pControl->Release();
+    int iCount;
+    int iSize;
+    hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
     if (FAILED(hr)) {
-        qWarning() << "failed to stop";
+        qWarning() << "failed to get capabilities";
         return;
     }
-    active = false;
 
-    if (opened) {
-        closeStream();
+    // Use preferred pixel format (first in the list)
+    // Then, pick the highest available resolution among the typical resolutions
+    // used for camera preview.
+    if (commonPreviewResolutions->isEmpty())
+        populateCommonResolutions();
+
+    long maxPixelCount = 0;
+    for (int iIndex = 0; iIndex < iCount; ++iIndex) {
+        hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc));
+        if (hr == S_OK) {
+            if ((pmt->majortype == MEDIATYPE_Video) &&
+                    (pmt->formattype == FORMAT_VideoInfo) &&
+                    (!m_sourcePreferredFormat.cbFormat ||
+                     m_sourcePreferredFormat.subtype == pmt->subtype)) {
+
+                pvi = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
+
+                QSize resolution(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight);
+                long pixelCount = resolution.width() * resolution.height();
+
+                if (!m_sourcePreferredFormat.cbFormat ||
+                        (pixelCount > maxPixelCount && commonPreviewResolutions->contains(resolution))) {
+                    _FreeMediaType(m_sourcePreferredFormat);
+                    _CopyMediaType(&m_sourcePreferredFormat, pmt);
+                    m_sourcePreferredResolution = resolution;
+                    maxPixelCount = pixelCount;
+                }
+            }
+            _FreeMediaType(*pmt);
+        }
     }
+
+    pConfig->Release();
+
+    if (!m_sourcePreferredResolution.isValid())
+       m_sourcePreferredResolution = QSize(640, 480);
 }
 
-void DSCameraSession::suspendStream()
+void DSCameraSession::populateCommonResolutions()
 {
-    // Pauses the stream
-    HRESULT hr;
+    commonPreviewResolutions->append(QSize(1920, 1080)); // 1080p
+    commonPreviewResolutions->append(QSize(1280, 720)); // 720p
+    commonPreviewResolutions->append(QSize(1024, 576)); // WSVGA
+    commonPreviewResolutions->append(QSize(720, 480)); // 480p (16:9)
+    commonPreviewResolutions->append(QSize(640, 480)); // 480p (4:3)
+    commonPreviewResolutions->append(QSize(352, 288)); // CIF
+    commonPreviewResolutions->append(QSize(320, 240)); // QVGA
+}
 
-    IMediaControl* pControl = 0;
-    hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
-    if (FAILED(hr)) {
-        qWarning() << "failed to get stream control";
-        return;
-    }
+HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
+{
+    *ppPin = 0;
+    IEnumPins *pEnum = 0;
+    IPin *pPin = 0;
 
-    hr = pControl->Pause();
-    pControl->Release();
+    HRESULT hr = pFilter->EnumPins(&pEnum);
     if (FAILED(hr)) {
-        qWarning() << "failed to pause";
-        return;
+        return hr;
     }
 
-    active = false;
-}
-
-void DSCameraSession::resumeStream()
-{
-    // Resumes a paused stream
-    startStream();
+    pEnum->Reset();
+    while (pEnum->Next(1, &pPin, NULL) == S_OK) {
+        PIN_DIRECTION ThisPinDir;
+        pPin->QueryDirection(&ThisPinDir);
+        if (ThisPinDir == PinDir) {
+            pEnum->Release();
+            *ppPin = pPin;
+            return S_OK;
+        }
+        pPin->Release();
+    }
+    pEnum->Release();
+    return E_FAIL;
 }
 
 QT_END_NAMESPACE
-
diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h
index 0fe12dec330974a505ea813863c992979cf012a5..2ba9c6109d1156bf36f49352ae18ad1473a48b91 100644
--- a/src/plugins/directshow/camera/dscamerasession.h
+++ b/src/plugins/directshow/camera/dscamerasession.h
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -51,6 +51,7 @@
 #include <QtMultimedia/qvideoframe.h>
 #include <QtMultimedia/qabstractvideosurface.h>
 #include <QtMultimedia/qvideosurfaceformat.h>
+#include <private/qmediastoragelocation_p.h>
 
 #include <tchar.h>
 #include <dshow.h>
@@ -75,18 +76,8 @@ struct ISampleGrabber;
 
 QT_BEGIN_NAMESPACE
 
-class DSVideoRenderer;
 class SampleGrabberCallbackPrivate;
 
-
-struct video_buffer {
-    unsigned char* buffer;
-    int            length;
-    qint64         time;
-};
-
-typedef QMap<unsigned int, QList<QSize> > FormatResolutionMap;
-
 class DSCameraSession : public QObject
 {
     Q_OBJECT
@@ -94,113 +85,82 @@ public:
     DSCameraSession(QObject *parent = 0);
     ~DSCameraSession();
 
-    bool deviceReady();
-    bool pictureInProgress();
-
-    // camera controls
-
-    int framerate() const;
-    void setFrameRate(int rate);
-    int brightness() const;
-    void setBrightness(int b);
-    int contrast() const;
-    void setContrast(int c);
-    int saturation() const;
-    void setSaturation(int s);
-    int hue() const;
-    void setHue(int h);
-    int sharpness() const;
-    void setSharpness(int s);
-    int zoom() const;
-    void setZoom(int z);
-    bool backlightCompensation() const;
-    void setBacklightCompensation(bool);
-    int whitelevel() const;
-    void setWhitelevel(int w);
-    int rotation() const;
-    void setRotation(int r);
-    bool flash() const;
-    void setFlash(bool f);
-    bool autofocus() const;
-    void setAutofocus(bool f);
-
-    QSize frameSize() const;
-    void setFrameSize(const QSize& s);
-    void setDevice(const QString &device);
-    QList<QVideoFrame::PixelFormat> supportedPixelFormats();
-    QVideoFrame::PixelFormat pixelFormat() const;
-    void setPixelFormat(QVideoFrame::PixelFormat fmt);
-    QList<QSize> supportedResolutions(QVideoFrame::PixelFormat format);
+    QCamera::Status status() const { return m_status; }
 
-    // media control
-
-    bool setOutputLocation(const QUrl &sink);
-    QUrl outputLocation() const;
-    qint64 position() const;
-    int state() const;
-    void record();
-    void pause();
-    void stop();
+    void setDevice(const QString &device);
 
-    void setSurface(QAbstractVideoSurface* surface);
+    bool load();
+    bool unload();
+    bool startPreview();
+    bool stopPreview();
 
+    bool isReadyForCapture();
     int captureImage(const QString &fileName);
 
-    AM_MEDIA_TYPE StillMediaType;
-    QList<video_buffer*> frames;
-    SampleGrabberCallbackPrivate* StillCapCB;
-
-    QMutex mutex;
+    void setSurface(QAbstractVideoSurface* surface);
 
 Q_SIGNALS:
-    void stateChanged(QCamera::State);
+    void statusChanged(QCamera::Status);
+    void imageExposed(int id);
     void imageCaptured(int id, const QImage &preview);
     void imageSaved(int id, const QString &fileName);
     void readyForCaptureChanged(bool);
+    void captureError(int id, int error, const QString &errorString);
 
 private Q_SLOTS:
-    void captureFrame();
+    void presentFrame();
+    void updateReadyForCapture();
 
 private:
-    QVideoSurfaceFormat actualFormat;
-    QList<QVideoFrame::PixelFormat> types;
-
-    QTime timeStamp;
-    bool graph;
-    bool active;
-    bool opened;
-    bool available;
-    QCamera::State m_state;
-    QByteArray m_device;
-    QUrl m_sink;
-    DSVideoRenderer*   m_output;
-    QAbstractVideoSurface* m_surface;
-    QVideoFrame::PixelFormat pixelF;
-    QSize m_windowSize;
-    FormatResolutionMap resolutions;
+    void setStatus(QCamera::Status status);
+    void populateCommonResolutions();
 
-    ICaptureGraphBuilder2* pBuild;
-    IGraphBuilder* pGraph;
-    IBaseFilter* pCap;
-    IBaseFilter* pSG_Filter;
-    ISampleGrabber *pSG;
+    void onFrameAvailable(const char *frameData, long len);
+    void saveCapturedImage(int id, const QImage &image, const QString &path);
 
-
-    QString m_snapshot;
-    int m_currentImageId;
-    bool needsHorizontalMirroring;
-    bool needsVerticalMirroring;
-protected:
-    HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin);
     bool createFilterGraph();
-    void updateProperties();
-    bool setProperties();
-    bool openStream();
-    void closeStream();
-    bool startStream();
-    void stopStream();
-    void suspendStream();
-    void resumeStream();
+    bool connectGraph();
+    void disconnectGraph();
+    void updateSourceCapabilities();
+    bool configurePreviewFormat();
+
+    QMutex m_presentMutex;
+    QMutex m_captureMutex;
+
+    // Capture Graph
+    ICaptureGraphBuilder2* m_graphBuilder;
+    IGraphBuilder* m_filterGraph;
+
+    // Source (camera)
+    QString m_sourceDeviceName;
+    IBaseFilter* m_sourceFilter;
+    AM_MEDIA_TYPE m_sourcePreferredFormat;
+    QSize m_sourcePreferredResolution;
+    bool m_needsHorizontalMirroring;
+
+    // Preview
+    IBaseFilter *m_previewFilter;
+    ISampleGrabber *m_previewSampleGrabber;
+    IBaseFilter *m_nullRendererFilter;
+    QVideoFrame m_currentFrame;
+    bool m_previewStarted;
+    QAbstractVideoSurface* m_surface;
+    QVideoSurfaceFormat m_previewSurfaceFormat;
+    QVideoFrame::PixelFormat m_previewPixelFormat;
+    QSize m_previewSize;
+
+    // Image capture
+    QString m_imageCaptureFileName;
+    QMediaStorageLocation m_fileNameGenerator;
+    bool m_readyForCapture;
+    int m_imageIdCounter;
+    int m_currentImageId;
+    QVideoFrame m_capturedFrame;
+
+    // Internal state
+    QCamera::Status m_status;
+
+    friend class SampleGrabberCallbackPrivate;
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
index e689b13d1a8ace5c7d813e39e27c57ae660ed9d0..329f6d63ac9ef1227a3d43561254819e5e272061 100644
--- a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
+++ b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
@@ -46,15 +46,19 @@
 QT_BEGIN_NAMESPACE
 
 DSImageCaptureControl::DSImageCaptureControl(DSCameraSession *session)
-    :QCameraImageCaptureControl(session), m_session(session), m_ready(false)
+    : QCameraImageCaptureControl(session)
+    , m_session(session)
 {
-    connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState()));
+    connect(m_session, SIGNAL(imageExposed(int)),
+            this, SIGNAL(imageExposed(int)));
     connect(m_session, SIGNAL(imageCaptured(int,QImage)),
         this, SIGNAL(imageCaptured(int,QImage)));
     connect(m_session, SIGNAL(imageSaved(int,QString)),
             this, SIGNAL(imageSaved(int,QString)));
     connect(m_session, SIGNAL(readyForCaptureChanged(bool)),
             this, SIGNAL(readyForCaptureChanged(bool)));
+    connect(m_session, SIGNAL(captureError(int,int,QString)),
+            this, SIGNAL(error(int,int,QString)));
 }
 
 DSImageCaptureControl::~DSImageCaptureControl()
@@ -63,7 +67,7 @@ DSImageCaptureControl::~DSImageCaptureControl()
 
 bool DSImageCaptureControl::isReadyForCapture() const
 {
-    return m_ready;
+    return m_session->isReadyForCapture();
 }
 
 int DSImageCaptureControl::capture(const QString &fileName)
@@ -71,12 +75,15 @@ int DSImageCaptureControl::capture(const QString &fileName)
    return m_session->captureImage(fileName);
 }
 
-void DSImageCaptureControl::updateState()
+QCameraImageCapture::DriveMode DSImageCaptureControl::driveMode() const
 {
-    bool ready = (m_session->state() == QCamera::ActiveState) &&
-                 !m_session->pictureInProgress();
-    if(m_ready != ready)
-        emit readyForCaptureChanged(m_ready = ready);
+    return QCameraImageCapture::SingleImageCapture;
+}
+
+void DSImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
+{
+    if (mode != QCameraImageCapture::SingleImageCapture)
+        qWarning("Drive mode not supported.");
 }
 
 QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.h b/src/plugins/directshow/camera/dsimagecapturecontrol.h
index e39539191e2b657a0b54e3ff7c1324e708bfadf1..af87659438cdf483093f85fa9cb7abacb1b8069d 100644
--- a/src/plugins/directshow/camera/dsimagecapturecontrol.h
+++ b/src/plugins/directshow/camera/dsimagecapturecontrol.h
@@ -52,23 +52,18 @@ class DSImageCaptureControl : public QCameraImageCaptureControl
     Q_OBJECT
 public:
     DSImageCaptureControl(DSCameraSession *session);
-    virtual ~DSImageCaptureControl();
+    ~DSImageCaptureControl();
 
     bool isReadyForCapture() const;
     int capture(const QString &fileName);
 
-    virtual QCameraImageCapture::DriveMode driveMode() const { return QCameraImageCapture::SingleImageCapture; }
-    virtual void setDriveMode(QCameraImageCapture::DriveMode mode) { Q_UNUSED(mode) }
-
-    virtual void cancelCapture() {}
-
-private slots:
-    void updateState();
+    QCameraImageCapture::DriveMode driveMode() const;
+    void setDriveMode(QCameraImageCapture::DriveMode mode);
 
+    void cancelCapture() {}
 
 private:
     DSCameraSession *m_session;
-    bool m_ready;
 };
 
 QT_END_NAMESPACE
diff --git a/src/plugins/directshow/camera/dsvideodevicecontrol.cpp b/src/plugins/directshow/camera/dsvideodevicecontrol.cpp
index ead1060d45e448428932ae1faaa7a365636e4353..28d4956ddc34a3574cdf8b51f2bc220373b096c1 100644
--- a/src/plugins/directshow/camera/dsvideodevicecontrol.cpp
+++ b/src/plugins/directshow/camera/dsvideodevicecontrol.cpp
@@ -102,7 +102,6 @@ void DSVideoDeviceControl::enumerateDevices(QList<QByteArray> *devices, QStringL
     devices->clear();
     descriptions->clear();
 
-    CoInitialize(NULL);
     ICreateDevEnum* pDevEnum = NULL;
     IEnumMoniker* pEnum = NULL;
     // Create the System device enumerator
@@ -148,7 +147,6 @@ void DSVideoDeviceControl::enumerateDevices(QList<QByteArray> *devices, QStringL
         }
         pDevEnum->Release();
     }
-    CoUninitialize();
 }
 
 void DSVideoDeviceControl::setSelectedDevice(int index)
diff --git a/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp b/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp
deleted file mode 100644
index 35b7955461a872c5550bbac5751698d39cbfc96b..0000000000000000000000000000000000000000
--- a/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp
+++ /dev/null
@@ -1,253 +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/qcoreevent.h>
-#include <QtCore/qtimer.h>
-
-#include "dsvideowidgetcontrol.h"
-#include "dscamerasession.h"
-
-QT_BEGIN_NAMESPACE
-
-DSVideoWidgetSurface::DSVideoWidgetSurface(QLabel *pWidget, QObject *parent)
-    : QAbstractVideoSurface(parent)
-{
-    widget = pWidget;
-    myPixmap = 0;
-}
-
-QList<QVideoFrame::PixelFormat> DSVideoWidgetSurface::supportedPixelFormats(
-        QAbstractVideoBuffer::HandleType handleType) const
-{
-    if (handleType == QAbstractVideoBuffer::NoHandle) {
-        return QList<QVideoFrame::PixelFormat>()
-                << QVideoFrame::Format_RGB32
-                << QVideoFrame::Format_RGB24;
-    } else {
-        return QList<QVideoFrame::PixelFormat>();
-    }
-}
-
-
-bool DSVideoWidgetSurface::present(const QVideoFrame &frame)
-{
-    QVideoFrame myFrame = frame;
-    myFrame.map(QAbstractVideoBuffer::ReadOnly);
-    QImage image(
-                     frame.bits(),
-                     frame.width(),
-                     frame.height(),
-                     frame.bytesPerLine(),
-                     imageFormat);
-    if (image.isNull())
-    {
-        // Try to adapt
-        QImage image2(
-                frame.bits(),
-                frame.width(),
-                frame.height(),
-                frame.bytesPerLine(),
-                QImage::Format_RGB888);
-        image = image2;
-    }
-    myFrame.unmap();
-    delete myPixmap;
-    myPixmap = new QPixmap(QPixmap::fromImage(image).scaled(widget->size()));
-    widget->setPixmap(*myPixmap);
-    widget->repaint();
-    return true;
-}
-
-void DSVideoWidgetSurface::setImageFormat(QImage::Format fmt)
-{
-    imageFormat = fmt;
-}
-
-void DSVideoWidgetSurface::updateVideoRect()
-{
-}
-
-void DSVideoWidgetSurface::paint(QPainter *painter)
-{
-    Q_UNUSED(painter)
-}
-
-
-DSVideoWidgetControl::DSVideoWidgetControl(DSCameraSession* session, QObject *parent) :
-    QVideoWidgetControl(parent),
-    m_session(session),
-    m_widget(new QLabel()),
-    m_fullScreen(false)
-{
-    m_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
-    m_widget->setAlignment(Qt::AlignCenter);
-    m_widget->setAttribute(Qt::WA_NoSystemBackground, true);
-
-    surface = new DSVideoWidgetSurface(m_widget);
-
-    QPalette palette;
-    palette.setColor(QPalette::Background, Qt::black);
-    m_widget->setPalette(palette);
-    m_widget->setAutoFillBackground( true );
-
-    // Request QEvents
-    m_widget->installEventFilter(this);
-    m_windowId = m_widget->effectiveWinId();
-
-    surface->setImageFormat(QImage::Format_RGB888);
-    session->setSurface(surface);
-}
-
-DSVideoWidgetControl::~DSVideoWidgetControl()
-{
-    delete m_widget;
-}
-
-bool DSVideoWidgetControl::eventFilter(QObject *object, QEvent *e)
-{
-    if (object == m_widget) {
-        switch (e->type()) {
-            case QEvent::ParentChange:
-            case QEvent::WinIdChange:
-            case QEvent::Show:
-                m_windowId = m_widget->effectiveWinId();
-                emit widgetUpdated();
-                break;
-            case QEvent::Resize:
-                emit widgetResized(m_widget->size());
-                break;
-            case QEvent::PolishRequest:
-                m_widget->ensurePolished();
-                break;
-
-            default:
-                // Do nothing
-                break;
-        }
-    }
-    return false;
-}
-
-QWidget *DSVideoWidgetControl::videoWidget()
-{
-    return m_widget;
-}
-
-Qt::AspectRatioMode DSVideoWidgetControl::aspectRatioMode() const
-{
-    return m_aspectRatioMode;
-}
-
-void DSVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode ratio)
-{
-    if (m_aspectRatioMode==ratio) {
-        return;
-    }
-    m_aspectRatioMode = ratio;
-
-    if (m_aspectRatioMode == Qt::KeepAspectRatio)
-        m_widget->setScaledContents(false);
-    else {
-        m_widget->setScaledContents(true);
-    }
-}
-
-bool DSVideoWidgetControl::isFullScreen() const
-{
-    return m_fullScreen;
-}
-
-void DSVideoWidgetControl::setFullScreen(bool fullScreen)
-{
-    if (m_widget && !fullScreen && m_fullScreen) {
-        m_widget->showNormal();
-        m_fullScreen = false;
-    } else if (m_widget && fullScreen) {
-        m_widget->showFullScreen();
-        m_fullScreen = true;
-    }
-
-    emit fullScreenChanged(fullScreen);
-}
-
-int DSVideoWidgetControl::brightness() const
-{
-    return 0;
-}
-
-void DSVideoWidgetControl::setBrightness(int brightness)
-{
-    Q_UNUSED(brightness);
-}
-
-int DSVideoWidgetControl::contrast() const
-{
-    return 0;
-}
-
-void DSVideoWidgetControl::setContrast(int contrast)
-{
-    Q_UNUSED(contrast);
-}
-
-int DSVideoWidgetControl::hue() const
-{
-    return 0;
-}
-
-void DSVideoWidgetControl::setHue(int hue)
-{
-    Q_UNUSED(hue);
-}
-
-int DSVideoWidgetControl::saturation() const
-{
-    return 0;
-}
-
-void DSVideoWidgetControl::setSaturation(int saturation)
-{
-    Q_UNUSED(saturation);
-}
-
-QT_END_NAMESPACE
-
-// End of file
diff --git a/src/plugins/directshow/camera/dsvideowidgetcontrol.h b/src/plugins/directshow/camera/dsvideowidgetcontrol.h
deleted file mode 100644
index 2a8775aee201852b1d0de32c03839f6884f9e1f8..0000000000000000000000000000000000000000
--- a/src/plugins/directshow/camera/dsvideowidgetcontrol.h
+++ /dev/null
@@ -1,150 +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$
-**
-****************************************************************************/
-
-#ifndef DSVIDEOWIDGETCONTROL_H
-#define DSVIDEOWIDGETCONTROL_H
-
-#include <QtCore/qobject.h>
-#include <QtWidgets>
-#include <QtMultimedia/qvideoframe.h>
-#include <QtMultimedia/qabstractvideosurface.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-
-#include <qvideowidgetcontrol.h>
-#include "dscameracontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-class DSVideoWidgetSurface : public QAbstractVideoSurface
-{
-     Q_OBJECT
- public:
-     DSVideoWidgetSurface(QLabel *pWidget, QObject *parent = 0);
-
-     QList<QVideoFrame::PixelFormat> supportedPixelFormats(
-             QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;
-
-     bool present(const QVideoFrame &frame);
-
-     QRect videoRect() const { return targetRect; }
-     void updateVideoRect();
-
-     void paint(QPainter *painter);
-     void setImageFormat(QImage::Format fmt);
-
- private:
-     QLabel *widget;
-     QImage::Format imageFormat;
-     QRect targetRect;
-     QSize imageSize;
-     QRect sourceRect;
-     QPixmap* myPixmap;
- };
-
-class DSVideoWidgetControl : public QVideoWidgetControl
-{
-    Q_OBJECT
-
-    DSVideoWidgetSurface* surface;
-public: // Constructor & Destructor
-
-    DSVideoWidgetControl(DSCameraSession* session, QObject *parent = 0);
-    virtual ~DSVideoWidgetControl();
-
-public: // QVideoWidgetControl
-
-    QWidget *videoWidget();
-
-    // Aspect Ratio
-    Qt::AspectRatioMode aspectRatioMode() const;
-    void setAspectRatioMode(Qt::AspectRatioMode ratio);
-
-    // Full Screen
-    bool isFullScreen() const;
-    void setFullScreen(bool fullScreen);
-
-    // Brightness
-    int brightness() const;
-    void setBrightness(int brightness);
-
-    // Contrast
-    int contrast() const;
-    void setContrast(int contrast);
-
-    // Hue
-    int hue() const;
-    void setHue(int hue);
-
-    // Saturation
-    int saturation() const;
-    void setSaturation(int saturation);
-
-public: // Internal
-
-    bool eventFilter(QObject *object, QEvent *event);
-
-/*
-Q_SIGNALS: // QVideoWidgetControl
-
-    void fullScreenChanged(bool fullScreen);
-    void brightnessChanged(int brightness);
-    void contrastChanged(int contrast);
-    void hueChanged(int hue);
-    void saturationChanged(int saturation);
-*/
-
-Q_SIGNALS: // Internal Signals
-
-    void widgetResized(QSize size);
-    void widgetUpdated();
-
-private: // Data
-
-    DSCameraSession*    m_session;
-    QLabel              *m_widget;
-    WId                 m_windowId;
-    Qt::AspectRatioMode m_aspectRatioMode;
-    bool                m_fullScreen;
-};
-
-QT_END_NAMESPACE
-
-#endif // DSVideoWidgetControl_H
diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro
index 4dad97113241445dbc693487e6048a9e0b409995..982741a1122272db52d3b5f8991a23d6b747ff3d 100644
--- a/src/plugins/directshow/directshow.pro
+++ b/src/plugins/directshow/directshow.pro
@@ -4,7 +4,7 @@ PLUGIN_TYPE=mediaservice
 PLUGIN_CLASS_NAME = DSServicePlugin
 load(qt_plugin)
 
-QT += multimedia
+QT += multimedia-private
 
 HEADERS += dsserviceplugin.h
 SOURCES += dsserviceplugin.cpp
diff --git a/src/plugins/directshow/dsserviceplugin.cpp b/src/plugins/directshow/dsserviceplugin.cpp
index d262febddff19cb4912c36ded55d8034b60443fa..954fab22c9c5b38d062a27482092a806770c2da4 100644
--- a/src/plugins/directshow/dsserviceplugin.cpp
+++ b/src/plugins/directshow/dsserviceplugin.cpp
@@ -79,15 +79,32 @@ extern const CLSID CLSID_VideoInputDeviceCategory;
 
 QT_USE_NAMESPACE
 
+static int g_refCount = 0;
+void addRefCount()
+{
+    if (++g_refCount == 1)
+        CoInitialize(NULL);
+}
+
+void releaseRefCount()
+{
+    if (--g_refCount == 0)
+        CoUninitialize();
+}
+
 QMediaService* DSServicePlugin::create(QString const& key)
 {
 #ifdef QMEDIA_DIRECTSHOW_CAMERA
-    if (key == QLatin1String(Q_MEDIASERVICE_CAMERA))
+    if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
+        addRefCount();
         return new DSCameraService;
+    }
 #endif
 #ifdef QMEDIA_DIRECTSHOW_PLAYER
-    if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
+    if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) {
+        addRefCount();
         return new DirectShowPlayerService;
+    }
 #endif
 
     return 0;
@@ -96,6 +113,7 @@ QMediaService* DSServicePlugin::create(QString const& key)
 void DSServicePlugin::release(QMediaService *service)
 {
     delete service;
+    releaseRefCount();
 }
 
 QMediaServiceProviderHint::Features DSServicePlugin::supportedFeatures(
@@ -154,6 +172,8 @@ QString DSServicePlugin::deviceDescription(const QByteArray &service, const QByt
 
 void DSServicePlugin::updateDevices() const
 {
+    addRefCount();
+
     m_defaultCameraDevice.clear();
     DSVideoDeviceControl::enumerateDevices(&m_cameraDevices, &m_cameraDescriptions);
 
@@ -162,6 +182,8 @@ void DSServicePlugin::updateDevices() const
     } else {
         m_defaultCameraDevice = m_cameraDevices.first();
     }
+
+    releaseRefCount();
 }
 #endif
 
diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp
index 0afeacb8555ce06a0d7bcd6cf0df92c648c98a74..1ac7669cd66dd90a53327a5840432a2b8fd4a7a6 100644
--- a/src/plugins/directshow/player/directshowplayerservice.cpp
+++ b/src/plugins/directshow/player/directshowplayerservice.cpp
@@ -111,7 +111,6 @@ DirectShowPlayerService::DirectShowPlayerService(QObject *parent)
     , m_seekable(false)
     , m_atEnd(false)
 {
-    CoInitialize(NULL);
     m_playerControl = new DirectShowPlayerControl(this);
     m_metaDataControl = new DirectShowMetaDataControl(this);
     m_audioEndpointControl = new DirectShowAudioEndpointControl(this);
@@ -153,7 +152,6 @@ DirectShowPlayerService::~DirectShowPlayerService()
 #endif
 
     ::CloseHandle(m_taskHandle);
-    CoUninitialize();
 }
 
 QMediaControl *DirectShowPlayerService::requestControl(const char *name)
diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/src/plugins/pulseaudio/qaudioinput_pulse.cpp
index 83075beda266287e79d0563ed2771b9b080304ee..89dc0861231e1c79061987d6e2ed4bc2a4ae8ae2 100644
--- a/src/plugins/pulseaudio/qaudioinput_pulse.cpp
+++ b/src/plugins/pulseaudio/qaudioinput_pulse.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -54,10 +54,6 @@ QT_BEGIN_NAMESPACE
 
 const int PeriodTimeMs = 50;
 
-// Map from void* (for userdata) to QPulseAudioInput instance
-// protected by pulse mainloop lock
-QMap<void *, QPulseAudioInput*> QPulseAudioInput::s_inputsMap;
-
 static void inputStreamReadCallback(pa_stream *stream, size_t length, void *userdata)
 {
     Q_UNUSED(userdata);
@@ -136,8 +132,8 @@ void QPulseAudioInput::sourceInfoCallback(pa_context *context, const pa_source_i
     Q_UNUSED(eol);
 
     Q_ASSERT(userdata);
-    QPulseAudioInput *that = QPulseAudioInput::s_inputsMap.value(userdata);
-    if (that && i) {
+    if (i) {
+        QPulseAudioInput *that = reinterpret_cast<QPulseAudioInput*>(userdata);
         that->m_volume = pa_sw_volume_to_linear(pa_cvolume_avg(&i->volume));
     }
 }
@@ -149,12 +145,11 @@ void QPulseAudioInput::inputVolumeCallback(pa_context *context, int success, voi
     if (!success)
         qWarning() << "QAudioInput: failed to set input volume";
 
-    QPulseAudioInput *that = QPulseAudioInput::s_inputsMap.value(userdata);
+    QPulseAudioInput *that = reinterpret_cast<QPulseAudioInput*>(userdata);
 
     // Regardless of success or failure, we update the volume property
-    if (that && that->m_stream) {
+    if (that->m_stream)
         pa_context_get_source_info_by_index(context, pa_stream_get_device_index(that->m_stream), sourceInfoCallback, userdata);
-    }
 }
 
 QPulseAudioInput::QPulseAudioInput(const QByteArray &device)
@@ -175,31 +170,39 @@ QPulseAudioInput::QPulseAudioInput(const QByteArray &device)
 {
     m_timer = new QTimer(this);
     connect(m_timer, SIGNAL(timeout()), SLOT(userFeed()));
-
-    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
-    s_inputsMap.insert(this, this);
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
 }
 
 QPulseAudioInput::~QPulseAudioInput()
 {
-    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
-    s_inputsMap.remove(this);
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
-
     close();
     disconnect(m_timer, SIGNAL(timeout()));
     QCoreApplication::processEvents();
     delete m_timer;
 }
 
+void QPulseAudioInput::setError(QAudio::Error error)
+{
+    if (m_errorState == error)
+        return;
+
+    m_errorState = error;
+    emit errorChanged(error);
+}
+
 QAudio::Error QPulseAudioInput::error() const
 {
     return m_errorState;
 }
 
+void QPulseAudioInput::setState(QAudio::State state)
+{
+    if (m_deviceState == state)
+        return;
+
+    m_deviceState = state;
+    emit stateChanged(state);
+}
+
 QAudio::State QPulseAudioInput::state() const
 {
     return m_deviceState;
@@ -218,41 +221,45 @@ QAudioFormat QPulseAudioInput::format() const
 
 void QPulseAudioInput::start(QIODevice *device)
 {
-    if (m_deviceState != QAudio::StoppedState)
-        close();
+    setState(QAudio::StoppedState);
+    setError(QAudio::NoError);
 
-    if (!m_pullMode && m_audioSource)
+    if (!m_pullMode && m_audioSource) {
         delete m_audioSource;
+        m_audioSource = 0;
+    }
 
-    m_pullMode = true;
-    m_audioSource = device;
-
-    m_deviceState = QAudio::ActiveState;
+    close();
 
     if (!open())
         return;
 
-    emit stateChanged(m_deviceState);
+    m_pullMode = true;
+    m_audioSource = device;
+
+    setState(QAudio::ActiveState);
 }
 
 QIODevice *QPulseAudioInput::start()
 {
-    if (m_deviceState != QAudio::StoppedState)
-        close();
+    setState(QAudio::StoppedState);
+    setError(QAudio::NoError);
 
-    if (!m_pullMode && m_audioSource)
+    if (!m_pullMode && m_audioSource) {
         delete m_audioSource;
+        m_audioSource = 0;
+    }
+
+    close();
+
+    if (!open())
+        return Q_NULLPTR;
 
     m_pullMode = false;
     m_audioSource = new InputPrivate(this);
     m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
 
-    m_deviceState = QAudio::IdleState;
-
-    if (!open())
-        return 0;
-
-    emit stateChanged(m_deviceState);
+    setState(QAudio::IdleState);
 
     return m_audioSource;
 }
@@ -262,40 +269,43 @@ void QPulseAudioInput::stop()
     if (m_deviceState == QAudio::StoppedState)
         return;
 
-    m_errorState = QAudio::NoError;
-    m_deviceState = QAudio::StoppedState;
-
     close();
-    emit stateChanged(m_deviceState);
+
+    setError(QAudio::NoError);
+    setState(QAudio::StoppedState);
 }
 
 bool QPulseAudioInput::open()
 {
     if (m_opened)
-        return false;
+        return true;
 
-#ifdef DEBUG_PULSE
-//    QTime now(QTime::currentTime());
-//    qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
-    m_clockStamp.restart();
-    m_timeStamp.restart();
-    m_elapsedTimeOffset = 0;
+    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
 
-    if (m_streamName.isNull())
-        m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
+    if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
+        setError(QAudio::FatalError);
+        setState(QAudio::StoppedState);
+        return false;
+    }
 
     pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
 
     if (!pa_sample_spec_valid(&spec)) {
-        m_errorState = QAudio::OpenError;
-        m_deviceState = QAudio::StoppedState;
-        emit stateChanged(m_deviceState);
+        setError(QAudio::OpenError);
+        setState(QAudio::StoppedState);
         return false;
     }
 
     m_spec = spec;
 
+#ifdef DEBUG_PULSE
+//    QTime now(QTime::currentTime());
+//    qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
+#endif
+
+    if (m_streamName.isNull())
+        m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
+
 #ifdef DEBUG_PULSE
         qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
         qDebug() << "Rate: " << spec.rate;
@@ -303,15 +313,13 @@ bool QPulseAudioInput::open()
         qDebug() << "Frame size: " << pa_frame_size(&spec);
 #endif
 
-    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
+    pulseEngine->lock();
     pa_channel_map channel_map;
 
     pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT);
 
-    if (!pa_channel_map_compatible(&channel_map, &spec)) {
+    if (!pa_channel_map_compatible(&channel_map, &spec))
         qWarning() << "Channel map doesn't match sample specification!";
-    }
 
     m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map);
 
@@ -338,13 +346,16 @@ bool QPulseAudioInput::open()
 
     if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) {
         qWarning() << "pa_stream_connect_record() failed!";
-        m_errorState = QAudio::FatalError;
+        pa_stream_unref(m_stream);
+        m_stream = 0;
+        pulseEngine->unlock();
+        setError(QAudio::OpenError);
+        setState(QAudio::StoppedState);
         return false;
     }
 
-    while (pa_stream_get_state(m_stream) != PA_STREAM_READY) {
+    while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
         pa_threaded_mainloop_wait(pulseEngine->mainloop());
-    }
 
     const pa_buffer_attr *actualBufferAttr = pa_stream_get_buffer_attr(m_stream);
     m_periodSize = actualBufferAttr->fragsize;
@@ -354,12 +365,16 @@ bool QPulseAudioInput::open()
 
     setPulseVolume();
 
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+    pulseEngine->unlock();
+
+    connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed);
 
     m_opened = true;
     m_timer->start(m_periodTime);
-    m_errorState = QAudio::NoError;
 
+    m_clockStamp.restart();
+    m_timeStamp.restart();
+    m_elapsedTimeOffset = 0;
     m_totalTimeValue = 0;
 
     return true;
@@ -367,21 +382,30 @@ bool QPulseAudioInput::open()
 
 void QPulseAudioInput::close()
 {
+    if (!m_opened)
+        return;
+
     m_timer->stop();
 
+    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+
     if (m_stream) {
-        QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
 
+        pa_stream_set_state_callback(m_stream, 0, 0);
         pa_stream_set_read_callback(m_stream, 0, 0);
+        pa_stream_set_underflow_callback(m_stream, 0, 0);
+        pa_stream_set_overflow_callback(m_stream, 0, 0);
 
         pa_stream_disconnect(m_stream);
         pa_stream_unref(m_stream);
         m_stream = 0;
 
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
     }
 
+    disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioInput::onPulseContextFailed);
+
     if (!m_pullMode && m_audioSource) {
         delete m_audioSource;
         m_audioSource = 0;
@@ -393,6 +417,7 @@ void QPulseAudioInput::close()
 void QPulseAudioInput::setPulseVolume()
 {
     QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+    Q_ASSERT(pulseEngine->context() != 0);
 
     pa_cvolume cvolume;
 
@@ -434,11 +459,8 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
 {
     m_bytesAvailable = checkBytesReady();
 
-    if (m_deviceState != QAudio::ActiveState) {
-        m_errorState = QAudio::NoError;
-        m_deviceState = QAudio::ActiveState;
-        emit stateChanged(m_deviceState);
-    }
+    setError(QAudio::NoError);
+    setState(QAudio::ActiveState);
 
     int readBytes = 0;
 
@@ -463,7 +485,8 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
 #endif
 
         QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
+
         const void *audioBuffer;
 
         // Second and third parameters (audioBuffer and length) to pa_stream_peek are output parameters,
@@ -471,7 +494,7 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
         // and the length is set to the length of this data.
         if (pa_stream_peek(m_stream, &audioBuffer, &readLength) < 0) {
             qWarning() << QString("pa_stream_peek() failed: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream))));
-            pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+            pulseEngine->unlock();
             return 0;
         }
 
@@ -480,11 +503,10 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
             actualLength = m_audioSource->write(static_cast<const char *>(audioBuffer), readLength);
 
             if (actualLength < qint64(readLength)) {
-                pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+                pulseEngine->unlock();
 
-                m_errorState = QAudio::UnderrunError;
-                m_deviceState = QAudio::IdleState;
-                emit stateChanged(m_deviceState);
+                setError(QAudio::UnderrunError);
+                setState(QAudio::IdleState);
 
                 return actualLength;
             }
@@ -509,7 +531,7 @@ qint64 QPulseAudioInput::read(char *data, qint64 len)
         readBytes += actualLength;
 
         pa_stream_drop(m_stream);
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
 
         if (!m_pullMode && readBytes >= len)
             break;
@@ -534,22 +556,18 @@ void QPulseAudioInput::resume()
         QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
         pa_operation *operation;
 
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
 
         operation = pa_stream_cork(m_stream, 0, inputStreamSuccessCallback, 0);
-
-        while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
-            pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
+        pulseEngine->wait(operation);
         pa_operation_unref(operation);
 
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
 
         m_timer->start(m_periodTime);
 
-        m_deviceState = QAudio::ActiveState;
-
-        emit stateChanged(m_deviceState);
+        setState(QAudio::ActiveState);
+        setError(QAudio::NoError);
     }
 }
 
@@ -557,23 +575,23 @@ void QPulseAudioInput::setVolume(qreal vol)
 {
     if (vol >= 0.0 && vol <= 1.0) {
         QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
         if (!qFuzzyCompare(m_volume, vol)) {
             m_volume = vol;
             if (m_opened) {
                 setPulseVolume();
             }
         }
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
     }
 }
 
 qreal QPulseAudioInput::volume() const
 {
     QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
+    pulseEngine->lock();
     qreal vol = m_volume;
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+    pulseEngine->unlock();
     return vol;
 }
 
@@ -614,23 +632,21 @@ qint64 QPulseAudioInput::processedUSecs() const
 void QPulseAudioInput::suspend()
 {
     if (m_deviceState == QAudio::ActiveState) {
+        setError(QAudio::NoError);
+        setState(QAudio::SuspendedState);
+
         m_timer->stop();
-        m_deviceState = QAudio::SuspendedState;
-        emit stateChanged(m_deviceState);
 
         QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
         pa_operation *operation;
 
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
 
         operation = pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, 0);
-
-        while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
-            pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
+        pulseEngine->wait(operation);
         pa_operation_unref(operation);
 
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
     }
 }
 
@@ -685,6 +701,14 @@ void QPulseAudioInput::reset()
     m_bytesAvailable = 0;
 }
 
+void QPulseAudioInput::onPulseContextFailed()
+{
+    close();
+
+    setError(QAudio::FatalError);
+    setState(QAudio::StoppedState);
+}
+
 InputPrivate::InputPrivate(QPulseAudioInput *audio)
 {
     m_audioDevice = qobject_cast<QPulseAudioInput*>(audio);
diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.h b/src/plugins/pulseaudio/qaudioinput_pulse.h
index bb72628f895d7589d6f17be1a5ba3be6ef2b6efa..e2c46c8e618476169d4185c24f75242296d2bfce 100644
--- a/src/plugins/pulseaudio/qaudioinput_pulse.h
+++ b/src/plugins/pulseaudio/qaudioinput_pulse.h
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -112,8 +112,12 @@ public:
 private slots:
     void userFeed();
     bool deviceReady();
+    void onPulseContextFailed();
 
 private:
+    void setState(QAudio::State state);
+    void setError(QAudio::Error error);
+
     int checkBytesReady();
     bool open();
     void close();
diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp
index 2b482ba405f97316e5b7712520f0c64ce82aa045..64a080663a7cfbabe0c2ce4cb00eb369fb29f790 100644
--- a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp
+++ b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -170,11 +170,29 @@ QPulseAudioOutput::~QPulseAudioOutput()
     QCoreApplication::processEvents();
 }
 
+void QPulseAudioOutput::setError(QAudio::Error error)
+{
+    if (m_errorState == error)
+        return;
+
+    m_errorState = error;
+    emit errorChanged(error);
+}
+
 QAudio::Error QPulseAudioOutput::error() const
 {
     return m_errorState;
 }
 
+void QPulseAudioOutput::setState(QAudio::State state)
+{
+    if (m_deviceState == state)
+        return;
+
+    m_deviceState = state;
+    emit stateChanged(state);
+}
+
 QAudio::State QPulseAudioOutput::state() const
 {
     return m_deviceState;
@@ -183,19 +201,15 @@ QAudio::State QPulseAudioOutput::state() const
 void QPulseAudioOutput::streamUnderflowCallback()
 {
     if (m_deviceState != QAudio::IdleState && !m_resuming) {
-        m_errorState = QAudio::UnderrunError;
-        emit errorChanged(m_errorState);
-        m_deviceState = QAudio::IdleState;
-        emit stateChanged(m_deviceState);
+        setError(QAudio::UnderrunError);
+        setState(QAudio::IdleState);
     }
 }
 
 void QPulseAudioOutput::start(QIODevice *device)
 {
-    if (m_deviceState != QAudio::StoppedState)
-        m_deviceState = QAudio::StoppedState;
-
-    m_errorState = QAudio::NoError;
+    setState(QAudio::StoppedState);
+    setError(QAudio::NoError);
 
     // Handle change of mode
     if (m_audioSource && !m_pullMode) {
@@ -205,22 +219,19 @@ void QPulseAudioOutput::start(QIODevice *device)
 
     close();
 
+    if (!open())
+        return;
+
     m_pullMode = true;
     m_audioSource = device;
 
-    m_deviceState = QAudio::ActiveState;
-
-    open();
-
-    emit stateChanged(m_deviceState);
+    setState(QAudio::ActiveState);
 }
 
 QIODevice *QPulseAudioOutput::start()
 {
-    if (m_deviceState != QAudio::StoppedState)
-        m_deviceState = QAudio::StoppedState;
-
-    m_errorState = QAudio::NoError;
+    setState(QAudio::StoppedState);
+    setError(QAudio::NoError);
 
     // Handle change of mode
     if (m_audioSource && !m_pullMode) {
@@ -230,15 +241,14 @@ QIODevice *QPulseAudioOutput::start()
 
     close();
 
+    if (!open())
+        return Q_NULLPTR;
+
     m_audioSource = new OutputPrivate(this);
     m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
     m_pullMode = false;
 
-    m_deviceState = QAudio::IdleState;
-
-    open();
-
-    emit stateChanged(m_deviceState);
+    setState(QAudio::IdleState);
 
     return m_audioSource;
 }
@@ -246,33 +256,38 @@ QIODevice *QPulseAudioOutput::start()
 bool QPulseAudioOutput::open()
 {
     if (m_opened)
+        return true;
+
+    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+
+    if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
+        setError(QAudio::FatalError);
+        setState(QAudio::StoppedState);
         return false;
+    }
 
     pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
 
     if (!pa_sample_spec_valid(&spec)) {
-        m_errorState = QAudio::OpenError;
-        m_deviceState = QAudio::StoppedState;
+        setError(QAudio::OpenError);
+        setState(QAudio::StoppedState);
         return false;
     }
 
     m_spec = spec;
     m_totalTimeValue = 0;
-    m_elapsedTimeOffset = 0;
-    m_timeStamp.restart();
 
     if (m_streamName.isNull())
         m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
 
 #ifdef DEBUG_PULSE
-        qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
-        qDebug() << "Rate: " << spec.rate;
-        qDebug() << "Channels: " << spec.channels;
-        qDebug() << "Frame size: " << pa_frame_size(&spec);
+    qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
+    qDebug() << "Rate: " << spec.rate;
+    qDebug() << "Channels: " << spec.channels;
+    qDebug() << "Frame size: " << pa_frame_size(&spec);
 #endif
 
-    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
+    pulseEngine->lock();
 
     qint64 bytesPerSecond = m_format.sampleRate() * m_format.channelCount() * m_format.sampleSize() / 8;
 
@@ -280,7 +295,7 @@ bool QPulseAudioOutput::open()
     if (!m_category.isNull())
         pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, m_category.toLatin1().constData());
 
-    m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &spec, 0, propList);
+    m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &m_spec, 0, propList);
     pa_proplist_free(propList);
 
     pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this);
@@ -312,15 +327,20 @@ bool QPulseAudioOutput::open()
 
     if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, &m_chVolume, NULL) < 0) {
         qWarning() << "pa_stream_connect_playback() failed!";
+        pa_stream_unref(m_stream);
+        m_stream = 0;
+        pulseEngine->unlock();
+        setError(QAudio::OpenError);
+        setState(QAudio::StoppedState);
         return false;
     }
 
-    while (pa_stream_get_state(m_stream) != PA_STREAM_READY) {
+    while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
         pa_threaded_mainloop_wait(pulseEngine->mainloop());
-    }
+
     const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream);
     m_periodTime = (m_category == LOW_LATENCY_CATEGORY_NAME) ? LowLatencyPeriodTimeMs : PeriodTimeMs;
-    m_periodSize = pa_usec_to_bytes(m_periodTime*1000, &spec);
+    m_periodSize = pa_usec_to_bytes(m_periodTime*1000, &m_spec);
     m_bufferSize = buffer->tlength;
     m_maxBufferSize = buffer->maxlength;
     m_audioBuffer = new char[m_maxBufferSize];
@@ -333,9 +353,12 @@ bool QPulseAudioOutput::open()
     qDebug() << "\tFragment size: " << buffer->fragsize;
 #endif
 
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+    pulseEngine->unlock();
+
+    connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioOutput::onPulseContextFailed);
 
     m_opened = true;
+
     m_tickTimer->start(m_periodTime);
 
     m_elapsedTimeOffset = 0;
@@ -347,28 +370,35 @@ bool QPulseAudioOutput::open()
 
 void QPulseAudioOutput::close()
 {
+    if (!m_opened)
+        return;
+
     m_tickTimer->stop();
 
+    QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+
     if (m_stream) {
-        QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
 
-        pa_stream_set_write_callback(m_stream, NULL, NULL);
+        pa_stream_set_state_callback(m_stream, 0, 0);
+        pa_stream_set_write_callback(m_stream, 0, 0);
+        pa_stream_set_underflow_callback(m_stream, 0, 0);
+        pa_stream_set_overflow_callback(m_stream, 0, 0);
+        pa_stream_set_latency_update_callback(m_stream, 0, 0);
 
         pa_operation *o = pa_stream_drain(m_stream, outputStreamDrainComplete, NULL);
-        if (!o) {
-            qWarning() << QString("pa_stream_drain(): %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream))));
-        } else {
+        if (o)
             pa_operation_unref(o);
-        }
 
         pa_stream_disconnect(m_stream);
         pa_stream_unref(m_stream);
         m_stream = NULL;
 
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
     }
 
+    disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioOutput::onPulseContextFailed);
+
     if (!m_pullMode && m_audioSource) {
         delete m_audioSource;
         m_audioSource = 0;
@@ -430,17 +460,14 @@ qint64 QPulseAudioOutput::write(const char *data, qint64 len)
 {
     QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
 
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
+    pulseEngine->lock();
     len = qMin(len, static_cast<qint64>(pa_stream_writable_size(m_stream)));
     pa_stream_write(m_stream, data, len, 0, 0, PA_SEEK_RELATIVE);
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+    pulseEngine->unlock();
     m_totalTimeValue += len;
 
-    m_errorState = QAudio::NoError;
-    if (m_deviceState != QAudio::ActiveState) {
-        m_deviceState = QAudio::ActiveState;
-        emit stateChanged(m_deviceState);
-    }
+    setError(QAudio::NoError);
+    setState(QAudio::ActiveState);
 
     return len;
 }
@@ -450,10 +477,10 @@ void QPulseAudioOutput::stop()
     if (m_deviceState == QAudio::StoppedState)
         return;
 
-    m_errorState = QAudio::NoError;
-    m_deviceState = QAudio::StoppedState;
     close();
-    emit stateChanged(m_deviceState);
+
+    setError(QAudio::NoError);
+    setState(QAudio::StoppedState);
 }
 
 int QPulseAudioOutput::bytesFree() const
@@ -462,9 +489,9 @@ int QPulseAudioOutput::bytesFree() const
         return 0;
 
     QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-    pa_threaded_mainloop_lock(pulseEngine->mainloop());
+    pulseEngine->lock();
     int writableSize = pa_stream_writable_size(m_stream);
-    pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+    pulseEngine->unlock();
     return writableSize;
 }
 
@@ -509,30 +536,22 @@ void QPulseAudioOutput::resume()
 
         QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
 
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
 
         pa_operation *operation = pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, NULL);
-
-        while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
-            pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
+        pulseEngine->wait(operation);
         pa_operation_unref(operation);
 
         operation = pa_stream_trigger(m_stream, outputStreamSuccessCallback, NULL);
-
-        while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
-            pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
+        pulseEngine->wait(operation);
         pa_operation_unref(operation);
 
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
 
-        m_deviceState = QAudio::ActiveState;
-
-        m_errorState = QAudio::NoError;
         m_tickTimer->start(m_periodTime);
 
-        emit stateChanged(m_deviceState);
+        setState(QAudio::ActiveState);
+        setError(QAudio::NoError);
     }
 }
 
@@ -549,24 +568,21 @@ QAudioFormat QPulseAudioOutput::format() const
 void QPulseAudioOutput::suspend()
 {
     if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
+        setError(QAudio::NoError);
+        setState(QAudio::SuspendedState);
+
         m_tickTimer->stop();
-        m_deviceState = QAudio::SuspendedState;
-        m_errorState = QAudio::NoError;
-        emit stateChanged(m_deviceState);
 
         QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
         pa_operation *operation;
 
-        pa_threaded_mainloop_lock(pulseEngine->mainloop());
+        pulseEngine->lock();
 
         operation = pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, NULL);
-
-        while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
-            pa_threaded_mainloop_wait(pulseEngine->mainloop());
-
+        pulseEngine->wait(operation);
         pa_operation_unref(operation);
 
-        pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+        pulseEngine->unlock();
     }
 }
 
@@ -601,8 +617,8 @@ qint64 OutputPrivate::writeData(const char *data, qint64 len)
     int retry = 0;
     qint64 written = 0;
 
-    if ((m_audioDevice->m_deviceState == QAudio::ActiveState)
-            ||(m_audioDevice->m_deviceState == QAudio::IdleState)) {
+    if ((m_audioDevice->m_deviceState == QAudio::ActiveState
+         || m_audioDevice->m_deviceState == QAudio::IdleState)) {
          while(written < len) {
             int chunk = m_audioDevice->write(data+written, (len-written));
             if (chunk <= 0)
@@ -623,7 +639,7 @@ void QPulseAudioOutput::setVolume(qreal vol)
             m_volume = vol;
             if (m_opened) {
                 QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-                pa_threaded_mainloop_lock(pulseEngine->mainloop());
+                pulseEngine->lock();
                 pa_volume_t paVolume;
                 if (qFuzzyCompare(vol, 0.0)) {
                     pa_cvolume_mute(&m_chVolume, m_spec.channels);
@@ -641,7 +657,7 @@ void QPulseAudioOutput::setVolume(qreal vol)
                     qWarning()<<"QAudioOutput: Failed to set volume";
                 else
                     pa_operation_unref(op);
-                pa_threaded_mainloop_unlock(pulseEngine->mainloop());
+                pulseEngine->unlock();
             }
         }
     }
@@ -664,6 +680,14 @@ QString QPulseAudioOutput::category() const
     return m_category;
 }
 
+void QPulseAudioOutput::onPulseContextFailed()
+{
+    close();
+
+    setError(QAudio::FatalError);
+    setState(QAudio::StoppedState);
+}
+
 QT_END_NAMESPACE
 
 #include "moc_qaudiooutput_pulse.cpp"
diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.h b/src/plugins/pulseaudio/qaudiooutput_pulse.h
index 5954b8975f021cacb3389ff207205498092cf177..fea151ba2426ed7685e64ebf5b3bd7143cb41cca 100644
--- a/src/plugins/pulseaudio/qaudiooutput_pulse.h
+++ b/src/plugins/pulseaudio/qaudiooutput_pulse.h
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -105,12 +105,16 @@ public:
     void streamUnderflowCallback();
 
 private:
+    void setState(QAudio::State state);
+    void setError(QAudio::Error error);
+
     bool open();
     void close();
     qint64 write(const char *data, qint64 len);
 
 private Q_SLOTS:
     void userFeed();
+    void onPulseContextFailed();
 
 private:
     QByteArray m_device;
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.cpp b/src/plugins/pulseaudio/qpulseaudioengine.cpp
index b7a3e66b75a83eff4af67d30ccca282eb1aab54d..05c8be89e6fd5489e559877df2742a67f18cf120 100644
--- a/src/plugins/pulseaudio/qpulseaudioengine.cpp
+++ b/src/plugins/pulseaudio/qpulseaudioengine.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -170,15 +170,17 @@ static void contextStateCallbackInit(pa_context *context, void *userdata)
     pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
 }
 
-static void contextStateCallback(pa_context *context, void *userdata)
+static void contextStateCallback(pa_context *c, void *userdata)
 {
-    Q_UNUSED(userdata);
-    Q_UNUSED(context);
+    QPulseAudioEngine *self = reinterpret_cast<QPulseAudioEngine*>(userdata);
+    pa_context_state_t state = pa_context_get_state(c);
 
 #ifdef DEBUG_PULSE
-    pa_context_state_t state = pa_context_get_state(context);
     qDebug() << QPulseAudioInternal::stateToQString(state);
 #endif
+
+    if (state == PA_CONTEXT_FAILED)
+        QMetaObject::invokeMethod(self, "onContextFailed", Qt::QueuedConnection);
 }
 
 Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine);
@@ -187,40 +189,59 @@ QPulseAudioEngine::QPulseAudioEngine(QObject *parent)
     : QObject(parent)
     , m_mainLoopApi(0)
     , m_context(0)
+    , m_prepared(false)
+{
+    prepare();
+}
+
+QPulseAudioEngine::~QPulseAudioEngine()
+{
+    if (m_prepared)
+        release();
+}
 
+void QPulseAudioEngine::prepare()
 {
     bool keepGoing = true;
     bool ok = true;
 
     m_mainLoop = pa_threaded_mainloop_new();
     if (m_mainLoop == 0) {
-        qWarning("Unable to create pulseaudio mainloop");
+        qWarning("PulseAudioService: unable to create pulseaudio mainloop");
         return;
     }
 
     if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
-        qWarning("Unable to start pulseaudio mainloop");
+        qWarning("PulseAudioService: unable to start pulseaudio mainloop");
         pa_threaded_mainloop_free(m_mainLoop);
+        m_mainLoop = 0;
         return;
     }
 
     m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop);
 
-    pa_threaded_mainloop_lock(m_mainLoop);
+    lock();
 
-    m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtmPulseContext:%1")).arg(::getpid()).toLatin1().constData());
-    pa_context_set_state_callback(m_context, contextStateCallbackInit, this);
+    m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtPulseAudio:%1")).arg(::getpid()).toLatin1().constData());
 
-    if (!m_context) {
-        qWarning("Unable to create new pulseaudio context");
+    if (m_context == 0) {
+        qWarning("PulseAudioService: Unable to create new pulseaudio context");
+        pa_threaded_mainloop_unlock(m_mainLoop);
         pa_threaded_mainloop_free(m_mainLoop);
+        m_mainLoop = 0;
+        onContextFailed();
         return;
     }
 
-    if (pa_context_connect(m_context, NULL, (pa_context_flags_t)0, NULL) < 0) {
-        qWarning("Unable to create a connection to the pulseaudio context");
+    pa_context_set_state_callback(m_context, contextStateCallbackInit, this);
+
+    if (pa_context_connect(m_context, 0, (pa_context_flags_t)0, 0) < 0) {
+        qWarning("PulseAudioService: pa_context_connect() failed");
         pa_context_unref(m_context);
+        pa_threaded_mainloop_unlock(m_mainLoop);
         pa_threaded_mainloop_free(m_mainLoop);
+        m_mainLoop = 0;
+        m_context = 0;
         return;
     }
 
@@ -241,47 +262,49 @@ QPulseAudioEngine::QPulseAudioEngine(QObject *parent)
                 break;
 
             case PA_CONTEXT_TERMINATED:
-                qCritical("Context terminated.");
+                qCritical("PulseAudioService: Context terminated.");
                 keepGoing = false;
                 ok = false;
                 break;
 
             case PA_CONTEXT_FAILED:
             default:
-                qCritical() << QString("Connection failure: %1").arg(pa_strerror(pa_context_errno(m_context)));
+                qCritical() << QString("PulseAudioService: Connection failure: %1").arg(pa_strerror(pa_context_errno(m_context)));
                 keepGoing = false;
                 ok = false;
         }
 
-        if (keepGoing) {
+        if (keepGoing)
             pa_threaded_mainloop_wait(m_mainLoop);
-        }
     }
 
     if (ok) {
         pa_context_set_state_callback(m_context, contextStateCallback, this);
     } else {
-        if (m_context) {
-            pa_context_unref(m_context);
-            m_context = 0;
-        }
+        pa_context_unref(m_context);
+        m_context = 0;
     }
 
-    pa_threaded_mainloop_unlock(m_mainLoop);
+    unlock();
 
     if (ok) {
-        serverInfo();
-        sinks();
-        sources();
+        updateDevices();
+        m_prepared = true;
+    } else {
+        pa_threaded_mainloop_free(m_mainLoop);
+        m_mainLoop = 0;
+        onContextFailed();
     }
 }
 
-QPulseAudioEngine::~QPulseAudioEngine()
+void QPulseAudioEngine::release()
 {
+    if (!m_prepared)
+        return;
+
     if (m_context) {
-        pa_threaded_mainloop_lock(m_mainLoop);
         pa_context_disconnect(m_context);
-        pa_threaded_mainloop_unlock(m_mainLoop);
+        pa_context_unref(m_context);
         m_context = 0;
     }
 
@@ -290,62 +313,52 @@ QPulseAudioEngine::~QPulseAudioEngine()
         pa_threaded_mainloop_free(m_mainLoop);
         m_mainLoop = 0;
     }
+
+    m_prepared = false;
 }
 
-void QPulseAudioEngine::serverInfo()
+void QPulseAudioEngine::updateDevices()
 {
-    pa_operation *operation;
-
-    pa_threaded_mainloop_lock(m_mainLoop);
-
-    operation = pa_context_get_server_info(m_context, serverInfoCallback, this);
+    lock();
 
+    // 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);
 
-    pa_threaded_mainloop_unlock(m_mainLoop);
-}
-
-void QPulseAudioEngine::sinks()
-{
-    pa_operation *operation;
-
-    pa_threaded_mainloop_lock(m_mainLoop);
-
+    // 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);
 
+    // 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);
 
-    pa_threaded_mainloop_unlock(m_mainLoop);
+    unlock();
 
-    // Swap the default sink to index 0
+    // Swap the default output to index 0
     m_sinks.removeOne(m_defaultSink);
     m_sinks.prepend(m_defaultSink);
+
+    // Swap the default input to index 0
+    m_sources.removeOne(m_defaultSource);
+    m_sources.prepend(m_defaultSource);
 }
 
-void QPulseAudioEngine::sources()
+void QPulseAudioEngine::onContextFailed()
 {
-    pa_operation *operation;
-
-    pa_threaded_mainloop_lock(m_mainLoop);
-
-    operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this);
+    // Give a chance to the connected slots to still use the Pulse main loop before releasing it.
+    emit contextFailed();
 
-    while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
-        pa_threaded_mainloop_wait(m_mainLoop);
-
-    pa_operation_unref(operation);
+    release();
 
-    pa_threaded_mainloop_unlock(m_mainLoop);
-
-    // Swap the default source to index 0
-    m_sources.removeOne(m_defaultSource);
-    m_sources.prepend(m_defaultSource);
+    // Try to reconnect later
+    QTimer::singleShot(3000, this, SLOT(prepare()));
 }
 
 QPulseAudioEngine *QPulseAudioEngine::instance()
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.h b/src/plugins/pulseaudio/qpulseaudioengine.h
index 1c6c2aaf479851ad101908dd0494915cc831a7b2..04591d035bfb1dded8490ca349080dc43b134f37 100644
--- a/src/plugins/pulseaudio/qpulseaudioengine.h
+++ b/src/plugins/pulseaudio/qpulseaudioengine.h
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/legal
 **
 ** This file is part of the Qt Toolkit.
@@ -74,12 +74,36 @@ public:
     pa_threaded_mainloop *mainloop() { return m_mainLoop; }
     pa_context *context() { return m_context; }
 
+    inline void lock()
+    {
+        if (m_mainLoop)
+            pa_threaded_mainloop_lock(m_mainLoop);
+    }
+
+    inline void unlock()
+    {
+        if (m_mainLoop)
+            pa_threaded_mainloop_unlock(m_mainLoop);
+    }
+
+    inline void wait(pa_operation *op)
+    {
+        while (m_mainLoop && pa_operation_get_state(op) == PA_OPERATION_RUNNING)
+            pa_threaded_mainloop_wait(m_mainLoop);
+    }
+
     QList<QByteArray> availableDevices(QAudio::Mode mode) const;
 
+Q_SIGNALS:
+    void contextFailed();
+
+private Q_SLOTS:
+    void prepare();
+    void onContextFailed();
+
 private:
-    void serverInfo();
-    void sinks();
-    void sources();
+    void updateDevices();
+    void release();
 
 public:
     QList<QByteArray> m_sinks;
@@ -93,6 +117,7 @@ private:
     pa_mainloop_api *m_mainLoopApi;
     pa_threaded_mainloop *m_mainLoop;
     pa_context *m_context;
+    bool m_prepared;
  };
 
 QT_END_NAMESPACE