diff mbox series

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

Message ID 20210412023142.8470-2-mindmark@gmail.com
State New
Headers show
Series [FFmpeg-devel,v2,1/2] libavdevice/avfoundation: add buffer fifo and output packets in order they arrive
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Mark Reid April 12, 2021, 2:31 a.m. UTC
From: Mark Reid <mindmark@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 mbox series

Patch

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 },