Source

Target

Commits (7)
Showing with 620 additions and 33 deletions
Qt 5.6.1 is a bug-fix release. It maintains both forward and backward
compatibility (source and binary) with Qt 5.6.0.
For more details, refer to the online documentation included in this
distribution. The documentation is also available online:
https://doc.qt.io/qt-5.6
The Qt version 5.6 series is binary compatible with the 5.5.x series.
Applications compiled for 5.5 will continue to run with 5.6.
Some of the changes listed in this file include issue tracking numbers
corresponding to tasks in the Qt Bug Tracker:
https://bugreports.qt.io/
Each of these identifiers can be entered in the bug tracker to obtain more
information about a particular change.
****************************************************************************
* Platform Specific Changes *
****************************************************************************
Android
-------
- [QTBUG-51911] fixed camera frames appearing flipped for a brief moment
when starting or stopping a video recording.
iOS / OS X
----------
- Fixed the camera resolution incorrectly being changed when switching
to image capture mode.
Linux
-----
- PulseAudio: fixed playback of short streams never starting with
QAudioOutput in pull mode.
- [QTBUG-40823][QTBUG-49461] changing the volume of a QAudioOutput or
QAudioInput doesn't affect the system volume anymore.
- [QTBUG-51607] fixed camera not working when QT_NO_GLIB is set.
QNX
---
- Fixed video playback in VMWare.
Windows
-------
- The DirectShow backend is now available on Windows CE.
- [QTBUG-49281] fixed a memory leak when stopping the camera.
- [QTBUG-53114] QMediaPlayer can now load UNC paths.
WinRT
-----
- [QTBUG-38802] the manifest now automatically includes permissions for
microphone and camera when a project contains QT += multimedia.
- [QTBUG-47803] fixed camera image capture signals not being emitted when
the first capture is done.
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef AVFAUDIOENCODERSETTINGSCONTROL_H
#define AVFAUDIOENCODERSETTINGSCONTROL_H
#include <qaudioencodersettingscontrol.h>
@class NSDictionary;
@class AVCaptureAudioDataOutput;
QT_BEGIN_NAMESPACE
class AVFCameraService;
class AVFAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
{
public:
explicit AVFAudioEncoderSettingsControl(AVFCameraService *service);
QStringList supportedAudioCodecs() const Q_DECL_OVERRIDE;
QString codecDescription(const QString &codecName) const Q_DECL_OVERRIDE;
QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const Q_DECL_OVERRIDE;
QAudioEncoderSettings audioSettings() const Q_DECL_OVERRIDE;
void setAudioSettings(const QAudioEncoderSettings &settings) Q_DECL_OVERRIDE;
NSDictionary *applySettings();
void unapplySettings();
private:
AVFCameraService *m_service;
QAudioEncoderSettings m_requestedSettings;
QAudioEncoderSettings m_actualSettings;
};
QT_END_NAMESPACE
#endif // AVFAUDIOENCODERSETTINGSCONTROL_H
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "avfaudioencodersettingscontrol.h"
#include "avfcameraservice.h"
#include "avfcamerasession.h"
#include <AVFoundation/AVFoundation.h>
#include <CoreAudio/CoreAudioTypes.h>
QT_BEGIN_NAMESPACE
struct AudioCodecInfo
{
QString description;
int id;
AudioCodecInfo() : id(0) { }
AudioCodecInfo(const QString &desc, int i)
: description(desc), id(i)
{ }
};
typedef QMap<QString, AudioCodecInfo> SupportedAudioCodecs;
Q_GLOBAL_STATIC_WITH_ARGS(QString , defaultCodec, (QLatin1String("aac")))
Q_GLOBAL_STATIC(SupportedAudioCodecs, supportedCodecs)
AVFAudioEncoderSettingsControl::AVFAudioEncoderSettingsControl(AVFCameraService *service)
: QAudioEncoderSettingsControl()
, m_service(service)
{
if (supportedCodecs->isEmpty()) {
supportedCodecs->insert(QStringLiteral("lpcm"),
AudioCodecInfo(QStringLiteral("Linear PCM"),
kAudioFormatLinearPCM));
supportedCodecs->insert(QStringLiteral("ulaw"),
AudioCodecInfo(QStringLiteral("PCM Mu-Law 2:1"),
kAudioFormatULaw));
supportedCodecs->insert(QStringLiteral("alaw"),
AudioCodecInfo(QStringLiteral("PCM A-Law 2:1"),
kAudioFormatALaw));
supportedCodecs->insert(QStringLiteral("ima4"),
AudioCodecInfo(QStringLiteral("IMA 4:1 ADPCM"),
kAudioFormatAppleIMA4));
supportedCodecs->insert(QStringLiteral("alac"),
AudioCodecInfo(QStringLiteral("Apple Lossless Audio Codec"),
kAudioFormatAppleLossless));
supportedCodecs->insert(QStringLiteral("aac"),
AudioCodecInfo(QStringLiteral("MPEG-4 Low Complexity AAC"),
kAudioFormatMPEG4AAC));
supportedCodecs->insert(QStringLiteral("aach"),
AudioCodecInfo(QStringLiteral("MPEG-4 High Efficiency AAC"),
kAudioFormatMPEG4AAC_HE));
supportedCodecs->insert(QStringLiteral("aacl"),
AudioCodecInfo(QStringLiteral("MPEG-4 AAC Low Delay"),
kAudioFormatMPEG4AAC_LD));
supportedCodecs->insert(QStringLiteral("aace"),
AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay"),
kAudioFormatMPEG4AAC_ELD));
supportedCodecs->insert(QStringLiteral("aacf"),
AudioCodecInfo(QStringLiteral("MPEG-4 AAC Enhanced Low Delay with SBR"),
kAudioFormatMPEG4AAC_ELD_SBR));
supportedCodecs->insert(QStringLiteral("aacp"),
AudioCodecInfo(QStringLiteral("MPEG-4 HE AAC V2"),
kAudioFormatMPEG4AAC_HE_V2));
supportedCodecs->insert(QStringLiteral("ilbc"),
AudioCodecInfo(QStringLiteral("iLBC"),
kAudioFormatiLBC));
}
}
QStringList AVFAudioEncoderSettingsControl::supportedAudioCodecs() const
{
return supportedCodecs->keys();
}
QString AVFAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
{
return supportedCodecs->value(codecName).description;
}
QList<int> AVFAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
{
Q_UNUSED(settings)
if (continuous)
*continuous = true;
return QList<int>() << 8000 << 96000;
}
QAudioEncoderSettings AVFAudioEncoderSettingsControl::audioSettings() const
{
return m_actualSettings;
}
void AVFAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
{
if (m_requestedSettings == settings)
return;
m_requestedSettings = m_actualSettings = settings;
}
NSDictionary *AVFAudioEncoderSettingsControl::applySettings()
{
if (m_service->session()->state() != QCamera::LoadedState &&
m_service->session()->state() != QCamera::ActiveState) {
return nil;
}
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
QString codec = m_requestedSettings.codec().isEmpty() ? *defaultCodec : m_requestedSettings.codec();
if (!supportedCodecs->contains(codec)) {
qWarning("Unsupported codec: '%s'", codec.toLocal8Bit().constData());
codec = *defaultCodec;
}
[settings setObject:[NSNumber numberWithInt:supportedCodecs->value(codec).id] forKey:AVFormatIDKey];
m_actualSettings.setCodec(codec);
#ifdef Q_OS_OSX
if (m_requestedSettings.encodingMode() == QMultimedia::ConstantQualityEncoding) {
int quality;
switch (m_requestedSettings.quality()) {
case QMultimedia::VeryLowQuality:
quality = AVAudioQualityMin;
break;
case QMultimedia::LowQuality:
quality = AVAudioQualityLow;
break;
case QMultimedia::HighQuality:
quality = AVAudioQualityHigh;
break;
case QMultimedia::VeryHighQuality:
quality = AVAudioQualityMax;
break;
case QMultimedia::NormalQuality:
default:
quality = AVAudioQualityMedium;
break;
}
[settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey];
} else
#endif
if (m_requestedSettings.bitRate() > 0){
[settings setObject:[NSNumber numberWithInt:m_requestedSettings.bitRate()] forKey:AVEncoderBitRateKey];
}
int sampleRate = m_requestedSettings.sampleRate();
int channelCount = m_requestedSettings.channelCount();
#ifdef Q_OS_IOS
// Some keys are mandatory only on iOS
if (codec == QLatin1String("lpcm")) {
[settings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsBigEndianKey];
[settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey];
[settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved];
}
if (codec == QLatin1String("alac"))
[settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey];
if (sampleRate <= 0)
sampleRate = codec == QLatin1String("ilbc") ? 8000 : 44100;
if (channelCount <= 0)
channelCount = codec == QLatin1String("ilbc") ? 1 : 2;
#endif
if (sampleRate > 0) {
[settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey];
m_actualSettings.setSampleRate(sampleRate);
}
if (channelCount > 0) {
[settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey];
m_actualSettings.setChannelCount(channelCount);
}
return settings;
}
void AVFAudioEncoderSettingsControl::unapplySettings()
{
m_actualSettings = m_requestedSettings;
}
QT_END_NAMESPACE
......@@ -61,7 +61,9 @@ class AVFImageEncoderControl;
class AVFCameraFlashControl;
class AVFMediaRecorderControl;
class AVFMediaRecorderControlIOS;
class AVFAudioEncoderSettingsControl;
class AVFVideoEncoderSettingsControl;
class AVFMediaContainerControl;
class AVFCameraService : public QMediaService
{
......@@ -88,7 +90,9 @@ public:
AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; }
AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; }
AVFCameraFlashControl *flashControl() const {return m_flashControl; }
AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; }
AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; }
AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
private:
AVFCameraSession *m_session;
......@@ -107,7 +111,9 @@ private:
AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl;
AVFImageEncoderControl *m_imageEncoderControl;
AVFCameraFlashControl *m_flashControl;
AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
AVFMediaContainerControl *m_mediaContainerControl;
};
QT_END_NAMESPACE
......
......@@ -53,7 +53,9 @@
#include "avfcameraviewfindersettingscontrol.h"
#include "avfimageencodercontrol.h"
#include "avfcameraflashcontrol.h"
#include "avfaudioencodersettingscontrol.h"
#include "avfvideoencodersettingscontrol.h"
#include "avfmediacontainercontrol.h"
#ifdef Q_OS_IOS
#include "avfcamerazoomcontrol.h"
......@@ -100,7 +102,9 @@ AVFCameraService::AVFCameraService(QObject *parent):
m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this);
m_imageEncoderControl = new AVFImageEncoderControl(this);
m_flashControl = new AVFCameraFlashControl(this);
m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this);
m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this);
m_mediaContainerControl = new AVFMediaContainerControl(this);
}
AVFCameraService::~AVFCameraService()
......@@ -132,7 +136,9 @@ AVFCameraService::~AVFCameraService()
delete m_viewfinderSettingsControl;
delete m_imageEncoderControl;
delete m_flashControl;
delete m_audioEncoderSettingsControl;
delete m_videoEncoderSettingsControl;
delete m_mediaContainerControl;
delete m_session;
}
......@@ -179,9 +185,15 @@ QMediaControl *AVFCameraService::requestControl(const char *name)
if (qstrcmp(name, QCameraFlashControl_iid) == 0)
return m_flashControl;
if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
return m_audioEncoderSettingsControl;
if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
return m_videoEncoderSettingsControl;
if (qstrcmp(name, QMediaContainerControl_iid) == 0)
return m_mediaContainerControl;
if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
AVFMediaVideoProbeControl *videoProbe = 0;
videoProbe = new AVFMediaVideoProbeControl(this);
......
......@@ -64,6 +64,7 @@ QT_END_NAMESPACE
QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVCaptureDeviceInput> m_audioInput;
QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVCaptureAudioDataOutput> m_audioOutput;
QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVAssetWriterInput> m_audioWriterInput;
AVCaptureDevice *m_audioCaptureDevice;
// High priority serial queue for video output:
QT_PREPEND_NAMESPACE(AVFScopedPointer)<dispatch_queue_t> m_videoQueue;
......@@ -87,6 +88,7 @@ QT_END_NAMESPACE
CMTime m_startTime;
CMTime m_lastTimeStamp;
NSDictionary *m_audioSettings;
NSDictionary *m_videoSettings;
}
......@@ -95,6 +97,7 @@ QT_END_NAMESPACE
- (bool)setupWithFileURL:(NSURL *)fileURL
cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service
audioSettings:(NSDictionary *)audioSettings
videoSettings:(NSDictionary *)videoSettings;
- (void)start;
......
......@@ -38,6 +38,7 @@
#include "avfcameraservice.h"
#include "avfcamerasession.h"
#include "avfcameradebug.h"
#include "avfmediacontainercontrol.h"
//#include <QtCore/qmutexlocker.h>
#include <QtCore/qmetaobject.h>
......@@ -73,7 +74,6 @@ bool qt_camera_service_isValid(AVFCameraService *service)
- (bool)addAudioCapture;
- (bool)addWriterInputs;
- (void)setQueues;
- (NSDictionary *)audioSettings;
- (void)updateDuration:(CMTime)newTimeStamp;
@end
......@@ -97,6 +97,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
m_startTime = kCMTimeInvalid;
m_lastTimeStamp = kCMTimeInvalid;
m_durationInMs.store(0);
m_audioSettings = nil;
m_videoSettings = nil;
}
......@@ -105,6 +106,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
- (bool)setupWithFileURL:(NSURL *)fileURL
cameraService:(AVFCameraService *)service
audioSettings:(NSDictionary *)audioSettings
videoSettings:(NSDictionary *)videoSettings
{
Q_ASSERT(fileURL);
......@@ -115,6 +117,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
}
m_service = service;
m_audioSettings = audioSettings;
m_videoSettings = videoSettings;
m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
......@@ -129,7 +132,9 @@ bool qt_camera_service_isValid(AVFCameraService *service)
// But we still can write video!
}
m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL fileType:AVFileTypeQuickTimeMovie error:nil]);
m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL
fileType:m_service->mediaContainerControl()->fileType()
error:nil]);
if (!m_assetWriter) {
qDebugCamera() << Q_FUNC_INFO << "failed to create asset writer";
return false;
......@@ -147,6 +152,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
[session removeInput:m_audioInput];
m_audioOutput.reset();
m_audioInput.reset();
m_audioCaptureDevice = 0;
}
m_assetWriter.reset();
return false;
......@@ -324,20 +330,22 @@ bool qt_camera_service_isValid(AVFCameraService *service)
AVCaptureSession *captureSession = m_service->session()->captureSession();
AVCaptureDevice *audioDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
if (!audioDevice) {
m_audioCaptureDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
if (!m_audioCaptureDevice) {
qWarning() << Q_FUNC_INFO << "no audio input device available";
return false;
} else {
NSError *error = nil;
m_audioInput.reset([[AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error] retain]);
m_audioInput.reset([[AVCaptureDeviceInput deviceInputWithDevice:m_audioCaptureDevice error:&error] retain]);
if (!m_audioInput || error) {
qWarning() << Q_FUNC_INFO << "failed to create audio device input";
m_audioCaptureDevice = 0;
m_audioInput.reset();
return false;
} else if (![captureSession canAddInput:m_audioInput]) {
qWarning() << Q_FUNC_INFO << "could not connect the audio input";
m_audioCaptureDevice = 0;
m_audioInput.reset();
return false;
} else {
......@@ -352,6 +360,7 @@ bool qt_camera_service_isValid(AVFCameraService *service)
} else {
qDebugCamera() << Q_FUNC_INFO << "failed to add audio output";
[captureSession removeInput:m_audioInput];
m_audioCaptureDevice = 0;
m_audioInput.reset();
m_audioOutput.reset();
return false;
......@@ -385,7 +394,10 @@ bool qt_camera_service_isValid(AVFCameraService *service)
m_cameraWriterInput.data().expectsMediaDataInRealTime = YES;
if (m_audioOutput) {
m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:[self audioSettings]]);
CMFormatDescriptionRef sourceFormat = m_audioCaptureDevice ? m_audioCaptureDevice.activeFormat.formatDescription : 0;
m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
outputSettings:m_audioSettings
sourceFormatHint:sourceFormat]);
if (!m_audioWriterInput) {
qDebugCamera() << Q_FUNC_INFO << "failed to create audio writer input";
// But we still can record video.
......@@ -415,20 +427,6 @@ bool qt_camera_service_isValid(AVFCameraService *service)
}
}
- (NSDictionary *)audioSettings
{
// TODO: these settings should be taken from
// the video/audio encoder settings control.
// For now we either take recommended (iOS >= 7.0)
// or nil - this seems to be good enough.
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0 && m_audioOutput)
return [m_audioOutput recommendedAudioSettingsForAssetWriterWithOutputFileType:AVFileTypeQuickTimeMovie];
#endif
return nil;
}
- (void)updateDuration:(CMTime)newTimeStamp
{
Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid));
......
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef AVFMEDIACONTAINERCONTROL_H
#define AVFMEDIACONTAINERCONTROL_H
#include <qmediacontainercontrol.h>
@class NSString;
QT_BEGIN_NAMESPACE
class AVFCameraService;
class AVFMediaContainerControl : public QMediaContainerControl
{
public:
explicit AVFMediaContainerControl(AVFCameraService *service);
QStringList supportedContainers() const Q_DECL_OVERRIDE;
QString containerFormat() const Q_DECL_OVERRIDE;
void setContainerFormat(const QString &format) Q_DECL_OVERRIDE;
QString containerDescription(const QString &formatMimeType) const Q_DECL_OVERRIDE;
NSString *fileType() const;
private:
QString m_format;
};
QT_END_NAMESPACE
#endif // AVFMEDIACONTAINERCONTROL_H
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "avfmediacontainercontrol.h"
#include <AVFoundation/AVMediaFormat.h>
QT_BEGIN_NAMESPACE
struct ContainerInfo
{
QString description;
NSString *fileType;
ContainerInfo() : fileType(nil) { }
ContainerInfo(const QString &desc, NSString *type)
: description(desc), fileType(type)
{ }
};
typedef QMap<QString, ContainerInfo> SupportedContainers;
Q_GLOBAL_STATIC(SupportedContainers, containers);
AVFMediaContainerControl::AVFMediaContainerControl(AVFCameraService *)
: QMediaContainerControl()
, m_format(QStringLiteral("mov")) // .mov is the default container format on Apple platforms
{
if (containers->isEmpty()) {
containers->insert(QStringLiteral("mov"),
ContainerInfo(QStringLiteral("QuickTime movie file format"),
AVFileTypeQuickTimeMovie));
containers->insert(QStringLiteral("mp4"),
ContainerInfo(QStringLiteral("MPEG-4 file format"),
AVFileTypeMPEG4));
containers->insert(QStringLiteral("m4v"),
ContainerInfo(QStringLiteral("iTunes video file format"),
AVFileTypeAppleM4V));
#ifdef Q_OS_IOS
containers->insert(QStringLiteral("3gp"),
ContainerInfo(QStringLiteral("3GPP file format"),
AVFileType3GPP));
#endif
}
}
QStringList AVFMediaContainerControl::supportedContainers() const
{
return containers->keys();
}
QString AVFMediaContainerControl::containerFormat() const
{
return m_format;
}
void AVFMediaContainerControl::setContainerFormat(const QString &format)
{
if (!containers->contains(format)) {
qWarning("Unsupported container format: '%s'", format.toLocal8Bit().constData());
return;
}
m_format = format;
}
QString AVFMediaContainerControl::containerDescription(const QString &formatMimeType) const
{
return containers->value(formatMimeType).description;
}
NSString *AVFMediaContainerControl::fileType() const
{
return containers->value(m_format).fileType;
}
QT_END_NAMESPACE
......@@ -37,7 +37,9 @@
#include "avfcameraservice.h"
#include "avfcameracontrol.h"
#include "avfaudioinputselectorcontrol.h"
#include "avfaudioencodersettingscontrol.h"
#include "avfvideoencodersettingscontrol.h"
#include "avfmediacontainercontrol.h"
#include <QtCore/qurl.h>
#include <QtCore/qfileinfo.h>
......@@ -233,6 +235,11 @@ void AVFMediaRecorderControl::applySettings()
return;
}
// Configure audio settings
[m_movieOutput setOutputSettings:m_service->audioEncoderSettingsControl()->applySettings()
forConnection:[m_movieOutput connectionWithMediaType:AVMediaTypeAudio]];
// Configure video settings
AVCaptureConnection *videoConnection = [m_movieOutput connectionWithMediaType:AVMediaTypeVideo];
NSDictionary *videoSettings = m_service->videoEncoderSettingsControl()->applySettings(videoConnection);
......@@ -243,6 +250,7 @@ void AVFMediaRecorderControl::applySettings()
void AVFMediaRecorderControl::unapplySettings()
{
m_service->audioEncoderSettingsControl()->unapplySettings();
m_service->videoEncoderSettingsControl()->unapplySettings([m_movieOutput connectionWithMediaType:AVMediaTypeVideo]);
}
......@@ -260,11 +268,13 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
QString outputLocationPath = m_outputLocation.scheme() == QLatin1String("file") ?
m_outputLocation.path() : m_outputLocation.toString();
QString extension = m_service->mediaContainerControl()->containerFormat();
QUrl actualLocation = QUrl::fromLocalFile(
m_storageLocation.generateFileName(outputLocationPath,
QCamera::CaptureVideo,
QLatin1String("clip_"),
QLatin1String("mp4")));
extension));
qDebugCamera() << "Video capture location:" << actualLocation.toString();
......
......@@ -100,6 +100,7 @@ private:
QMediaRecorder::State m_state;
QMediaRecorder::Status m_lastStatus;
NSDictionary *m_audioSettings;
NSDictionary *m_videoSettings;
};
......
......@@ -38,7 +38,9 @@
#include "avfcameracontrol.h"
#include "avfcameraservice.h"
#include "avfcameradebug.h"
#include "avfaudioencodersettingscontrol.h"
#include "avfvideoencodersettingscontrol.h"
#include "avfmediacontainercontrol.h"
#include "avfcamerautility.h"
#include <QtCore/qdebug.h>
......@@ -79,6 +81,7 @@ AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service
, m_service(service)
, m_state(QMediaRecorder::StoppedState)
, m_lastStatus(QMediaRecorder::UnloadedStatus)
, m_audioSettings(nil)
, m_videoSettings(nil)
{
Q_ASSERT(service);
......@@ -111,6 +114,8 @@ AVFMediaRecorderControlIOS::~AVFMediaRecorderControlIOS()
{
[m_writer abort];
if (m_audioSettings)
[m_audioSettings release];
if (m_videoSettings)
[m_videoSettings release];
}
......@@ -163,8 +168,13 @@ void AVFMediaRecorderControlIOS::applySettings()
return;
}
AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
// audio settings
m_audioSettings = m_service->audioEncoderSettingsControl()->applySettings();
if (m_audioSettings)
[m_audioSettings retain];
// video settings
AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
m_videoSettings = m_service->videoEncoderSettingsControl()->applySettings(conn);
if (m_videoSettings)
[m_videoSettings retain];
......@@ -172,9 +182,15 @@ void AVFMediaRecorderControlIOS::applySettings()
void AVFMediaRecorderControlIOS::unapplySettings()
{
m_service->audioEncoderSettingsControl()->unapplySettings();
AVCaptureConnection *conn = [m_service->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo];
m_service->videoEncoderSettingsControl()->unapplySettings(conn);
if (m_audioSettings) {
[m_audioSettings release];
m_audioSettings = nil;
}
if (m_videoSettings) {
[m_videoSettings release];
m_videoSettings = nil;
......@@ -215,7 +231,8 @@ void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
const QString path(m_outputLocation.scheme() == QLatin1String("file") ?
m_outputLocation.path() : m_outputLocation.toString());
const QUrl fileURL(QUrl::fromLocalFile(m_storageLocation.generateFileName(path, QCamera::CaptureVideo,
QLatin1String("clip_"), QLatin1String("mp4"))));
QLatin1String("clip_"),
m_service->mediaContainerControl()->containerFormat())));
NSURL *nsFileURL = fileURL.toNSURL();
if (!nsFileURL) {
......@@ -245,7 +262,9 @@ void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state)
applySettings();
if ([m_writer setupWithFileURL:nsFileURL cameraService:m_service videoSettings:m_videoSettings]) {
if ([m_writer setupWithFileURL:nsFileURL cameraService:m_service
audioSettings:m_audioSettings
videoSettings:m_videoSettings]) {
m_state = QMediaRecorder::RecordingState;
m_lastStatus = QMediaRecorder::StartingStatus;
......
......@@ -38,7 +38,9 @@ HEADERS += \
avfcameraviewfindersettingscontrol.h \
avfimageencodercontrol.h \
avfcameraflashcontrol.h \
avfvideoencodersettingscontrol.h
avfvideoencodersettingscontrol.h \
avfmediacontainercontrol.h \
avfaudioencodersettingscontrol.h
OBJECTIVE_SOURCES += \
avfcameraserviceplugin.mm \
......@@ -59,7 +61,9 @@ OBJECTIVE_SOURCES += \
avfcameraviewfindersettingscontrol.mm \
avfimageencodercontrol.mm \
avfcameraflashcontrol.mm \
avfvideoencodersettingscontrol.mm
avfvideoencodersettingscontrol.mm \
avfmediacontainercontrol.mm \
avfaudioencodersettingscontrol.mm
osx {
......
......@@ -38,6 +38,7 @@
#include <QtCore/qfunctions_winrt.h>
#include <QtCore/QGlobalStatic>
#include <QtCore/QLoggingCategory>
#include <QtCore/QMetaMethod>
#include <QtCore/QPointer>
#include <QtGui/QOpenGLContext>
......@@ -58,6 +59,8 @@ using namespace Microsoft::WRL;
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcMMVideoRender, "qt.mm.videorender")
#define BREAK_IF_FAILED(msg) RETURN_IF_FAILED(msg, break)
#define CONTINUE_IF_FAILED(msg) RETURN_IF_FAILED(msg, continue)
......@@ -66,6 +69,7 @@ struct QWinRTVideoRendererControlGlobal
{
QWinRTVideoRendererControlGlobal()
{
qCDebug(lcMMVideoRender) << __FUNCTION__;
HRESULT hr;
D3D_FEATURE_LEVEL featureLevels[] =
......@@ -202,6 +206,7 @@ ID3D11Device *QWinRTAbstractVideoRendererControl::d3dDevice()
// This is required so that subclasses can stop the render thread before deletion
void QWinRTAbstractVideoRendererControl::shutdown()
{
qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
if (d->renderThread.isRunning()) {
d->renderThread.requestInterruption();
......@@ -212,6 +217,7 @@ void QWinRTAbstractVideoRendererControl::shutdown()
QWinRTAbstractVideoRendererControl::QWinRTAbstractVideoRendererControl(const QSize &size, QObject *parent)
: QVideoRendererControl(parent), d_ptr(new QWinRTAbstractVideoRendererControlPrivate)
{
qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
d->format = QVideoSurfaceFormat(size, QVideoFrame::Format_BGRA32,
......@@ -232,6 +238,7 @@ QWinRTAbstractVideoRendererControl::QWinRTAbstractVideoRendererControl(const QSi
QWinRTAbstractVideoRendererControl::~QWinRTAbstractVideoRendererControl()
{
qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
CriticalSectionLocker locker(&d->mutex);
shutdown();
......@@ -253,6 +260,7 @@ void QWinRTAbstractVideoRendererControl::setSurface(QAbstractVideoSurface *surfa
void QWinRTAbstractVideoRendererControl::syncAndRender()
{
qCDebug(lcMMVideoRender) << __FUNCTION__;
Q_D(QWinRTAbstractVideoRendererControl);
QThread *currentThread = QThread::currentThread();
......@@ -334,6 +342,7 @@ void QWinRTAbstractVideoRendererControl::setScanLineDirection(QVideoSurfaceForma
void QWinRTAbstractVideoRendererControl::setActive(bool active)
{
qCDebug(lcMMVideoRender) << __FUNCTION__ << active;
Q_D(QWinRTAbstractVideoRendererControl);
if (d->active == active)
......@@ -351,7 +360,7 @@ void QWinRTAbstractVideoRendererControl::setActive(bool active)
return;
}
d->renderThread.requestInterruption();
shutdown();
if (d->surface && d->surface->isActive())
d->surface->stop();
}
......
......@@ -101,7 +101,13 @@ HRESULT getMediaStreamResolutions(IMediaDeviceController *device,
ComPtr<IMediaEncodingProperties> properties;
hr = (*propertiesList)->GetAt(index, &properties);
Q_ASSERT_SUCCEEDED(hr);
if (type == MediaStreamType_VideoRecord || type == MediaStreamType_VideoPreview) {
HString propertyType;
hr = properties->get_Type(propertyType.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
const HStringReference videoRef = HString::MakeReference(L"Video");
const HStringReference imageRef = HString::MakeReference(L"Image");
if (propertyType == videoRef) {
ComPtr<IVideoEncodingProperties> videoProperties;
hr = properties.As(&videoProperties);
Q_ASSERT_SUCCEEDED(hr);
......@@ -111,13 +117,10 @@ HRESULT getMediaStreamResolutions(IMediaDeviceController *device,
hr = videoProperties->get_Height(&height);
Q_ASSERT_SUCCEEDED(hr);
resolutions->append(QSize(width, height));
} else if (type == MediaStreamType_Photo) {
} else if (propertyType == imageRef) {
ComPtr<IImageEncodingProperties> imageProperties;
hr = properties.As(&imageProperties);
// Asking for Photo also returns video resolutions in addition
// We skip those, as we are only interested in image Type
if (FAILED(hr) || !imageProperties)
continue;
Q_ASSERT_SUCCEEDED(hr);
UINT32 width, height;
hr = imageProperties->get_Width(&width);
Q_ASSERT_SUCCEEDED(hr);
......