[FFmpeg-devel] [PATCH v2 2/2] libavdevice/avfoundation: add option to set audio sample rate and use native device formats

mindmark at gmail.com mindmark at gmail.com
Mon Apr 12 05:31:42 EEST 2021


From: Mark Reid <mindmark at gmail.com>

This also seems to prevent the audio format changing after format has been identified. 
This can happen in ffplay and might have something to do with sdl configuring the audio devices.

---
 libavdevice/avfoundation.m | 123 ++++++++++++++++++++++++++++---------
 1 file changed, 94 insertions(+), 29 deletions(-)

diff --git a/libavdevice/avfoundation.m b/libavdevice/avfoundation.m
index 5ac6ec4183..70226cfdc8 100644
--- a/libavdevice/avfoundation.m
+++ b/libavdevice/avfoundation.m
@@ -118,11 +118,8 @@ typedef struct
 
     int             audio_channels;
     int             audio_bits_per_sample;
-    int             audio_float;
-    int             audio_be;
-    int             audio_signed_integer;
-    int             audio_packed;
     int             audio_non_interleaved;
+    int             audio_sample_rate;
 
     int32_t         *audio_buffer;
     int             audio_buffer_size;
@@ -632,12 +629,47 @@ static int add_video_device(AVFormatContext *s, AVCaptureDevice *video_device)
     return 0;
 }
 
