[FFmpeg-devel] [PATCH v4 3/3] libavdevice/avfoundation.m: Allow to select devices by unique ID.

Marvin Scholz epirat07 at gmail.com
Fri Dec 17 23:51:59 EET 2021



On 17 Dec 2021, at 16:12, Romain Beauxis wrote:

> This is the third patch of a series of 3 that cleanup and enhance the
> avfoundation implementation for libavdevice.
>
> Changes:
> v2: None
> v3:
>   * Switched unique ID to use system-prodvided unique ID
>   * Implemented unique IDs for screen capture
> v4: Cleanup
>
> This patch adds a unique ID to avfoundation devices. This is needed
> because device index can change while the machine is running when
> devices are plugged or unplugged and device names can be tricky to use
> with localization and etc.
>
> Example of output:
> ./ffmpeg -f avfoundation -list_devices true -i ""
> [...]
> [AVFoundation indev @ 0x158705230] AVFoundation video devices:
> [AVFoundation indev @ 0x158705230] [0] FaceTime HD Camera (ID:
> 47B4B64B70674B9CAD2BAE273A71F4B5)
> [AVFoundation indev @ 0x158705230] [1] Capture screen 0 (ID:
> AvfilterAvfoundationCaptureScreen1)
> [AVFoundation indev @ 0x158705230] AVFoundation audio devices:
> [AVFoundation indev @ 0x158705230] [0] Loopback Audio (ID:
> com.rogueamoeba.Loopback.A5668B36-711E-4DF5-8A8D-7148508C735B)
> [AVFoundation indev @ 0x158705230] [1] MacBook Pro Microphone (ID:
> BuiltInMicrophoneDevice)
>
> Notes:
> * Unique names do not seem to follow any specific pattern. I have used
> one similar to the builtin microphone for screen capture
> * The : substitution is actually required. The loopback device above 
> did
> have it in its name.
>

Is there no way to escape the : in the command so that we would not
need to mess with the ID the system gives us?
And if we need to, it would be ideal to have a fully reversible way of
doing so, as then you could just reverse the mangling and use
`deviceWithUniqueID:` instead of iterating all devices.

That said, if thats not easily doable I am fine with the patch as-is,
aside from the minor comments below, thanks for your work on this.

