diff options
author | Lars Knoll <lars.knoll@qt.io> | 2021-02-05 14:46:11 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2021-02-09 19:03:56 +0000 |
commit | ea27ae88abcf82e4c034d48f5b45e16f0fd29a6b (patch) | |
tree | 267af0abf6128c33b7724c588de028303841e92d | |
parent | 177dd889e1f1a046c04770e4afe85d29b1476a84 (diff) |
More changes for QMediaFormat handling on the backend
The codecs supported by a certain muxer/demuxer are actually
something we need to determine from the backend. So a static
mapping in QMediaFormat will not really work.
Change-Id: I848c607ed222eba160a7c9c1c7b216b991e5ceba
Reviewed-by: Doris Verria <doris.verria@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
25 files changed, 700 insertions, 1071 deletions
diff --git a/examples/multimedia/audiorecorder/audiorecorder.cpp b/examples/multimedia/audiorecorder/audiorecorder.cpp index 3a73dd7f0..93913a23b 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.cpp +++ b/examples/multimedia/audiorecorder/audiorecorder.cpp @@ -87,12 +87,12 @@ AudioRecorder::AudioRecorder() //audio codecs ui->audioCodecBox->addItem(tr("Default"), QVariant(QString())); - for (auto &codec : QMediaEncoderInfo::supportedAudioCodecs()) + for (auto &codec : QMediaFormat().supportedAudioCodecs(QMediaFormat::Encode)) ui->audioCodecBox->addItem(QMediaFormat::audioCodecDescription(codec), QVariant::fromValue(codec)); //containers ui->containerBox->addItem(tr("Default"), QVariant(QString())); - for (auto &container : QMediaEncoderInfo::supportedFileFormats()) { + for (auto &container : QMediaFormat().supportedFileFormats(QMediaFormat::Encode)) { if (container < QMediaFormat::AAC) // ### Somewhat hacky, skip video formats continue; ui->containerBox->addItem(QMediaFormat::fileFormatDescription(container), QVariant::fromValue(container)); diff --git a/examples/multimediawidgets/camera/camera.cpp b/examples/multimediawidgets/camera/camera.cpp index f573970d2..f5d507d46 100644 --- a/examples/multimediawidgets/camera/camera.cpp +++ b/examples/multimediawidgets/camera/camera.cpp @@ -85,17 +85,17 @@ Camera::Camera() : ui(new Ui::Camera) setCamera(QMediaDeviceManager::defaultVideoInput()); qDebug() << "Supported Containers:"; - auto containers = QMediaEncoderInfo::supportedFileFormats(); + auto containers = QMediaFormat().supportedFileFormats(QMediaFormat::Encode); for (const auto c : containers) qDebug() << " " << QMediaFormat::fileFormatName(c); qDebug() << "Supported Audio Codecs:"; - auto audio = QMediaEncoderInfo::supportedAudioCodecs(); + auto audio = QMediaFormat().supportedAudioCodecs(QMediaFormat::Encode); for (const auto c : audio) qDebug() << " " << QMediaFormat::audioCodecName(c); qDebug() << "Supported Video Codecs:"; - auto video = QMediaEncoderInfo::supportedVideoCodecs(); + auto video = QMediaFormat().supportedVideoCodecs(QMediaFormat::Encode); for (const auto c : video) qDebug() << " " << QMediaFormat::videoCodecName(c); } diff --git a/examples/multimediawidgets/camera/videosettings.cpp b/examples/multimediawidgets/camera/videosettings.cpp index b41462489..127c21388 100644 --- a/examples/multimediawidgets/camera/videosettings.cpp +++ b/examples/multimediawidgets/camera/videosettings.cpp @@ -68,8 +68,8 @@ VideoSettings::VideoSettings(QMediaRecorder *mediaRecorder, QWidget *parent) ui->setupUi(this); //audio codecs - ui->audioCodecBox->addItem(tr("Default audio codec"), QVariant(QString())); - const auto supportedAudioCodecs = QMediaEncoderInfo::supportedAudioCodecs(); + ui->audioCodecBox->addItem(tr("Default audio codec"), QVariant::fromValue(QMediaFormat::AudioCodec::Unspecified)); + const auto supportedAudioCodecs = QMediaFormat().supportedAudioCodecs(QMediaFormat::Encode); for (const auto &codec : supportedAudioCodecs) { QString description = QMediaFormat::audioCodecDescription(codec); ui->audioCodecBox->addItem(QMediaFormat::audioCodecName(codec) + ": " + description, QVariant::fromValue(codec)); @@ -80,8 +80,8 @@ VideoSettings::VideoSettings(QMediaRecorder *mediaRecorder, QWidget *parent) ui->audioSampleRateBox->setRange(sampleRates.minimum, sampleRates.maximum); //video codecs - ui->videoCodecBox->addItem(tr("Default video codec"), QVariant(QString())); - const auto supportedVideoCodecs = QMediaEncoderInfo::supportedVideoCodecs(); + ui->videoCodecBox->addItem(tr("Default video codec"), QVariant::fromValue(QMediaFormat::VideoCodec::Unspecified)); + const auto supportedVideoCodecs = QMediaFormat().supportedVideoCodecs(QMediaFormat::Encode); for (const auto &codec : supportedVideoCodecs) { QString description = QMediaFormat::videoCodecDescription(codec); ui->videoCodecBox->addItem(QMediaFormat::videoCodecName(codec) + ": " + description, QVariant::fromValue(codec)); @@ -103,8 +103,8 @@ VideoSettings::VideoSettings(QMediaRecorder *mediaRecorder, QWidget *parent) // } //containers - ui->containerFormatBox->addItem(tr("Default container"), QVariant(QString())); - const auto formats = QMediaEncoderInfo::supportedFileFormats(); + ui->containerFormatBox->addItem(tr("Default container"), QVariant::fromValue(QMediaFormat::UnspecifiedFormat)); + const auto formats = QMediaFormat().supportedFileFormats(QMediaFormat::Encode); for (auto format : formats) { ui->containerFormatBox->addItem(QMediaFormat::fileFormatName(format) + ": " + QMediaFormat::fileFormatDescription(format), QVariant::fromValue(format)); diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt index 6ed2d10b9..2b3fbbb34 100644 --- a/src/multimedia/CMakeLists.txt +++ b/src/multimedia/CMakeLists.txt @@ -200,7 +200,6 @@ qt_internal_extend_target(Multimedia CONDITION QT_FEATURE_gstreamer platform/gstreamer/audio/qaudiooutput_gstreamer.cpp platform/gstreamer/audio/qaudiooutput_gstreamer_p.h platform/gstreamer/audio/qgstreameraudiodecodercontrol.cpp platform/gstreamer/audio/qgstreameraudiodecodercontrol_p.h platform/gstreamer/common/qgstappsrc.cpp platform/gstreamer/common/qgstappsrc_p.h - platform/gstreamer/common/qgstcodecsinfo.cpp platform/gstreamer/common/qgstcodecsinfo_p.h platform/gstreamer/common/qgstreamerbufferprobe.cpp platform/gstreamer/common/qgstreamerbufferprobe_p.h platform/gstreamer/common/qgstreamerbushelper.cpp platform/gstreamer/common/qgstreamerbushelper_p.h platform/gstreamer/common/qgstreamermessage.cpp platform/gstreamer/common/qgstreamermessage_p.h diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp index d39058d6a..5be3f6cfe 100644 --- a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp +++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp @@ -45,6 +45,7 @@ #include "qandroidmultimediautils_p.h" #include "qandroidvideooutput_p.h" #include "qandroidglobal_p.h" +#include <QtCore/qmimetype.h> #include <algorithm> @@ -247,37 +248,7 @@ void QAndroidCaptureSession::start() m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation()); } - const char *extension = "mp4"; - switch(m_encoderSettings.format()) { - case QMediaFormat::MPEG4: - break; - case QMediaFormat::Ogg: - extension = "ogg"; - break; - case QMediaFormat::QuickTime: - extension = "mov"; - break; - case QMediaFormat::WebM: - extension = "webm"; - break; - case QMediaFormat::AAC: - extension = "aac"; - break; - case QMediaFormat::MP3: - extension = "mp3"; - break; - case QMediaFormat::Mpeg4Audio: - extension = "m4a"; - break; - case QMediaFormat::ALAC: - case QMediaFormat::ASF: - case QMediaFormat::AVI: - case QMediaFormat::Matroska: - case QMediaFormat::FLAC: - case QMediaFormat::Wave: - case QMediaFormat::UnspecifiedFormat: - break; - } + QString extension = m_encoderSettings.mimeType().preferredSuffix(); // Set output file QString filePath = m_mediaStorageLocation.generateFileName( @@ -287,7 +258,7 @@ void QAndroidCaptureSession::start() : QMediaStorageLocation::Sounds, m_cameraSession ? QLatin1String("VID_") : QLatin1String("REC_"), - QString::fromUtf8(extension)); + extension); m_usedOutputLocation = QUrl::fromLocalFile(filePath); m_mediaRecorder->setOutputFile(filePath); @@ -399,7 +370,7 @@ qint64 QAndroidCaptureSession::duration() const void QAndroidCaptureSession::setEncoderSettings(const QMediaEncoderSettings &settings) { m_encoderSettings = settings; - m_encoderSettings.resolveFormat(m_cameraSession ? QMediaEncoderSettings::AudioAndVideo : QMediaEncoderSettings::AudioOnly); + m_encoderSettings.resolveFormat(); m_encoderSettingsDirty = true; } diff --git a/src/multimedia/platform/android/qandroidformatsinfo.cpp b/src/multimedia/platform/android/qandroidformatsinfo.cpp index b8491492a..1faaca87b 100644 --- a/src/multimedia/platform/android/qandroidformatsinfo.cpp +++ b/src/multimedia/platform/android/qandroidformatsinfo.cpp @@ -43,15 +43,15 @@ QT_BEGIN_NAMESPACE QAndroidFormatInfo::QAndroidFormatInfo() { - // ### Properly determine the set of supported codecs, this is a minimal set gathered from the old code base - m_decodableAudioCodecs << QMediaFormat::AudioCodec::AAC << QMediaFormat::AudioCodec::MP3 << QMediaFormat::AudioCodec::Opus - << QMediaFormat::AudioCodec::Vorbis; - m_encodableAudioCodecs = m_decodableAudioCodecs; - m_decodableVideoCodecs << QMediaFormat::VideoCodec::MPEG4 << QMediaFormat::VideoCodec::H264 << QMediaFormat::VideoCodec::H265; - m_encodableVideoCodecs = m_decodableVideoCodecs; - m_decodableFileFormats << QMediaFormat::FileFormat::MPEG4 << QMediaFormat::FileFormat::MP3 << QMediaFormat::AAC - << QMediaFormat::Ogg << QMediaFormat::WebM; - m_encodableFileFormats = m_decodableFileFormats; + decoders = { + { QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {} }, + { QMediaFormat::MP3, { QMediaFormat::AudioCodec::MP3}, {} }, + { QMediaFormat::Ogg, { QMediaFormat::AudioCodec::Opus, QMediaFormat::AudioCodec::Vorbis }, + { QMediaFormat::VideoCodec::VP8 } }, + { QMediaFormat::MPEG4, { QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::AAC }, + { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265 } } + }; + encoders = decoders; } QAndroidFormatInfo::~QAndroidFormatInfo() @@ -59,34 +59,4 @@ QAndroidFormatInfo::~QAndroidFormatInfo() } -QList<QMediaFormat::FileFormat> QAndroidFormatInfo::decodableMediaContainers() const -{ - return m_decodableFileFormats; -} - -QList<QMediaFormat::AudioCodec> QAndroidFormatInfo::decodableAudioCodecs() const -{ - return m_decodableAudioCodecs; -} - -QList<QMediaFormat::VideoCodec> QAndroidFormatInfo::decodableVideoCodecs() const -{ - return m_decodableVideoCodecs; -} - -QList<QMediaFormat::FileFormat> QAndroidFormatInfo::encodableMediaContainers() const -{ - return m_encodableFileFormats; -} - -QList<QMediaFormat::AudioCodec> QAndroidFormatInfo::encodableAudioCodecs() const -{ - return m_encodableAudioCodecs; -} - -QList<QMediaFormat::VideoCodec> QAndroidFormatInfo::encodableVideoCodecs() const -{ - return m_encodableVideoCodecs; -} - QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/qandroidformatsinfo_p.h b/src/multimedia/platform/android/qandroidformatsinfo_p.h index c3f744ebc..461400ed2 100644 --- a/src/multimedia/platform/android/qandroidformatsinfo_p.h +++ b/src/multimedia/platform/android/qandroidformatsinfo_p.h @@ -60,23 +60,6 @@ class QAndroidFormatInfo : public QMediaPlatformFormatInfo public: QAndroidFormatInfo(); ~QAndroidFormatInfo(); - - QList<QMediaFormat::FileFormat> decodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> decodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> decodableVideoCodecs() const override; - - QList<QMediaFormat::FileFormat> encodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> encodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> encodableVideoCodecs() const override; - -private: - QList<QMediaFormat::FileFormat> m_decodableFileFormats; - QList<QMediaFormat::AudioCodec> m_decodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_decodableVideoCodecs; - - QList<QMediaFormat::FileFormat> m_encodableFileFormats; - QList<QMediaFormat::AudioCodec> m_encodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_encodableVideoCodecs; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/camera/avfcameradebug_p.h b/src/multimedia/platform/darwin/camera/avfcameradebug_p.h index 616e53d99..cbb64b316 100644 --- a/src/multimedia/platform/darwin/camera/avfcameradebug_p.h +++ b/src/multimedia/platform/darwin/camera/avfcameradebug_p.h @@ -57,7 +57,7 @@ QT_USE_NAMESPACE -//#define AVF_DEBUG_CAMERA +#define AVF_DEBUG_CAMERA #ifdef AVF_DEBUG_CAMERA #define qDebugCamera qDebug diff --git a/src/multimedia/platform/darwin/camera/avfmediarecordercontrol.mm b/src/multimedia/platform/darwin/camera/avfmediarecordercontrol.mm index 721f2c1fa..69c56d402 100644 --- a/src/multimedia/platform/darwin/camera/avfmediarecordercontrol.mm +++ b/src/multimedia/platform/darwin/camera/avfmediarecordercontrol.mm @@ -48,6 +48,7 @@ #include "avfcamerautility_p.h" #include <QtCore/qurl.h> +#include <QtCore/qmimetype.h> #include <QtCore/qfileinfo.h> #include <QtMultimedia/qcameracontrol.h> #include <CoreAudio/CoreAudio.h> @@ -469,9 +470,13 @@ void AVFMediaRecorderControl::applySettings() return; } - bool videoEnabled = m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo); QMediaEncoderSettings resolved = m_settings; - resolved.resolveFormat(videoEnabled ? QMediaEncoderSettings::AudioAndVideo : QMediaEncoderSettings::AudioOnly); + resolved.resolveFormat(); + qDebug() << "file profile" << QMediaFormat::fileFormatName(resolved.format()); + qDebug() << "video profile" << QMediaFormat::videoCodecName(resolved.videoCodec()); + qDebug() << "audio profile" << QMediaFormat::audioCodecName(resolved.audioCodec()); + + const AVFConfigurationLock lock(m_session->videoCaptureDevice()); // prevents activeFormat from being overridden // Configure audio settings [m_movieOutput setOutputSettings:avfAudioSettings(resolved) @@ -483,8 +488,6 @@ void AVFMediaRecorderControl::applySettings() NSDictionary *videoSettings = avfVideoSettings(resolved, captureDevice, videoConnection); - const AVFConfigurationLock lock(m_session->videoCaptureDevice()); // prevents activeFormat from being overridden - [m_movieOutput setOutputSettings:videoSettings forConnection:videoConnection]; } @@ -540,7 +543,9 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state) QString outputLocationPath = m_outputLocation.scheme() == QLatin1String("file") ? m_outputLocation.path() : m_outputLocation.toString(); - QString extension = "mov";// ######m_service->mediaContainerControl()->containerFormat(); + QMediaEncoderSettings resolved = m_settings; + resolved.resolveFormat(); + QString extension = resolved.mimeType().preferredSuffix(); QUrl actualLocation = QUrl::fromLocalFile( m_storageLocation.generateFileName(outputLocationPath, @@ -550,7 +555,7 @@ void AVFMediaRecorderControl::setState(QMediaRecorder::State state) qDebugCamera() << "Video capture location:" << actualLocation.toString(); - applySettings(); +// applySettings(); [m_movieOutput startRecordingToOutputFileURL:actualLocation.toNSURL() recordingDelegate:m_recorderDelagate]; diff --git a/src/multimedia/platform/darwin/qdarwinformatsinfo.mm b/src/multimedia/platform/darwin/qdarwinformatsinfo.mm index 16e8458e2..11a458dea 100644 --- a/src/multimedia/platform/darwin/qdarwinformatsinfo.mm +++ b/src/multimedia/platform/darwin/qdarwinformatsinfo.mm @@ -62,14 +62,14 @@ static struct { QMediaFormat::VideoCodec value; } videoCodecMap[] = { // See CMVideoCodecType for the four character code names of codecs - { "video/mp4; codecs=\"mp1v\"", QMediaFormat::VideoCodec::MPEG1 }, - { "video/mp4; codecs=\"mp2v\"", QMediaFormat::VideoCodec::MPEG2 }, - { "video/mp4; codecs=\"mp4v\"", QMediaFormat::VideoCodec::MPEG4 }, - { "video/mp4; codecs=\"avc1\"", QMediaFormat::VideoCodec::H264 }, - { "video/mp4; codecs=\"hvc1\"", QMediaFormat::VideoCodec::H265 }, - { "video/mp4; codecs=\"vp09\"", QMediaFormat::VideoCodec::VP9 }, - { "video/mp4; codecs=\"av01\"", QMediaFormat::VideoCodec::AV1 }, // ### ???? - { "video/mp4; codecs=\"jpeg\"", QMediaFormat::VideoCodec::MotionJPEG }, + { "; codecs=\"mp1v\"", QMediaFormat::VideoCodec::MPEG1 }, + { "; codecs=\"mp2v\"", QMediaFormat::VideoCodec::MPEG2 }, + { "; codecs=\"mp4v\"", QMediaFormat::VideoCodec::MPEG4 }, + { "; codecs=\"avc1\"", QMediaFormat::VideoCodec::H264 }, + { "; codecs=\"hvc1\"", QMediaFormat::VideoCodec::H265 }, + { "; codecs=\"vp09\"", QMediaFormat::VideoCodec::VP9 }, + { "; codecs=\"av01\"", QMediaFormat::VideoCodec::AV1 }, // ### ???? + { "; codecs=\"jpeg\"", QMediaFormat::VideoCodec::MotionJPEG }, { nullptr, QMediaFormat::VideoCodec::Unspecified } }; @@ -78,11 +78,15 @@ static struct { QMediaFormat::AudioCodec value; } audioCodecMap[] = { // AudioFile.h - { "audio/mp3", QMediaFormat::AudioCodec::MP3 }, - { "audio/aac", QMediaFormat::AudioCodec::AAC }, - { "video/mp4; codecs=\"ac-3\"", QMediaFormat::AudioCodec::AC3 }, - { "video/mp4; codecs=\"ec-3\"", QMediaFormat::AudioCodec::EAC3 }, - { "audio/flac", QMediaFormat::AudioCodec::FLAC }, + // ### The next two entries do not work, probably because they contain non a space and period and AVFoundation doesn't like that + // We know they are supported on all Apple platforms, so we'll add them manually below +// { "; codecs=\".mp3\"", QMediaFormat::AudioCodec::MP3 }, +// { "; codecs=\"aac \"", QMediaFormat::AudioCodec::AAC }, + { "; codecs=\"ac-3\"", QMediaFormat::AudioCodec::AC3 }, + { "; codecs=\"ec-3\"", QMediaFormat::AudioCodec::EAC3 }, + { "; codecs=\"flac\"", QMediaFormat::AudioCodec::FLAC }, + { "; codecs=\"alac\"", QMediaFormat::AudioCodec::FLAC }, + { "; codecs=\"opus\"", QMediaFormat::AudioCodec::Opus }, { nullptr, QMediaFormat::AudioCodec::Unspecified }, }; @@ -92,85 +96,86 @@ QDarwinFormatInfo::QDarwinFormatInfo() for (AVFileType filetype in avtypes) { auto *m = mediaContainerMap; while (m->name) { - if (!strcmp(filetype.UTF8String, m->name)) { - m_decodableMediaContainers.append(m->value); - break; + if (strcmp(filetype.UTF8String, m->name)) { + ++m; + continue; } - ++m; - } - } - { - auto *m = videoCodecMap; - while (m->name) { - if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:m->name]]) - m_decodableVideoCodecs << m->value; - ++m; - } - } - { - auto *m = audioCodecMap; - while (m->name) { - qDebug() << "audio" << m->name << [AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:m->name]]; - if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:m->name]]) - m_decodableAudioCodecs << m->value; + QList<QMediaFormat::VideoCodec> video; + QList<QMediaFormat::AudioCodec> audio; +// qDebug() << "Media container" << m->name << "supported"; + + auto *v = videoCodecMap; + while (v->name) { + QByteArray extendedMimetype = m->name; + extendedMimetype += v->name; +// qDebug() << "video" << extendedMimetype << [AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]]; + if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]]) + video << v->value; + ++v; + } + + auto *a = audioCodecMap; + while (a->name) { + QByteArray extendedMimetype = m->name; + extendedMimetype += a->name; +// qDebug() << "audio" << extendedMimetype << [AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]]; + if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]]) + audio << a->value; + ++a; + } + // Added manually, see comment in the list above + if (m->value <= QMediaFormat::AAC) + audio << QMediaFormat::AudioCodec::AAC; + if (m->value < QMediaFormat::AAC || m->value == QMediaFormat::MP3) + audio << QMediaFormat::AudioCodec::MP3; + + decoders << CodecMap{ m->value, audio, video }; ++m; } } - // Audio format seems to be symmetric - m_encodableAudioCodecs = m_decodableAudioCodecs; +#if 1 + // ### Verify that this is correct + encoders = decoders; +#else // ### Haven't seen a good way to figure this out. // seems AVFoundation only supports those for encoding - m_encodableMediaContainers << QMediaFormat::MPEG4 << QMediaFormat::QuickTime; - // AVCaptureVideoDataOutput.availableVideoCodecTypes does not mention H265 even though it is supported - m_encodableVideoCodecs << QMediaFormat::VideoCodec::H264 << QMediaFormat::VideoCodec::H265 << QMediaFormat::VideoCodec::MotionJPEG; + encoders = { + { QMediaFormat::MPEG4, + { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3, }, + { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } }, + { QMediaFormat::QuickTime, + { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3, }, + { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } }, + { QMediaFormat::AAC, + { QMediaFormat::AudioCodec::AAC }, + {} }, + { QMediaFormat::MP3, + { QMediaFormat::AudioCodec::MP3 }, + {} }, + { QMediaFormat::FLAC, + { QMediaFormat::AudioCodec::FLAC }, + {} }, + { QMediaFormat::Mpeg4Audio, + { QMediaFormat::AudioCodec::AAC }, + {} } + }; +#endif } QDarwinFormatInfo::~QDarwinFormatInfo() { } -QList<QMediaFormat::FileFormat> QDarwinFormatInfo::decodableMediaContainers() const -{ - return m_decodableMediaContainers; -} - -QList<QMediaFormat::AudioCodec> QDarwinFormatInfo::decodableAudioCodecs() const -{ - return m_decodableAudioCodecs; -} - -QList<QMediaFormat::VideoCodec> QDarwinFormatInfo::decodableVideoCodecs() const -{ - return m_decodableVideoCodecs; -} - -QList<QMediaFormat::FileFormat> QDarwinFormatInfo::encodableMediaContainers() const -{ - return m_encodableMediaContainers; -} - -QList<QMediaFormat::AudioCodec> QDarwinFormatInfo::encodableAudioCodecs() const -{ - return m_encodableAudioCodecs; -} - -QList<QMediaFormat::VideoCodec> QDarwinFormatInfo::encodableVideoCodecs() const -{ - return m_encodableVideoCodecs; -} - int QDarwinFormatInfo::audioFormatForCodec(QMediaFormat::AudioCodec codec) { int codecId = kAudioFormatMPEG4AAC; switch (codec) { case QMediaFormat::AudioCodec::Unspecified: case QMediaFormat::AudioCodec::DolbyTrueHD: - case QMediaFormat::AudioCodec::Opus: case QMediaFormat::AudioCodec::Vorbis: case QMediaFormat::AudioCodec::Wave: - case QMediaFormat::AudioCodec::WindowsMediaAudio: // Unsupported, shouldn't happen. Fall back to AAC case QMediaFormat::AudioCodec::AAC: codecId = kAudioFormatMPEG4AAC; @@ -186,6 +191,10 @@ int QDarwinFormatInfo::audioFormatForCodec(QMediaFormat::AudioCodec codec) break; case QMediaFormat::AudioCodec::FLAC: codecId = kAudioFormatFLAC; + case QMediaFormat::AudioCodec::ALAC: + codecId = kAudioFormatAppleLossless; + case QMediaFormat::AudioCodec::Opus: + codecId = kAudioFormatOpus; break; } return codecId; diff --git a/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h b/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h index ad53c745b..44e03c5b2 100644 --- a/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h +++ b/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h @@ -64,25 +64,8 @@ public: QDarwinFormatInfo(); ~QDarwinFormatInfo(); - QList<QMediaFormat::FileFormat> decodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> decodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> decodableVideoCodecs() const override; - - QList<QMediaFormat::FileFormat> encodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> encodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> encodableVideoCodecs() const override; - static int audioFormatForCodec(QMediaFormat::AudioCodec codec); static NSString *videoFormatForCodec(QMediaFormat::VideoCodec codec); - -private: - QList<QMediaFormat::FileFormat> m_decodableMediaContainers; - QList<QMediaFormat::AudioCodec> m_decodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_decodableVideoCodecs; - - QList<QMediaFormat::FileFormat> m_encodableMediaContainers; - QList<QMediaFormat::AudioCodec> m_encodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_encodableVideoCodecs; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/common.pri b/src/multimedia/platform/gstreamer/common/common.pri index b57cf4d00..54167b557 100644 --- a/src/multimedia/platform/gstreamer/common/common.pri +++ b/src/multimedia/platform/gstreamer/common/common.pri @@ -7,7 +7,6 @@ HEADERS += \ $$PWD/qgstreamerbufferprobe_p.h \ $$PWD/qgstreamervideorendererinterface_p.h \ $$PWD/qgstreamervideorenderer_p.h \ - $$PWD/qgstcodecsinfo_p.h \ $$PWD/qgstreamervideowindow_p.h \ $$PWD/qgstreamervideooverlay_p.h \ $$PWD/qgstreamerplayersession_p.h \ @@ -24,7 +23,6 @@ SOURCES += \ $$PWD/qgstreamerbufferprobe.cpp \ $$PWD/qgstreamervideorendererinterface.cpp \ $$PWD/qgstreamervideorenderer.cpp \ - $$PWD/qgstcodecsinfo.cpp \ $$PWD/qgstreamervideowindow.cpp \ $$PWD/qgstreamervideooverlay.cpp \ $$PWD/qgstreamerplayersession.cpp \ diff --git a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp deleted file mode 100644 index 06e034198..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qgstcodecsinfo_p.h" -#include "qgstutils_p.h" -#include <QtCore/qset.h> - -#include <gst/pbutils/pbutils.h> - -static QSet<QString> streamTypes(GstElementFactory *factory, GstPadDirection direction) -{ - QSet<QString> types; - const GList *pads = gst_element_factory_get_static_pad_templates(factory); - for (const GList *pad = pads; pad; pad = g_list_next(pad)) { - GstStaticPadTemplate *templ = reinterpret_cast<GstStaticPadTemplate *>(pad->data); - if (templ->direction == direction) { - GstCaps *caps = gst_static_caps_get(&templ->static_caps); - for (uint i = 0; i < gst_caps_get_size(caps); ++i) { - GstStructure *structure = gst_caps_get_structure(caps, i); - types.insert(QString::fromUtf8(gst_structure_get_name(structure))); - } - gst_caps_unref(caps); - } - } - - return types; -} - -QGstCodecsInfo::QGstCodecsInfo(QGstCodecsInfo::ElementType elementType) -{ - updateCodecs(elementType); - for (auto &codec : supportedCodecs()) { - GstElementFactory *factory = gst_element_factory_find(codecElement(codec).constData()); - if (factory) { - GstPadDirection direction = elementType == Muxer ? GST_PAD_SINK : GST_PAD_SRC; - m_streamTypes.insert(codec, streamTypes(factory, direction)); - gst_object_unref(GST_OBJECT(factory)); - } - } -} - -QStringList QGstCodecsInfo::supportedCodecs() const -{ - return m_codecs; -} - -QString QGstCodecsInfo::codecDescription(const QString &codec) const -{ - return m_codecInfo.value(codec).description; -} - -QByteArray QGstCodecsInfo::codecElement(const QString &codec) const - -{ - return m_codecInfo.value(codec).elementName; -} - -QStringList QGstCodecsInfo::codecOptions(const QString &codec) const -{ - QStringList options; - - QByteArray elementName = m_codecInfo.value(codec).elementName; - if (elementName.isEmpty()) - return options; - - GstElement *element = gst_element_factory_make(elementName, nullptr); - if (element) { - guint numProperties; - GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(element), - &numProperties); - for (guint j = 0; j < numProperties; ++j) { - GParamSpec *property = properties[j]; - // ignore some properties - if (strcmp(property->name, "name") == 0 || strcmp(property->name, "parent") == 0) - continue; - - options.append(QLatin1String(property->name)); - } - g_free(properties); - gst_object_unref(element); - } - - return options; -} - -void QGstCodecsInfo::updateCodecs(ElementType elementType) -{ - m_codecs.clear(); - m_codecInfo.clear(); - - GstPadDirection padDirection = GST_PAD_SRC; - if (elementType == Demuxer || elementType == AudioDecoder || elementType == VideoDecoder) - padDirection = GST_PAD_SINK; - - GList *elements = elementFactories(elementType); - - QSet<QByteArray> fakeEncoderMimeTypes; - fakeEncoderMimeTypes << "unknown/unknown" - << "audio/x-raw-int" << "audio/x-raw-float" - << "video/x-raw-yuv" << "video/x-raw-rgb"; - - QSet<QByteArray> fieldsToAdd; - fieldsToAdd << "mpegversion" << "layer" << "layout" - << "variant"; - - GList *element = elements; - while (element) { - GstElementFactory *factory = (GstElementFactory *)element->data; - element = element->next; - - const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - if (padTemplate->direction == padDirection) { - GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps); - for (uint i=0; i<gst_caps_get_size(caps); i++) { - const GstStructure *structure = gst_caps_get_structure(caps, i); - - //skip "fake" encoders - if (fakeEncoderMimeTypes.contains(gst_structure_get_name(structure))) - continue; - - GstStructure *newStructure = qt_gst_structure_new_empty(gst_structure_get_name(structure)); - - //add structure fields to distinguish between formats with similar mime types, - //like audio/mpeg - for (int j=0; j<gst_structure_n_fields(structure); j++) { - const gchar* fieldName = gst_structure_nth_field_name(structure, j); - if (fieldsToAdd.contains(fieldName)) { - const GValue *value = gst_structure_get_value(structure, fieldName); - GType valueType = G_VALUE_TYPE(value); - - //don't add values of range type, - //gst_pb_utils_get_codec_description complains about not fixed caps - - if (valueType != GST_TYPE_INT_RANGE && valueType != GST_TYPE_DOUBLE_RANGE && - valueType != GST_TYPE_FRACTION_RANGE && valueType != GST_TYPE_LIST && - valueType != GST_TYPE_ARRAY) - gst_structure_set_value(newStructure, fieldName, value); - } - } - - GstCaps *newCaps = gst_caps_new_full(newStructure, nullptr); - - gchar *capsString = gst_caps_to_string(newCaps); - QString codec = QLatin1String(capsString); - if (capsString) - g_free(capsString); - // skip stuff that's not really a known audio/video codec - if (codec.startsWith("application")) - continue; - GstRank rank = GstRank(gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory))); - - // If two elements provide the same codec, use the highest ranked one - QMap<QString, CodecInfo>::const_iterator it = m_codecInfo.constFind(codec); - if (it == m_codecInfo.constEnd() || it->rank < rank) { - if (it == m_codecInfo.constEnd()) - m_codecs.append(codec); - - CodecInfo info; - info.elementName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); - - gchar *description = gst_pb_utils_get_codec_description(newCaps); - info.description = QString::fromUtf8(description); - if (description) - g_free(description); - - info.rank = rank; - - m_codecInfo.insert(codec, info); - } - - gst_caps_unref(newCaps); - } - gst_caps_unref(caps); - } - } - } - - gst_plugin_feature_list_free(elements); -} - -GList *QGstCodecsInfo::elementFactories(ElementType elementType) const -{ - GstElementFactoryListType gstElementType = 0; - switch (elementType) { - case AudioEncoder: - gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER; - break; - case VideoEncoder: - // GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER also lists image encoders. We don't want these here. - gstElementType = (GstElementFactoryListType)(GST_ELEMENT_FACTORY_TYPE_ENCODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO); - break; - case Muxer: - gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER; - break; - case AudioDecoder: - gstElementType = (GstElementFactoryListType)(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO); - break; - case VideoDecoder: - gstElementType = (GstElementFactoryListType)(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO); - break; - case Demuxer: - gstElementType = GST_ELEMENT_FACTORY_TYPE_DEMUXER; - break; - } - - GList *list = gst_element_factory_list_get_elements(gstElementType, GST_RANK_MARGINAL); - if (elementType == AudioEncoder) { - // Manually add "audioconvert" to the list - // to allow linking with various containers. - auto factory = gst_element_factory_find("audioconvert"); - if (factory) - list = g_list_prepend(list, factory); - } - - return list; -} - -QSet<QString> QGstCodecsInfo::supportedStreamTypes(const QString &codec) const -{ - return m_streamTypes.value(codec); -} - -QStringList QGstCodecsInfo::supportedCodecs(const QSet<QString> &types) const -{ - QStringList result; - for (auto &candidate : supportedCodecs()) { - auto candidateTypes = supportedStreamTypes(candidate); - if (candidateTypes.intersects(types)) - result << candidate; - } - - return result; -} diff --git a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h deleted file mode 100644 index fb2fb71d9..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGSTCODECSINFO_H -#define QGSTCODECSINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <QtCore/qmap.h> -#include <QtCore/qstringlist.h> -#include <QSet> - -#include <gst/gst.h> - -QT_BEGIN_NAMESPACE - -class Q_MULTIMEDIA_EXPORT QGstCodecsInfo -{ -public: - enum ElementType { AudioEncoder, VideoEncoder, Muxer, AudioDecoder, VideoDecoder, Demuxer }; - - struct CodecInfo { - QString description; - QByteArray elementName; - GstRank rank; - }; - - QGstCodecsInfo(ElementType elementType); - - QStringList supportedCodecs() const; - QString codecDescription(const QString &codec) const; - QByteArray codecElement(const QString &codec) const; - QStringList codecOptions(const QString &codec) const; - QSet<QString> supportedStreamTypes(const QString &codec) const; - QStringList supportedCodecs(const QSet<QString> &types) const; - -private: - void updateCodecs(ElementType elementType); - GList *elementFactories(ElementType elementType) const; - - QStringList m_codecs; - QMap<QString, CodecInfo> m_codecInfo; - QMap<QString, QSet<QString>> m_streamTypes; -}; - -Q_DECLARE_TYPEINFO(QGstCodecsInfo::CodecInfo, Q_RELOCATABLE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstutils_p.h b/src/multimedia/platform/gstreamer/common/qgstutils_p.h index 7b6b6ded1..5cf5035ea 100644 --- a/src/multimedia/platform/gstreamer/common/qgstutils_p.h +++ b/src/multimedia/platform/gstreamer/common/qgstutils_p.h @@ -195,6 +195,10 @@ class QGstCaps { public: QGstCaps(const GstCaps *c) : caps(c) {} const GstCaps *caps; + void unref() { + gst_caps_unref(const_cast<GstCaps *>(caps)); + caps = nullptr; + } bool isNull() const { return !caps; } diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp index 193ccaa34..131e18e80 100644 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp +++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp @@ -129,25 +129,17 @@ GstElement *QGstreamerCaptureSession::buildFileSink() static GstEncodingContainerProfile *createContainerProfile(const QMediaEncoderSettings &settings) { - GstCaps *caps = nullptr; - auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - if (!formatInfo->encodableMediaContainers().contains(settings.format())) - return nullptr; - - const char *format = formatInfo->nativeFormat(settings.format()); - Q_ASSERT(format); - - caps = gst_caps_from_string(format); + QGstCaps caps = formatInfo->formatCaps(settings.format()); GstEncodingContainerProfile *profile = (GstEncodingContainerProfile *)gst_encoding_container_profile_new( "container_profile", (gchar *)"custom container profile", - caps, + const_cast<GstCaps *>(caps.caps), NULL); //preset - gst_caps_unref(caps); + caps.unref(); return profile; } @@ -156,23 +148,17 @@ static GstEncodingProfile *createVideoProfile(const QMediaEncoderSettings &setti { auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - if (!formatInfo->encodableVideoCodecs().contains(settings.videoCodec())) - return nullptr; - - const char *codec = formatInfo->nativeFormat(settings.videoCodec()); - Q_ASSERT(codec); - GstCaps *caps = gst_caps_from_string(codec); - - if (!caps) + QGstCaps caps = formatInfo->videoCaps(settings); + if (caps.isNull()) return nullptr; GstEncodingVideoProfile *profile = gst_encoding_video_profile_new( - caps, + const_cast<GstCaps *>(caps.caps), nullptr, NULL, //restriction 0); //presence - gst_caps_unref(caps); + caps.unref(); gst_encoding_video_profile_set_pass(profile, 0); gst_encoding_video_profile_set_variableframerate(profile, TRUE); @@ -184,21 +170,17 @@ static GstEncodingProfile *createAudioProfile(const QMediaEncoderSettings &setti { auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - if (!formatInfo->encodableAudioCodecs().contains(settings.audioCodec())) + QGstCaps caps = formatInfo->audioCaps(settings); + if (caps.isNull()) return nullptr; - const char *codec = formatInfo->nativeFormat(settings.audioCodec()); - Q_ASSERT(codec); - - GstCaps *caps = gst_caps_from_string(codec); - GstEncodingProfile *profile = (GstEncodingProfile *)gst_encoding_audio_profile_new( - caps, + const_cast<GstCaps *>(caps.caps), nullptr, //preset NULL, //restriction 0); //presence - gst_caps_unref(caps); + caps.unref(); return profile; } diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp index 7c60ed4d1..fabf6cf20 100644 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp +++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp @@ -42,6 +42,7 @@ #include <QtGui/qdesktopservices.h> #include <QStandardPaths> #include "qaudiodeviceinfo.h" +#include <qmimetype.h> QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session) : QMediaRecorderControl(session), @@ -154,9 +155,9 @@ void QGstreamerRecorderControl::record() m_state = QMediaRecorder::RecordingState; if (m_outputLocation.isEmpty()) { - QString container;// ### = m_session->mediaContainerControl()->containerExtension(); + QString container = resolvedEncoderSettings().mimeType().preferredSuffix(); if (container.isEmpty()) - container = "raw"; + container = QString::fromLatin1("raw"); m_session->setOutputLocation(QUrl(generateFileName(defaultDir(), container))); } @@ -232,12 +233,10 @@ void QGstreamerRecorderControl::setEncoderSettings(const QMediaEncoderSettings & QMediaEncoderSettings QGstreamerRecorderControl::resolvedEncoderSettings() const { QMediaEncoderSettings f = m_settings; - f.resolveFormat(m_session->captureMode() & QGstreamerCaptureSession::Video ? - QMediaEncoderSettings::AudioAndVideo : QMediaEncoderSettings::AudioOnly); + f.resolveFormat(); return f; } - bool QGstreamerRecorderControl::isMuted() const { return m_session->isMuted(); diff --git a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp index 1e5c41969..b0fd8e53e 100644 --- a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp +++ b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp @@ -40,135 +40,308 @@ #include "qgstreamerformatsinfo_p.h" #include "private/qgstutils_p.h" -#include "private/qgstcodecsinfo_p.h" QT_BEGIN_NAMESPACE -static struct { - const char *name; - QMediaFormat::FileFormat value; -} videoFormatsMap[] = { - { "video/x-ms-asf", QMediaFormat::FileFormat::ASF }, - { "video/x-msvideo", QMediaFormat::FileFormat::AVI }, - { "video/x-matroska", QMediaFormat::FileFormat::Matroska }, - { "video/mpeg", QMediaFormat::FileFormat::MPEG4 }, - { "video/quicktime", QMediaFormat::FileFormat::QuickTime }, - { "video/ogg", QMediaFormat::FileFormat::Ogg }, - { "video/webm", QMediaFormat::FileFormat::WebM }, - { nullptr, QMediaFormat::FileFormat::UnspecifiedFormat } -}; - -static struct { - const char *name; - QMediaFormat::FileFormat value; -} audioFormatsMap[] = { - { "audio/mpeg, mpegversion=(int)1, layer=(int)3", QMediaFormat::FileFormat::MP3 }, - { "audio/mpeg, mpegversion=(int)4", QMediaFormat::FileFormat::AAC }, - { "audio/x-flac", QMediaFormat::FileFormat::FLAC }, - { "audio/x-alac", QMediaFormat::FileFormat::ALAC }, - { nullptr, QMediaFormat::FileFormat::UnspecifiedFormat }, -}; - -static struct { - const char *name; - QMediaFormat::VideoCodec value; -} videoCodecMap[] = { - { "video/mpeg, mpegversion=(int)1", QMediaFormat::VideoCodec::MPEG1 }, - { "video/mpeg, mpegversion=(int)2", QMediaFormat::VideoCodec::MPEG2 }, - { "video/mpeg, mpegversion=(int)4", QMediaFormat::VideoCodec::MPEG4 }, - { "video/x-h264", QMediaFormat::VideoCodec::H264 }, - { "video/x-h265", QMediaFormat::VideoCodec::H265 }, - { "video/x-vp8", QMediaFormat::VideoCodec::VP8 }, - { "video/x-vp9", QMediaFormat::VideoCodec::VP9 }, - { "video/x-av1", QMediaFormat::VideoCodec::AV1 }, - { "video/x-theora", QMediaFormat::VideoCodec::Theora }, - { "video/x-jpeg", QMediaFormat::VideoCodec::MotionJPEG }, - { nullptr, QMediaFormat::VideoCodec::Unspecified } -}; - -static struct { - const char *name; - QMediaFormat::AudioCodec value; -} audioCodecMap[] = { - { "audio/mpeg, mpegversion=(int)1, layer=(int)3", QMediaFormat::AudioCodec::MP3 }, - { "audio/mpeg, mpegversion=(int)4", QMediaFormat::AudioCodec::AAC }, - { "audio/x-ac3", QMediaFormat::AudioCodec::AC3 }, - { "audio/x-eac3", QMediaFormat::AudioCodec::EAC3 }, - { "audio/x-flac", QMediaFormat::AudioCodec::FLAC }, - { "audio/x-alac", QMediaFormat::AudioCodec::ALAC }, - { "audio/x-true-hd", QMediaFormat::AudioCodec::DolbyTrueHD }, - { "audio/x-vorbis", QMediaFormat::AudioCodec::Vorbis }, - { nullptr, QMediaFormat::AudioCodec::Unspecified }, -}; - -template<typename Map, typename Hash> -static auto getList(QGstCodecsInfo::ElementType type, Map *map, Hash &hash) +static QMediaFormat::AudioCodec audioCodecForCaps(QGstStructure structure) { - using T = decltype(map->value); - QList<T> list; - QGstCodecsInfo info(type); - auto codecs = info.supportedCodecs(); - for (const auto &c : codecs) { - Map *m = map; - while (m->name) { - if (m->name == c.toLatin1()) { - list.append(m->value); - hash.insert(m->value, m->name); - break; - } - ++m; + const char *name = structure.name(); + + if (!name || strncmp(name, "audio/", 6)) + return QMediaFormat::AudioCodec::Unspecified; + name += 6; + if (!strcmp(name, "mpeg")) { + auto version = structure["mpegversion"].toInt(); + if (version == 1) { + auto layer = structure["layer"].toInt(); + if (layer == 3) + return QMediaFormat::AudioCodec::MP3; } + if (version == 4) + return QMediaFormat::AudioCodec::AAC; + } else if (!strcmp(name, "x-ac3")) { + return QMediaFormat::AudioCodec::AC3; + } else if (!strcmp(name, "x-eac3")) { + return QMediaFormat::AudioCodec::EAC3; + } else if (!strcmp(name, "x-flac")) { + return QMediaFormat::AudioCodec::FLAC; + } else if (!strcmp(name, "x-alac")) { + return QMediaFormat::AudioCodec::ALAC; + } else if (!strcmp(name, "x-true-hd")) { + return QMediaFormat::AudioCodec::DolbyTrueHD; + } else if (!strcmp(name, "x-vorbis")) { + return QMediaFormat::AudioCodec::Vorbis; + } else if (!strcmp(name, "x-opus")) { + return QMediaFormat::AudioCodec::Opus; + } else if (!strcmp(name, "x-wav")) { + return QMediaFormat::AudioCodec::Wave; } - return list; + return QMediaFormat::AudioCodec::Unspecified; } -QGstreamerFormatsInfo::QGstreamerFormatsInfo() +static QMediaFormat::VideoCodec videoCodecForCaps(QGstStructure structure) +{ + const char *name = structure.name(); + + if (!name || strncmp(name, "video/", 6)) + return QMediaFormat::VideoCodec::Unspecified; + name += 6; + + if (!strcmp(name, "mpeg")) { + auto version = structure["mpegversion"].toInt(); + if (version == 1) + return QMediaFormat::VideoCodec::MPEG1; + else if (version == 2) + return QMediaFormat::VideoCodec::MPEG2; + else if (version == 4) + return QMediaFormat::VideoCodec::MPEG4; + } else if (!strcmp(name, "x-h264")) { + return QMediaFormat::VideoCodec::H264; + } else if (!strcmp(name, "x-h265")) { + return QMediaFormat::VideoCodec::H265; + } else if (!strcmp(name, "x-vp8")) { + return QMediaFormat::VideoCodec::VP8; + } else if (!strcmp(name, "x-vp9")) { + return QMediaFormat::VideoCodec::VP9; + } else if (!strcmp(name, "x-av1")) { + return QMediaFormat::VideoCodec::AV1; + } else if (!strcmp(name, "x-theora")) { + return QMediaFormat::VideoCodec::Theora; + } else if (!strcmp(name, "x-jpeg")) { + return QMediaFormat::VideoCodec::MotionJPEG; + } + return QMediaFormat::VideoCodec::Unspecified; +} + +static QMediaFormat::FileFormat fileFormatForCaps(QGstStructure structure) { - m_decodableMediaContainers = getList(QGstCodecsInfo::Demuxer, videoFormatsMap, formatToCaps); - m_decodableMediaContainers.append(getList(QGstCodecsInfo::AudioDecoder, audioFormatsMap, formatToCaps)); - m_decodableAudioCodecs = getList(QGstCodecsInfo::AudioDecoder, audioCodecMap, audioToCaps); - m_decodableVideoCodecs = getList(QGstCodecsInfo::VideoDecoder, videoCodecMap, videoToCaps); - - m_encodableMediaContainers = getList(QGstCodecsInfo::Muxer, videoFormatsMap, formatToCaps); - m_encodableMediaContainers.append(getList(QGstCodecsInfo::AudioEncoder, audioFormatsMap, formatToCaps)); - m_encodableAudioCodecs = getList(QGstCodecsInfo::AudioEncoder, audioCodecMap, audioToCaps); - m_encodableVideoCodecs = getList(QGstCodecsInfo::VideoEncoder, videoCodecMap, videoToCaps); + const char *name = structure.name(); + + if (!strcmp(name, "video/x-ms-asf")) { + return QMediaFormat::FileFormat::ASF; + } else if (!strcmp(name, "video/x-msvideo")) { + return QMediaFormat::FileFormat::AVI; + } else if (!strcmp(name, "video/x-matroska")) { + return QMediaFormat::FileFormat::Matroska; + } else if (!strcmp(name, "video/quicktime")) { + auto variant = structure["variant"].toString(); + if (!variant) + return QMediaFormat::FileFormat::QuickTime; + else if (!strcmp(variant, "iso")) + return QMediaFormat::FileFormat::MPEG4; + } else if (!strcmp(name, "video/ogg")) { + return QMediaFormat::FileFormat::Ogg; + } else if (!strcmp(name, "video/webm")) { + return QMediaFormat::FileFormat::WebM; + } + return QMediaFormat::UnspecifiedFormat; } -QGstreamerFormatsInfo::~QGstreamerFormatsInfo() +static QPair<QList<QMediaFormat::AudioCodec>, QList<QMediaFormat::VideoCodec>> getCodecsList(bool decode) { + QList<QMediaFormat::AudioCodec> audio; + QList<QMediaFormat::VideoCodec> video; + GstPadDirection padDirection = decode ? GST_PAD_SINK : GST_PAD_SRC; + + GList *elementList = gst_element_factory_list_get_elements(decode ? GST_ELEMENT_FACTORY_TYPE_DECODER : GST_ELEMENT_FACTORY_TYPE_ENCODER, + GST_RANK_MARGINAL); + + GList *element = elementList; + while (element) { + GstElementFactory *factory = (GstElementFactory *)element->data; + element = element->next; + + const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + if (padTemplate->direction == padDirection) { + QGstCaps caps = gst_static_caps_get(&padTemplate->static_caps); + + for (int i = 0; i < caps.size(); i++) { + QGstStructure structure = caps.at(i); + auto a = audioCodecForCaps(structure); + if (a != QMediaFormat::AudioCodec::Unspecified) + audio.append(a); + auto v = videoCodecForCaps(structure); + if (v != QMediaFormat::VideoCodec::Unspecified) + video.append(v); + } + caps.unref(); + } + } + } + gst_plugin_feature_list_free(elementList); + return {audio, video}; } -QList<QMediaFormat::FileFormat> QGstreamerFormatsInfo::decodableMediaContainers() const +QList<QGstreamerFormatsInfo::CodecMap> QGstreamerFormatsInfo::getMuxerList(bool demuxer, + QList<QMediaFormat::AudioCodec> supportedAudioCodecs, + QList<QMediaFormat::VideoCodec> supportedVideoCodecs) +{ + QList<QGstreamerFormatsInfo::CodecMap> muxers; + + GstPadDirection padDirection = demuxer ? GST_PAD_SINK : GST_PAD_SRC; + + GList *elementList = gst_element_factory_list_get_elements(demuxer ? GST_ELEMENT_FACTORY_TYPE_DEMUXER : GST_ELEMENT_FACTORY_TYPE_MUXER, + GST_RANK_MARGINAL); + GList *element = elementList; + while (element) { + GstElementFactory *factory = (GstElementFactory *)element->data; + element = element->next; + + QList<QMediaFormat::FileFormat> fileFormats; + + const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + if (padTemplate->direction == padDirection) { + QGstCaps caps = gst_static_caps_get(&padTemplate->static_caps); + + for (int i = 0; i < caps.size(); i++) { + QGstStructure structure = caps.at(i); + auto fmt = fileFormatForCaps(structure); + if (fmt != QMediaFormat::UnspecifiedFormat) + fileFormats.append(fmt); + } + caps.unref(); + } + } + if (fileFormats.isEmpty()) + continue; + + QList<QMediaFormat::AudioCodec> audioCodecs; + QList<QMediaFormat::VideoCodec> videoCodecs; + + padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + // check the other side for supported inputs/outputs + if (padTemplate->direction != padDirection) { + QGstCaps caps = gst_static_caps_get(&padTemplate->static_caps); + + for (int i = 0; i < caps.size(); i++) { + QGstStructure structure = caps.at(i); + auto audio = audioCodecForCaps(structure); + if (audio != QMediaFormat::AudioCodec::Unspecified && supportedAudioCodecs.contains(audio)) + audioCodecs.append(audio); + auto video = videoCodecForCaps(structure); + if (video != QMediaFormat::VideoCodec::Unspecified && supportedVideoCodecs.contains(video)) + videoCodecs.append(video); + } + caps.unref(); + } + } + if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) { + for (auto f : qAsConst(fileFormats)) + muxers.append({f, audioCodecs, videoCodecs}); + } + } + gst_plugin_feature_list_free(elementList); + return muxers; +} + +#if 0 +static void dumpMuxers(const QList<QGstreamerFormatsInfo::CodecMap> &muxerList) { - return m_decodableMediaContainers; + for (const auto &m : muxerList) { + qDebug() << " " << QMediaFormat::fileFormatName(m.format); + qDebug() << " Audio"; + for (const auto &a : m.audio) + qDebug() << " " << QMediaFormat::audioCodecName(a); + qDebug() << " Video"; + for (const auto &v : m.video) + qDebug() << " " << QMediaFormat::videoCodecName(v); + } + } +#endif -QList<QMediaFormat::AudioCodec> QGstreamerFormatsInfo::decodableAudioCodecs() const +QGstreamerFormatsInfo::QGstreamerFormatsInfo() { - return m_decodableAudioCodecs; + auto codecs = getCodecsList(/*decode = */ true); + decoders = getMuxerList(true, codecs.first, codecs.second); + + codecs = getCodecsList(/*decode = */ false); + encoders = getMuxerList(/* demuxer = */false, codecs.first, codecs.second); + //dumpMuxers(encoders); } -QList<QMediaFormat::VideoCodec> QGstreamerFormatsInfo::decodableVideoCodecs() const +QGstreamerFormatsInfo::~QGstreamerFormatsInfo() { - return m_decodableVideoCodecs; } -QList<QMediaFormat::FileFormat> QGstreamerFormatsInfo::encodableMediaContainers() const +QGstCaps QGstreamerFormatsInfo::formatCaps(const QMediaFormat &f) const { - return m_encodableMediaContainers; + auto format = f.format(); + Q_ASSERT(format != QMediaFormat::UnspecifiedFormat); + + const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = { + "video/x-ms-asf", // ASF + "video/x-msvideo", // AVI + "video/x-matroska", // Matroska + "video/quicktime, variant=(string)iso", // MPEG4 + "video/ogg", // Ogg + "video/quicktime", // QuickTime + "video/webm", // WebM + "audio/mpeg, mpegversion=(int)4", // AAC + "audio/x-flac", // FLAC + "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3 + "audio/mpeg, mpegversion=(int)4", // Mpeg4Audio + "audio/x-alac", // ALAC + "audio/x-wav" // Wave + + }; + return gst_caps_from_string(capsForFormat[format]); } -QList<QMediaFormat::AudioCodec> QGstreamerFormatsInfo::encodableAudioCodecs() const +QGstCaps QGstreamerFormatsInfo::audioCaps(const QMediaFormat &f) const { - return m_encodableAudioCodecs; + auto codec = f.audioCodec(); + if (codec == QMediaFormat::AudioCodec::Unspecified) + return nullptr; + + const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = { + "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3 + "audio/mpeg, mpegversion=(int)4", // AAC + "audio/x-ac3", // AC3 + "audio/x-eac3", // EAC3 + "audio/x-flac", // FLAC + "audio/x-true-hd", // DolbyTrueHD + "audio/x-opus", // Opus + "audio/x-vorbis", // Vorbis + "audio/x-alac", // ALAC + "audio/x-wav", // WAVE + }; + return gst_caps_from_string(capsForCodec[(int)codec]); } -QList<QMediaFormat::VideoCodec> QGstreamerFormatsInfo::encodableVideoCodecs() const +QGstCaps QGstreamerFormatsInfo::videoCaps(const QMediaFormat &f) const { - return m_encodableVideoCodecs; + auto codec = f.videoCodec(); + if (codec == QMediaFormat::VideoCodec::Unspecified) + return nullptr; + + const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = { + "video/mpeg, mpegversion=(int)1", // MPEG1, + "video/mpeg, mpegversion=(int)2", // MPEG2, + "video/mpeg, mpegversion=(int)4", // MPEG4, + "video/x-h264", // H264, + "video/x-h265", // H265, + "video/x-vp8", // VP8, + "video/x-vp9", // VP9, + "video/x-av1", // AV1, + "video/x-theora", // Theora, + "video/x-jpeg", // MotionJPEG, + }; + return gst_caps_from_string(capsForCodec[(int)codec]); } QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h index e81319532..cb9d4d880 100644 --- a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h +++ b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h @@ -54,6 +54,7 @@ #include <private/qmediaplatformformatinfo_p.h> #include <qhash.h> #include <qlist.h> +#include <private/qgstutils_p.h> QT_BEGIN_NAMESPACE @@ -63,30 +64,14 @@ public: QGstreamerFormatsInfo(); ~QGstreamerFormatsInfo(); - QList<QMediaFormat::FileFormat> decodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> decodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> decodableVideoCodecs() const override; + QGstCaps formatCaps(const QMediaFormat &f) const; + QGstCaps audioCaps(const QMediaFormat &f) const; + QGstCaps videoCaps(const QMediaFormat &f) const; + // ### +// QGstCaps audioEncoderCaps(const QMediaEncoderSettings &f) const; +// QGstCaps videoEncoderCaps(const QMediaEncoderSettings &f) const; - QList<QMediaFormat::FileFormat> encodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> encodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> encodableVideoCodecs() const override; - - const char *nativeFormat(QMediaFormat::FileFormat f) const { return formatToCaps.value(f); } - const char *nativeFormat(QMediaFormat::AudioCodec c) const { return audioToCaps.value(c); } - const char *nativeFormat(QMediaFormat::VideoCodec c) const { return videoToCaps.value(c); } - -private: - QList<QMediaFormat::FileFormat> m_decodableMediaContainers; - QList<QMediaFormat::AudioCodec> m_decodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_decodableVideoCodecs; - - QList<QMediaFormat::FileFormat> m_encodableMediaContainers; - QList<QMediaFormat::AudioCodec> m_encodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_encodableVideoCodecs; - - QHash<QMediaFormat::FileFormat, const char *> formatToCaps; - QHash<QMediaFormat::AudioCodec, const char *> audioToCaps; - QHash<QMediaFormat::VideoCodec, const char *> videoToCaps; + QList<CodecMap> getMuxerList(bool demuxer, QList<QMediaFormat::AudioCodec> audioCodecs, QList<QMediaFormat::VideoCodec> videoCodecs); }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qmediaplatformformatinfo.cpp b/src/multimedia/platform/qmediaplatformformatinfo.cpp index e157a2455..20a5d580c 100644 --- a/src/multimedia/platform/qmediaplatformformatinfo.cpp +++ b/src/multimedia/platform/qmediaplatformformatinfo.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qmediaplatformformatinfo_p.h" +#include <qset.h> QT_BEGIN_NAMESPACE @@ -45,4 +46,72 @@ QMediaPlatformFormatInfo::QMediaPlatformFormatInfo() = default; QMediaPlatformFormatInfo::~QMediaPlatformFormatInfo() = default; +QList<QMediaFormat::FileFormat> QMediaPlatformFormatInfo::supportedFileFormats(const QMediaFormat &constraints, QMediaFormat::ConversionMode m) const +{ + QSet<QMediaFormat::FileFormat> formats; + + const auto &codecMap = (m == QMediaFormat::Encode) ? encoders : decoders; + for (const auto &m : codecMap) { + if (constraints.mode() == QMediaFormat::AudioAndVideo && m.video.isEmpty()) + continue; + if (constraints.audioCodec() != QMediaFormat::AudioCodec::Unspecified && !m.audio.contains(constraints.audioCodec())) + continue; + if (constraints.videoCodec() != QMediaFormat::VideoCodec::Unspecified && !m.video.contains(constraints.videoCodec())) + continue; + formats.insert(m.format); + } + return formats.values(); +} + +QList<QMediaFormat::AudioCodec> QMediaPlatformFormatInfo::supportedAudioCodecs(const QMediaFormat &constraints, QMediaFormat::ConversionMode m) const +{ + QSet<QMediaFormat::AudioCodec> codecs; + + const auto &codecMap = (m == QMediaFormat::Encode) ? encoders : decoders; + for (const auto &m : codecMap) { + if (constraints.format() != QMediaFormat::UnspecifiedFormat && m.format != constraints.format()) + continue; + if (constraints.videoCodec() != QMediaFormat::VideoCodec::Unspecified && !m.video.contains(constraints.videoCodec())) + continue; + for (const auto &c : m.audio) + codecs.insert(c); + } + return codecs.values(); +} + +QList<QMediaFormat::VideoCodec> QMediaPlatformFormatInfo::supportedVideoCodecs(const QMediaFormat &constraints, QMediaFormat::ConversionMode m) const +{ + if (constraints.mode() == QMediaFormat::AudioOnly) + return {}; + + QSet<QMediaFormat::VideoCodec> codecs; + + const auto &codecMap = (m == QMediaFormat::Encode) ? encoders : decoders; + for (const auto &m : codecMap) { + if (constraints.format() != QMediaFormat::UnspecifiedFormat && m.format != constraints.format()) + continue; + if (constraints.audioCodec() != QMediaFormat::AudioCodec::Unspecified && !m.audio.contains(constraints.audioCodec())) + continue; + for (const auto &c : m.video) + codecs.insert(c); + } + return codecs.values(); +} + +bool QMediaPlatformFormatInfo::isSupported(const QMediaFormat &format, QMediaFormat::ConversionMode m) const +{ + const auto &codecMap = (m == QMediaFormat::Encode) ? encoders : decoders; + + for (const auto &m : codecMap) { + if (m.format != format.format()) + continue; + if (!m.audio.contains(format.audioCodec())) + continue; + if (format.mode() == QMediaFormat::AudioAndVideo && !m.video.contains(format.videoCodec())) + continue; + return true; + } + return false; +} + QT_END_NAMESPACE diff --git a/src/multimedia/platform/qmediaplatformformatinfo_p.h b/src/multimedia/platform/qmediaplatformformatinfo_p.h index 0a3d06cb3..1cba1b086 100644 --- a/src/multimedia/platform/qmediaplatformformatinfo_p.h +++ b/src/multimedia/platform/qmediaplatformformatinfo_p.h @@ -62,13 +62,19 @@ public: QMediaPlatformFormatInfo(); virtual ~QMediaPlatformFormatInfo(); - virtual QList<QMediaFormat::FileFormat> decodableMediaContainers() const = 0; - virtual QList<QMediaFormat::AudioCodec> decodableAudioCodecs() const = 0; - virtual QList<QMediaFormat::VideoCodec> decodableVideoCodecs() const = 0; + QList<QMediaFormat::FileFormat> supportedFileFormats(const QMediaFormat &constraints, QMediaFormat::ConversionMode m) const; + QList<QMediaFormat::AudioCodec> supportedAudioCodecs(const QMediaFormat &constraints, QMediaFormat::ConversionMode m) const; + QList<QMediaFormat::VideoCodec> supportedVideoCodecs(const QMediaFormat &constraints, QMediaFormat::ConversionMode m) const; - virtual QList<QMediaFormat::FileFormat> encodableMediaContainers() const = 0; - virtual QList<QMediaFormat::AudioCodec> encodableAudioCodecs() const = 0; - virtual QList<QMediaFormat::VideoCodec> encodableVideoCodecs() const = 0; + bool isSupported(const QMediaFormat &format, QMediaFormat::ConversionMode m) const; + + struct CodecMap { + QMediaFormat::FileFormat format; + QList<QMediaFormat::AudioCodec> audio; + QList<QMediaFormat::VideoCodec> video; + }; + QList<CodecMap> encoders; + QList<CodecMap> decoders; }; QT_END_NAMESPACE diff --git a/src/multimedia/qmediaformat.cpp b/src/multimedia/qmediaformat.cpp index 80fe154b0..6ae2314be 100644 --- a/src/multimedia/qmediaformat.cpp +++ b/src/multimedia/qmediaformat.cpp @@ -46,110 +46,6 @@ QT_BEGIN_NAMESPACE namespace { -// info from https://en.wikipedia.org/wiki/Comparison_of_video_container_formats -constexpr bool audioSupportMatrix[QMediaFormat::FileFormat::LastFileFormat + 1][(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = -{ - // MP3, AAC, AC3, EAC3, FLAC, DTHD, Opus,Vorbis, Wave, ALAC - // Container formats (Audio and Video) - { true, true, true, true, true, false, true, false, false, true }, // ASF - { true, true, true, false, true, false, true, false, false, true }, // AVI, - { true, true, true, true, true, true, true, true, false, true }, // Matroska, - { true, true, true, true, true, true, true, false, false, true }, // MPEG4, - { false, false, false, false, true, false, true, true, false, false }, // Ogg, - { true, true, true, true, false, false, false, false, false, true }, // QuickTime, - { false, false, false, false, true, false, true, true, false, false }, // WebM, - // Audio Formats - { false, true, false, false, false, false, false, false, false, false }, // AAC, - { false, false, false, false, true, false, false, false, false, false }, // FLAC, - { true, false, false, false, false, false, false, false, false, false }, // Mpeg3, - { true, true, true, true, true, true, true, false, false, true }, // Mpeg4Audio, - { false, false, false, false, false, false, false, false, false, true }, // ALAC, - { false, false, false, false, false, false, false, false, true, false }, // Wave, -}; - -inline bool formatSupportsCodec(QMediaFormat::FileFormat format, QMediaFormat::AudioCodec codec) -{ - return audioSupportMatrix[format][(int)codec]; -} - -// info from https://en.wikipedia.org/wiki/Comparison_of_video_container_formats -constexpr bool videoSupportMatrix[QMediaFormat::FileFormat::LastFileFormat + 1][(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = -{ - //MPEG1, MPEG2, MPEG4, H264, H265, VP8, VP9, AV1,Theora, MotionJPEG, - // Container formats (Audio and Video) - { true, true, true, true, true, false, false, false, false,false }, // ASF - { true, true, true, true, true, true, true, false, true, true }, // AVI, - { true, true, true, true, true, true, true, true, true, false }, // Matroska, - { true, true, true, true, true, true, true, true, true, true }, // MPEG4, - { false, true, true, true, true, false, false, false, false, true }, // Ogg, - { false, false, false, false, false, false, false, false, true, false }, // QuickTime, - { false, false, false, false, false, true, true, true, false, false }, // WebM, - // Audio Formats - { false, false, false, false, false, false, false, false, false, false }, // AAC, - { false, false, false, false, false, false, false, false, false, false }, // FLAC, - { false, false, false, false, false, false, false, false, false, false }, // Mpeg3, - { false, false, false, false, false, false, false, false, false, false }, // Mpeg4Audio, - { false, false, false, false, false, false, false, false, false, false }, // ALAC, - { false, false, false, false, false, false, false, false, false, false }, // Wave, -}; - -inline bool formatSupportsCodec(QMediaFormat::FileFormat format, QMediaFormat::VideoCodec codec) -{ - return videoSupportMatrix[format][(int)codec]; -} - - -constexpr QMediaFormat::FileFormat videoFormatPriorityList[] = -{ - QMediaFormat::MPEG4, - QMediaFormat::QuickTime, - QMediaFormat::AVI, - QMediaFormat::WebM, - QMediaFormat::ASF, - QMediaFormat::Matroska, - QMediaFormat::Ogg, - QMediaFormat::UnspecifiedFormat -}; - -constexpr QMediaFormat::FileFormat audioFormatPriorityList[] = -{ - QMediaFormat::AAC, - QMediaFormat::MP3, - QMediaFormat::Mpeg4Audio, - QMediaFormat::FLAC, - QMediaFormat::ALAC, - QMediaFormat::Wave, - QMediaFormat::UnspecifiedFormat -}; - -constexpr QMediaFormat::AudioCodec audioPriorityList[] = -{ - QMediaFormat::AudioCodec::AAC, - QMediaFormat::AudioCodec::MP3, - QMediaFormat::AudioCodec::AC3, - QMediaFormat::AudioCodec::Opus, - QMediaFormat::AudioCodec::EAC3, - QMediaFormat::AudioCodec::DolbyTrueHD, - QMediaFormat::AudioCodec::FLAC, - QMediaFormat::AudioCodec::Vorbis, - QMediaFormat::AudioCodec::Wave, - QMediaFormat::AudioCodec::Unspecified -}; - -constexpr QMediaFormat::VideoCodec videoPriorityList[] = -{ - QMediaFormat::VideoCodec::H265, - QMediaFormat::VideoCodec::VP9, - QMediaFormat::VideoCodec::H264, - QMediaFormat::VideoCodec::AV1, - QMediaFormat::VideoCodec::VP8, - QMediaFormat::VideoCodec::Theora, - QMediaFormat::VideoCodec::MPEG4, - QMediaFormat::VideoCodec::MPEG2, - QMediaFormat::VideoCodec::MPEG1, - QMediaFormat::VideoCodec::MotionJPEG, -}; - const char *mimeTypeForFormat[QMediaFormat::LastFileFormat + 2] = { "", @@ -169,85 +65,6 @@ const char *mimeTypeForFormat[QMediaFormat::LastFileFormat + 2] = "audio/wave", }; - -} - -void QMediaFormatPrivate::resolveForEncoding(QMediaFormat *f, bool audioOnly) -{ - auto supportedFormats = QMediaEncoderInfo::supportedFileFormats(); - auto supportedAudioCodecs = QMediaEncoderInfo::supportedAudioCodecs(); - auto supportedVideoCodecs = QMediaEncoderInfo::supportedVideoCodecs(); - - auto bestSupportedFileFormat = [&](QMediaFormat::AudioCodec audio = QMediaFormat::AudioCodec::Unspecified, - QMediaFormat::VideoCodec video = QMediaFormat::VideoCodec::Unspecified) - { - auto *list = audioOnly ? audioFormatPriorityList : videoFormatPriorityList; - while (*list != QMediaFormat::UnspecifiedFormat) { - if (supportedFormats.contains(*list)) { - if ((audio == QMediaFormat::AudioCodec::Unspecified || formatSupportsCodec(f->fmt, audio)) && - (video == QMediaFormat::VideoCodec::Unspecified || formatSupportsCodec(f->fmt, video))) - break; - } - ++list; - } - return *list; - }; - - // reset non supported formats and codecs - if (!supportedFormats.contains(f->fmt)) - f->fmt = QMediaFormat::UnspecifiedFormat; - if (!supportedAudioCodecs.contains(f->audio)) - f->audio = QMediaFormat::AudioCodec::Unspecified; - if (audioOnly || !supportedVideoCodecs.contains(f->video)) - f->video = QMediaFormat::VideoCodec::Unspecified; - - // try finding a file format that is supported - if (f->fmt == QMediaFormat::UnspecifiedFormat) - f->fmt = bestSupportedFileFormat(f->audio, f->video); - // try without the audio codec - if (f->fmt == QMediaFormat::UnspecifiedFormat) - f->fmt = bestSupportedFileFormat(QMediaFormat::AudioCodec::Unspecified, f->video); - // try without the video codec - if (f->fmt == QMediaFormat::UnspecifiedFormat) - f->fmt = bestSupportedFileFormat(f->audio); - // give me a format that's supported - if (f->fmt == QMediaFormat::UnspecifiedFormat) - f->fmt = bestSupportedFileFormat(); - // still nothing? Give up - if (f->fmt == QMediaFormat::UnspecifiedFormat) - return; - - // now that we have a format, reset codecs that won't work - if (f->audio != QMediaFormat::AudioCodec::Unspecified) { - if (!formatSupportsCodec(f->fmt, f->audio) || !supportedAudioCodecs.contains(f->audio)) - f->audio = QMediaFormat::AudioCodec::Unspecified; - } - if (f->video != QMediaFormat::VideoCodec::Unspecified) { - if (!formatSupportsCodec(f->fmt, f->video) || !supportedVideoCodecs.contains(f->video)) - f->video = QMediaFormat::VideoCodec::Unspecified; - } - - // find a matching audio format - if (f->audio == QMediaFormat::AudioCodec::Unspecified) { - auto *list = audioPriorityList; - while (*list != QMediaFormat::AudioCodec::Unspecified) { - if (supportedAudioCodecs.contains(*list) && formatSupportsCodec(f->fmt, *list)) - break; - ++list; - } - f->audio = *list; - } - - // find a matching video format - if (!audioOnly && f->video == QMediaFormat::VideoCodec::Unspecified) { - auto *list = videoPriorityList; - while (*list != QMediaFormat::VideoCodec::Unspecified) { - if (supportedVideoCodecs.contains(*list) && formatSupportsCodec(f->fmt, *list)) - break; - ++list; - } - f->video = *list; - } } /*! \enum QMediaFormat::FileFormat @@ -274,8 +91,9 @@ void QMediaFormatPrivate::resolveForEncoding(QMediaFormat *f, bool audioOnly) */ // these are non inline to make a possible future addition of a d pointer binary compatible -QMediaFormat::QMediaFormat(FileFormat format) - : fmt(format) +QMediaFormat::QMediaFormat(FileFormat format, Mode mode) + : fmtMode(mode), + fmt(format) { Q_UNUSED(d); } @@ -305,13 +123,6 @@ QMediaFormat &QMediaFormat::operator=(const QMediaFormat &other) = default; \sa videoCodec(), QMediaFormat::VideoCodec */ -bool QMediaFormat::setVideoCodec(VideoCodec codec) -{ - if (!videoSupportMatrix[fmt][(int)codec]) - return false; - video = codec; - return true; -} /*! \fn QMediaFormat::VideoCodec QMediaFormat::videoCodec() const @@ -326,13 +137,6 @@ bool QMediaFormat::setVideoCodec(VideoCodec codec) \sa audioCodec(), QMediaFormat::AudioCodec */ -bool QMediaFormat::setAudioCodec(QMediaFormat::AudioCodec codec) -{ - if (!audioSupportMatrix[fmt][(int)codec]) - return false; - audio = codec; - return true; -} /*! \fn QMediaFormat::AudioCodec QMediaFormat::audioCodec() const @@ -353,19 +157,7 @@ bool QMediaFormat::setAudioCodec(QMediaFormat::AudioCodec codec) */ bool QMediaFormat::canDecode() const { - if (!QMediaDecoderInfo::supportedFileFormats().contains(fmt)) - return false; - if (audio == QMediaFormat::AudioCodec::Unspecified && video == QMediaFormat::VideoCodec::Unspecified) - return false; - if (audio != QMediaFormat::AudioCodec::Unspecified) { - if (!QMediaDecoderInfo::supportedAudioCodecs().contains(audio)) - return false; - } - if (video != QMediaFormat::VideoCodec::Unspecified) { - if (!QMediaDecoderInfo::supportedVideoCodecs().contains(video)) - return false; - } - return true; + return QMediaPlatformIntegration::instance()->formatInfo()->isSupported(*this, Decode); } /*! @@ -375,43 +167,62 @@ bool QMediaFormat::canDecode() const */ bool QMediaFormat::canEncode() const { - if (!QMediaEncoderInfo::supportedFileFormats().contains(fmt)) - return false; - if (audio == QMediaFormat::AudioCodec::Unspecified && video == QMediaFormat::VideoCodec::Unspecified) - return false; - if (audio != QMediaFormat::AudioCodec::Unspecified) { - if (!QMediaEncoderInfo::supportedAudioCodecs().contains(audio)) - return false; - } - if (video != QMediaFormat::VideoCodec::Unspecified) { - if (!QMediaEncoderInfo::supportedVideoCodecs().contains(video)) - return false; - } - return true; + return QMediaPlatformIntegration::instance()->formatInfo()->isSupported(*this, Encode); } /*! - Returns true if is is an audio-only file format. + Returns the mimetype for the file format used in this media format. + + \sa format(), setFormat() */ -bool QMediaFormat::isAudioFormat() const +QMimeType QMediaFormat::mimeType() const { - return fmt >= AAC; + return QMimeDatabase().mimeTypeForName(QString::fromLatin1(mimeTypeForFormat[fmt + 1])); } /*! - Returns the mimetype for the file format used in this media format. + Returns a list of container formats that are supported for \a mode. - \sa format(), setFormat() + The list is constrained by the chosen audio and video codec and will only match file + formats that can be created with those codecs. + + To get all supported file formats, run this query on a default constructed QMediaFormat. */ -QMimeType QMediaFormat::mimeType() const +QList<QMediaFormat::FileFormat> QMediaFormat::supportedFileFormats(QMediaFormat::ConversionMode m) { - return QMimeDatabase().mimeTypeForName(QString::fromLatin1(mimeTypeForFormat[fmt + 1])); + return QMediaPlatformIntegration::instance()->formatInfo()->supportedFileFormats(*this, m); +} + +/*! + Returns a list of video codecs that are supported for \a mode. + + The list is constrained by the chosen file format and audio codec and will only return + the video codecs that can be used with those settings. + + To get all supported video codecs, run this query on a default constructed QMediaFormat. + */ +QList<QMediaFormat::VideoCodec> QMediaFormat::supportedVideoCodecs(QMediaFormat::ConversionMode m) +{ + return QMediaPlatformIntegration::instance()->formatInfo()->supportedVideoCodecs(*this, m); +} + +/*! + Returns a list of audio codecs that are supported for \a mode. + + The list is constrained by the chosen file format and video codec and will only return + the audio codecs that can be used with those settings. + + To get all supported audio codecs, run this query on a default constructed QMediaFormat. + */ +QList<QMediaFormat::AudioCodec> QMediaFormat::supportedAudioCodecs(QMediaFormat::ConversionMode m) +{ + return QMediaPlatformIntegration::instance()->formatInfo()->supportedAudioCodecs(*this, m); } QString QMediaFormat::fileFormatName(QMediaFormat::FileFormat c) { constexpr const char *descriptions[QMediaFormat::LastFileFormat + 2] = { - "Unspecified File Format" + "Unspecified", "ASF", "AVI", "Matroska", @@ -524,81 +335,4 @@ QString QMediaFormat::videoCodecDescription(QMediaFormat::VideoCodec c) return QString::fromUtf8(descriptions[int(c) + 1]); } -/*! \class QMediaDecoderInfo - - QMediaDecoderInfo describes the media formats supported for decoding - on the current platform. - - Qt Multimedia might be able to decode formats that are not listed - in the QMediaFormat::FileFormat, QMediaFormat::AudioCodec and QMediaFormat::VideoCodec enums. - - \sa QMediaFormat::canDecode() -*/ - -/*! - Returns a list of container formats that are supported for decoding by - Qt Multimedia. - - This does not imply that Qt can successfully decode the media file or - stream, as the audio or video codec used within the container might not - be supported. - */ -QList<QMediaFormat::FileFormat> QMediaDecoderInfo::supportedFileFormats() -{ - return QMediaPlatformIntegration::instance()->formatInfo()->decodableMediaContainers(); -} - -/*! - Returns a list of video codecs that are supported for decoding by - Qt Multimedia. - */ -QList<QMediaFormat::VideoCodec> QMediaDecoderInfo::supportedVideoCodecs() -{ - return QMediaPlatformIntegration::instance()->formatInfo()->decodableVideoCodecs(); -} - -/*! - Returns a list of audio codecs that are supported for decoding by - Qt Multimedia. - */ -QList<QMediaFormat::AudioCodec> QMediaDecoderInfo::supportedAudioCodecs() -{ - return QMediaPlatformIntegration::instance()->formatInfo()->decodableAudioCodecs(); -} - -/*! \class QMediaEncodecInfo - - QMediaEncoderInfo describes the media formats supported for - encoding on the current platform. - - \sa QMediaFormat::canEncode() -*/ - -/*! - Returns a list of container formats that are supported for encoding by - Qt Multimedia. - */ -QList<QMediaFormat::FileFormat> QMediaEncoderInfo::supportedFileFormats() -{ - return QMediaPlatformIntegration::instance()->formatInfo()->encodableMediaContainers(); -} - -/*! - Returns a list of video codecs that are supported for encoding by - Qt Multimedia. - */ -QList<QMediaFormat::VideoCodec> QMediaEncoderInfo::supportedVideoCodecs() -{ - return QMediaPlatformIntegration::instance()->formatInfo()->encodableVideoCodecs(); -} - -/*! - Returns a list of audio codecs that are supported for encoding by - Qt Multimedia. - */ -QList<QMediaFormat::AudioCodec> QMediaEncoderInfo::supportedAudioCodecs() -{ - return QMediaPlatformIntegration::instance()->formatInfo()->encodableAudioCodecs(); -} - QT_END_NAMESPACE diff --git a/src/multimedia/qmediaformat.h b/src/multimedia/qmediaformat.h index a488fd378..6b3684ab6 100644 --- a/src/multimedia/qmediaformat.h +++ b/src/multimedia/qmediaformat.h @@ -49,11 +49,7 @@ QT_BEGIN_NAMESPACE class QMimeType; class QMediaFormat; class QMediaEncoderSettings; -class QMediaFormatPrivate -{ - friend class QMediaEncoderSettings; - static void resolveForEncoding(QMediaFormat *fmt, bool audioOnly); -}; +class QMediaFormatPrivate; class Q_MULTIMEDIA_EXPORT QMediaFormat { @@ -108,7 +104,17 @@ public: LastVideoCodec = MotionJPEG }; - QMediaFormat(FileFormat format = UnspecifiedFormat); + enum ConversionMode { + Encode, + Decode + }; + + enum Mode { + AudioOnly, + AudioAndVideo + }; + + QMediaFormat(FileFormat format = UnspecifiedFormat, Mode = AudioAndVideo); ~QMediaFormat(); QMediaFormat(const QMediaFormat &other); QMediaFormat &operator=(const QMediaFormat &other); @@ -116,19 +122,24 @@ public: FileFormat format() const { return fmt; } void setFormat(FileFormat f) { fmt = f; } - bool setVideoCodec(VideoCodec codec); + void setVideoCodec(VideoCodec codec) { video = codec; } VideoCodec videoCodec() const { return video; } - bool setAudioCodec(AudioCodec codec); + void setAudioCodec(AudioCodec codec) { audio = codec; } AudioCodec audioCodec() const { return audio; } bool canDecode() const; bool canEncode() const; - bool isAudioFormat() const; + void setMode(Mode m) { fmtMode = m; } + Mode mode() const { return fmtMode; } QMimeType mimeType() const; + QList<QMediaFormat::FileFormat> supportedFileFormats(ConversionMode m); + QList<QMediaFormat::VideoCodec> supportedVideoCodecs(ConversionMode m); + QList<QMediaFormat::AudioCodec> supportedAudioCodecs(ConversionMode m); + static QString fileFormatName(QMediaFormat::FileFormat c); static QString audioCodecName(QMediaFormat::AudioCodec c); static QString videoCodecName(QMediaFormat::VideoCodec c); @@ -139,29 +150,13 @@ public: protected: friend class QMediaFormatPrivate; + Mode fmtMode; FileFormat fmt; AudioCodec audio = AudioCodec::Unspecified; VideoCodec video = VideoCodec::Unspecified; QMediaFormatPrivate *d = nullptr; }; - -class Q_MULTIMEDIA_EXPORT QMediaDecoderInfo -{ -public: - static QList<QMediaFormat::FileFormat> supportedFileFormats(); - static QList<QMediaFormat::VideoCodec> supportedVideoCodecs(); - static QList<QMediaFormat::AudioCodec> supportedAudioCodecs(); -}; - -class Q_MULTIMEDIA_EXPORT QMediaEncoderInfo -{ -public: - static QList<QMediaFormat::FileFormat> supportedFileFormats(); - static QList<QMediaFormat::VideoCodec> supportedVideoCodecs(); - static QList<QMediaFormat::AudioCodec> supportedAudioCodecs(); -}; - QT_END_NAMESPACE #endif diff --git a/src/multimedia/recording/qmediaencodersettings.cpp b/src/multimedia/recording/qmediaencodersettings.cpp index 1c0414b8d..68df20216 100644 --- a/src/multimedia/recording/qmediaencodersettings.cpp +++ b/src/multimedia/recording/qmediaencodersettings.cpp @@ -42,6 +42,61 @@ QT_BEGIN_NAMESPACE +namespace { + + +constexpr QMediaFormat::FileFormat videoFormatPriorityList[] = +{ + QMediaFormat::MPEG4, + QMediaFormat::QuickTime, + QMediaFormat::AVI, + QMediaFormat::WebM, + QMediaFormat::ASF, + QMediaFormat::Matroska, + QMediaFormat::Ogg, + QMediaFormat::UnspecifiedFormat +}; + +constexpr QMediaFormat::FileFormat audioFormatPriorityList[] = +{ + QMediaFormat::AAC, + QMediaFormat::MP3, + QMediaFormat::Mpeg4Audio, + QMediaFormat::FLAC, + QMediaFormat::ALAC, + QMediaFormat::Wave, + QMediaFormat::UnspecifiedFormat +}; + +constexpr QMediaFormat::AudioCodec audioPriorityList[] = +{ + QMediaFormat::AudioCodec::AAC, + QMediaFormat::AudioCodec::MP3, + QMediaFormat::AudioCodec::AC3, + QMediaFormat::AudioCodec::Opus, + QMediaFormat::AudioCodec::EAC3, + QMediaFormat::AudioCodec::DolbyTrueHD, + QMediaFormat::AudioCodec::FLAC, + QMediaFormat::AudioCodec::Vorbis, + QMediaFormat::AudioCodec::Wave, + QMediaFormat::AudioCodec::Unspecified +}; + +constexpr QMediaFormat::VideoCodec videoPriorityList[] = +{ + QMediaFormat::VideoCodec::H265, + QMediaFormat::VideoCodec::VP9, + QMediaFormat::VideoCodec::H264, + QMediaFormat::VideoCodec::AV1, + QMediaFormat::VideoCodec::VP8, + QMediaFormat::VideoCodec::Theora, + QMediaFormat::VideoCodec::MPEG4, + QMediaFormat::VideoCodec::MPEG2, + QMediaFormat::VideoCodec::MPEG1, + QMediaFormat::VideoCodec::MotionJPEG, +}; + +} /*! \class QMediaEncoderSettings @@ -159,9 +214,92 @@ void QMediaEncoderSettings::setQuality(QMultimedia::EncodingQuality quality) Settings that are not supported by the encoder will be modified to the closest match that is supported. */ -void QMediaEncoderSettings::resolveFormat(QMediaEncoderSettings::ResolveMode mode) -{ - QMediaFormatPrivate::resolveForEncoding(this, mode == AudioOnly); +void QMediaEncoderSettings::resolveFormat() +{ + if (canEncode()) + return; + + QMediaFormat nullFormat; + auto supportedFormats = nullFormat.supportedFileFormats(QMediaFormat::Encode); + auto supportedAudioCodecs = nullFormat.supportedAudioCodecs(QMediaFormat::Encode); + auto supportedVideoCodecs = nullFormat.supportedVideoCodecs(QMediaFormat::Encode); + + auto bestSupportedFileFormat = [&](QMediaFormat::AudioCodec audio = QMediaFormat::AudioCodec::Unspecified, + QMediaFormat::VideoCodec video = QMediaFormat::VideoCodec::Unspecified) + { + QMediaFormat f; + f.setAudioCodec(audio); + f.setVideoCodec(video); + auto supportedFormats = f.supportedFileFormats(QMediaFormat::Encode); + auto *list = (fmtMode == AudioOnly) ? audioFormatPriorityList : videoFormatPriorityList; + while (*list != QMediaFormat::UnspecifiedFormat) { + if (supportedFormats.contains(*list)) + break; + ++list; + } + return *list; + }; + + // reset non supported formats and codecs + if (!supportedFormats.contains(fmt)) + fmt = QMediaFormat::UnspecifiedFormat; + if (!supportedAudioCodecs.contains(audio)) + audio = QMediaFormat::AudioCodec::Unspecified; + if ((fmtMode == AudioOnly) || !supportedVideoCodecs.contains(video)) + video = QMediaFormat::VideoCodec::Unspecified; + + if (!(fmtMode == AudioOnly)) { + // try finding a file format that is supported + if (fmt == QMediaFormat::UnspecifiedFormat) + fmt = bestSupportedFileFormat(audio, video); + // try without the audio codec + if (fmt == QMediaFormat::UnspecifiedFormat) + fmt = bestSupportedFileFormat(QMediaFormat::AudioCodec::Unspecified, video); + } + // try without the video codec + if (fmt == QMediaFormat::UnspecifiedFormat) + fmt = bestSupportedFileFormat(audio); + // give me a format that's supported + if (fmt == QMediaFormat::UnspecifiedFormat) + fmt = bestSupportedFileFormat(); + // still nothing? Give up + if (fmt == QMediaFormat::UnspecifiedFormat) + return; + + // find a working video codec + if (!(fmtMode == AudioOnly)) { + // reset the audio codec, so that we won't throw away the video codec + // if it is supported (choosing the specified video codec has higher + // priority than the specified audio codec) + auto a = audio; + audio = QMediaFormat::AudioCodec::Unspecified; + auto videoCodecs = this->supportedVideoCodecs(QMediaFormat::Encode); + if (!videoCodecs.contains(video)) { + // not supported, try to find a replacement + auto *list = videoPriorityList; + while (*list != QMediaFormat::VideoCodec::Unspecified) { + if (videoCodecs.contains(*list)) + break; + ++list; + } + video = *list; + } + audio = a; + } else { + video = QMediaFormat::VideoCodec::Unspecified; + } + + // and a working audio codec + auto audioCodecs = this->supportedAudioCodecs(QMediaFormat::Encode); + if (!audioCodecs.contains(audio)) { + auto *list = audioPriorityList; + while (*list != QMediaFormat::AudioCodec::Unspecified) { + if (audioCodecs.contains(*list)) + break; + ++list; + } + audio = *list; + } } /*! diff --git a/src/multimedia/recording/qmediaencodersettings.h b/src/multimedia/recording/qmediaencodersettings.h index 673c6f3a4..b0a68af2d 100644 --- a/src/multimedia/recording/qmediaencodersettings.h +++ b/src/multimedia/recording/qmediaencodersettings.h @@ -66,12 +66,7 @@ public: QMultimedia::EncodingQuality quality() const; void setQuality(QMultimedia::EncodingQuality quality); - enum ResolveMode { - AudioOnly, - AudioAndVideo - }; - - void resolveFormat(ResolveMode mode = AudioAndVideo); + void resolveFormat(); QSize videoResolution() const; void setVideoResolution(const QSize &); |