Revision control

Copy as Markdown

// 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 "qffmpegmediaformatinfo_p.h"
#include "qffmpegcodecstorage_p.h"
#include "qffmpeg_ranges_p.h"
#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/private/qflatmap_p.h>
#include <QtMultimedia/qaudioformat.h>
#include <QtMultimedia/private/qmultimedia_ranges_p.h>
#include <QtGui/qimagewriter.h>
QT_BEGIN_NAMESPACE
using namespace Qt::Literals;
namespace {
template <size_t MapSize, typename Key, typename Value>
auto makeReverseLookupTable(std::initializer_list<std::pair<const Key, Value>> map)
{
using namespace QtMultimediaPrivate;
auto keyValuePairs = map | views::transform([](const auto &pair) {
return std::pair<const Value, Key>(pair.second, pair.first);
}) | ranges::to<std::vector<std::pair<const Value, Key>>>();
return keyValuePairs | ranges::to<QVarLengthFlatMap<Value, Key, MapSize>>();
}
static constexpr auto videoCodecMap =
std::initializer_list<std::pair<const QMediaFormat::VideoCodec, AVCodecID>>{
{ QMediaFormat::VideoCodec::MPEG1, AV_CODEC_ID_MPEG1VIDEO },
{ QMediaFormat::VideoCodec::MPEG2, AV_CODEC_ID_MPEG2VIDEO },
{ QMediaFormat::VideoCodec::MPEG4, AV_CODEC_ID_MPEG4 },
{ QMediaFormat::VideoCodec::H264, AV_CODEC_ID_H264 },
{ QMediaFormat::VideoCodec::H265, AV_CODEC_ID_HEVC },
{ QMediaFormat::VideoCodec::VP8, AV_CODEC_ID_VP8 },
{ QMediaFormat::VideoCodec::VP9, AV_CODEC_ID_VP9 },
{ QMediaFormat::VideoCodec::AV1, AV_CODEC_ID_AV1 },
{ QMediaFormat::VideoCodec::Theora, AV_CODEC_ID_THEORA },
{ QMediaFormat::VideoCodec::WMV, AV_CODEC_ID_WMV3 },
{ QMediaFormat::VideoCodec::MotionJPEG, AV_CODEC_ID_MJPEG },
};
} // namespace
AVCodecID codecId(QMediaFormat::VideoCodec codec)
{
static const QVarLengthFlatMap<QMediaFormat::VideoCodec, AVCodecID, videoCodecMap.size()> map(
videoCodecMap.begin(), videoCodecMap.end());
const auto it = map.find(codec);
return it != map.end() ? it->second : AV_CODEC_ID_NONE;
}
QMediaFormat::VideoCodec videoCodecFromId(AVCodecID id)
{
using namespace QtMultimediaPrivate;
static const auto map = makeReverseLookupTable<videoCodecMap.size()>(videoCodecMap);
const auto it = map.find(id);
return it != map.end() ? it->second : QMediaFormat::VideoCodec::Unspecified;
}
static constexpr auto audioCodecMap =
std::initializer_list<std::pair<const QMediaFormat::AudioCodec, AVCodecID>>{
{ QMediaFormat::AudioCodec::MP3, AV_CODEC_ID_MP3 },
{ QMediaFormat::AudioCodec::AAC, AV_CODEC_ID_AAC },
{ QMediaFormat::AudioCodec::AC3, AV_CODEC_ID_AC3 },
{ QMediaFormat::AudioCodec::EAC3, AV_CODEC_ID_EAC3 },
{ QMediaFormat::AudioCodec::FLAC, AV_CODEC_ID_FLAC },
{ QMediaFormat::AudioCodec::DolbyTrueHD, AV_CODEC_ID_TRUEHD },
{ QMediaFormat::AudioCodec::Opus, AV_CODEC_ID_OPUS },
{ QMediaFormat::AudioCodec::Vorbis, AV_CODEC_ID_VORBIS },
{ QMediaFormat::AudioCodec::Wave, AV_CODEC_ID_PCM_S16LE },
{ QMediaFormat::AudioCodec::WMA, AV_CODEC_ID_WMAPRO },
{ QMediaFormat::AudioCodec::ALAC, AV_CODEC_ID_ALAC },
};
AVCodecID codecId(QMediaFormat::AudioCodec codec)
{
static const QVarLengthFlatMap<QMediaFormat::AudioCodec, AVCodecID, audioCodecMap.size()> map(
audioCodecMap.begin(), audioCodecMap.end());
const auto it = map.find(codec);
return it != map.end() ? it->second : AV_CODEC_ID_NONE;
}
QMediaFormat::AudioCodec audioCodecFromId(AVCodecID id)
{
using namespace QtMultimediaPrivate;
static const auto map = makeReverseLookupTable<audioCodecMap.size()>(audioCodecMap);
const auto it = map.find(id);
return it != map.end() ? it->second : QMediaFormat::AudioCodec::Unspecified;
}
// mimetypes are mostly copied from qmediaformat.cpp. Unfortunately, FFmpeg uses
// in some cases slightly different mimetypes
constexpr struct
{
QMediaFormat::FileFormat fileFormat;
const char *mimeType;
const char *name; // disambiguate if we have several muxers/demuxers
} s_outputFormatMap[] = {
{ QMediaFormat::WMV, "video/x-ms-asf", "asf" },
{ QMediaFormat::AVI, "video/x-msvideo", nullptr },
{ QMediaFormat::Matroska, "video/x-matroska", nullptr },
{ QMediaFormat::MPEG4, "video/mp4", "mp4" },
{ QMediaFormat::Ogg, "video/ogg", nullptr },
// QuickTime is the same as MP4
{ QMediaFormat::WebM, "video/webm", "webm" },
// Audio Formats
// Mpeg4Audio is the same as MP4 without the video codecs
{ QMediaFormat::AAC, "audio/aac", nullptr },
// WMA is the same as WMV
{ QMediaFormat::FLAC, "audio/x-flac", nullptr },
{ QMediaFormat::MP3, "audio/mpeg", "mp3" },
{ QMediaFormat::Wave, "audio/x-wav", nullptr },
{ QMediaFormat::Ogg, "audio/ogg", nullptr },
};
QMediaFormat::FileFormat formatForAVFormat(const AVOutputFormat *format)
{
if (!format->mime_type || !*format->mime_type)
return QMediaFormat::UnspecifiedFormat;
for (const auto &m : s_outputFormatMap) {
if (!strcmp(m.mimeType, format->mime_type)) {
// check if the name matches. This is used to disambiguate where FFmpeg provides
// multiple muxers or demuxers
if (!m.name || !strcmp(m.name, format->name))
return m.fileFormat;
}
}
return QMediaFormat::UnspecifiedFormat;
}
const AVOutputFormat *avFormatForFormat(QMediaFormat::FileFormat format)
{
if (format == QMediaFormat::QuickTime || format == QMediaFormat::Mpeg4Audio)
format = QMediaFormat::MPEG4;
if (format == QMediaFormat::WMA)
format = QMediaFormat::WMV;
for (const auto &m : s_outputFormatMap) {
if (m.fileFormat == format)
return av_guess_format(m.name, nullptr, m.mimeType);
}
return nullptr;
}
// Seems like FFmpeg uses different names for muxers and demuxers of the same format.
// that makes it somewhat cumbersome to detect things correctly.
// The input formats have a comma separated list of short names. We check the first one of those
// as the docs specify that you only append to the list
constexpr struct
{
QMediaFormat::FileFormat fileFormat;
QLatin1String name;
} s_inputFormatMap[] = {
{ QMediaFormat::WMV, "asf"_L1 },
{ QMediaFormat::AVI, "avi"_L1 },
{ QMediaFormat::Matroska, "matroska"_L1 },
{ QMediaFormat::MPEG4, "mov"_L1 },
{ QMediaFormat::Ogg, "ogg"_L1 },
{ QMediaFormat::WebM, "webm"_L1 },
// Audio Formats
// Mpeg4Audio is the same as MP4 without the video codecs
{ QMediaFormat::AAC, "aac"_L1 },
// WMA is the same as WMV
{ QMediaFormat::FLAC, "flac"_L1 },
{ QMediaFormat::MP3, "mp3"_L1 },
{ QMediaFormat::Wave, "wav"_L1 },
};
QMediaFormat::FileFormat formatForAVInputFormat(const AVInputFormat &format)
{
if (!format.name)
return QMediaFormat::UnspecifiedFormat;
const QLatin1String formatName(format.name);
for (const auto &m : s_inputFormatMap) {
if (formatName.startsWith(m.name))
return m.fileFormat;
}
return QMediaFormat::UnspecifiedFormat;
}
enum class CodecType : uint8_t {
Audio,
Video,
};
template <CodecType Type>
bool codecSupportsFormat(const AVOutputFormat *format, AVCodecID codecId)
{
const int result = avformat_query_codec(format, codecId, FF_COMPLIANCE_NORMAL);
switch (result) {
case 1:
return true;
case AVERROR_PATCHWELCOME:
case 0:
return false;
default:
break;
}
if (result < 0) {
// A negative result means that the codec may work, but information is unavailable
switch (Type) {
case CodecType::Audio:
return codecId == format->audio_codec;
case CodecType::Video:
return codecId == format->video_codec;
}
}
return false;
}
QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
{
namespace ranges = QtMultimediaPrivate::ranges;
namespace views = QtMultimediaPrivate::views;
using VideoCodec = QMediaFormat::VideoCodec;
using AudioCodec = QMediaFormat::AudioCodec;
QList<AudioCodec> audioEncoders; // All audio encoders that Qt support
QList<AudioCodec> extraAudioDecoders; // All audio decoders that do not support encoding
QList<VideoCodec> videoEncoders; // All video encoders that Qt support
QList<VideoCodec> extraVideoDecoders; // All video decoders that do not support encoding
using Descriptors =
QFFmpeg::FFmpegValueIteratorRange<const AVCodecDescriptor *, avcodec_descriptor_next>;
// Sort all FFmpeg's codecs into the buckets
for (const AVCodecDescriptor *descriptor : Descriptors{}) {
const bool canEncode{ QFFmpeg::findAVEncoder(descriptor->id).has_value() };
const bool canDecode{ QFFmpeg::findAVDecoder(descriptor->id).has_value() };
const bool isAVCodec = canEncode || canDecode;
if (!isAVCodec)
continue; // subtitle codec
const VideoCodec videoCodec = videoCodecForAVCodecId(descriptor->id);
const AudioCodec audioCodec = audioCodecForAVCodecId(descriptor->id);
if (descriptor->type == AVMEDIA_TYPE_VIDEO && videoCodec != VideoCodec::Unspecified) {
if (canEncode) {
if (!videoEncoders.contains(videoCodec))
videoEncoders.append(videoCodec);
} else if (canDecode) {
if (!extraVideoDecoders.contains(videoCodec))
extraVideoDecoders.append(videoCodec);
}
} else if (descriptor->type == AVMEDIA_TYPE_AUDIO && audioCodec != AudioCodec::Unspecified) {
if (canEncode) {
if (!audioEncoders.contains(audioCodec))
audioEncoders.append(audioCodec);
} else if (canDecode) {
if (!extraAudioDecoders.contains(audioCodec))
extraAudioDecoders.append(audioCodec);
}
}
}
// Update 'encoders' list with muxer/encoder combinations that Qt supports
using MuxerRange = QFFmpeg::FFmpegOpaqueIteratorRange<const AVOutputFormat *, av_muxer_iterate>;
for (const AVOutputFormat *outputFormat : MuxerRange()) {
QMediaFormat::FileFormat mediaFormat = formatForAVFormat(outputFormat);
if (mediaFormat == QMediaFormat::UnspecifiedFormat)
continue;
CodecMap encoder;
encoder.format = mediaFormat;
auto supportedAudioCodecs = audioEncoders | views::filter([&](AudioCodec codec) {
return codecSupportsFormat<CodecType::Audio>(outputFormat, codecId(codec));
});
encoder.audio = supportedAudioCodecs | ranges::to<QList<AudioCodec>>();
auto supportedVideoCodecs = videoEncoders | views::filter([&](VideoCodec codec) {
return codecSupportsFormat<CodecType::Video>(outputFormat, codecId(codec));
});
encoder.video = supportedVideoCodecs | ranges::to<QList<VideoCodec>>();
// If no encoders support either audio or video, we skip this format.
if (encoder.audio.isEmpty() && encoder.video.isEmpty())
continue;
// Handle special cases
switch (encoder.format) {
case QMediaFormat::WMV:
// add WMA
encoders.append({ QMediaFormat::WMA, encoder.audio, {} });
break;
case QMediaFormat::MPEG4:
// add Mpeg4Audio and QuickTime
encoders.append({ QMediaFormat::QuickTime, encoder.audio, encoder.video });
encoders.append({ QMediaFormat::Mpeg4Audio, encoder.audio, {} });
break;
case QMediaFormat::Wave:
// FFmpeg allows other encoded formats in WAV containers, but we do not want that
if (!encoder.audio.contains(AudioCodec::Wave))
continue;
encoder.audio = { AudioCodec::Wave };
break;
default:
break;
}
encoders.append(encoder);
}
// FFmpeg doesn't allow querying supported codecs for demuxers.
// We take a simple approximation stating that we can decode what we
// can encode. That's a safe subset.
decoders = encoders;
if constexpr (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows) {
// MediaFoundation HVEC encoder fails when processing frames
for (auto &encoder : encoders) {
auto h265index = encoder.video.indexOf(VideoCodec::H265);
if (h265index >= 0)
encoder.video.removeAt(h265index);
}
}
// FFmpeg can currently only decode WMA and WMV, not encode
auto findDecoder = [&](QMediaFormat::FileFormat fmt) -> CodecMap * {
auto it = ranges::find_if(decoders, [&](const CodecMap &m) {
return m.format == fmt;
});
return it != decoders.end() ? &*it : nullptr;
};
if (extraAudioDecoders.contains(AudioCodec::WMA)) {
if (auto *wma = findDecoder(QMediaFormat::WMA))
wma->audio.append(AudioCodec::WMA);
if (auto *wmv = findDecoder(QMediaFormat::WMV))
wmv->audio.append(AudioCodec::WMA);
}
if (extraVideoDecoders.contains(VideoCodec::WMV)) {
if (auto *wmv = findDecoder(QMediaFormat::WMV))
wmv->video.append(VideoCodec::WMV);
}
// Add image formats we support. We currently simply use Qt's built-in image write
// to save images. That doesn't give us HDR support or support for larger bit depths,
// but most cameras can currently not generate those anyway.
const QList<QByteArray> imgFormats = QImageWriter::supportedImageFormats();
for (const auto &f : imgFormats) {
if (f == "png")
imageFormats.append(QImageCapture::PNG);
else if (f == "jpeg")
imageFormats.append(QImageCapture::JPEG);
else if (f == "tiff")
imageFormats.append(QImageCapture::Tiff);
else if (f == "webp")
imageFormats.append(QImageCapture::WebP);
}
fixupCodecMaps();
}
QFFmpegMediaFormatInfo::~QFFmpegMediaFormatInfo() = default;
QMediaFormat::AudioCodec QFFmpegMediaFormatInfo::audioCodecForAVCodecId(AVCodecID id)
{
return audioCodecFromId(id);
}
QMediaFormat::VideoCodec QFFmpegMediaFormatInfo::videoCodecForAVCodecId(AVCodecID id)
{
return videoCodecFromId(id);
}
QMediaFormat::FileFormat
QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(const AVInputFormat &format)
{
return formatForAVInputFormat(format);
}
const AVOutputFormat *
QFFmpegMediaFormatInfo::outputFormatForFileFormat(QMediaFormat::FileFormat format)
{
return avFormatForFormat(format);
}
AVCodecID QFFmpegMediaFormatInfo::codecIdForVideoCodec(QMediaFormat::VideoCodec codec)
{
return codecId(codec);
}
AVCodecID QFFmpegMediaFormatInfo::codecIdForAudioCodec(QMediaFormat::AudioCodec codec)
{
return codecId(codec);
}
QAudioFormat::SampleFormat QFFmpegMediaFormatInfo::sampleFormat(AVSampleFormat format)
{
switch (format) {
case AV_SAMPLE_FMT_NONE:
default:
return QAudioFormat::Unknown;
case AV_SAMPLE_FMT_U8: ///< unsigned 8 bits
case AV_SAMPLE_FMT_U8P: ///< unsigned 8 bits: planar
return QAudioFormat::UInt8;
case AV_SAMPLE_FMT_S16: ///< signed 16 bits
case AV_SAMPLE_FMT_S16P: ///< signed 16 bits: planar
return QAudioFormat::Int16;
case AV_SAMPLE_FMT_S32: ///< signed 32 bits
case AV_SAMPLE_FMT_S32P: ///< signed 32 bits: planar
return QAudioFormat::Int32;
case AV_SAMPLE_FMT_FLT: ///< float
case AV_SAMPLE_FMT_FLTP: ///< float: planar
return QAudioFormat::Float;
case AV_SAMPLE_FMT_DBL: ///< double
case AV_SAMPLE_FMT_DBLP: ///< double: planar
case AV_SAMPLE_FMT_S64: ///< signed 64 bits
case AV_SAMPLE_FMT_S64P: ///< signed 64 bits, planar
// let's use float
return QAudioFormat::Float;
}
}
AVSampleFormat QFFmpegMediaFormatInfo::avSampleFormat(QAudioFormat::SampleFormat format)
{
switch (format) {
case QAudioFormat::UInt8:
return AV_SAMPLE_FMT_U8;
case QAudioFormat::Int16:
return AV_SAMPLE_FMT_S16;
case QAudioFormat::Int32:
return AV_SAMPLE_FMT_S32;
case QAudioFormat::Float:
return AV_SAMPLE_FMT_FLT;
default:
return AV_SAMPLE_FMT_NONE;
}
}
int64_t QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::ChannelConfig channelConfig)
{
int64_t avChannelLayout = 0;
if (channelConfig & (1 << QAudioFormat::FrontLeft))
avChannelLayout |= AV_CH_FRONT_LEFT;
if (channelConfig & (1 << QAudioFormat::FrontRight))
avChannelLayout |= AV_CH_FRONT_RIGHT;
if (channelConfig & (1 << QAudioFormat::FrontCenter))
avChannelLayout |= AV_CH_FRONT_CENTER;
if (channelConfig & (1 << QAudioFormat::LFE))
avChannelLayout |= AV_CH_LOW_FREQUENCY;
if (channelConfig & (1 << QAudioFormat::BackLeft))
avChannelLayout |= AV_CH_BACK_LEFT;
if (channelConfig & (1 << QAudioFormat::BackRight))
avChannelLayout |= AV_CH_BACK_RIGHT;
if (channelConfig & (1 << QAudioFormat::FrontLeftOfCenter))
avChannelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
if (channelConfig & (1 << QAudioFormat::FrontRightOfCenter))
avChannelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
if (channelConfig & (1 << QAudioFormat::BackCenter))
avChannelLayout |= AV_CH_BACK_CENTER;
if (channelConfig & (1 << QAudioFormat::LFE2))
avChannelLayout |= AV_CH_LOW_FREQUENCY_2;
if (channelConfig & (1 << QAudioFormat::SideLeft))
avChannelLayout |= AV_CH_SIDE_LEFT;
if (channelConfig & (1 << QAudioFormat::SideRight))
avChannelLayout |= AV_CH_SIDE_RIGHT;
if (channelConfig & (1 << QAudioFormat::TopFrontLeft))
avChannelLayout |= AV_CH_TOP_FRONT_LEFT;
if (channelConfig & (1 << QAudioFormat::TopFrontRight))
avChannelLayout |= AV_CH_TOP_FRONT_RIGHT;
if (channelConfig & (1 << QAudioFormat::TopFrontCenter))
avChannelLayout |= AV_CH_TOP_FRONT_CENTER;
if (channelConfig & (1 << QAudioFormat::TopCenter))
avChannelLayout |= AV_CH_TOP_CENTER;
if (channelConfig & (1 << QAudioFormat::TopBackLeft))
avChannelLayout |= AV_CH_TOP_BACK_LEFT;
if (channelConfig & (1 << QAudioFormat::TopBackRight))
avChannelLayout |= AV_CH_TOP_BACK_RIGHT;
if (channelConfig & (1 << QAudioFormat::TopBackCenter))
avChannelLayout |= AV_CH_TOP_BACK_CENTER;
if (channelConfig & (1 << QAudioFormat::TopSideLeft))
avChannelLayout |= AV_CH_TOP_SIDE_LEFT;
if (channelConfig & (1 << QAudioFormat::TopSideRight))
avChannelLayout |= AV_CH_TOP_SIDE_RIGHT;
if (channelConfig & (1 << QAudioFormat::BottomFrontCenter))
avChannelLayout |= AV_CH_BOTTOM_FRONT_CENTER;
if (channelConfig & (1 << QAudioFormat::BottomFrontLeft))
avChannelLayout |= AV_CH_BOTTOM_FRONT_LEFT;
if (channelConfig & (1 << QAudioFormat::BottomFrontRight))
avChannelLayout |= AV_CH_BOTTOM_FRONT_RIGHT;
return avChannelLayout;
}
QAudioFormat::ChannelConfig QFFmpegMediaFormatInfo::channelConfigForAVLayout(int64_t avChannelLayout)
{
quint32 channelConfig = 0;
if (avChannelLayout & AV_CH_FRONT_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontLeft);
if (avChannelLayout & AV_CH_FRONT_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontRight);
if (avChannelLayout & AV_CH_FRONT_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontCenter);
if (avChannelLayout & AV_CH_LOW_FREQUENCY)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::LFE);
if (avChannelLayout & AV_CH_BACK_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackLeft);
if (avChannelLayout & AV_CH_BACK_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackRight);
if (avChannelLayout & AV_CH_FRONT_LEFT_OF_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontLeftOfCenter);
if (avChannelLayout & AV_CH_FRONT_RIGHT_OF_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::FrontRightOfCenter);
if (avChannelLayout & AV_CH_BACK_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BackCenter);
if (avChannelLayout & AV_CH_LOW_FREQUENCY_2)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::LFE2);
if (avChannelLayout & AV_CH_SIDE_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::SideLeft);
if (avChannelLayout & AV_CH_SIDE_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::SideRight);
if (avChannelLayout & AV_CH_TOP_FRONT_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontLeft);
if (avChannelLayout & AV_CH_TOP_FRONT_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontRight);
if (avChannelLayout & AV_CH_TOP_FRONT_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopFrontCenter);
if (avChannelLayout & AV_CH_TOP_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopCenter);
if (avChannelLayout & AV_CH_TOP_BACK_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackLeft);
if (avChannelLayout & AV_CH_TOP_BACK_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackRight);
if (avChannelLayout & AV_CH_TOP_BACK_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopBackCenter);
if (avChannelLayout & AV_CH_TOP_SIDE_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopSideLeft);
if (avChannelLayout & AV_CH_TOP_SIDE_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::TopSideRight);
if (avChannelLayout & AV_CH_BOTTOM_FRONT_CENTER)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontCenter);
if (avChannelLayout & AV_CH_BOTTOM_FRONT_LEFT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontLeft);
if (avChannelLayout & AV_CH_BOTTOM_FRONT_RIGHT)
channelConfig |= QAudioFormat::channelConfig(QAudioFormat::BottomFrontRight);
return QAudioFormat::ChannelConfig(channelConfig);
}
QAudioFormat
QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(const AVCodecParameters &codecpar)
{
QAudioFormat format;
format.setSampleFormat(sampleFormat(AVSampleFormat(codecpar.format)));
format.setSampleRate(codecpar.sample_rate);
#if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
uint64_t channelLayout = 0;
if (codecpar.ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
channelLayout = codecpar.ch_layout.u.mask;
else
channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar.ch_layout.nb_channels));
#else
uint64_t channelLayout = codecpar.channel_layout;
if (!channelLayout)
channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar.channels));
#endif
format.setChannelConfig(channelConfigForAVLayout(channelLayout));
return format;
}
QT_END_NAMESPACE