[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