-list_formats
-pixel_format
-video_size
-framerate
---
 libavdevice/avfoundation_dec.m | 203 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 186 insertions(+), 17 deletions(-)

diff --git a/libavdevice/avfoundation_dec.m b/libavdevice/avfoundation_dec.m
index b4b6d78..b6dd25c 100644
--- a/libavdevice/avfoundation_dec.m
+++ b/libavdevice/avfoundation_dec.m
@@ -30,6 +30,7 @@
 #include "libavformat/internal.h"
 #include "libavutil/time.h"
 #include "libavutil/mathematics.h"
+#include "libavutil/parseutils.h"
 
 #include "avdevice.h"
 
@@ -91,9 +92,15 @@ typedef struct AVFoundationCaptureContext {
     AVClass *class;
     /* AVOptions */
     int list_devices;
-    enum AVPixelFormat pixel_format;
+    int list_formats;
+    char* pixel_format;
+    char* video_size;   /* String describing video size */
+    char* framerate;    /* String describing the framerate */
+
 
     int video_stream_index;
+    int width, height;
+    AVRational internal_framerate;
 
     int64_t first_pts;
     int frames_captured;
@@ -118,6 +125,10 @@ static const AVOption options[] = {
     { "all",          "Show all the supported devices",  OFFSET(list_devices), 
 AV_OPT_TYPE_CONST,  {.i64 = ALL_DEVICES },   0, INT_MAX, DEC, "list_devices" },
     { "audio",        "Show only the audio devices",     OFFSET(list_devices), 
 AV_OPT_TYPE_CONST,  {.i64 = AUDIO_DEVICES }, 0, INT_MAX, DEC, "list_devices" },
     { "video",        "Show only the video devices",     OFFSET(list_devices), 
 AV_OPT_TYPE_CONST,  {.i64 = VIDEO_DEVICES }, 0, INT_MAX, DEC, "list_devices" },
+    { "list_formats", "List available formats and exit", OFFSET(list_formats), 
 AV_OPT_TYPE_INT,    {.i64 = 0 },             0, INT_MAX, DEC, "list_formats" },
+    { "pixel_format", "Preferred pixel format",          OFFSET(pixel_format), 
 AV_OPT_TYPE_STRING, {.str = NULL},           0, 0, DEC},
+    { "video_size",   "A string describing frame size, such as 640x480 or 
hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0, DEC },
+    { "framerate",    "A string representing desired framerate", 
OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = NULL},       0, 0, DEC },
     { NULL },
 };
 
@@ -152,6 +163,23 @@ static int 
avfoundation_list_capture_devices(AVFormatContext *s)
     return AVERROR_EXIT;
 }
 
