Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Romain Beauxis <toots@rastageeks.org>
To: ffmpeg-devel@ffmpeg.org
Subject: [FFmpeg-devel] [PATCH v4 3/3] libavdevice/avfoundation.m: Allow to select devices by unique ID.
Date: Fri, 17 Dec 2021 09:12:49 -0600
Message-ID: <b0da2587-6a5b-eb42-c605-03e495e5ca30@rastageeks.org> (raw)


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.

Signed-off-by: Romain Beauxis <toots@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.
   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@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

             reply	other threads:[~2021-12-17 15:13 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-17 15:12 Romain Beauxis [this message]
2021-12-17 21:51 ` Marvin Scholz
2021-12-18 15:58   ` Romain Beauxis

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b0da2587-6a5b-eb42-c605-03e495e5ca30@rastageeks.org \
    --to=toots@rastageeks.org \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git