From patchwork Fri Dec 31 16:03:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Romain Beauxis X-Patchwork-Id: 32968 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp14074101iog; Fri, 31 Dec 2021 08:03:43 -0800 (PST) X-Google-Smtp-Source: ABdhPJyAzKXPOCOn75WjIj77obGcB25yh/lpHF6gBvL5tD4WCVBdpMQc8CrfP97lGH59teUIwhyd X-Received: by 2002:a05:6402:1d4b:: with SMTP id dz11mr34486671edb.15.1640966623554; Fri, 31 Dec 2021 08:03:43 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1640966623; cv=none; d=google.com; s=arc-20160816; b=Inc107MD1z+AuBNYYrhq3pRQZQ+szQxygYpKSwtDB0H2YSiOAakffMk/Wfbj6JkaFd 0X+lsEs3lndCJV0iU10vZWIDVb8xUiUGPWl7Uv9rLvlH/j5GCPHQPP3DoxOV6tg0O2sI z+Ep8Vjh6tvyuI9inPmt8YM3zUxQTqdklo2NoEImRJPnJX84aOYIj07jN6YTXRArTqM0 hQpUfOOBOFMIdx+YsNy+q9rItxXgK1GybPfLEHm5r5vAYgnnXGD4oZZNNCdVG4MpHlSK dFYWE9EuBoPoa6+MyTt1eC+NFpgnjdSPhOMgSahU5fWYquV8TI1zUDREg+p0lTpd278A AKMg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:to:date:message-id:mime-version:from :delivered-to; bh=uskCgIzwoZ9opsXyg6tL581yAqo917sHwJ2KWcC1bb4=; b=I3pHByvp3y0GuLykFnVDyb2WxU0WPzTnyMyTZbrZYU8ZMGZGETVOLghdsrOg9K8iUQ qluy0XvTBTkjKarUnT8xSyaT5PzDrTpQOnb+7PN7pAGsqjfzVwNy0SpxjUfKF2v3Au3M loSnoA/+ixBczmzNWuq+l2BQlbMMGcSNmX4zlMmPpgiRAdLeSy42FkgF4VLz7VjmXsae xCmAPo0hwhXbGHJVLYTARdCGpV5CEKuDBJc6rdVIahZ/QEgTwbLSQWOKAge9XYeyqAcN VqrAnGBMIEPSXyl1CYGasOFffuX3D95X7wGZO9af+47ozYYMsJMd6KpNM1UvxDKNm05e 2wbQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id l17si13953112edc.520.2021.12.31.08.03.43; Fri, 31 Dec 2021 08:03:43 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 653F468B03F; Fri, 31 Dec 2021 18:03:40 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f48.google.com (mail-qv1-f48.google.com [209.85.219.48]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3C3A368AED3 for ; Fri, 31 Dec 2021 18:03:34 +0200 (EET) Received: by mail-qv1-f48.google.com with SMTP id kk22so25047479qvb.0 for ; Fri, 31 Dec 2021 08:03:34 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:content-transfer-encoding:mime-version :subject:message-id:date:cc:to; bh=CzyHweL8Sw2AzqNvJeQZOwwpuyhXSSszKy/uXBdSVGY=; b=FwmfSKPA89QQ9jQhiXoK7VDrVomp1NECJgBARaRJpJT3mmP8PsdQpFi9rubZUzWNyA 3YDz196xwdGK7NRPWHOf+eiNoVWx0AykvQImQOPmMF+Oxm7CtNCwajGcFbgBhIfQHZVI Oaes5xBnQK41pCtILoTCmK0XMjQ/OcWy3WZFAyIjynMX9iDBAl2lR7eZmEROkjRERFTQ pw3ckuUml5aZtOaYLDbWuDW+Asm8O32UBk2bKq/4p+YGn4XhnsSnZE769qLDtK8LmTSj lJhJToOCciAdI7Rr+MVs6WYjQhFnj3R+rKLAK0DWqJ9R05b4HAyschhTBe9HvjP3Vlvh cMiA== X-Gm-Message-State: AOAM533meDhmC2D7zF/cujc2ydPLXmd+hU2CKqG1z9JktM0eUSGcMYZv 2ERKK3qhNxZwN26OxElQpnUjg8MTd0WNNQ== X-Received: by 2002:a05:6214:da3:: with SMTP id h3mr33076328qvh.112.1640966612607; Fri, 31 Dec 2021 08:03:32 -0800 (PST) Received: from smtpclient.apple ([172.58.171.222]) by smtp.gmail.com with ESMTPSA id d4sm22136028qkn.79.2021.12.31.08.03.31 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 31 Dec 2021 08:03:32 -0800 (PST) From: Romain Beauxis Mime-Version: 1.0 (Mac OS X Mail 15.0 \(3693.40.0.1.81\)) Message-Id: Date: Fri, 31 Dec 2021 10:03:30 -0600 To: ffmpeg-devel@ffmpeg.org X-Mailer: Apple Mail (2.3693.40.0.1.81) Subject: [FFmpeg-devel] [PATCH v7 3/3] libavdevice/avfoundation.m: Allow to select devices by unique ID X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Thilo Borgmann , Aman Karmani Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: iDcGKm8j6bwO Signed-off-by: Romain Beauxis — 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 v5: Fix indentation/wrapping v6: None v7: None 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. 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 9d8020311a..858c0fa4e4 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 @@ -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 c740745fce..bad0cd7155 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] + #define av_log_avfoundation_error(str, err) \ av_log(s, AV_LOG_ERROR, "Avfoundation: %s, %s\n", str, \ [[[NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil] localizedDescription] UTF8String] \ @@ -805,21 +807,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 @@ -827,9 +831,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; } @@ -891,14 +896,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; @@ -909,10 +929,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; @@ -963,7 +996,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; }