> Signed-off-by: Romain Beauxis <toots at rastageeks.org>
> ---
>  doc/indevs.texi            |  6 ++--
>  libavdevice/avfoundation.m | 72 
> +++++++++++++++++++++++++++++---------
>  2 files changed, 60 insertions(+), 18 deletions(-)
>
> diff --git a/doc/indevs.texi b/doc/indevs.texi
> index 5be647f70a..2b55399c8c 100644
> --- a/doc/indevs.texi
> +++ b/doc/indevs.texi
> @@ -114,7 +114,7 @@ The input filename has to be given in the 
> following syntax:
>  -i "[[VIDEO]:[AUDIO]]"
>  @end example
>  The first entry selects the video input while the latter selects the 
> audio input.
> -The stream has to be specified by the device name or the device index 
> as shown by the device list.
> +The stream has to be specified by the device name, index or ID as 
> shown by the device list.
>  Alternatively, the video and/or audio input device can be chosen by 
> index using the
>  @option{
>      -video_device_index <INDEX>
> @@ -127,7 +127,9 @@ and/or
>  device name or index given in the input filename.
>   All available devices can be enumerated by using 
> @option{-list_devices true}, listing
> -all device names and corresponding indices.
> +all device names, corresponding indices and IDs, when available. 
> Device name can be +tricky to use when localized and device index can 
> change when devices are plugged or unplugged. A device
> +hash, when available, uniquely identifies a device and should not 
> change over time.

This should say ID I think, as hash was never mentioned before.

>   There are two device name aliases:
>  @table @code
> diff --git a/libavdevice/avfoundation.m b/libavdevice/avfoundation.m
> index b602cfbe95..25286507d6 100644
> --- a/libavdevice/avfoundation.m
> +++ b/libavdevice/avfoundation.m
> @@ -39,6 +39,8 @@
>  #include "libavutil/imgutils.h"
>  #include "avdevice.h"
>  +#define CLEANUP_DEVICE_ID(s) [[s 
> stringByReplacingOccurrencesOfString:@":" withString:@"."] UTF8String]
> +
>  static const int avf_time_base = 1000000;
>   static const AVRational avf_time_base_q = {
> @@ -797,21 +799,23 @@ static int avf_read_header(AVFormatContext *s)
>          int index = 0;
>          av_log(ctx, AV_LOG_INFO, "AVFoundation video devices:\n");
>          for (AVCaptureDevice *device in devices) {
> -            const char *name = [[device localizedName] UTF8String];
> -            index            = [devices indexOfObject:device];
> -            av_log(ctx, AV_LOG_INFO, "[%d] %s\n", index, name);
> +            const char *name     = [[device localizedName] 
> UTF8String];
> +            const char *uniqueId = CLEANUP_DEVICE_ID([device 
> uniqueID]);
> +            index                = [devices indexOfObject:device];
> +            av_log(ctx, AV_LOG_INFO, "[%d] %s (ID: %s)\n", index, 
> name, uniqueId);
>          }
>          for (AVCaptureDevice *device in devices_muxed) {
> -            const char *name = [[device localizedName] UTF8String];
> -            index            = [devices count] + [devices_muxed 
> indexOfObject:device];
> -            av_log(ctx, AV_LOG_INFO, "[%d] %s\n", index, name);
> +            const char *name     = [[device localizedName] 
> UTF8String];
> +            const char *uniqueId = CLEANUP_DEVICE_ID([device 
> uniqueID]);
> +            index                = [devices count] + [devices_muxed 
> indexOfObject:device];
> +            av_log(ctx, AV_LOG_INFO, "[%d] %s (ID: %s)\n", index, 
> name, uniqueId);
>          }
>  #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
>          if (num_screens > 0) {
>              CGDirectDisplayID screens[num_screens];
>              CGGetActiveDisplayList(num_screens, screens, 
> &num_screens);
>              for (int i = 0; i < num_screens; i++) {
> -                av_log(ctx, AV_LOG_INFO, "[%d] Capture screen %d\n", 
> ctx->num_video_devices + i, i);
> +                av_log(ctx, AV_LOG_INFO, "[%d] Capture screen %d (ID: 
> AvfilterAvfoundationCaptureScreen%d)\n", ctx->num_video_devices + i, 
> i, screens[i]);
>              }
>          }
>  #endif
> @@ -819,9 +823,10 @@ static int avf_read_header(AVFormatContext *s)
>          av_log(ctx, AV_LOG_INFO, "AVFoundation audio devices:\n");
>          devices = [AVCaptureDevice 
> devicesWithMediaType:AVMediaTypeAudio];
>          for (AVCaptureDevice *device in devices) {
> -            const char *name = [[device localizedName] UTF8String];
> -            int index  = [devices indexOfObject:device];
> -            av_log(ctx, AV_LOG_INFO, "[%d] %s\n", index, name);
> +            const char *name     = [[device localizedName] 
> UTF8String];
> +            const char *uniqueId = CLEANUP_DEVICE_ID([device 
> uniqueID]);
> +            int index            = [devices indexOfObject:device];
> +            av_log(ctx, AV_LOG_INFO, "[%d] %s (ID: %s)\n", index, 
> name, uniqueId);
>          }
>           goto fail;
>      }
> @@ -883,14 +888,29 @@ static int avf_read_header(AVFormatContext *s)
>          } else {
>          // looking for video inputs
>          for (AVCaptureDevice *device in devices) {
> -            if (!strncmp(ctx->video_filename, [[device localizedName] 
> UTF8String], strlen(ctx->video_filename))) {
> +            const char *name = [[device localizedName] UTF8String];
> +            if (!strncmp(ctx->video_filename, name, 
> strlen(ctx->video_filename))) {
> +                video_device = device;
> +                break;
> +            }
> +
> +            const char *uniqueId = CLEANUP_DEVICE_ID([device 
> uniqueID]);
> +            if (!strncmp(ctx->video_filename, uniqueId, 
> strlen(ctx->video_filename))) {
>                  video_device = device;
>                  break;
>              }
>          }
>          // looking for muxed inputs
>          for (AVCaptureDevice *device in devices_muxed) {
> -            if (!strncmp(ctx->video_filename, [[device localizedName] 
> UTF8String], strlen(ctx->video_filename))) {
> +            const char *name = [[device localizedName] UTF8String];
> +            if (!strncmp(ctx->video_filename, name, 
> strlen(ctx->video_filename))) {
> +                video_device = device;
> +                ctx->video_is_muxed = 1;
> +                break;
> +            }
> +
> +            const char *uniqueId = CLEANUP_DEVICE_ID([device 
> uniqueID]);
> +            if (!strncmp(ctx->video_filename, uniqueId, 
> strlen(ctx->video_filename))) {
>                  video_device = device;
>                  ctx->video_is_muxed = 1;
>                  break;
> @@ -901,10 +921,23 @@ static int avf_read_header(AVFormatContext *s)
>          // looking for screen inputs
>          if (!video_device) {
>              int idx;
> +            CGDirectDisplayID screens[num_screens];
> +            CGGetActiveDisplayList(num_screens, screens, 
> &num_screens);
> +            AVCaptureScreenInput* capture_screen_input = NULL;
> +
>              if(sscanf(ctx->video_filename, "Capture screen %d", &idx) 
> && idx < num_screens) {
> -                CGDirectDisplayID screens[num_screens];
> -                CGGetActiveDisplayList(num_screens, screens, 
> &num_screens);
> -                AVCaptureScreenInput* capture_screen_input = 
> [[[AVCaptureScreenInput alloc] initWithDisplayID:screens[idx]] 
> autorelease];
> +                capture_screen_input = [[[AVCaptureScreenInput alloc] 
> initWithDisplayID:screens[idx]] autorelease];
> +            }
> +
> +            if(sscanf(ctx->video_filename, 
> "AvfilterAvfoundationCaptureScreen%d", &idx)) {
> +                for (int i = 0; i < num_screens; i++) {
> +                    if (screens[i] == idx) {
> +                        capture_screen_input = 
> [[[AVCaptureScreenInput alloc] initWithDisplayID:idx] autorelease];
> +                    }
> +                }
> +            }
> +
> +            if (capture_screen_input) {
>                  video_device = (AVCaptureDevice*) 
> capture_screen_input;
>                  ctx->video_device_index = ctx->num_video_devices + 
> idx;
>                  ctx->video_is_screen = 1;
> @@ -955,7 +988,14 @@ static int avf_read_header(AVFormatContext *s)
>          NSArray *devices = [AVCaptureDevice 
> devicesWithMediaType:AVMediaTypeAudio];
>           for (AVCaptureDevice *device in devices) {
> -            if (!strncmp(ctx->audio_filename, [[device localizedName] 
> UTF8String], strlen(ctx->audio_filename))) {
> +            const char *name = [[device localizedName] UTF8String];
> +            if (!strncmp(ctx->audio_filename, name, 
> strlen(ctx->audio_filename))) {
> +                audio_device = device;
> +                break;
> +            }
> +
> +            const char *uniqueId = CLEANUP_DEVICE_ID([device 
> uniqueID]);
> +            if (!strncmp(ctx->audio_filename, uniqueId, 
> strlen(ctx->audio_filename))) {
>                  audio_device = device;
>                  break;
>              }
> -- 
> 2.32.0 (Apple Git-132)
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".


More information about the ffmpeg-devel mailing list