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 /src/multimedia/platform | |
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>
Diffstat (limited to 'src/multimedia/platform')
17 files changed, 482 insertions, 714 deletions
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 |