+static enum AVCodecID find_audio_codec_id(const AudioStreamBasicDescription *basic_desc)
+{
+    int audio_float           = basic_desc->mFormatFlags & kAudioFormatFlagIsFloat;
+    int audio_signed_integer  = basic_desc->mFormatFlags & kAudioFormatFlagIsSignedInteger;
+    int audio_be              = basic_desc->mFormatFlags & kAudioFormatFlagIsBigEndian;
+    int audio_packed          = basic_desc->mFormatFlags & kAudioFormatFlagIsPacked;
+    int audio_bits_per_sample = basic_desc->mBitsPerChannel;
+
+    if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
+        audio_float &&
+        audio_bits_per_sample == 32 &&
+        audio_packed) {
+        return audio_be ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE;
+    } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
+        audio_signed_integer &&
+        audio_bits_per_sample == 16 &&
+        audio_packed) {
+        return audio_be ? AV_CODEC_ID_PCM_S16BE : AV_CODEC_ID_PCM_S16LE;
+    } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
+        audio_signed_integer &&
+        audio_bits_per_sample == 24 &&
+        audio_packed) {
+        return audio_be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE;
+    } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
+        audio_signed_integer &&
+        audio_bits_per_sample == 32 &&
+        audio_packed) {
+        return audio_be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE;
+    } else {
+        return AV_CODEC_ID_NONE;
+    }
+}
+
 static int add_audio_device(AVFormatContext *s, AVCaptureDevice *audio_device)
 {
     AVFContext *ctx = (AVFContext*)s->priv_data;
     NSError *error  = nil;
     AVCaptureDeviceInput* audio_dev_input = [[[AVCaptureDeviceInput alloc] initWithDevice:audio_device error:&error] autorelease];
     dispatch_queue_t queue;
+    NSObject *format = nil;
+    const AudioStreamBasicDescription *format_desc = NULL;
 
     if (!audio_dev_input) {
         av_log(s, AV_LOG_ERROR, "Failed to create AV capture input device: %s\n",
@@ -660,6 +692,61 @@ static int add_audio_device(AVFormatContext *s, AVCaptureDevice *audio_device)
         return 1;
     }
 
+#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
+
+    for (format in [audio_device valueForKey:@"formats"]) {
+        CMFormatDescriptionRef formatDescription;
+        formatDescription = (CMFormatDescriptionRef) [format performSelector:@selector(formatDescription)];
+        const AudioStreamBasicDescription *desc = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
+
+        if (desc->mSampleRate == ctx->audio_sample_rate) {
+            format_desc = desc;
+            break;
+        }
+    }
+
+    if(!format_desc) {
+        av_log(s, AV_LOG_ERROR, "Selected audio sample rate (%d Hz) is not supported\n", ctx->audio_sample_rate);
+        av_log(s, AV_LOG_ERROR, "Supported audio formats:\n");
+        for (format in [audio_device valueForKey:@"formats"]) {
+            const char *codec_name;
+            CMFormatDescriptionRef formatDescription;
+            formatDescription = (CMFormatDescriptionRef) [format performSelector:@selector(formatDescription)];
+            const AudioStreamBasicDescription *desc = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
+
+            enum AVCodecID codec_id = find_audio_codec_id(desc);
+            if (codec_id == AV_CODEC_ID_NONE) {
+                continue;
+            }
+
+            codec_name =  avcodec_get_name(codec_id);
+            av_log(s, AV_LOG_ERROR, "  %s, %d ch, %0.0f Hz \n", codec_name, desc->mChannelsPerFrame, desc->mSampleRate);
+        }
+
+        format_desc = CMAudioFormatDescriptionGetStreamBasicDescription(audio_device.activeFormat.formatDescription);
+        if (format_desc)
+            av_log(s, AV_LOG_WARNING, "Overriding selected sample rate with active sample rate: %0.0f Hz instead\n", format_desc->mSampleRate);
+    }
+
+    if (format_desc) {
+        int is_float = format_desc->mFormatFlags & kAudioFormatFlagIsFloat;
+        int audio_non_interleaved = format_desc->mFormatFlags & kAudioFormatFlagIsNonInterleaved;
+        int audio_be = format_desc->mFormatFlags & kAudioFormatFlagIsBigEndian;
+
+        NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
+                                 [NSNumber numberWithUnsignedInt:kAudioFormatLinearPCM],              AVFormatIDKey,
+                                 [NSNumber numberWithFloat:format_desc->mSampleRate],                 AVSampleRateKey,
+                                 [NSNumber numberWithUnsignedInteger:format_desc->mChannelsPerFrame], AVNumberOfChannelsKey,
+                                 [NSNumber numberWithInt:format_desc->mBitsPerChannel],               AVLinearPCMBitDepthKey,
+                                 [NSNumber numberWithBool:is_float],                                  AVLinearPCMIsFloatKey,
+                                 [NSNumber numberWithBool:audio_non_interleaved],                     AVLinearPCMIsNonInterleaved,
+                                 [NSNumber numberWithBool:audio_be],                                  AVLinearPCMIsBigEndianKey,
+                                 nil];
+
+        ctx->audio_output.audioSettings = settings;
+    }
+#endif
+
     ctx->avf_audio_delegate = [[AVFAudioReceiver alloc] initWithContext:ctx];
 
     queue = dispatch_queue_create("avf_audio_queue", NULL);
@@ -769,33 +856,10 @@ static int get_audio_config(AVFormatContext *s)
 
     ctx->audio_channels        = basic_desc->mChannelsPerFrame;
     ctx->audio_bits_per_sample = basic_desc->mBitsPerChannel;
-    ctx->audio_float           = basic_desc->mFormatFlags & kAudioFormatFlagIsFloat;
-    ctx->audio_be              = basic_desc->mFormatFlags & kAudioFormatFlagIsBigEndian;
-    ctx->audio_signed_integer  = basic_desc->mFormatFlags & kAudioFormatFlagIsSignedInteger;
-    ctx->audio_packed          = basic_desc->mFormatFlags & kAudioFormatFlagIsPacked;
     ctx->audio_non_interleaved = basic_desc->mFormatFlags & kAudioFormatFlagIsNonInterleaved;
 
-    if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
-        ctx->audio_float &&
-        ctx->audio_bits_per_sample == 32 &&
-        ctx->audio_packed) {
-        stream->codecpar->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE;
-    } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
-        ctx->audio_signed_integer &&
-        ctx->audio_bits_per_sample == 16 &&
-        ctx->audio_packed) {
-        stream->codecpar->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_S16BE : AV_CODEC_ID_PCM_S16LE;
-    } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
-        ctx->audio_signed_integer &&
-        ctx->audio_bits_per_sample == 24 &&
-        ctx->audio_packed) {
-        stream->codecpar->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE;
-    } else if (basic_desc->mFormatID == kAudioFormatLinearPCM &&
-        ctx->audio_signed_integer &&
-        ctx->audio_bits_per_sample == 32 &&
-        ctx->audio_packed) {
-        stream->codecpar->codec_id = ctx->audio_be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE;
-    } else {
+    stream->codecpar->codec_id = find_audio_codec_id(basic_desc);
+    if (stream->codecpar->codec_id == AV_CODEC_ID_NONE) {
         av_log(s, AV_LOG_ERROR, "audio format is not supported\n");
         return 1;
     }
@@ -1286,6 +1350,7 @@ static const AVOption options[] = {
     { "pixel_format", "set pixel format", offsetof(AVFContext, pixel_format), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_YUV420P}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM},
     { "framerate", "set frame rate", offsetof(AVFContext, framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
     { "video_size", "set video size", offsetof(AVFContext, width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
+    { "audio_sample_rate", "set audio sample rate in Hz", offsetof(AVFContext, audio_sample_rate), AV_OPT_TYPE_INT, {.i64 = 48000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
     { "capture_cursor", "capture the screen cursor", offsetof(AVFContext, capture_cursor), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
     { "capture_mouse_clicks", "capture the screen mouse clicks", offsetof(AVFContext, capture_mouse_clicks), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
     { "capture_raw_data", "capture the raw data from device connection", offsetof(AVFContext, capture_raw_data), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
-- 
2.29.2



More information about the ffmpeg-devel mailing list