Revision control
++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++
++ #include "qwindowsmultimediautils_p.h"
++
++ #include <QtMultimedia/private/qcameradevice_p.h>
++ #include <QtCore/qt_windows.h>
++ #include <QtCore/qvarlengtharray.h>
++ #include <QtCore/private/qflatmap_p.h>
++
++ #include <mfapi.h>
++ #include <mfidl.h>
++
++ #include <cstring>
++
++ static_assert(WINVER >= _WIN32_WINNT_WIN10, "Win10 required for newer audio formats.");
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QWMF {
++
++ namespace {
++
++ struct GuidLess
++ {
++ bool operator()(const GUID &lhs, const GUID &rhs) const
++ {
++ return std::memcmp(&lhs, &rhs, sizeof(GUID)) < 0;
++ }
++ };
++
++ } // namespace
++
++ QVideoFrameFormat::PixelFormat pixelFormatFromMediaSubtype(const GUID &subtype)
++ {
++ static const QVarLengthFlatMap<GUID, QVideoFrameFormat::PixelFormat, 13, GuidLess> map({
++ #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
++ { MFVideoFormat_ARGB32, QVideoFrameFormat::Format_BGRA8888 },
++ { MFVideoFormat_RGB32, QVideoFrameFormat::Format_BGRX8888 },
++ #else
++ { MFVideoFormat_ARGB32, QVideoFrameFormat::Format_ARGB8888 },
++ { MFVideoFormat_RGB32, QVideoFrameFormat::Format_XRGB8888 },
++ #endif
++ { MFVideoFormat_AYUV, QVideoFrameFormat::Format_AYUV },
++ { MFVideoFormat_I420, QVideoFrameFormat::Format_YUV420P },
++ { MFVideoFormat_UYVY, QVideoFrameFormat::Format_UYVY },
++ { MFVideoFormat_YV12, QVideoFrameFormat::Format_YV12 },
++ { MFVideoFormat_NV12, QVideoFrameFormat::Format_NV12 },
++ { MFVideoFormat_YUY2, QVideoFrameFormat::Format_YUYV },
++ { MFVideoFormat_P010, QVideoFrameFormat::Format_P010 },
++ { MFVideoFormat_P016, QVideoFrameFormat::Format_P016 },
++ { MFVideoFormat_L8, QVideoFrameFormat::Format_Y8 },
++ { MFVideoFormat_L16, QVideoFrameFormat::Format_Y16 },
++ { MFVideoFormat_MJPG, QVideoFrameFormat::Format_Jpeg },
++ });
++
++ const auto it = map.find(subtype);
++ return it != map.end() ? it->second : QVideoFrameFormat::Format_Invalid;
++ }
++
++ QVideoFrameFormat::ColorRange colorRangeFromNominalRange(UINT32 nominalRange)
++ {
++ switch (nominalRange) {
++ case MFNominalRange_0_255:
++ return QVideoFrameFormat::ColorRange_Full;
++ case MFNominalRange_16_235:
++ return QVideoFrameFormat::ColorRange_Video;
++ default:
++ return QVideoFrameFormat::ColorRange_Unknown;
++ }
++ }
++
++ QVideoFrameFormat::ColorSpace colorSpaceFromMatrix(UINT32 yuvMatrix)
++ {
++ switch (yuvMatrix) {
++ case MFVideoTransferMatrix_BT709:
++ return QVideoFrameFormat::ColorSpace_BT709;
++ case MFVideoTransferMatrix_BT601:
++ return QVideoFrameFormat::ColorSpace_BT601;
++ case MFVideoTransferMatrix_BT2020_10:
++ case MFVideoTransferMatrix_BT2020_12:
++ return QVideoFrameFormat::ColorSpace_BT2020;
++ default:
++ return QVideoFrameFormat::ColorSpace_Undefined;
++ }
++ }
++
++ GUID videoFormatForCodec(QMediaFormat::VideoCodec codec)
++ {
++ switch (codec) {
++ case QMediaFormat::VideoCodec::MPEG1:
++ return MFVideoFormat_MPG1;
++ case QMediaFormat::VideoCodec::MPEG2:
++ return MFVideoFormat_MPEG2;
++ case QMediaFormat::VideoCodec::MPEG4:
++ return MFVideoFormat_MP4V;
++ case QMediaFormat::VideoCodec::H264:
++ return MFVideoFormat_H264;
++ case QMediaFormat::VideoCodec::H265:
++ return MFVideoFormat_H265;
++ case QMediaFormat::VideoCodec::VP8:
++ return MFVideoFormat_VP80;
++ case QMediaFormat::VideoCodec::VP9:
++ return MFVideoFormat_VP90;
++ case QMediaFormat::VideoCodec::AV1:
++ return MFVideoFormat_AV1;
++ case QMediaFormat::VideoCodec::WMV:
++ return MFVideoFormat_WMV3;
++ case QMediaFormat::VideoCodec::MotionJPEG:
++ return MFVideoFormat_MJPG;
++ default:
++ return MFVideoFormat_H264;
++ }
++ }
++
++ QMediaFormat::VideoCodec codecForVideoFormat(GUID format)
++ {
++ static const QVarLengthFlatMap<GUID, QMediaFormat::VideoCodec, 15, GuidLess> map({
++ { MFVideoFormat_MPG1, QMediaFormat::VideoCodec::MPEG1 },
++ { MFVideoFormat_MPEG2, QMediaFormat::VideoCodec::MPEG2 },
++ { MFVideoFormat_MP4V, QMediaFormat::VideoCodec::MPEG4 },
++ { MFVideoFormat_M4S2, QMediaFormat::VideoCodec::MPEG4 },
++ { MFVideoFormat_MP4S, QMediaFormat::VideoCodec::MPEG4 },
++ { MFVideoFormat_MP43, QMediaFormat::VideoCodec::MPEG4 },
++ { MFVideoFormat_H264, QMediaFormat::VideoCodec::H264 },
++ { MFVideoFormat_H265, QMediaFormat::VideoCodec::H265 },
++ { MFVideoFormat_VP80, QMediaFormat::VideoCodec::VP8 },
++ { MFVideoFormat_VP90, QMediaFormat::VideoCodec::VP9 },
++ { MFVideoFormat_AV1, QMediaFormat::VideoCodec::AV1 },
++ { MFVideoFormat_WMV1, QMediaFormat::VideoCodec::WMV },
++ { MFVideoFormat_WMV2, QMediaFormat::VideoCodec::WMV },
++ { MFVideoFormat_WMV3, QMediaFormat::VideoCodec::WMV },
++ { MFVideoFormat_MJPG, QMediaFormat::VideoCodec::MotionJPEG },
++ });
++
++ const auto it = map.find(format);
++ return it != map.end() ? it->second : QMediaFormat::VideoCodec::Unspecified;
++ }
++
++ GUID audioFormatForCodec(QMediaFormat::AudioCodec codec)
++ {
++ switch (codec) {
++ case QMediaFormat::AudioCodec::MP3:
++ return MFAudioFormat_MP3;
++ case QMediaFormat::AudioCodec::AAC:
++ return MFAudioFormat_AAC;
++ case QMediaFormat::AudioCodec::ALAC:
++ return MFAudioFormat_ALAC;
++ case QMediaFormat::AudioCodec::FLAC:
++ return MFAudioFormat_FLAC;
++ case QMediaFormat::AudioCodec::Vorbis:
++ return MFAudioFormat_Vorbis;
++ case QMediaFormat::AudioCodec::Wave:
++ return MFAudioFormat_PCM;
++ case QMediaFormat::AudioCodec::Opus:
++ return MFAudioFormat_Opus;
++ case QMediaFormat::AudioCodec::AC3:
++ return MFAudioFormat_Dolby_AC3;
++ case QMediaFormat::AudioCodec::EAC3:
++ return MFAudioFormat_Dolby_DDPlus;
++ case QMediaFormat::AudioCodec::WMA:
++ return MFAudioFormat_WMAudioV9;
++ default:
++ return MFAudioFormat_AAC;
++ }
++ }
++
++ QMediaFormat::AudioCodec codecForAudioFormat(GUID format)
++ {
++ static const QVarLengthFlatMap<GUID, QMediaFormat::AudioCodec, 12, GuidLess> map({
++ { MFAudioFormat_MP3, QMediaFormat::AudioCodec::MP3 },
++ { MFAudioFormat_AAC, QMediaFormat::AudioCodec::AAC },
++ { MFAudioFormat_ALAC, QMediaFormat::AudioCodec::ALAC },
++ { MFAudioFormat_FLAC, QMediaFormat::AudioCodec::FLAC },
++ { MFAudioFormat_Vorbis, QMediaFormat::AudioCodec::Vorbis },
++ { MFAudioFormat_PCM, QMediaFormat::AudioCodec::Wave },
++ { MFAudioFormat_Opus, QMediaFormat::AudioCodec::Opus },
++ { MFAudioFormat_Dolby_AC3, QMediaFormat::AudioCodec::AC3 },
++ { MFAudioFormat_Dolby_DDPlus, QMediaFormat::AudioCodec::EAC3 },
++ { MFAudioFormat_WMAudioV8, QMediaFormat::AudioCodec::WMA },
++ { MFAudioFormat_WMAudioV9, QMediaFormat::AudioCodec::WMA },
++ { MFAudioFormat_WMAudio_Lossless, QMediaFormat::AudioCodec::WMA },
++ });
++
++ const auto it = map.find(format);
++ return it != map.end() ? it->second : QMediaFormat::AudioCodec::Unspecified;
++ }
++
++ std::optional<GUID> containerForVideoFileFormat(QMediaFormat::FileFormat format)
++ {
++ switch (format) {
++ case QMediaFormat::FileFormat::MPEG4:
++ return MFTranscodeContainerType_MPEG4;
++ case QMediaFormat::FileFormat::WMV:
++ return MFTranscodeContainerType_ASF;
++ case QMediaFormat::FileFormat::AVI:
++ return MFTranscodeContainerType_AVI;
++ default:
++ return std::nullopt;
++ }
++ }
++
++ std::optional<GUID> containerForAudioFileFormat(QMediaFormat::FileFormat format)
++ {
++ switch (format) {
++ case QMediaFormat::FileFormat::MP3:
++ return MFTranscodeContainerType_MP3;
++ case QMediaFormat::FileFormat::AAC:
++ return MFTranscodeContainerType_ADTS;
++ case QMediaFormat::FileFormat::Mpeg4Audio:
++ return MFTranscodeContainerType_MPEG4;
++ case QMediaFormat::FileFormat::WMA:
++ return MFTranscodeContainerType_ASF;
++ case QMediaFormat::FileFormat::FLAC:
++ return MFTranscodeContainerType_FLAC;
++ case QMediaFormat::FileFormat::Wave:
++ return MFTranscodeContainerType_WAVE;
++ default:
++ return std::nullopt;
++ }
++ }
++
++ std::optional<QCameraFormat> cameraFormatFromMediaType(IMFMediaType *mediaFormat)
++ {
++ GUID subtype = GUID_NULL;
++ if (FAILED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
++ return std::nullopt;
++
++ auto pixelFormat = pixelFormatFromMediaSubtype(subtype);
++ if (pixelFormat == QVideoFrameFormat::Format_Invalid)
++ return std::nullopt;
++
++ UINT32 nominalRange = 0;
++ auto colorRange = QVideoFrameFormat::ColorRange_Unknown;
++
++ if (SUCCEEDED(mediaFormat->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &nominalRange)))
++ colorRange = colorRangeFromNominalRange(nominalRange);
++
++ UINT32 yuvMatrix = 0;
++ auto colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
++
++ if (SUCCEEDED(mediaFormat->GetUINT32(MF_MT_YUV_MATRIX , &yuvMatrix)))
++ colorSpace = colorSpaceFromMatrix(yuvMatrix);
++
++ UINT32 width = 0u;
++ UINT32 height = 0u;
++ if (FAILED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width, &height)))
++ return std::nullopt;
++ QSize resolution{ int(width), int(height) };
++
++ UINT32 num = 0u;
++ UINT32 den = 0u;
++ float minFr = 0.f;
++ float maxFr = 0.f;
++
++ if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN, &num, &den)))
++ minFr = float(num) / float(den);
++
++ if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX, &num, &den)))
++ maxFr = float(num) / float(den);
++
++ // The range attributes are optional: a source enumerating one media type per
++ // discrete frame rate states only MF_MT_FRAME_RATE.
++ if ((minFr == 0.f || maxFr == 0.f)
++ && SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE, &num, &den))) {
++ const float frameRate = float(num) / float(den);
++ if (minFr == 0.f)
++ minFr = frameRate;
++ if (maxFr == 0.f)
++ maxFr = frameRate;
++ }
++
++ auto *f = new QCameraFormatPrivate{
++ QSharedData(), pixelFormat, resolution, minFr, maxFr, colorRange, colorSpace,
++ };
++ return f->create();
++ }
++
++ } // namespace QWMF
++
++ QT_END_NAMESPACE