6#include <QtMultimedia/private/qapple_utils_p.h>
7#include <QtMultimedia/private/qmultimedia_ranges_p.h>
9#include <QtCore/qdebug.h>
10#include <QtCore/private/qcore_mac_p.h>
12#include <AVFoundation/AVFoundation.h>
13#include <AudioToolbox/AudioToolbox.h>
14#include <VideoToolbox/VideoToolbox.h>
22 const char *__nonnull
name;
27} mediaContainerMap[] = {
28 {
"video/x-ms-asf", QMediaFormat::WMV, QMediaFormat::AudioCodec::Unspecified },
29 {
"video/avi", QMediaFormat::AVI, QMediaFormat::AudioCodec::Unspecified },
30 {
"video/x-matroska", QMediaFormat::Matroska, QMediaFormat::AudioCodec::Unspecified },
31 {
"video/mp4", QMediaFormat::MPEG4, QMediaFormat::AudioCodec::Unspecified },
32 {
"video/quicktime", QMediaFormat::QuickTime, QMediaFormat::AudioCodec::Unspecified },
33 {
"audio/ogg", QMediaFormat::Ogg, QMediaFormat::AudioCodec::Unspecified },
34 {
"audio/mp4", QMediaFormat::Mpeg4Audio, QMediaFormat::AudioCodec::Unspecified },
35 {
"audio/aac", QMediaFormat::AAC, QMediaFormat::AudioCodec::AAC },
36 {
"audio/mp3", QMediaFormat::MP3, QMediaFormat::AudioCodec::MP3 },
37 {
"audio/flac", QMediaFormat::FLAC, QMediaFormat::AudioCodec::FLAC },
38 {
"audio/x-wav", QMediaFormat::Wave, QMediaFormat::AudioCodec::Wave },
42 const char *__nonnull name;
46 {
"; codecs=\"mp1v\"", QMediaFormat::VideoCodec::MPEG1 },
47 {
"; codecs=\"mp2v\"", QMediaFormat::VideoCodec::MPEG2 },
48 {
"; codecs=\"mp4v\"", QMediaFormat::VideoCodec::MPEG4 },
49 {
"; codecs=\"avc1\"", QMediaFormat::VideoCodec::H264 },
50 {
"; codecs=\"hvc1\"", QMediaFormat::VideoCodec::H265 },
51 {
"; codecs=\"vp09\"", QMediaFormat::VideoCodec::VP9 },
52 {
"; codecs=\"av01\"", QMediaFormat::VideoCodec::AV1 },
53 {
"; codecs=\"jpeg\"", QMediaFormat::VideoCodec::MotionJPEG },
57 const char *__nonnull name;
58 QMediaFormat::AudioCodec codec;
61 {
"; codecs=\"mp4a.40.2\"", QMediaFormat::AudioCodec::AAC },
62 {
"; codecs=\"ac-3\"", QMediaFormat::AudioCodec::AC3 },
63 {
"; codecs=\"ec-3\"", QMediaFormat::AudioCodec::EAC3 },
64 {
"; codecs=\"flac\"", QMediaFormat::AudioCodec::FLAC },
65 {
"; codecs=\"alac\"", QMediaFormat::AudioCodec::ALAC },
66 {
"; codecs=\"opus\"", QMediaFormat::AudioCodec::Opus },
67 {
"; codecs=\"vorbis\"", QMediaFormat::AudioCodec::Vorbis },
72 if (formatID == kAudioFormatLinearPCM)
75 return AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders,
76 sizeof(formatID), &formatID, &size) == noErr
82 if (formatID == kAudioFormatLinearPCM)
85 return AudioFormatGetPropertyInfo(kAudioFormatProperty_Decoders,
sizeof(formatID), &formatID,
93 using namespace QtMultimediaPrivate;
95 QCFType<CFArrayRef> list;
96 if (VTCopyVideoEncoderList(
nullptr, &list) != noErr || !list)
100 CFArrayRange<CFDictionaryRef>(list) | views::transform([](CFDictionaryRef entry) {
101 auto *typeRef = CFNumberRef(CFDictionaryGetValue(entry, kVTVideoEncoderList_CodecType));
102 CMVideoCodecType type = 0;
103 CFNumberGetValue(typeRef, kCFNumberSInt32Type, &type);
107 return results | ranges::to<std::set<CMVideoCodecType>>();
112 return fileFormat < QMediaFormat::Mpeg4Audio;
117 return [AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype]];
122 const QByteArray extendedMimetype = QByteArrayView(containerName) + codecName;
123 return isPlayableExtendedMIMEType(extendedMimetype.constData());
126constexpr auto recordGetCodec = QtMultimediaPrivate::views::transform([](
const auto &record) {
132 namespace views = QtMultimediaPrivate::views;
133 namespace ranges = QtMultimediaPrivate::ranges;
134 using AudioCodec = QMediaFormat::AudioCodec;
135 using VideoCodec = QMediaFormat::VideoCodec;
136 using CodecMap = QPlatformMediaFormatInfo::CodecMap;
138 const bool canDecodeMp3 = canEncodeDecodeFormat(kAudioFormatMPEGLayer3);
140 QList<CodecMap> decoders;
141 auto avtypes = [AVURLAsset audiovisualMIMETypes];
143 for (AVFileType filetype : avtypes) {
144 for (
auto [containerName, fileFormat, implicitCodec] : mediaContainerMap) {
145 if (strcmp(filetype.UTF8String, containerName))
148 QList<VideoCodec> video;
149 QList<AudioCodec> audio;
151 if (implicitCodec != QMediaFormat::AudioCodec::Unspecified) {
154 audio << implicitCodec;
158 if (isVideoContainer(fileFormat)) {
159 switch (fileFormat) {
160 case QMediaFormat::AVI: {
163 if (!isPlayableExtendedMIMEType(containerName))
168 video << VideoCodec::MPEG4 << VideoCodec::H264 << VideoCodec::MotionJPEG;
173 video = videoCodecMap | views::filter([&](
const auto &record) {
174 return isPlayableExtendedMIMEType(containerName, record.name);
176 | ranges::to<QList<VideoCodec>>();
181 audio = audioCodecMap | views::filter([&](
const auto &record) {
182 return isPlayableExtendedMIMEType(containerName, record.name);
184 | ranges::to<QList<AudioCodec>>();
190 && (isVideoContainer(fileFormat) || fileFormat == QMediaFormat::Mpeg4Audio))
191 audio << AudioCodec::MP3;
194 decoders << CodecMap{
206 namespace views = QtMultimediaPrivate::views;
207 namespace ranges = QtMultimediaPrivate::ranges;
208 using AudioCodec = QMediaFormat::AudioCodec;
209 using VideoCodec = QMediaFormat::VideoCodec;
210 using CodecMap = QPlatformMediaFormatInfo::CodecMap;
212 static constexpr struct AudioProbeRecord
217 { kAudioFormatMPEG4AAC, AudioCodec::AAC },
218 { kAudioFormatMPEGLayer3, AudioCodec::MP3 },
219 { kAudioFormatAC3, AudioCodec::AC3 },
220 { kAudioFormatEnhancedAC3, AudioCodec::EAC3 },
221 { kAudioFormatFLAC, AudioCodec::FLAC },
222 { kAudioFormatAppleLossless, AudioCodec::ALAC },
223 { kAudioFormatOpus, AudioCodec::Opus },
224 { kAudioFormatLinearPCM, AudioCodec::Wave },
227 {
'vorb', QMediaFormat::AudioCodec::Vorbis },
230 static constexpr struct VideoProbeRecord
232 CMVideoCodecType type;
235 { kCMVideoCodecType_H264, VideoCodec::H264 },
236 { kCMVideoCodecType_HEVC, VideoCodec::H265 },
237 { kCMVideoCodecType_JPEG, VideoCodec::MotionJPEG },
238 { kCMVideoCodecType_MPEG4Video, VideoCodec::MPEG4 },
239 { kCMVideoCodecType_AV1, VideoCodec::AV1 },
242 {
'mp1v', VideoCodec::MPEG1 },
243 {
'mp2v', VideoCodec::MPEG2 },
246 auto filterCodecs = [](
const auto &codecsSet) {
247 return views::filter([&](
auto c) {
248 return codecsSet.count(c);
252 const std::set<AudioCodec> supportedAudioCodecs = audioProbes
253 | views::filter([](
const auto &record) {
254 return canEncodeAudioFormat(record.fmtId);
256 | ranges::to<std::set<AudioCodec>>();
258 const std::set<CMVideoCodecType> vtCodecTypes = availableVTVideoEncoderCodecTypes();
260 const std::set<VideoCodec> supportedVideoCodecs = videoProbes
261 | views::filter([&](
const auto &record) {
262 return ranges::contains(vtCodecTypes, record.type);
264 | ranges::to<std::set<VideoCodec>>();
266 auto audioFormats = [&](
auto candidates) -> QList<AudioCodec> {
267 return candidates | filterCodecs(supportedAudioCodecs) | ranges::to<QList<AudioCodec>>();
270 auto videoFormats = [&](
auto candidates) -> QList<VideoCodec> {
271 return candidates | filterCodecs(supportedVideoCodecs) | ranges::to<QList<VideoCodec>>();
276 const QList allSupportedVideoCodecs = videoFormats(videoProbes | recordGetCodec);
277 const QList allSupportedAudioCodecs = audioFormats(audioProbes | recordGetCodec);
280 CodecMap{ QMediaFormat::MPEG4, allSupportedAudioCodecs, allSupportedVideoCodecs },
281 CodecMap{ QMediaFormat::QuickTime, allSupportedAudioCodecs, allSupportedVideoCodecs },
282 CodecMap{ QMediaFormat::Mpeg4Audio,
283 audioFormats(std::array{ AudioCodec::AAC, AudioCodec::ALAC, AudioCodec::FLAC }),
285 CodecMap{ QMediaFormat::Wave, audioFormats(std::array{ AudioCodec::Wave }), {} },
293 decoders = enumerateDecoders();
294 encoders = enumerateEncoders();
297 imageFormats << QImageCapture::JPEG;
306 int codecId = kAudioFormatMPEG4AAC;
308 case QMediaFormat::AudioCodec::Unspecified:
309 case QMediaFormat::AudioCodec::DolbyTrueHD:
310 case QMediaFormat::AudioCodec::Vorbis:
311 case QMediaFormat::AudioCodec::WMA:
313 case QMediaFormat::AudioCodec::AAC:
314 codecId = kAudioFormatMPEG4AAC;
316 case QMediaFormat::AudioCodec::MP3:
317 codecId = kAudioFormatMPEGLayer3;
319 case QMediaFormat::AudioCodec::AC3:
320 codecId = kAudioFormatAC3;
322 case QMediaFormat::AudioCodec::EAC3:
323 codecId = kAudioFormatEnhancedAC3;
325 case QMediaFormat::AudioCodec::FLAC:
326 codecId = kAudioFormatFLAC;
328 case QMediaFormat::AudioCodec::ALAC:
329 codecId = kAudioFormatAppleLossless;
331 case QMediaFormat::AudioCodec::Opus:
332 codecId = kAudioFormatOpus;
334 case QMediaFormat::AudioCodec::Wave:
335 codecId = kAudioFormatLinearPCM;
343 case QMediaFormat::VideoCodec::MPEG1:
346 case QMediaFormat::VideoCodec::MPEG2:
349 case QMediaFormat::VideoCodec::MPEG4:
352 case QMediaFormat::VideoCodec::H264:
355 case QMediaFormat::VideoCodec::VP9:
358 case QMediaFormat::VideoCodec::MotionJPEG:
360 case QMediaFormat::VideoCodec::H265:
362 case QMediaFormat::VideoCodec::AV1:
364 case QMediaFormat::VideoCodec::Unspecified:
365 case QMediaFormat::VideoCodec::VP8:
366 case QMediaFormat::VideoCodec::Theora:
367 case QMediaFormat::VideoCodec::WMV:
369 qDebug() <<
"Unsupported video codec" << codec <<
", using H.265 as fallback";
377 case QMediaFormat::MPEG4:
378 return AVFileTypeMPEG4;
379 case QMediaFormat::QuickTime:
380 return AVFileTypeQuickTimeMovie;
381 case QMediaFormat::MP3:
382 return AVFileTypeMPEGLayer3;
383 case QMediaFormat::Mpeg4Audio:
384 return AVFileTypeAppleM4A;
385 case QMediaFormat::Wave:
386 return AVFileTypeWAVE;
388 return AVFileTypeQuickTimeMovie;
Combined button and popup list for selecting options.
bool isPlayableExtendedMIMEType(const char *extendedMimetype)
constexpr bool isVideoContainer(QMediaFormat::FileFormat fileFormat)
bool canEncodeDecodeFormat(AudioFormatID formatID)
std::set< CMVideoCodecType > availableVTVideoEncoderCodecTypes()
QList< QPlatformMediaFormatInfo::CodecMap > enumerateDecoders()
QMediaFormat::VideoCodec codec
constexpr auto recordGetCodec
bool canEncodeAudioFormat(AudioFormatID formatID)
QMediaFormat::AudioCodec implicitCodec
QMediaFormat::FileFormat fileFormat
const char *__nonnull name
QList< QPlatformMediaFormatInfo::CodecMap > enumerateEncoders()
bool isPlayableExtendedMIMEType(const char *containerName, const char *codecName)