lib: media_player: fix libvlc_MediaPlayerMediaChanged event
[vlc.git] / modules / video_filter / ci_filters.m
blob154fc44012ccc6377c05b6df971a2722acbc30c4
1 /*****************************************************************************
2  * ci_filters.m: Video filters for MacOSX OpenGL video output
3  *****************************************************************************
4  * Copyright © 2017 VLC authors, VideoLAN and VideoLabs
5  *
6  * Author: Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <assert.h>
28 #include <stdatomic.h>
30 #include <vlc_common.h>
31 #include <vlc_filter.h>
32 #include <vlc_picture.h>
33 #include <vlc_plugin.h>
34 #include <vlc_modules.h>
35 #include <vlc_mouse.h>
36 #include "filter_picture.h"
37 #include "vt_utils.h"
39 #include <CoreImage/CIContext.h>
40 #include <CoreImage/CIImage.h>
41 #include <CoreImage/CIFilter.h>
42 #include <CoreImage/CIVector.h>
44 enum    filter_type
46     FILTER_NONE = -1,
47     FILTER_ADJUST_HUE,
48     FILTER_ADJUST_COLOR_CONTROLS,
49     FILTER_ADJUST_GAMMA,
50     FILTER_INVERT,
51     FILTER_POSTERIZE,
52     FILTER_SEPIA,
53     FILTER_SHARPEN,
54     FILTER_PSYCHEDELIC,
55     FILTER_CUSTOM,
56     NUM_FILTERS,
57     NUM_MAX_EQUIVALENT_VLC_FILTERS = 3
60 #define NUM_FILTER_PARAM_MAX    4
62 struct  filter_chain
64     enum filter_type            filter;
65     CIFilter *                  ci_filter;
66     _Atomic float               ci_params[NUM_FILTER_PARAM_MAX];
67     struct filter_chain *       next;
68     union {
69         struct
70         {
71 #define PSYCHEDELIC_COUNT_DEFAULT 5
72 #define PSYCHEDELIC_COUNT_MIN 3
73 #define PSYCHEDELIC_COUNT_MAX 40
74             int x, y;
75             unsigned count;
76         } psychedelic;
77     } ctx;
80 struct  ci_filters_ctx
82     CVPixelBufferPoolRef        cvpx_pool;
83     video_format_t              cvpx_pool_fmt;
84     CIContext *                 ci_ctx;
85     struct filter_chain *       fchain;
86     filter_t *                  src_converter;
87     filter_t *                  dst_converter;
90 typedef struct filter_sys_t
92     char const *                psz_filter;
93     bool                        mouse_moved;
94     vlc_mouse_t                 old_mouse;
95     vlc_mouse_t                 mouse;
96     struct ci_filters_ctx *     ctx;
97 } filter_sys_t;
99 struct  range
101     float       min;
102     float       max;
105 struct  filter_desc
107     struct
108     {
109         char const *            vlc;
110         NSString *              ci;
111     } const             name_desc;
113     struct      filter_param_desc
114     {
115         char const *            vlc;
116         NSString *              ci;
117         struct
118         {
119             struct range    vlc;
120             struct range    ci;
121         }                       ranges[2];
122         int                     vlc_type;
123     } const             param_descs[NUM_FILTER_PARAM_MAX];
124     void (*pf_init)(filter_t *filter, struct filter_chain *fchain);
125     void (*pf_control)(filter_t *filter, struct filter_chain *fchain);
128 static void filter_PsychedelicInit(filter_t *filter, struct filter_chain *fchain);
129 static void filter_PsychedelicControl(filter_t *filter, struct filter_chain *fchain);
131 static struct filter_desc       filter_desc_table[] =
133     [FILTER_ADJUST_HUE] =
134     {
135         { "adjust", @"CIHueAdjust" },
136         {
137             { "hue", @"inputAngle", {{{-180.f, +180.f}, {+3.f, -3.f}}}, VLC_VAR_FLOAT }
138         }
139     },
140     [FILTER_ADJUST_COLOR_CONTROLS] =
141     {
142         { "adjust", @"CIColorControls" },
143         {
144             { "contrast",   @"inputContrast",   {{{.0f, 2.f}, {.0f, 2.f}}},   VLC_VAR_FLOAT },
145             { "brightness", @"inputBrightness", {{{.0f, 2.f}, {-1.f, +1.f}}}, VLC_VAR_FLOAT },
146             { "saturation", @"inputSaturation", {{{.0f, 3.f}, {.0f, 2.7f}}},  VLC_VAR_FLOAT }
147         }
148     },
149     [FILTER_ADJUST_GAMMA] =
150     {
151         { "adjust", @"CIGammaAdjust" },
152         {
153             { "gamma", @"inputPower", {{{.01f, 1.f}, {10.f, 1.f}}, {{1.f, 10.f}, {1.f, .01f}}}, VLC_VAR_FLOAT }
154         }
155     },
156     [FILTER_INVERT] =
157     {
158         { "invert", @"CIColorInvert" }
159     },
160     [FILTER_POSTERIZE] =
161     {
162         { "posterize", @"CIColorPosterize" },
163         {
164             { "posterize-level", @"inputLevels", {{{2.f, 256.f}, {2.f, 256.f}}}, VLC_VAR_INTEGER }
165         }
166     },
167     [FILTER_SEPIA] =
168     {
169         { "sepia", @"CISepiaTone" },
170         {
171             { "sepia-intensity", @"inputIntensity", {{{.0f, 255.f}, {.0f, 1.f}}}, VLC_VAR_INTEGER }
172         }
173     },
174     [FILTER_SHARPEN] =
175     {
176         { "sharpen", @"CISharpenLuminance" },
177         {
178             { "sharpen-sigma", @"inputSharpness", {{{.0f, 2.f}, {.0f, 5.f}}}, VLC_VAR_FLOAT }
179         }
180     },
181     [FILTER_PSYCHEDELIC] =
182     {
183         { "psychedelic", @"CIKaleidoscope" }, { { } },
184         filter_PsychedelicInit,
185         filter_PsychedelicControl
186     },
187     [FILTER_CUSTOM] =
188     {
189         { "custom" }, { { } },
190         filter_PsychedelicInit,
191         filter_PsychedelicControl
192     },
195 #define GET_CI_VALUE(vlc_value, vlc_range, ci_range)               \
196     ((vlc_value - vlc_range.min) * (ci_range.max - ci_range.min) / \
197      (vlc_range.max - vlc_range.min) + ci_range.min)
199 static struct filter_chain *
200 filter_chain_AddFilter(struct filter_chain **fchain, enum filter_type filter)
202     struct filter_chain *elem = calloc(1, sizeof(*elem));
203     if (!elem)
204         return NULL;
205     elem->filter = filter;
207     if (!*fchain)
208         *fchain = elem;
209     else
210     {
211         struct filter_chain *it = *fchain;
212         while (it->next) it = it->next;
213         it->next = elem;
214     }
216     return elem;
219 static void
220 filter_chain_RemoveFilter(struct filter_chain **fchain,
221                           enum filter_type filter)
223     struct filter_chain *prev = NULL;
224     struct filter_chain *to_del;
226     for (to_del = *fchain; to_del && to_del->filter != filter;
227          to_del = to_del->next)
228         prev = to_del;
229     assert(to_del);
230     if (!prev)
231         *fchain = to_del->next;
232     else
233         prev->next = to_del->next;
235     free(to_del);
238 static void
239 filter_desc_table_GetFilterTypes
240 (char const *vlc_filter_name,
241  enum filter_type filter_types[NUM_MAX_EQUIVALENT_VLC_FILTERS])
243     int j = 0;
244     for (int i = 0; i < NUM_FILTERS; ++i)
245         if (!strcmp(filter_desc_table[i].name_desc.vlc, vlc_filter_name))
246         {
247             assert(j < NUM_MAX_EQUIVALENT_VLC_FILTERS);
248             filter_types[j++] = i;
249         }
250     assert(j);
251     while (j < NUM_MAX_EQUIVALENT_VLC_FILTERS)
252         filter_types[j++] = FILTER_NONE;
255 static inline NSString *
256 filter_desc_table_GetFilterName(enum filter_type type)
257 { assert(type < NUM_FILTERS);
258     return filter_desc_table[type].name_desc.ci;
261 static float
262 filter_ConvertParam(float f_vlc_val,
263                     struct filter_param_desc const *param_desc)
265     struct range clip_range = { param_desc->ranges[0].vlc.min,
266                                 param_desc->ranges[1].vlc.max
267                                 ? param_desc->ranges[1].vlc.max
268                                 : param_desc->ranges[0].vlc.max };
269     f_vlc_val = VLC_CLIP(f_vlc_val, clip_range.min, clip_range.max);
271     unsigned int range_idx;
272     for (range_idx = 0; range_idx < 2; ++range_idx)
273         if (f_vlc_val >= param_desc->ranges[range_idx].vlc.min &&
274             f_vlc_val <= param_desc->ranges[range_idx].vlc.max)
275             break;
276     assert(range_idx < 2);
278     return GET_CI_VALUE(f_vlc_val,
279                         param_desc->ranges[range_idx].vlc,
280                         param_desc->ranges[range_idx].ci);
283 static int
284 ParamsCallback(vlc_object_t *obj,
285                char const *psz_var,
286                vlc_value_t oldval, vlc_value_t newval,
287                void *p_data)
289     VLC_UNUSED(obj); VLC_UNUSED(oldval);
290     struct filter_chain *filter = p_data;
291     struct filter_param_desc const *filter_param_descs =
292         filter_desc_table[filter->filter].param_descs;
294     unsigned int i = 0;
295     while (i < NUM_FILTER_PARAM_MAX &&
296            strcmp(filter_param_descs[i].vlc, psz_var))
297         ++i;
298     assert(i < NUM_FILTER_PARAM_MAX);
300     float new_vlc_val;
301     if (filter_param_descs[i].vlc_type == VLC_VAR_FLOAT)
302         new_vlc_val = newval.f_float;
303     else if (filter_param_descs[i].vlc_type == VLC_VAR_INTEGER)
304         new_vlc_val = newval.i_int;
305     else
306         vlc_assert_unreachable();
308     atomic_store(filter->ci_params + i,
309                  filter_ConvertParam(new_vlc_val, filter_param_descs + i));
311     return VLC_SUCCESS;
314 static void filter_PsychedelicInit(filter_t *filter, struct filter_chain *fchain)
316     filter_sys_t *sys = filter->p_sys;
317     fchain->ctx.psychedelic.x = filter->fmt_in.video.i_width / 2;
318     fchain->ctx.psychedelic.y = filter->fmt_in.video.i_height / 2;
319     fchain->ctx.psychedelic.count = PSYCHEDELIC_COUNT_DEFAULT;
322 static void filter_PsychedelicControl(filter_t *filter, struct filter_chain *fchain)
324     filter_sys_t *sys = filter->p_sys;
326     if (sys->mouse_moved)
327     {
328         fchain->ctx.psychedelic.x = sys->mouse.i_x;
329         fchain->ctx.psychedelic.y = filter->fmt_in.video.i_height
330                                             - sys->mouse.i_y - 1;
332         if (sys->mouse.i_pressed)
333         {
334             fchain->ctx.psychedelic.count++;
335             if (fchain->ctx.psychedelic.count > PSYCHEDELIC_COUNT_MAX)
336                 fchain->ctx.psychedelic.count = PSYCHEDELIC_COUNT_MIN;
337         }
338     }
339     CIVector *ci_vector =
340         [CIVector vectorWithX: (float)fchain->ctx.psychedelic.x
341                             Y: (float)fchain->ctx.psychedelic.y];
342     @try {
343         [fchain->ci_filter setValue: ci_vector
344                              forKey: @"inputCenter"];
345         [fchain->ci_filter setValue: [NSNumber numberWithFloat: fchain->ctx.psychedelic.count]
346                              forKey: @"inputCount"];
347     }
348     @catch (NSException * e) { /* inputCenter key doesn't exist */ }
351 static picture_t *
352 Filter(filter_t *filter, picture_t *src)
354     filter_sys_t *p_sys = filter->p_sys;
355     struct ci_filters_ctx *ctx = p_sys->ctx;
356     enum filter_type filter_types[NUM_MAX_EQUIVALENT_VLC_FILTERS];
358     filter_desc_table_GetFilterTypes(p_sys->psz_filter, filter_types);
359     if (ctx->fchain->filter != filter_types[0])
360         return src;
362     picture_t *dst = picture_NewFromFormat(&ctx->cvpx_pool_fmt);
363     if (!dst)
364         goto error;
366     CVPixelBufferRef cvpx = cvpxpool_new_cvpx(ctx->cvpx_pool);
367     if (!cvpx)
368         goto error;
370     if (cvpxpic_attach(dst, cvpx, filter->vctx_out, NULL))
371     {
372         CFRelease(cvpx);
373         goto error;
374     }
375     CFRelease(cvpx);
377     if (ctx->src_converter)
378     {
379         src = ctx->dst_converter->pf_video_filter(ctx->src_converter, src);
380         if (!src)
381             return NULL;
382     }
384     @autoreleasepool {
385         CIImage *ci_img = [CIImage imageWithCVImageBuffer: cvpxpic_get_ref(src)];
386         if (!ci_img)
387             goto error;
389         for (struct filter_chain *fchain = ctx->fchain;
390              fchain; fchain = fchain->next)
391         {
392             [fchain->ci_filter setValue: ci_img
393                                  forKey: kCIInputImageKey];
395             for (unsigned int i = 0; i < NUM_FILTER_PARAM_MAX &&
396                      filter_desc_table[fchain->filter].param_descs[i].vlc; ++i)
397             {
398                 NSString *ci_param_name =
399                     filter_desc_table[fchain->filter].param_descs[i].ci;
400                 float ci_value = atomic_load(fchain->ci_params + i);
402                 [fchain->ci_filter setValue: [NSNumber numberWithFloat: ci_value]
403                                      forKey: ci_param_name];
404             }
406             if (filter_desc_table[fchain->filter].pf_control)
407                 filter_desc_table[fchain->filter].pf_control(filter, fchain);
408             ci_img = [fchain->ci_filter valueForKey: kCIOutputImageKey];
409         }
411         [ctx->ci_ctx render: ci_img
412             toCVPixelBuffer: cvpx];
413     } /* autoreleasepool */
415     CopyInfoAndRelease(dst, src);
417     if (ctx->dst_converter)
418     {
419         dst = ctx->dst_converter->pf_video_filter(ctx->dst_converter, dst);
420         if (!dst)
421             return NULL;
422     }
424     p_sys->mouse_moved = false;
425     return dst;
427 error:
428     if (dst)
429         picture_Release(dst);
430     picture_Release(src);
431     p_sys->mouse_moved = false;
432     return NULL;
435 static int
436 Mouse(filter_t *filter, struct vlc_mouse_t *new,
437       const struct vlc_mouse_t *old)
439     filter_sys_t *sys = filter->p_sys;
440     sys->old_mouse = *old;
441     sys->mouse = *new;
442     sys->mouse_moved = true;
443     return VLC_SUCCESS;
446 static int
447 Open_FilterInit(filter_t *filter, struct filter_chain *fchain)
449     struct filter_param_desc const *filter_param_descs =
450         filter_desc_table[fchain->filter].param_descs;
451     NSString *ci_filter_name = filter_desc_table_GetFilterName(fchain->filter);
452     if (ci_filter_name == nil)
453     {
454         char *psz_filter_name = var_InheritString(filter, "ci-filter");
455         if (psz_filter_name)
456             ci_filter_name = [NSString stringWithUTF8String:psz_filter_name];
457         free(psz_filter_name);
458     }
460     fchain->ci_filter = [CIFilter filterWithName: ci_filter_name];
461     if (!fchain->ci_filter)
462     {
463         msg_Warn(filter, "filter '%s' could not be created",
464                  [ci_filter_name UTF8String]);
465         return VLC_EGENERIC;
466     }
468     for (int i = 0; i < NUM_FILTER_PARAM_MAX && filter_param_descs[i].vlc; ++i)
469     {
470         NSString *ci_param_name = filter_param_descs[i].ci;
471         char const *vlc_param_name = filter_param_descs[i].vlc;
473         float vlc_param_val;
474         if (filter_param_descs[i].vlc_type == VLC_VAR_FLOAT)
475             vlc_param_val = var_CreateGetFloatCommand(filter, vlc_param_name);
476         else if (filter_param_descs[i].vlc_type == VLC_VAR_INTEGER)
477             vlc_param_val =
478                 (float)var_CreateGetIntegerCommand(filter, vlc_param_name);
479         else
480             vlc_assert_unreachable();
482         atomic_init(fchain->ci_params + i,
483                     filter_ConvertParam(vlc_param_val,
484                                         filter_param_descs + i));
486         var_AddCallback(filter, filter_param_descs[i].vlc,
487                         ParamsCallback, fchain);
488     }
489     if (filter_desc_table[fchain->filter].pf_init)
490         filter_desc_table[fchain->filter].pf_init(filter, fchain);
492     return VLC_SUCCESS;
495 static int
496 Open_CreateFilters(filter_t *filter, struct filter_chain **p_last_filter,
497                    enum filter_type filter_types[NUM_MAX_EQUIVALENT_VLC_FILTERS])
499     struct filter_chain *new_filter;
501     for (unsigned int i = 0;
502          i < NUM_MAX_EQUIVALENT_VLC_FILTERS
503              && filter_types[i] != FILTER_NONE; ++i)
504     {
505         new_filter = filter_chain_AddFilter(p_last_filter, filter_types[i]);
506         if (!new_filter)
507             return VLC_EGENERIC;
508         p_last_filter = &new_filter;
509         if (Open_FilterInit(filter, new_filter) != VLC_SUCCESS)
510         {
511             for (unsigned int j = 0; j < i ; ++j)
512                 filter_chain_RemoveFilter(p_last_filter, filter_types[i]);
513             return VLC_EGENERIC;
514         }
515     }
517     return VLC_SUCCESS;
520 static void
521 cvpx_video_context_Destroy(void *priv)
523     struct ci_filters_ctx *ctx = priv;
525     if (ctx->src_converter)
526     {
527         module_unneed(ctx->src_converter, ctx->src_converter->p_module);
528         vlc_object_delete(ctx->src_converter);
529     }
530     if (ctx->dst_converter)
531     {
532         module_unneed(ctx->dst_converter, ctx->dst_converter->p_module);
533         vlc_object_delete(ctx->dst_converter);
534     }
536     if (ctx->cvpx_pool)
537         CVPixelBufferPoolRelease(ctx->cvpx_pool);
540 static filter_t *
541 CVPX_to_CVPX_converter_Create(filter_t *filter, bool to_rgba)
543     filter_t *converter = vlc_object_create(filter, sizeof(filter_t));
544     if (!converter)
545         return NULL;
547     converter->fmt_in = filter->fmt_out;
548     converter->fmt_out = filter->fmt_out;
550     if (to_rgba)
551     {
552         converter->fmt_out.video.i_chroma =
553         converter->fmt_out.i_codec = VLC_CODEC_CVPX_BGRA;
554     }
555     else
556     {
557         converter->fmt_in.video.i_chroma =
558         converter->fmt_in.i_codec = VLC_CODEC_CVPX_BGRA;
559     }
561     converter->p_module = module_need(converter, "video converter", NULL, false);
562     if (!converter->p_module)
563     {
564         vlc_object_delete(converter);
565         return NULL;
566     }
568     return converter;
571 static int
572 Open(vlc_object_t *obj, char const *psz_filter)
574     filter_t *filter = (filter_t *)obj;
576     switch (filter->fmt_in.video.i_chroma)
577     {
578         case VLC_CODEC_CVPX_NV12:
579         case VLC_CODEC_CVPX_UYVY:
580         case VLC_CODEC_CVPX_I420:
581         case VLC_CODEC_CVPX_BGRA:
582             break;
583         default:
584             return VLC_EGENERIC;
585     }
587     if (filter->vctx_in == NULL ||
588         vlc_video_context_GetType(filter->vctx_in) != VLC_VIDEO_CONTEXT_CVPX)
589         return VLC_EGENERIC;
591     filter_sys_t *p_sys = filter->p_sys = calloc(1, sizeof(filter_sys_t));
592     if (!filter->p_sys)
593         return VLC_ENOMEM;
595     enum filter_type filter_types[NUM_MAX_EQUIVALENT_VLC_FILTERS];
596     filter_desc_table_GetFilterTypes(psz_filter, filter_types);
598     struct ci_filters_ctx *ctx =
599         vlc_video_context_GetCVPXPrivate(filter->vctx_in, CVPX_VIDEO_CONTEXT_CIFILTERS);
601     if (ctx)
602         filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
603     else
604     {
605         static const struct vlc_video_context_operations ops = {
606             cvpx_video_context_Destroy,
607         };
608         vlc_decoder_device *dec_dev =
609             filter_HoldDecoderDeviceType(filter,
610                                          VLC_DECODER_DEVICE_VIDEOTOOLBOX);
611         if (!dec_dev)
612         {
613             msg_Err(filter, "Missing decoder device");
614             goto error;
615         }
616         filter->vctx_out =
617             vlc_video_context_CreateCVPX(dec_dev, CVPX_VIDEO_CONTEXT_CIFILTERS,
618                                          sizeof(struct ci_filters_ctx), &ops);
619         vlc_decoder_device_Release(dec_dev);
620         if (!filter->vctx_out)
621             goto error;
623         ctx = vlc_video_context_GetCVPXPrivate(filter->vctx_out,
624                                                CVPX_VIDEO_CONTEXT_CIFILTERS);
625         assert(ctx);
627         ctx->src_converter = ctx->dst_converter = NULL;
628         ctx->fchain = NULL;
629         ctx->cvpx_pool = nil;
630         ctx->cvpx_pool_fmt = filter->fmt_out.video;
632         if (filter->fmt_in.video.i_chroma != VLC_CODEC_CVPX_NV12
633          && filter->fmt_in.video.i_chroma != VLC_CODEC_CVPX_BGRA)
634         {
635             ctx->src_converter =
636                     CVPX_to_CVPX_converter_Create(filter, true);
637             ctx->dst_converter = ctx->src_converter ?
638                     CVPX_to_CVPX_converter_Create(filter, false) : NULL;
639             if (!ctx->src_converter || !ctx->dst_converter)
640                 goto error;
641             ctx->cvpx_pool_fmt.i_chroma = VLC_CODEC_CVPX_BGRA;
642         }
644 #if !TARGET_OS_IPHONE
645         CGLContextObj glctx = var_InheritAddress(filter, "macosx-glcontext");
646         if (!glctx)
647         {
648             msg_Err(filter, "can't find 'macosx-glcontext' var");
649             goto error;
650         }
651         ctx->ci_ctx = [CIContext contextWithCGLContext: glctx
652                                            pixelFormat: nil
653                                             colorSpace: nil
654                                                options: @{
655                                                 kCIContextWorkingColorSpace : [NSNull null],
656                                                 kCIContextOutputColorSpace : [NSNull null],
657                                                }];
658 #else
659         CVEAGLContext eaglctx = var_InheritAddress(filter, "ios-eaglcontext");
660         if (!eaglctx)
661         {
662             msg_Err(filter, "can't find 'ios-eaglcontext' var");
663             goto error;
664         }
665         ctx->ci_ctx = [CIContext contextWithEAGLContext: eaglctx];
666 #endif
667         if (!ctx->ci_ctx)
668             goto error;
670         ctx->cvpx_pool = cvpxpool_create(&ctx->cvpx_pool_fmt, 2);
671         if (!ctx->cvpx_pool)
672             goto error;
673     }
674     if (Open_CreateFilters(filter, &ctx->fchain, filter_types))
675         goto error;
677     p_sys->psz_filter = psz_filter;
678     p_sys->ctx = ctx;
680     filter->pf_video_filter = Filter;
681     filter->pf_video_mouse = Mouse;
683     return VLC_SUCCESS;
685 error:
686     if (filter->vctx_out)
687         vlc_video_context_Release(filter->vctx_out);
688     free(p_sys);
689     return VLC_EGENERIC;
692 static int
693 OpenAdjust(vlc_object_t *obj)
695     return Open(obj, "adjust");
698 static int
699 OpenInvert(vlc_object_t *obj)
701     return Open(obj, "invert");
704 static int
705 OpenPosterize(vlc_object_t *obj)
707     return Open(obj, "posterize");
710 static int
711 OpenSepia(vlc_object_t *obj)
713     return Open(obj, "sepia");
716 static int
717 OpenSharpen(vlc_object_t *obj)
719     return Open(obj, "sharpen");
722 static int
723 OpenPsychedelic(vlc_object_t *obj)
725     return Open(obj, "psychedelic");
728 static int
729 OpenCustom(vlc_object_t *obj)
731     return Open(obj, "custom");
734 static void
735 Close(vlc_object_t *obj)
737     filter_t *filter = (filter_t *)obj;
738     filter_sys_t *p_sys = filter->p_sys;
739     struct ci_filters_ctx *ctx = p_sys->ctx;
740     enum filter_type filter_types[NUM_MAX_EQUIVALENT_VLC_FILTERS];
742     filter_desc_table_GetFilterTypes(p_sys->psz_filter, filter_types);
743     for (unsigned int i = 0;
744          i < NUM_MAX_EQUIVALENT_VLC_FILTERS && filter_types[i] != FILTER_NONE;
745          ++i)
746         filter_chain_RemoveFilter(&ctx->fchain, filter_types[i]);
748     vlc_video_context_Release(filter->vctx_out);
749     free(p_sys);
752 #define CI_CUSTOM_FILTER_TEXT N_("Use a specific Core Image Filter")
753 #define CI_CUSTOM_FILTER_LONGTEXT N_( \
754     "Example: 'CICrystallize', 'CIBumpDistortion', 'CIThermal', 'CIComicEffect'")
756 vlc_module_begin()
757     set_capability("video filter", 0)
758     set_category(CAT_VIDEO)
759     set_subcategory(SUBCAT_VIDEO_VFILTER)
760     set_description(N_("Mac OS X hardware video filters"))
762     add_submodule()
763     set_callbacks(OpenAdjust, Close)
764     add_shortcut("adjust")
766     add_submodule()
767     set_callbacks(OpenInvert, Close)
768     add_shortcut("invert")
770     add_submodule()
771     set_callbacks(OpenPosterize, Close)
772     add_shortcut("posterize")
774     add_submodule()
775     set_callbacks(OpenSepia, Close)
776     add_shortcut("sepia")
778     add_submodule()
779     set_callbacks(OpenSharpen, Close)
780     add_shortcut("sharpen")
782     add_submodule()
783     set_callbacks(OpenPsychedelic, Close)
784     add_shortcut("psychedelic")
786     add_submodule()
787     set_callbacks(OpenCustom, Close)
788     add_shortcut("ci")
789     add_string("ci-filter", "CIComicEffect", CI_CUSTOM_FILTER_TEXT, CI_CUSTOM_FILTER_LONGTEXT, true);
790 vlc_module_end()