From 934c9b6dee03b8b71bd71f06d03185c56f94bd76 Mon Sep 17 00:00:00 2001 From: Doris Verria Date: Mon, 28 Mar 2022 02:09:24 +0200 Subject: darwin: Don't specify source format hint for audio recording We were initializing the audio writer input with a format hint equal to the audio capture device's active format. This is wrong as we can't ensure that the captured audio buffers will be in that same format. This caused failure in recording because the captured audio buffers had the kAudioFormatFlagIsNonInterleaved set, while the format hint didn't, causing the internal calls to the AudioConverter to fail with errors. To fix, do not specify a format hint but instead setup the audio settings dictionary with the necessary keys. Force a default value for AVNumberOfChannelsKey since it is now required, and for recording with more than 2 channels, provide a supported channel layout. Fixes: QTBUG-98262 Done-with: Vladimir Belyavsky Change-Id: I537d82e729c91a66d85aaa61c563a1ecd1a91af3 Reviewed-by: Vladimir Belyavsky Reviewed-by: Lars Knoll (cherry picked from commit 980c145069c589085b4dee4b4c2b69e3b1a0adc3) Reviewed-by: Doris Verria --- .../platform/darwin/camera/avfcamerautility.mm | 33 ++++++++++++++++++++++ .../platform/darwin/camera/avfcamerautility_p.h | 1 + .../platform/darwin/camera/avfmediaassetwriter.mm | 6 +--- .../platform/darwin/camera/avfmediaencoder.mm | 33 +++++++++++++--------- 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/multimedia/platform/darwin/camera/avfcamerautility.mm b/src/multimedia/platform/darwin/camera/avfcamerautility.mm index 444162523..f5c90bb77 100644 --- a/src/multimedia/platform/darwin/camera/avfcamerautility.mm +++ b/src/multimedia/platform/darwin/camera/avfcamerautility.mm @@ -711,4 +711,37 @@ std::optional> qt_supported_channel_counts_for_format(int codecId) return result; } +QList qt_supported_channel_layout_tags_for_format(int codecId, int noChannels) +{ + QList result; + AudioStreamBasicDescription sf = {}; + sf.mFormatID = codecId; + sf.mChannelsPerFrame = noChannels; + UInt32 size; + OSStatus err = AudioFormatGetPropertyInfo( + kAudioFormatProperty_AvailableEncodeChannelLayoutTags, + sizeof(sf), + &sf, + &size); + + if (err != noErr) + return result; + + UInt32 noTags = (UInt32)size / sizeof(UInt32); + AudioChannelLayoutTag tagsArr[noTags]; + + err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeChannelLayoutTags, + sizeof(sf), + &sf, + &size, + tagsArr); + if (err != noErr) + return result; + + for (UInt32 i = 0; i < noTags; i++) + result << tagsArr[i]; + + return result; +} + QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/camera/avfcamerautility_p.h b/src/multimedia/platform/darwin/camera/avfcamerautility_p.h index 26a023be9..00ff4bb29 100644 --- a/src/multimedia/platform/darwin/camera/avfcamerautility_p.h +++ b/src/multimedia/platform/darwin/camera/avfcamerautility_p.h @@ -195,6 +195,7 @@ void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection QList qt_supported_sample_rates_for_format(int codecId); QList qt_supported_bit_rates_for_format(int codecId); std::optional> qt_supported_channel_counts_for_format(int codecId); +QList qt_supported_channel_layout_tags_for_format(int codecId, int noChannels); QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm b/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm index 61f248c72..243f8455a 100644 --- a/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm +++ b/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm @@ -529,12 +529,8 @@ using AVFAtomicInt64 = QAtomicInteger; m_audioWriterInput.reset(); if (m_audioQueue) { - CMFormatDescriptionRef sourceFormat = session->audioCaptureDevice() - ? session->audioCaptureDevice().activeFormat.formatDescription - : 0; m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio - outputSettings:m_audioSettings - sourceFormatHint:sourceFormat]); + outputSettings:m_audioSettings]); if (!m_audioWriterInput) { qWarning() << Q_FUNC_INFO << "failed to create audio writer input"; // But we still can record video. diff --git a/src/multimedia/platform/darwin/camera/avfmediaencoder.mm b/src/multimedia/platform/darwin/camera/avfmediaencoder.mm index 87e7a357f..b669dd960 100644 --- a/src/multimedia/platform/darwin/camera/avfmediaencoder.mm +++ b/src/multimedia/platform/darwin/camera/avfmediaencoder.mm @@ -139,12 +139,14 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin { NSMutableDictionary *settings = [NSMutableDictionary dictionary]; + // Codec int codecId = QDarwinFormatInfo::audioFormatForCodec(encoderSettings.mediaFormat().audioCodec()); [settings setObject:[NSNumber numberWithInt:codecId] forKey:AVFormatIDKey]; // Setting AVEncoderQualityKey is not allowed when format ID is alac or lpcm if (codecId != kAudioFormatAppleLossless && codecId != kAudioFormatLinearPCM && encoderSettings.encodingMode() == QMediaRecorder::ConstantQualityEncoding) { + // AudioQuality int quality; switch (encoderSettings.quality()) { case QMediaRecorder::VeryLowQuality: @@ -166,6 +168,7 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin } [settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey]; } else { + // BitRate bool isBitRateSupported = false; int bitRate = encoderSettings.audioBitRate(); if (bitRate > 0) { @@ -183,6 +186,7 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin } } + // SampleRate int sampleRate = encoderSettings.audioSampleRate(); bool isSampleRateSupported = false; if (sampleRate >= 8000 && sampleRate <= 192000) { @@ -194,7 +198,11 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin } } } + if (!isSampleRateSupported) + sampleRate = 44100; + [settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey]; + // Channels int channelCount = encoderSettings.audioChannelCount(); bool isChannelCountSupported = false; if (channelCount > 0) { @@ -213,16 +221,20 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin } } -#ifdef Q_OS_IOS - // Some keys are mandatory only on iOS - if (!isSampleRateSupported) { - sampleRate = 44100; - isSampleRateSupported = true; +if (isChannelCountSupported && channelCount > 2) { + AudioChannelLayout channelLayout; + memset(&channelLayout, 0, sizeof(AudioChannelLayout)); + auto channelLayoutTags = qt_supported_channel_layout_tags_for_format(codecId, channelCount); + if (channelLayoutTags.size()) { + channelLayout.mChannelLayoutTag = channelLayoutTags.first(); + [settings setObject:[NSData dataWithBytes: &channelLayout length: sizeof(channelLayout)] forKey:AVChannelLayoutKey]; + } else { + isChannelCountSupported = false; + } } - if (!isChannelCountSupported) { + if (!isChannelCountSupported) channelCount = 2; - isChannelCountSupported = true; - } + [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey]; if (codecId == kAudioFormatAppleLossless) [settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey]; @@ -233,11 +245,6 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey]; [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved]; } -#endif - if (isSampleRateSupported) - [settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey]; - if (isChannelCountSupported) - [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey]; return settings; } -- cgit v1.2.3