summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp')
-rw-r--r--src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp367
1 files changed, 270 insertions, 97 deletions
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