+static int list_formats(AVFormatContext *s)
+{
+    av_log(s, AV_LOG_VERBOSE, "Supported pixel formats (first is more 
efficient):\n");
+    AVCaptureVideoDataOutput* out = [[AVCaptureVideoDataOutput alloc] init];
+
+    for (NSNumber* cv_pixel_format in [out availableVideoCVPixelFormatTypes]) {
+        OSType cv_fmt = [cv_pixel_format intValue];
+        enum AVPixelFormat pix_fmt = core_video_to_pix_fmt(cv_fmt);
+        if (pix_fmt != AV_PIX_FMT_NONE) {
+            av_log(s, AV_LOG_VERBOSE, "  %s: %d\n",
+                   av_get_pix_fmt_name(pix_fmt),
+                   cv_fmt);
+        }
+    }
+    return AVERROR_EXIT;
+}
+
 static void lock_frames(AVFoundationCaptureContext* ctx)
 {
     pthread_mutex_lock(&ctx->frame_lock);
@@ -206,6 +234,99 @@ static void unlock_frames(AVFoundationCaptureContext* ctx)
 
 @end
 
+/**
+ * Configure the video device.
+ */
+static bool configure_video_device(AVFormatContext *s, AVCaptureDevice 
*video_device)
+{
+    AVFoundationCaptureContext *ctx = s->priv_data;
+    AVCaptureDeviceFormat* selected_format = nil;
+    AVFrameRateRange* selected_range = nil;
+    double framerate = av_q2d(ctx->internal_framerate);
+    double epsilon = 0.00000001;
+
+    for (AVCaptureDeviceFormat* format in [video_device formats]) {
+        CMFormatDescriptionRef formatDescription;
+        CMVideoDimensions dimensions;
+
+        formatDescription = (CMFormatDescriptionRef) format.formatDescription;
+        dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
+
+        if ((ctx->width == 0 && ctx->height == 0) ||
+            (dimensions.width == ctx->width && dimensions.height == 
ctx->height)) {
+
+            av_log(s, AV_LOG_INFO, "Trying video size %dx%d\n",
+                dimensions.width, dimensions.height);
+            ctx->width = dimensions.width;
+            ctx->height = dimensions.height;
+            selected_format = format;
+            if (framerate) {
+                av_log(s, AV_LOG_INFO, "Checking support for framerate %f\n",
+                    framerate);
+                for (AVFrameRateRange* range in 
format.videoSupportedFrameRateRanges) {
+                        if (range.minFrameRate <= (framerate + epsilon) &&
+                                range.maxFrameRate >= (framerate - epsilon)) {
+                        selected_range = range;
+                        break;
+                    }
+                }
+            } else {
+                selected_range = format.videoSupportedFrameRateRanges[0];
+                framerate = selected_range.maxFrameRate;
+                break;
+            }
+
+            if (selected_format && selected_range)
+                break;
+        }
+    }
+
+    if (!selected_format) {
+        av_log(s, AV_LOG_ERROR, "Selected video size (%dx%d) is not supported 
by the device\n",
+            ctx->width, ctx->height);
+        return false;
+    } else {
+        av_log(s, AV_LOG_INFO, "Setting video size to %dx%d\n",
+            ctx->width, ctx->height);
+    }
+
+    if (framerate && !selected_range) {
+        av_log(s, AV_LOG_ERROR, "Selected framerate (%f) is not supported by 
the device\n",
+            framerate);
+        return false;
+    } else {
+        av_log(s, AV_LOG_INFO, "Setting framerate to %f\n",
+            framerate);
+    }
+
+    if ([video_device lockForConfiguration:NULL] == YES) {
+        [video_device setActiveFormat:selected_format];
+        [video_device setActiveVideoMinFrameDuration:CMTimeMake(1, framerate)];
+        [video_device setActiveVideoMaxFrameDuration:CMTimeMake(1, framerate)];
+    } else {
+        av_log(s, AV_LOG_ERROR, "Could not lock device for configuration\n");
+        return false;
+    }
+    return true;
+}
+
+static void print_supported_formats(AVFormatContext *s, AVCaptureDevice 
*device)
+{
+    av_log(s, AV_LOG_WARNING, "Supported modes:\n");
+    for (AVCaptureDeviceFormat* format in [device formats]) {
+        CMFormatDescriptionRef formatDescription;
+        CMVideoDimensions dimensions;
+
+        formatDescription = (CMFormatDescriptionRef) format.formatDescription;
+        dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
+
+        for (AVFrameRateRange* range in format.videoSupportedFrameRateRanges) {
+            av_log(s, AV_LOG_WARNING, "  %dx%d@[%f %f]fps\n",
+                dimensions.width, dimensions.height,
+                range.minFrameRate, range.maxFrameRate);
+        }
+    }
+}
 
 static int setup_stream(AVFormatContext *s, AVCaptureDevice *device)
 {
@@ -217,6 +338,13 @@ static int setup_stream(AVFormatContext *s, 
AVCaptureDevice *device)
     AVCaptureSession *session = (__bridge AVCaptureSession*)ctx->session;
     input = [AVCaptureDeviceInput deviceInputWithDevice:device
                                                   error:&error];
+
+    if (!configure_video_device(s, device)) {
+        av_log(s, AV_LOG_ERROR, "device configuration failed\n");
+        print_supported_formats(s, device);
+        return AVERROR(EINVAL);
+    }
+
     // add the input devices
     if (!input) {
         av_log(s, AV_LOG_ERROR, "%s\n",
@@ -235,7 +363,6 @@ static int setup_stream(AVFormatContext *s, AVCaptureDevice 
*device)
     if ([device hasMediaType:AVMediaTypeVideo]) {
         AVCaptureVideoDataOutput* out = [[AVCaptureVideoDataOutput alloc] 
init];
         NSNumber *core_video_fmt = nil;
-        enum AVPixelFormat pixel_format;
         if (!out) {
             av_log(s, AV_LOG_ERROR, "Failed to init AV video output\n");
             return AVERROR(EINVAL);
@@ -243,17 +370,27 @@ static int setup_stream(AVFormatContext *s, 
AVCaptureDevice *device)
 
         [out setAlwaysDiscardsLateVideoFrames:YES];
 
-        // Map the first supported pixel format
-        av_log(s, AV_LOG_VERBOSE, "Supported pixel formats:\n");
-        for (NSNumber *cv_pixel_format in [out 
availableVideoCVPixelFormatTypes]) {
-            OSType cv_fmt = [cv_pixel_format intValue];
-            enum AVPixelFormat pix_fmt = core_video_to_pix_fmt(cv_fmt);
-            if (pix_fmt != AV_PIX_FMT_NONE) {
-                av_log(s, AV_LOG_VERBOSE, "  %s: %d\n",
-                       av_get_pix_fmt_name(pix_fmt),
-                       cv_fmt);
-                core_video_fmt = cv_pixel_format;
-                pixel_format   = pix_fmt;
+        if (ctx->pixel_format) {
+            // Try to use specified pixel format
+            core_video_fmt = [NSNumber 
numberWithInt:pix_fmt_to_core_video(av_get_pix_fmt(ctx->pixel_format))];
+            if ([[out availableVideoCVPixelFormatTypes] 
indexOfObject:core_video_fmt] != NSNotFound) {
+                av_log(s, AV_LOG_VERBOSE, "Pixel format %s supported!\n", 
ctx->pixel_format);
+            } else {
+                core_video_fmt = nil;
+            }
+        }
+
+        if (!ctx->pixel_format || !core_video_fmt) {
+            av_log(s, AV_LOG_VERBOSE, "Pixel format not supported or not 
provided, overriding...\n");
+            for (NSNumber *cv_pixel_format in [out 
availableVideoCVPixelFormatTypes]) {
+                OSType cv_fmt = [cv_pixel_format intValue];
+                enum AVPixelFormat pix_fmt = core_video_to_pix_fmt(cv_fmt);
+                // Use the first one in the list, it will be the most effective
+                if (pix_fmt != AV_PIX_FMT_NONE) {
+                    core_video_fmt = cv_pixel_format;
+                    ctx->pixel_format = 
av_strdup(av_get_pix_fmt_name(pix_fmt));;
+                    break;
+                }
             }
         }
 
@@ -262,9 +399,9 @@ static int setup_stream(AVFormatContext *s, AVCaptureDevice 
*device)
             return AVERROR(EINVAL);
         } else {
             av_log(s, AV_LOG_INFO, "Using %s.\n",
-                   av_get_pix_fmt_name(pixel_format));
+                    ctx->pixel_format);
         }
-        ctx->pixel_format          = pixel_format;
+
         NSDictionary *capture_dict = [NSDictionary 
dictionaryWithObject:core_video_fmt
                                                                  
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
         [out setVideoSettings:capture_dict];
@@ -318,7 +455,7 @@ static int get_video_config(AVFormatContext *s)
     stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
     stream->codec->width      = (int)image_buffer_size.width;
     stream->codec->height     = (int)image_buffer_size.height;
-    stream->codec->pix_fmt    = ctx->pixel_format;
+    stream->codec->pix_fmt    = av_get_pix_fmt(ctx->pixel_format);
 
     CFRelease(ctx->current_frame);
     ctx->current_frame = nil;
@@ -431,13 +568,16 @@ static int setup_streams(AVFormatContext *s)
     }
 
     if (ret < 0) {
-        av_log(s, AV_LOG_ERROR, "No device could be added");
+        av_log(s, AV_LOG_ERROR, "No device could be added\n");
         return ret;
     }
 
     av_log(s, AV_LOG_INFO, "Starting session!\n");
     [(__bridge AVCaptureSession*)ctx->session startRunning];
 
+    // Session is started, unlock device
+    [device unlockForConfiguration];
+
     av_log(s, AV_LOG_INFO, "Checking video config\n");
     if (get_video_config(s)) {
         destroy_context(ctx);
@@ -451,8 +591,37 @@ static int avfoundation_read_header(AVFormatContext *s)
 {
     AVFoundationCaptureContext *ctx = s->priv_data;
     ctx->first_pts = av_gettime();
+
+    AVRational framerate_q = { 0 , 1 };
+    ctx->internal_framerate = framerate_q;
+
     if (ctx->list_devices)
         return avfoundation_list_capture_devices(s);
+    if (ctx->list_formats) {
+        return list_formats(s);
+    }
+
+    if (ctx->pixel_format) {
+        if (av_get_pix_fmt(ctx->pixel_format) == AV_PIX_FMT_NONE) {
+            av_log(s, AV_LOG_ERROR, "No such input format: %s.\n",
+                   ctx->pixel_format);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    if (ctx->video_size &&
+        (av_parse_video_size(&ctx->width, &ctx->height, ctx->video_size)) < 0) 
{
+        av_log(s, AV_LOG_ERROR, "Could not parse video size '%s'.\n",
+               ctx->video_size);
+        return AVERROR(EINVAL);
+    }
+
+    if (ctx->framerate &&
+        (av_parse_video_rate(&ctx->internal_framerate, ctx->framerate)) < 0) {
+        av_log(s, AV_LOG_ERROR, "Could not parse framerate '%s'.\n",
+               ctx->framerate);
+        return AVERROR(EINVAL);
+    }
 
     return setup_streams(s);
 }
-- 
2.6.2

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to