123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- /*
- * Copyright (c) 2016 Thilo Borgmann
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * FFmpeg is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- /**
- * @file
- * Video processing based on Apple's CoreImage API
- */
- #import <QuartzCore/CoreImage.h>
- #import <AppKit/AppKit.h>
- #include "avfilter.h"
- #include "formats.h"
- #include "internal.h"
- #include "video.h"
- #include "libavutil/internal.h"
- #include "libavutil/opt.h"
- #include "libavutil/pixdesc.h"
- typedef struct CoreImageContext {
- const AVClass *class;
- int is_video_source; ///< filter is used as video source
- int w, h; ///< video size
- AVRational sar; ///< sample aspect ratio
- AVRational frame_rate; ///< video frame rate
- AVRational time_base; ///< stream time base
- int64_t duration; ///< duration expressed in microseconds
- int64_t pts; ///< increasing presentation time stamp
- AVFrame *picref; ///< cached reference containing the painted picture
- CFTypeRef glctx; ///< OpenGL context
- CGContextRef cgctx; ///< Bitmap context for image copy
- CFTypeRef input_image; ///< Input image container for passing into Core Image API
- CGColorSpaceRef color_space; ///< Common color space for input image and cgcontext
- int bits_per_component; ///< Shared bpc for input-output operation
- char *filter_string; ///< The complete user provided filter definition
- CFTypeRef *filters; ///< CIFilter object for all requested filters
- int num_filters; ///< Amount of filters in *filters
- char *output_rect; ///< Rectangle to be filled with filter intput
- int list_filters; ///< Option used to list all available filters including generators
- int list_generators; ///< Option used to list all available generators
- } CoreImageContext;
- static int config_output(AVFilterLink *link)
- {
- CoreImageContext *ctx = link->src->priv;
- link->w = ctx->w;
- link->h = ctx->h;
- link->sample_aspect_ratio = ctx->sar;
- link->frame_rate = ctx->frame_rate;
- link->time_base = ctx->time_base;
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
- ctx->bits_per_component = av_get_bits_per_pixel(desc) / desc->nb_components;
- return 0;
- }
- /** Determine image properties from input link of filter chain.
- */
- static int config_input(AVFilterLink *link)
- {
- CoreImageContext *ctx = link->dst->priv;
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
- ctx->bits_per_component = av_get_bits_per_pixel(desc) / desc->nb_components;
- return 0;
- }
- /** Print a list of all available filters including options and respective value ranges and defaults.
- */
- static void list_filters(CoreImageContext *ctx)
- {
- // querying filters and attributes
- NSArray *filter_categories = nil;
- if (ctx->list_generators && !ctx->list_filters) {
- filter_categories = [NSArray arrayWithObjects:kCICategoryGenerator, nil];
- }
- NSArray *filter_names = [CIFilter filterNamesInCategories:filter_categories];
- NSEnumerator *filters = [filter_names objectEnumerator];
- NSString *filter_name;
- while (filter_name = [filters nextObject]) {
- av_log(ctx, AV_LOG_INFO, "Filter: %s\n", [filter_name UTF8String]);
- NSString *input;
- CIFilter *filter = [CIFilter filterWithName:filter_name];
- NSDictionary *filter_attribs = [filter attributes]; // <nsstring, id>
- NSArray *filter_inputs = [filter inputKeys]; // <nsstring>
- for (input in filter_inputs) {
- NSDictionary *input_attribs = [filter_attribs valueForKey:input];
- NSString *input_class = [input_attribs valueForKey:kCIAttributeClass];
- if ([input_class isEqualToString:@"NSNumber"]) {
- NSNumber *value_default = [input_attribs valueForKey:kCIAttributeDefault];
- NSNumber *value_min = [input_attribs valueForKey:kCIAttributeSliderMin];
- NSNumber *value_max = [input_attribs valueForKey:kCIAttributeSliderMax];
- av_log(ctx, AV_LOG_INFO, "\tOption: %s\t[%s]\t[%s %s][%s]\n",
- [input UTF8String],
- [input_class UTF8String],
- [[value_min stringValue] UTF8String],
- [[value_max stringValue] UTF8String],
- [[value_default stringValue] UTF8String]);
- } else {
- av_log(ctx, AV_LOG_INFO, "\tOption: %s\t[%s]\n",
- [input UTF8String],
- [input_class UTF8String]);
- }
- }
- }
- }
- static int query_formats(AVFilterContext *fctx)
- {
- static const enum AVPixelFormat inout_fmts_rgb[] = {
- AV_PIX_FMT_ARGB,
- AV_PIX_FMT_NONE
- };
- AVFilterFormats *inout_formats;
- int ret;
- if (!(inout_formats = ff_make_format_list(inout_fmts_rgb))) {
- return AVERROR(ENOMEM);
- }
- if ((ret = ff_formats_ref(inout_formats, &fctx->inputs[0]->out_formats)) < 0 ||
- (ret = ff_formats_ref(inout_formats, &fctx->outputs[0]->in_formats)) < 0) {
- return ret;
- }
- return 0;
- }
- static int query_formats_src(AVFilterContext *fctx)
- {
- static const enum AVPixelFormat inout_fmts_rgb[] = {
- AV_PIX_FMT_ARGB,
- AV_PIX_FMT_NONE
- };
- AVFilterFormats *inout_formats;
- int ret;
- if (!(inout_formats = ff_make_format_list(inout_fmts_rgb))) {
- return AVERROR(ENOMEM);
- }
- if ((ret = ff_formats_ref(inout_formats, &fctx->outputs[0]->in_formats)) < 0) {
- return ret;
- }
- return 0;
- }
- static int apply_filter(CoreImageContext *ctx, AVFilterLink *link, AVFrame *frame)
- {
- int i;
- // (re-)initialize input image
- const CGSize frame_size = {
- frame->width,
- frame->height
- };
- NSData *data = [NSData dataWithBytesNoCopy:frame->data[0]
- length:frame->height*frame->linesize[0]
- freeWhenDone:NO];
- CIImage *ret = [(__bridge CIImage*)ctx->input_image initWithBitmapData:data
- bytesPerRow:frame->linesize[0]
- size:frame_size
- format:kCIFormatARGB8
- colorSpace:ctx->color_space]; //kCGColorSpaceGenericRGB
- if (!ret) {
- av_log(ctx, AV_LOG_ERROR, "Input image could not be initialized.\n");
- return AVERROR_EXTERNAL;
- }
- CIFilter *filter = NULL;
- CIImage *filter_input = (__bridge CIImage*)ctx->input_image;
- CIImage *filter_output = NULL;
- // successively apply all filters
- for (i = 0; i < ctx->num_filters; i++) {
- if (i) {
- // set filter input to previous filter output
- filter_input = [(__bridge CIImage*)ctx->filters[i-1] valueForKey:kCIOutputImageKey];
- CGRect out_rect = [filter_input extent];
- if (out_rect.size.width > frame->width || out_rect.size.height > frame->height) {
- // do not keep padded image regions after filtering
- out_rect.origin.x = 0.0f;
- out_rect.origin.y = 0.0f;
- out_rect.size.width = frame->width;
- out_rect.size.height = frame->height;
- }
- filter_input = [filter_input imageByCroppingToRect:out_rect];
- }
- filter = (__bridge CIFilter*)ctx->filters[i];
- // do not set input image for the first filter if used as video source
- if (!ctx->is_video_source || i) {
- @try {
- [filter setValue:filter_input forKey:kCIInputImageKey];
- } @catch (NSException *exception) {
- if (![[exception name] isEqualToString:NSUndefinedKeyException]) {
- av_log(ctx, AV_LOG_ERROR, "An error occurred: %s.", [exception.reason UTF8String]);
- return AVERROR_EXTERNAL;
- } else {
- av_log(ctx, AV_LOG_WARNING, "Selected filter does not accept an input image.\n");
- }
- }
- }
- }
- // get output of last filter
- filter_output = [filter valueForKey:kCIOutputImageKey];
- if (!filter_output) {
- av_log(ctx, AV_LOG_ERROR, "Filter output not available.\n");
- return AVERROR_EXTERNAL;
- }
- // do not keep padded image regions after filtering
- CGRect out_rect = [filter_output extent];
- if (out_rect.size.width > frame->width || out_rect.size.height > frame->height) {
- av_log(ctx, AV_LOG_DEBUG, "Cropping output image.\n");
- out_rect.origin.x = 0.0f;
- out_rect.origin.y = 0.0f;
- out_rect.size.width = frame->width;
- out_rect.size.height = frame->height;
- }
- CGImageRef out = [(__bridge CIContext*)ctx->glctx createCGImage:filter_output
- fromRect:out_rect];
- if (!out) {
- av_log(ctx, AV_LOG_ERROR, "Cannot create valid output image.\n");
- }
- // create bitmap context on the fly for rendering into current frame->data[]
- if (ctx->cgctx) {
- CGContextRelease(ctx->cgctx);
- ctx->cgctx = NULL;
- }
- size_t out_width = CGImageGetWidth(out);
- size_t out_height = CGImageGetHeight(out);
- if (out_width > frame->width || out_height > frame->height) { // this might result in segfault
- av_log(ctx, AV_LOG_WARNING, "Output image has unexpected size: %lux%lu (expected: %ix%i). This may crash...\n",
- out_width, out_height, frame->width, frame->height);
- }
- ctx->cgctx = CGBitmapContextCreate(frame->data[0],
- frame->width,
- frame->height,
- ctx->bits_per_component,
- frame->linesize[0],
- ctx->color_space,
- (uint32_t)kCGImageAlphaPremultipliedFirst); // ARGB
- if (!ctx->cgctx) {
- av_log(ctx, AV_LOG_ERROR, "CGBitmap context cannot be created.\n");
- return AVERROR_EXTERNAL;
- }
- // copy ("draw") the output image into the frame data
- CGRect rect = {{0,0},{frame->width, frame->height}};
- if (ctx->output_rect) {
- @try {
- NSString *tmp_string = [NSString stringWithUTF8String:ctx->output_rect];
- NSRect tmp = NSRectFromString(tmp_string);
- rect = NSRectToCGRect(tmp);
- } @catch (NSException *exception) {
- av_log(ctx, AV_LOG_ERROR, "An error occurred: %s.", [exception.reason UTF8String]);
- return AVERROR_EXTERNAL;
- }
- if (rect.size.width == 0.0f) {
- av_log(ctx, AV_LOG_WARNING, "Width of output rect is zero.\n");
- }
- if (rect.size.height == 0.0f) {
- av_log(ctx, AV_LOG_WARNING, "Height of output rect is zero.\n");
- }
- }
- CGContextDrawImage(ctx->cgctx, rect, out);
- return ff_filter_frame(link, frame);
- }
- /** Apply all valid filters successively to the input image.
- * The final output image is copied from the GPU by "drawing" using a bitmap context.
- */
- static int filter_frame(AVFilterLink *link, AVFrame *frame)
- {
- return apply_filter(link->dst->priv, link->dst->outputs[0], frame);
- }
- static int request_frame(AVFilterLink *link)
- {
- CoreImageContext *ctx = link->src->priv;
- AVFrame *frame;
- if (ctx->duration >= 0 &&
- av_rescale_q(ctx->pts, ctx->time_base, AV_TIME_BASE_Q) >= ctx->duration) {
- return AVERROR_EOF;
- }
- if (!ctx->picref) {
- ctx->picref = ff_get_video_buffer(link, ctx->w, ctx->h);
- if (!ctx->picref) {
- return AVERROR(ENOMEM);
- }
- }
- frame = av_frame_clone(ctx->picref);
- if (!frame) {
- return AVERROR(ENOMEM);
- }
- frame->pts = ctx->pts;
- frame->key_frame = 1;
- frame->interlaced_frame = 0;
- frame->pict_type = AV_PICTURE_TYPE_I;
- frame->sample_aspect_ratio = ctx->sar;
- ctx->pts++;
- return apply_filter(ctx, link, frame);
- }
- /** Set an option of the given filter to the provided key-value pair.
- */
- static void set_option(CoreImageContext *ctx, CIFilter *filter, const char *key, const char *value)
- {
- NSString *input_key = [NSString stringWithUTF8String:key];
- NSString *input_val = [NSString stringWithUTF8String:value];
- NSDictionary *filter_attribs = [filter attributes]; // <nsstring, id>
- NSDictionary *input_attribs = [filter_attribs valueForKey:input_key];
- NSString *input_class = [input_attribs valueForKey:kCIAttributeClass];
- NSString *input_type = [input_attribs valueForKey:kCIAttributeType];
- if (!input_attribs) {
- av_log(ctx, AV_LOG_WARNING, "Skipping unknown option: \"%s\".\n",
- [input_key UTF8String]); // [[filter name] UTF8String]) not currently defined...
- return;
- }
- av_log(ctx, AV_LOG_DEBUG, "key: %s, val: %s, #attribs: %lu, class: %s, type: %s\n",
- [input_key UTF8String],
- [input_val UTF8String],
- input_attribs ? (unsigned long)[input_attribs count] : -1,
- [input_class UTF8String],
- [input_type UTF8String]);
- if ([input_class isEqualToString:@"NSNumber"]) {
- float input = input_val.floatValue;
- NSNumber *max_value = [input_attribs valueForKey:kCIAttributeSliderMax];
- NSNumber *min_value = [input_attribs valueForKey:kCIAttributeSliderMin];
- NSNumber *used_value = nil;
- #define CLAMP_WARNING do { \
- av_log(ctx, AV_LOG_WARNING, "Value of \"%f\" for option \"%s\" is out of range [%f %f], clamping to \"%f\".\n", \
- input, \
- [input_key UTF8String], \
- min_value.floatValue, \
- max_value.floatValue, \
- used_value.floatValue); \
- } while(0)
- if (input > max_value.floatValue) {
- used_value = max_value;
- CLAMP_WARNING;
- } else if (input < min_value.floatValue) {
- used_value = min_value;
- CLAMP_WARNING;
- } else {
- used_value = [NSNumber numberWithFloat:input];
- }
- [filter setValue:used_value forKey:input_key];
- } else if ([input_class isEqualToString:@"CIVector"]) {
- CIVector *input = [CIVector vectorWithString:input_val];
- if (!input) {
- av_log(ctx, AV_LOG_WARNING, "Skipping invalid CIVctor description: \"%s\".\n",
- [input_val UTF8String]);
- return;
- }
- [filter setValue:input forKey:input_key];
- } else if ([input_class isEqualToString:@"CIColor"]) {
- CIColor *input = [CIColor colorWithString:input_val];
- if (!input) {
- av_log(ctx, AV_LOG_WARNING, "Skipping invalid CIColor description: \"%s\".\n",
- [input_val UTF8String]);
- return;
- }
- [filter setValue:input forKey:input_key];
- } else if ([input_class isEqualToString:@"NSString"]) { // set display name as string with latin1 encoding
- [filter setValue:input_val forKey:input_key];
- } else if ([input_class isEqualToString:@"NSData"]) { // set display name as string with latin1 encoding
- NSData *input = [NSData dataWithBytes:(const void*)[input_val cStringUsingEncoding:NSISOLatin1StringEncoding]
- length:[input_val lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding]];
- if (!input) {
- av_log(ctx, AV_LOG_WARNING, "Skipping invalid NSData description: \"%s\".\n",
- [input_val UTF8String]);
- return;
- }
- [filter setValue:input forKey:input_key];
- } else {
- av_log(ctx, AV_LOG_WARNING, "Skipping unsupported option class: \"%s\".\n",
- [input_class UTF8String]);
- avpriv_report_missing_feature(ctx, "Handling of some option classes");
- return;
- }
- }
- /** Create a filter object by a given name and set all options to defaults.
- * Overwrite any option given by the user to the provided value in filter_options.
- */
- static CIFilter* create_filter(CoreImageContext *ctx, const char *filter_name, AVDictionary *filter_options)
- {
- // create filter object
- CIFilter *filter = [CIFilter filterWithName:[NSString stringWithUTF8String:filter_name]];
- // set default options
- [filter setDefaults];
- // set user options
- if (filter_options) {
- AVDictionaryEntry *o = NULL;
- while ((o = av_dict_get(filter_options, "", o, AV_DICT_IGNORE_SUFFIX))) {
- set_option(ctx, filter, o->key, o->value);
- }
- }
- return filter;
- }
- static av_cold int init(AVFilterContext *fctx)
- {
- CoreImageContext *ctx = fctx->priv;
- AVDictionary *filter_dict = NULL;
- AVDictionaryEntry *f = NULL;
- AVDictionaryEntry *o = NULL;
- int ret;
- int i;
- if (ctx->list_filters || ctx->list_generators) {
- list_filters(ctx);
- return AVERROR_EXIT;
- }
- if (ctx->filter_string) {
- // parse filter string (filter=name@opt=val@opt2=val2#name2@opt3=val3) for filters separated by #
- av_log(ctx, AV_LOG_DEBUG, "Filter_string: %s\n", ctx->filter_string);
- ret = av_dict_parse_string(&filter_dict, ctx->filter_string, "@", "#", AV_DICT_MULTIKEY); // parse filter_name:all_filter_options
- if (ret) {
- av_log(ctx, AV_LOG_ERROR, "Parsing of filters failed.\n");
- return AVERROR(EIO);
- }
- ctx->num_filters = av_dict_count(filter_dict);
- av_log(ctx, AV_LOG_DEBUG, "Filter count: %i\n", ctx->num_filters);
- // allocate CIFilter array
- ctx->filters = av_mallocz_array(ctx->num_filters, sizeof(CIFilter*));
- if (!ctx->filters) {
- av_log(ctx, AV_LOG_ERROR, "Could not allocate filter array.\n");
- return AVERROR(ENOMEM);
- }
- // parse filters for option key-value pairs (opt=val@opt2=val2) separated by @
- i = 0;
- while ((f = av_dict_get(filter_dict, "", f, AV_DICT_IGNORE_SUFFIX))) {
- AVDictionary *filter_options = NULL;
- if (strncmp(f->value, "default", 7)) { // not default
- ret = av_dict_parse_string(&filter_options, f->value, "=", "@", 0); // parse option_name:option_value
- if (ret) {
- av_log(ctx, AV_LOG_ERROR, "Parsing of filter options for \"%s\" failed.\n", f->key);
- return AVERROR(EIO);
- }
- }
- if (av_log_get_level() >= AV_LOG_DEBUG) {
- av_log(ctx, AV_LOG_DEBUG, "Creating filter %i: \"%s\":\n", i, f->key);
- if (!filter_options) {
- av_log(ctx, AV_LOG_DEBUG, "\tusing default options\n");
- } else {
- while ((o = av_dict_get(filter_options, "", o, AV_DICT_IGNORE_SUFFIX))) {
- av_log(ctx, AV_LOG_DEBUG, "\t%s: %s\n", o->key, o->value);
- }
- }
- }
- ctx->filters[i] = CFBridgingRetain(create_filter(ctx, f->key, filter_options));
- if (!ctx->filters[i]) {
- av_log(ctx, AV_LOG_ERROR, "Could not create filter \"%s\".\n", f->key);
- return AVERROR(EINVAL);
- }
- i++;
- }
- } else {
- av_log(ctx, AV_LOG_ERROR, "No filters specified.\n");
- return AVERROR(EINVAL);
- }
- // create GPU context on OSX
- const NSOpenGLPixelFormatAttribute attr[] = {
- NSOpenGLPFAAccelerated,
- NSOpenGLPFANoRecovery,
- NSOpenGLPFAColorSize, 32,
- 0
- };
- NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:(void *)&attr];
- ctx->color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
- ctx->glctx = CFBridgingRetain([CIContext contextWithCGLContext:CGLGetCurrentContext()
- pixelFormat:[pixel_format CGLPixelFormatObj]
- colorSpace:ctx->color_space
- options:nil]);
- if (!ctx->glctx) {
- av_log(ctx, AV_LOG_ERROR, "CIContext not created.\n");
- return AVERROR_EXTERNAL;
- }
- // Creating an empty input image as input container for the context
- ctx->input_image = CFBridgingRetain([CIImage emptyImage]);
- return 0;
- }
- static av_cold int init_src(AVFilterContext *fctx)
- {
- CoreImageContext *ctx = fctx->priv;
- ctx->is_video_source = 1;
- ctx->time_base = av_inv_q(ctx->frame_rate);
- ctx->pts = 0;
- return init(fctx);
- }
- static av_cold void uninit(AVFilterContext *fctx)
- {
- #define SafeCFRelease(ptr) do { \
- if (ptr) { \
- CFRelease(ptr); \
- ptr = NULL; \
- } \
- } while (0)
- CoreImageContext *ctx = fctx->priv;
- SafeCFRelease(ctx->glctx);
- SafeCFRelease(ctx->cgctx);
- SafeCFRelease(ctx->color_space);
- SafeCFRelease(ctx->input_image);
- if (ctx->filters) {
- for (int i = 0; i < ctx->num_filters; i++) {
- SafeCFRelease(ctx->filters[i]);
- }
- av_freep(&ctx->filters);
- }
- av_frame_free(&ctx->picref);
- }
- static const AVFilterPad vf_coreimage_inputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .filter_frame = filter_frame,
- .config_props = config_input,
- },
- { NULL }
- };
- static const AVFilterPad vf_coreimage_outputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- },
- { NULL }
- };
- static const AVFilterPad vsrc_coreimagesrc_outputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .request_frame = request_frame,
- .config_props = config_output,
- },
- { NULL }
- };
- #define OFFSET(x) offsetof(CoreImageContext, x)
- #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
- #define GENERATOR_OPTIONS \
- {"size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS}, \
- {"s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS}, \
- {"rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS}, \
- {"r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS}, \
- {"duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS}, \
- {"d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS}, \
- {"sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, {.dbl = 1}, 0, INT_MAX, FLAGS},
- #define FILTER_OPTIONS \
- {"list_filters", "list available filters", OFFSET(list_filters), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags = FLAGS}, \
- {"list_generators", "list available generators", OFFSET(list_generators), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags = FLAGS}, \
- {"filter", "names and options of filters to apply", OFFSET(filter_string), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS}, \
- {"output_rect", "output rectangle within output image", OFFSET(output_rect), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS},
- // definitions for coreimage video filter
- static const AVOption coreimage_options[] = {
- FILTER_OPTIONS
- { NULL }
- };
- AVFILTER_DEFINE_CLASS(coreimage);
- AVFilter ff_vf_coreimage = {
- .name = "coreimage",
- .description = NULL_IF_CONFIG_SMALL("Video filtering using CoreImage API."),
- .init = init,
- .uninit = uninit,
- .priv_size = sizeof(CoreImageContext),
- .priv_class = &coreimage_class,
- .inputs = vf_coreimage_inputs,
- .outputs = vf_coreimage_outputs,
- .query_formats = query_formats,
- };
- // definitions for coreimagesrc video source
- static const AVOption coreimagesrc_options[] = {
- GENERATOR_OPTIONS
- FILTER_OPTIONS
- { NULL }
- };
- AVFILTER_DEFINE_CLASS(coreimagesrc);
- AVFilter ff_vsrc_coreimagesrc = {
- .name = "coreimagesrc",
- .description = NULL_IF_CONFIG_SMALL("Video source using image generators of CoreImage API."),
- .init = init_src,
- .uninit = uninit,
- .priv_size = sizeof(CoreImageContext),
- .priv_class = &coreimagesrc_class,
- .inputs = NULL,
- .outputs = vsrc_coreimagesrc_outputs,
- .query_formats = query_formats_src,
- };
|