]> git.sesse.net Git - vlc/blob - modules/video_filter/atmo/atmo.cpp
84b3cd94bcc4d0ff6ee4f5890e7c1089fad76291
[vlc] / modules / video_filter / atmo / atmo.cpp
1 /*****************************************************************************
2 * atmo.cpp : "Atmo Light" video filter
3 *****************************************************************************
4 * Copyright (C) 2000-2006 the VideoLAN team
5 * $Id$
6 *
7 * Authors: AndrĂ© Weber (WeberAndre@gmx.de)
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29 #include <math.h>                                            /* sin(), cos() */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 // #define __ATMO_DEBUG__
36 // [:Zs]+$
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_vout.h>
40
41 #include <vlc_playlist.h>
42 #include "vlc_filter.h"
43
44 #include "AtmoDefs.h"
45 #include "AtmoDynData.h"
46 #include "AtmoLiveView.h"
47 #include "AtmoTools.h"
48 #include "AtmoExternalCaptureInput.h"
49 #include "AtmoConfig.h"
50 #include "AtmoConnection.h"
51 #include "AtmoSerialConnection.h"
52
53
54 /*****************************************************************************
55 * Local prototypes
56 *****************************************************************************/
57 /* directly to vlc related functions required that the module is accepted */
58 static int  CreateFilter    ( vlc_object_t * );
59 static void DestroyFilter   ( vlc_object_t * );
60 static picture_t * Filter( filter_t *, picture_t *);
61
62 /* callback for global variable state pause / continue / stop events */
63 static void AddStateVariableCallback( filter_t *);
64 static void DelStateVariableCallback( filter_t *);
65 static int StateCallback(vlc_object_t *, char const *,
66                          vlc_value_t, vlc_value_t, void *);
67
68 /* callback for variable crop-update */
69 static void AddCropVariableCallback( filter_t *);
70 static void DelCropVariableCallback( filter_t *);
71 static int CropCallback(vlc_object_t *, char const *,
72                         vlc_value_t, vlc_value_t, void *);
73
74 /* callback for atmo settings variables whose change
75    should be immediately realized and applied to output
76 */
77 static void DelAtmoSettingsVariablesCallbacks(filter_t *);
78 static void AddAtmoSettingsVariablesCallbacks(filter_t *);
79 static int AtmoSettingsCallback(vlc_object_t *, char const *,
80                                 vlc_value_t, vlc_value_t, void *);
81
82
83 #if defined(__ATMO_DEBUG__)
84 static void atmo_parse_crop(char *psz_cropconfig,
85                             video_format_t fmt_in,
86                             video_format_t fmt_render,
87                             int &i_visible_width,
88                             int &i_visible_height,
89                             int &i_x_offset,
90                             int &i_y_offset );
91 #endif
92
93
94 /* function to shutdown the fade thread which is started on pause*/
95 static void CheckAndStopFadeThread(filter_t *);
96
97 /* extracts a small RGB (BGR) Image from an YUV image */
98 static void ExtractMiniImage_YUV(filter_sys_t *, picture_t *, uint8_t *);
99
100 #if defined(__ATMO_DEBUG__)
101 void SaveBitmap(filter_sys_t *p_sys, uint8_t *p_pixels, char *psz_filename);
102 #endif
103
104 /*****************************************************************************
105 * External Prototypes for the AtmoCtrlLib.DLL
106 *****************************************************************************/
107 /*
108 * if effectmode = emLivePicture then the source could be GDI (Screencapture)
109 * or External - this means another application delivers Pixeldata to AtmoWin
110 * Clientsoftware through  AtmoCtrlLib.DLL and the COM Api
111 */
112 #define lvsGDI           0
113 #define lvsExternal      1
114
115
116 /*
117 strings for settings menus and hints
118 */
119 #define MODULE_DESCRIPTION N_ ( \
120  "This module allows to control an so called AtmoLight device which "\
121  "is connected to your computer.\n"\
122  "AtmoLight is the homebrew version of that what Philips calls AmbiLight.\n"\
123  "If you need further informations feel free to visit us at\n\n"\
124  "http://www.vdr-wiki.de/wiki/index.php/Atmo-plugin\n"\
125  "http://www.vdr-wiki.de/wiki/index.php/AtmoWin\n\n"\
126  "there you will find detailed descriptions how to build it for your self and "\
127  "where you can get the required parts and so on.\n There you can also see "\
128  "pictures and some movies showing such a device in live action...")
129
130
131
132 #if defined( __ATMO_DEBUG__ )
133 #   define SAVEFRAMES_TEXT     N_("Save Debug Frames")
134 #   define SAVEFRAMES_LONGTEXT N_("Writes every 128th miniframe to a folder.")
135 #   define FRAMEPATH_TEXT      N_("Debug Frame Folder")
136 #   define FRAMEPATH_LONGTEXT  N_("defines the path where the debugframes " \
137                                   "should be saved")
138 #endif
139
140 #define WIDTH_TEXT             N_("Extracted Image Width")
141 #define WIDTH_LONGTEXT         N_("defines the width of the mini image for " \
142                                   "further processing (64 is default)")
143
144 #define HEIGHT_TEXT            N_("Extracted Image Height")
145 #define HEIGHT_LONGTEXT        N_("defines the height of the mini image for " \
146                                   "further processing (48 is default)")
147
148 #define PCOLOR_TEXT            N_("use Pause Color")
149 #define PCOLOR_LONGTEXT        N_("use the color defined below if the user " \
150                           "paused the video.(have light to get another beer?)")
151 #define PCOLOR_RED_TEXT        N_("Pause-Red")
152 #define PCOLOR_RED_LONGTEXT    N_("the red component of pause color")
153 #define PCOLOR_GREEN_TEXT      N_("Pause-Green")
154 #define PCOLOR_GREEN_LONGTEXT  N_("the green component of pause color")
155 #define PCOLOR_BLUE_TEXT       N_("Pause-Blue")
156 #define PCOLOR_BLUE_LONGTEXT   N_("the blue component of pause color")
157 #define FADESTEPS_TEXT         N_("Pause-Fadesteps")
158 #define FADESTEPS_LONGTEXT     N_("Number of steps to change current color " \
159                                   "to pause color (each step takes 40ms)")
160
161 #define ECOLOR_RED_TEXT        N_("End-Red")
162 #define ECOLOR_RED_LONGTEXT    N_("the red component of the shutdown color")
163 #define ECOLOR_GREEN_TEXT      N_("End-Green")
164 #define ECOLOR_GREEN_LONGTEXT  N_("the green component of the shutdown color")
165 #define ECOLOR_BLUE_TEXT       N_("End-Blue")
166 #define ECOLOR_BLUE_LONGTEXT   N_("the blue component of the shutdown color")
167 #define EFADESTEPS_TEXT        N_("End-Fadesteps")
168 #define EFADESTEPS_LONGTEXT  N_("Number of steps to change current color to " \
169                              "end color for dimming up the light in cinema " \
170                              "style... (each step takes 40ms)")
171
172 #define USEWHITEADJ_TEXT       N_("Use Software White adjust")
173 #define USEWHITEADJ_LONGTEXT   N_("Should the buildin driver do a white " \
174                                   "adjust or you LED stripes? recommend.")
175 #define WHITE_RED_TEXT         N_("White Red")
176 #define WHITE_RED_LONGTEXT     N_("Red value of a pure white on your "\
177                                   "LED stripes.")
178 #define WHITE_GREEN_TEXT       N_("White Green")
179 #define WHITE_GREEN_LONGTEXT   N_("Green value of a pure white on your "\
180                                   "LED stripes.")
181 #define WHITE_BLUE_TEXT        N_("White Blue")
182 #define WHITE_BLUE_LONGTEXT    N_("Blue value of a pure white on your "\
183                                   "LED stripes.")
184
185 #define SERIALDEV_TEXT         N_("Serial Port/Device")
186 #define SERIALDEV_LONGTEXT   N_("Name of the serial port where the AtmoLight "\
187                        "controller is attached to\n on Windows usually "\
188                        "something like COM1 or COM2 on Linux /dev/ttyS01 f.e.")
189
190 #define EDGE_TEXT            N_("Edge Weightning")
191 #define EDGE_LONGTEXT        N_("increasing this value will result in color "\
192                                 "more depending on the border of the frame")
193 #define BRIGHTNESS_TEXT     N_("Brightness")
194 #define BRIGHTNESS_LONGTEXT N_("overall Brightness of you LED stripes")
195 #define DARKNESS_TEXT       N_("Darkness Limit")
196 #define DARKNESS_LONGTEXT   N_("pixels with a saturation lower than this will "\
197                                "be ignored should be greater than one for "\
198                                "letterboxed videos")
199 #define HUEWINSIZE_TEXT     N_("Hue windowing")
200 #define HUEWINSIZE_LONGTEXT N_("used for statistics")
201 #define SATWINSIZE_TEXT     N_("Sat windowing")
202 #define SATWINSIZE_LONGTEXT N_("used for statistics")
203
204 #define MEANLENGTH_TEXT     N_("Filter length (ms)")
205 #define MEANLENGTH_LONGTEXT N_("Time it takes until a color is complete "\
206                                 "changed, removes flickering")
207 #define MEANTHRESHOLD_TEXT     N_("Filter threshold")
208 #define MEANTHRESHOLD_LONGTEXT N_("How much a color must changed, for an "\
209                                   "imediate color change")
210 #define MEANPERCENTNEW_TEXT     N_("Filter Smoothness %")
211 #define MEANPERCENTNEW_LONGTEXT N_("Filter Smoothness")
212
213 #define FILTERMODE_TEXT        N_("Filtermode")
214 #define FILTERMODE_LONGTEXT    N_("kind of filtering which should be use to "\
215                                   "calcuate the color output")
216 static int pi_filtermode_values[] = {
217        (int)afmNoFilter,
218        (int)afmCombined,
219        (int)afmPercent
220 };
221 static const char *ppsz_filtermode_descriptions[] = {
222         N_("No Filtering"),
223         N_("Combined"),
224         N_("Percent")
225 };
226
227 #define FRAMEDELAY_TEXT       N_("Framedelay")
228 #define FRAMEDELAY_LONGTEXT   N_("helps to get video out and light effects "\
229                              "insync values around 20ms should do the trick")
230
231
232 #define CHANNEL_0_ASSIGN_TEXT N_("Channel summary")
233 #define CHANNEL_1_ASSIGN_TEXT N_("Channel left")
234 #define CHANNEL_2_ASSIGN_TEXT N_("Channel right")
235 #define CHANNEL_3_ASSIGN_TEXT N_("Channel top")
236 #define CHANNEL_4_ASSIGN_TEXT N_("Channel bottom")
237
238 #define CHANNELASSIGN_LONGTEXT N_("maps the hardware channel X to logical "\
239                                   "channel Y to fix wrong wiring:-)")
240 static int pi_channel_assignment_values[] = {
241     -1,
242      0,
243      1,
244      2,
245      3,
246      4
247 };
248 static const char *ppsz_channel_assignment_descriptions[] = {
249         N_("disabled"),
250         N_("summary"),
251         N_("left"),
252         N_("right"),
253         N_("top"),
254         N_("bottom")
255 };
256
257 #define ZONE_0_GRADIENT_TEXT N_("summary gradient")
258 #define ZONE_1_GRADIENT_TEXT N_("left gradient")
259 #define ZONE_2_GRADIENT_TEXT N_("right gradient")
260 #define ZONE_3_GRADIENT_TEXT N_("top gradient")
261 #define ZONE_4_GRADIENT_TEXT N_("bottom gradient")
262 #define ZONE_X_GRADIENT_LONG_TEXT N_("defines a small bitmap with 64x48 "\
263                                      "pixels, containing a grayscale gradient")
264
265 #if defined( WIN32 )
266 #   define ATMOWINEXE_TEXT      N_("Filename of AtmoWinA.exe")
267 #   define ATMOWINEXE_LONGTEXT  N_("if you wan't that the AtmoLight control "\
268                                    "software is launched by\nVLC enter the "\
269                                    "complete Filename of AtmoWinA.exe here")
270 #   define USEBUILDIN_TEXT      N_("Use buildin AtmoLight")
271 #   define USEBUILDIN_LONGTEXT N_("VideoLan will directly use your AtmoLight "\
272                                   "hardware without running the external "\
273                                   "AtmoWinA.exe Userspace driver.")
274 #endif
275
276 #define CFG_PREFIX "atmo-"
277
278 /*****************************************************************************
279 * Module descriptor
280 *****************************************************************************/
281 vlc_module_begin();
282 set_description( N_("AtmoLight Filter") );
283 set_help( MODULE_DESCRIPTION );
284 set_shortname( N_( "AtmoLight" ));
285 set_capability( "video filter2", 0 );
286
287 set_category( CAT_VIDEO );
288 set_subcategory( SUBCAT_VIDEO_VFILTER );
289
290 #if defined(WIN32)
291 set_section( N_("Choose between the buildin AtmoLight "\
292                  "driver or the external" ), 0 );
293
294 /*
295     only on win32 exists the option to use the buildin driver or
296     the more flexible external driver application
297 */
298 add_bool(CFG_PREFIX "usebuildin", true, NULL,
299          USEBUILDIN_TEXT, USEBUILDIN_LONGTEXT, false);
300 add_string(CFG_PREFIX "serialdev", "COM1", NULL,
301            SERIALDEV_TEXT, SERIALDEV_LONGTEXT, false );
302
303 /*
304     on win32 the executeable external driver application
305     for automatic start if needed
306 */
307 add_file(CFG_PREFIX "atmowinexe", NULL, NULL,
308          ATMOWINEXE_TEXT, ATMOWINEXE_LONGTEXT, false );
309 #else
310 set_section( N_("Enter connection of your AtmoLight hardware" ), 0 );
311 add_string(CFG_PREFIX "serialdev", "/dev/ttyS01", NULL,
312            SERIALDEV_TEXT, SERIALDEV_LONGTEXT, false );
313 #endif
314
315 /*
316     color which is showed if you want durring pausing
317     your movie ... used for both buildin / external
318 */
319 set_section( N_("Illuminate the room with this color on pause" ), 0 );
320 add_bool(CFG_PREFIX "usepausecolor", false, NULL,
321          PCOLOR_TEXT, PCOLOR_LONGTEXT, false);
322 add_integer_with_range(CFG_PREFIX "pcolor-red",   0, 0, 255, NULL,
323                        PCOLOR_RED_TEXT, PCOLOR_RED_LONGTEXT, false);
324 add_integer_with_range(CFG_PREFIX "pcolor-green", 0, 0, 255, NULL,
325                        PCOLOR_GREEN_TEXT, PCOLOR_GREEN_LONGTEXT, false);
326 add_integer_with_range(CFG_PREFIX "pcolor-blue",  192, 0, 255, NULL,
327                        PCOLOR_BLUE_TEXT, PCOLOR_BLUE_LONGTEXT, false);
328 add_integer_with_range(CFG_PREFIX "fadesteps", 50, 1, 250, NULL,
329                        FADESTEPS_TEXT, FADESTEPS_LONGTEXT, false);
330
331 /*
332     color which is showed if you finished watching your movie ...
333     used for both buildin / external
334 */
335 set_section( N_("Illuminate the room with this color on shutdown" ), 0 );
336 add_integer_with_range(CFG_PREFIX "ecolor-red",   192, 0, 255, NULL,
337                        ECOLOR_RED_TEXT,   ECOLOR_RED_LONGTEXT,   false);
338 add_integer_with_range(CFG_PREFIX "ecolor-green", 192, 0, 255, NULL,
339                        ECOLOR_GREEN_TEXT, ECOLOR_GREEN_LONGTEXT, false);
340 add_integer_with_range(CFG_PREFIX "ecolor-blue",  192, 0, 255, NULL,
341                        ECOLOR_BLUE_TEXT,  ECOLOR_BLUE_LONGTEXT,  false);
342 add_integer_with_range(CFG_PREFIX "efadesteps",    50, 1, 250, NULL,
343                        EFADESTEPS_TEXT,   EFADESTEPS_LONGTEXT,    false);
344
345 /*
346  settings only for the buildin driver (if external driver app is used
347  these parameters are ignored.)
348
349  definition of parameters for the buildin filter ...
350 */
351 set_section( N_("Settings only for buildin Live Video Processor" ), 0 );
352
353 add_integer_with_range(CFG_PREFIX "EdgeWeightning",   8, 1, 30, NULL,
354                        EDGE_TEXT, EDGE_LONGTEXT, false);
355
356 add_integer_with_range(CFG_PREFIX "Brightness",   100, 50, 300, NULL,
357                        BRIGHTNESS_TEXT, BRIGHTNESS_LONGTEXT, false);
358
359 add_integer_with_range(CFG_PREFIX "DarknessLimit",   5, 0, 10, NULL,
360                        DARKNESS_TEXT, DARKNESS_LONGTEXT, false);
361
362 add_integer_with_range(CFG_PREFIX "HueWinSize",   3, 0, 5, NULL,
363                        HUEWINSIZE_TEXT, HUEWINSIZE_LONGTEXT, false);
364
365 add_integer_with_range(CFG_PREFIX "SatWinSize",   3, 0, 5, NULL,
366                        SATWINSIZE_TEXT, SATWINSIZE_LONGTEXT, false);
367
368 add_integer(CFG_PREFIX "filtermode", (int)afmCombined, NULL,
369             FILTERMODE_TEXT, FILTERMODE_LONGTEXT, false );
370
371 change_integer_list(pi_filtermode_values, ppsz_filtermode_descriptions, 0 );
372
373 add_integer_with_range(CFG_PREFIX "MeanLength",    300, 300, 5000, NULL,
374                        MEANLENGTH_TEXT, MEANLENGTH_LONGTEXT, false);
375
376 add_integer_with_range(CFG_PREFIX "MeanThreshold",  40, 1, 100, NULL,
377                        MEANTHRESHOLD_TEXT, MEANTHRESHOLD_LONGTEXT, false);
378
379 add_integer_with_range(CFG_PREFIX "PercentNew", 50, 1, 100, NULL,
380                       MEANPERCENTNEW_TEXT, MEANPERCENTNEW_LONGTEXT, false);
381
382 add_integer_with_range(CFG_PREFIX "FrameDelay", 18, 0, 35, NULL,
383                        FRAMEDELAY_TEXT, FRAMEDELAY_LONGTEXT, false);
384
385 /*
386   output channel reordering
387 */
388 set_section( N_("Change channel assignment (fixes wrong wiring)" ), 0 );
389 add_integer( CFG_PREFIX "channel_0", 0, NULL,
390             CHANNEL_0_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
391 change_integer_list( pi_channel_assignment_values,
392                      ppsz_channel_assignment_descriptions, 0 );
393
394 add_integer( CFG_PREFIX "channel_1", 1, NULL,
395             CHANNEL_1_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
396 change_integer_list( pi_channel_assignment_values,
397                      ppsz_channel_assignment_descriptions, 0 );
398
399 add_integer( CFG_PREFIX "channel_2", 2, NULL,
400             CHANNEL_2_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
401 change_integer_list( pi_channel_assignment_values,
402                      ppsz_channel_assignment_descriptions, 0 );
403
404 add_integer( CFG_PREFIX "channel_3", 3, NULL,
405             CHANNEL_3_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
406 change_integer_list( pi_channel_assignment_values,
407                      ppsz_channel_assignment_descriptions, 0 );
408
409 add_integer( CFG_PREFIX "channel_4", 4, NULL,
410             CHANNEL_4_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
411 change_integer_list( pi_channel_assignment_values,
412                      ppsz_channel_assignment_descriptions, 0 );
413
414 /*
415   LED color white calibration
416 */
417 set_section( N_("Adjust the white light to your LED stripes" ), 0 );
418 add_bool(CFG_PREFIX "whiteadj", true, NULL,
419          USEWHITEADJ_TEXT, USEWHITEADJ_LONGTEXT, false);
420 add_integer_with_range(CFG_PREFIX "white-red",   255, 0, 255, NULL,
421                        WHITE_RED_TEXT,   WHITE_RED_LONGTEXT,   false);
422
423 add_integer_with_range(CFG_PREFIX "white-green", 255, 0, 255, NULL,
424                        WHITE_GREEN_TEXT, WHITE_GREEN_LONGTEXT, false);
425
426 add_integer_with_range(CFG_PREFIX "white-blue",  255, 0, 255, NULL,
427                        WHITE_BLUE_TEXT,  WHITE_BLUE_LONGTEXT,  false);
428 /* end of definition of parameter for the buildin filter ... part 1 */
429
430
431 /*
432 only for buildin (external has own definition) per default the calucation
433 used linear gradients for assigning a priority to the pixel - depending
434 how near they are to the border ...for changing this you can create 64x48
435 Pixel BMP files - which contain your own grayscale... (you can produce funny
436 effects with this...) the images MUST not compressed, should have 24-bit per
437 pixel, or a simple 256 color grayscale palette
438 */
439 set_section( N_("Change gradients" ), 0 );
440 add_file(CFG_PREFIX "gradient_zone_0", NULL, NULL,
441          ZONE_0_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
442 add_file(CFG_PREFIX "gradient_zone_1", NULL, NULL,
443          ZONE_1_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
444 add_file(CFG_PREFIX "gradient_zone_2", NULL, NULL,
445          ZONE_2_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
446 add_file(CFG_PREFIX "gradient_zone_3", NULL, NULL,
447          ZONE_3_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
448 add_file(CFG_PREFIX "gradient_zone_4", NULL, NULL,
449          ZONE_4_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
450
451
452 #if defined(__ATMO_DEBUG__)
453 add_bool(CFG_PREFIX "saveframes", false, NULL,
454          SAVEFRAMES_TEXT, SAVEFRAMES_LONGTEXT, false);
455 add_string(CFG_PREFIX "framepath", "", NULL,
456            FRAMEPATH_TEXT, FRAMEPATH_LONGTEXT, false );
457 #endif
458 /*
459    may be later if computers gets more power ;-) than now we increase
460    the samplesize from which we do the stats for output color calculation
461 */
462 add_integer_with_range(CFG_PREFIX "width",  64, 64, 512, NULL,
463                        WIDTH_TEXT,  WIDTH_LONGTEXT, true);
464 add_integer_with_range(CFG_PREFIX "height", 48, 48, 384, NULL,
465                        HEIGHT_TEXT,  HEIGHT_LONGTEXT, true);
466
467 add_shortcut( "atmo" );
468 set_callbacks( CreateFilter, DestroyFilter  );
469 vlc_module_end();
470
471
472 static const char *const ppsz_filter_options[] = {
473 #if defined(WIN32)
474         "usebuildin",
475 #endif
476         "serialdev",
477
478
479         "EdgeWeightning",
480         "Brightness",
481         "DarknessLimit",
482         "HueWinSize",
483         "SatWinSize",
484
485         "filtermode",
486
487         "MeanLength",
488         "MeanThreshold",
489         "PercentNew",
490         "FrameDelay",
491
492         "channel_0",
493         "channel_1",
494         "channel_2",
495         "channel_3",
496         "channel_4",
497
498         "whiteadj",
499         "white-red",
500         "white-green",
501         "white-blue",
502
503         "usepausecolor",
504         "pcolor-red",
505         "pcolor-green",
506         "pcolor-blue",
507         "fadesteps",
508
509         "ecolor-red",
510         "ecolor-green",
511         "ecolor-blue",
512         "efadesteps",
513
514
515 #if defined(WIN32 )
516         "usebuildin",
517         "atmowinexe",
518 #endif
519 #if defined(__ATMO_DEBUG__)
520         "saveframes" ,
521         "framepath",
522 #endif
523         "width",
524         "height",
525         "gradient_zone_0",
526         "gradient_zone_1",
527         "gradient_zone_2",
528         "gradient_zone_3",
529         "gradient_zone_4",
530         NULL
531 };
532
533
534 /*****************************************************************************
535 * fadethread_t: Color Fading Thread
536 *****************************************************************************
537 * changes slowly the color of the output if videostream gets paused...
538 *****************************************************************************
539 */
540 typedef struct
541 {
542     VLC_COMMON_MEMBERS
543         filter_t *p_filter;
544     /* tell the thread which color should be the target of fading */
545     uint8_t ui_red;
546     uint8_t ui_green;
547     uint8_t ui_blue;
548     /* how many steps should happen until this */
549     int i_steps;
550
551 } fadethread_t;
552
553 static void FadeToColorThread(fadethread_t *p_fadethread);
554
555
556 /*****************************************************************************
557 * filter_sys_t: AtmoLight filter method descriptor
558 *****************************************************************************
559 * It describes the AtmoLight specific properties of an video filter.
560 *****************************************************************************/
561 struct filter_sys_t
562 {
563     /*
564     special for the access of the p_fadethread member all other members
565     need no special protection so far!
566     */
567     vlc_mutex_t filter_lock;
568
569     bool b_enabled;
570     int32_t i_AtmoOldEffect;
571     bool b_pause_live;
572
573     int32_t i_atmo_width;
574     int32_t i_atmo_height;
575
576 #if defined(__ATMO_DEBUG__)
577     bool  b_saveframes;
578     int i_framecounter;
579     char sz_framepath[MAX_PATH];
580 #endif
581
582     /* light color durring movie pause ... */
583     bool  b_usepausecolor;
584     uint8_t ui_pausecolor_red;
585     uint8_t ui_pausecolor_green;
586     uint8_t ui_pausecolor_blue;
587     int i_fadesteps;
588
589     /* light color on movie finish ... */
590     uint8_t ui_endcolor_red;
591     uint8_t ui_endcolor_green;
592     uint8_t ui_endcolor_blue;
593     int i_endfadesteps;
594
595     fadethread_t *p_fadethread;
596
597     /* Variables for buildin driver only... */
598
599     /* is only present and initialized if the internal driver is used*/
600     CAtmoConfig *p_atmo_config;
601     /* storage for temporal settings "volatile" */
602     CAtmoDynData *p_atmo_dyndata;
603     /* initialized for buildin driver with AtmoCreateTransferBuffers */
604     BITMAPINFOHEADER mini_image_format;
605     /* is only use buildin driver! */
606     uint8_t *p_atmo_transfer_buffer;
607     /* end buildin driver */
608
609     /*
610     contains the real output size of the video calculated on
611     change event of the variable "crop" from vout
612     */
613     int32_t i_crop_x_offset;
614     int32_t i_crop_y_offset;
615     int32_t i_crop_width;
616     int32_t i_crop_height;
617
618     void (*pf_extract_mini_image) (filter_sys_t *p_sys,
619         picture_t *p_inpic,
620         uint8_t *p_transfer_dest);
621
622 #if defined( WIN32 )
623     /* External Library as wrapper arround COM Stuff */
624     HINSTANCE h_AtmoCtrl;
625     int32_t (*pf_ctrl_atmo_initialize) (void);
626     void (*pf_ctrl_atmo_finalize) (int32_t what);
627     int32_t (*pf_ctrl_atmo_switch_effect) (int32_t);
628     int32_t (*pf_ctrl_atmo_set_live_source) (int32_t);
629     void (*pf_ctrl_atmo_create_transfer_buffers) (int32_t, int32_t,
630                                                   int32_t , int32_t);
631     uint8_t* (*pf_ctrl_atmo_lock_transfer_buffer) (void);
632     void (*pf_ctrl_atmo_send_pixel_data) (void);
633 #endif
634 };
635
636 /*
637 initialize previously configured Atmo Light environment
638 - if internal is enabled try to access the device on the serial port
639 - if not internal is enabled and we are on win32 try to initialize
640 the previously loaded DLL ...
641
642 Return Values may be: -1 (failed for some reason - filter will be disabled)
643 1 Ok. lets rock
644 */
645 static int32_t AtmoInitialize(filter_t *p_filter, bool b_for_thread)
646 {
647     filter_sys_t *p_sys = p_filter->p_sys;
648     if(p_sys->p_atmo_config)
649     {
650         if(b_for_thread == false)
651         {
652             /* open com port */
653             /* setup Output Threads ... */
654             msg_Dbg( p_filter, "open serial connection %s",
655                 p_sys->p_atmo_config->getSerialDevice());
656
657             if(CAtmoTools::RecreateConnection(p_sys->p_atmo_dyndata) == ATMO_TRUE)
658             {
659                 msg_Dbg( p_filter, "start live view thread ...");
660                 CAtmoTools::SwitchEffect(p_sys->p_atmo_dyndata, emLivePicture);
661                 msg_Dbg( p_filter, "live view thread launched...");
662                 return 1;
663
664             } else {
665                 msg_Err( p_filter,"failed to open serial device? some other software/driver may use it?");
666             }
667         }
668 #if defined(WIN32)
669     } else if(p_sys->pf_ctrl_atmo_initialize)
670     {
671         /* on win32 with active ctrl dll */
672         return p_sys->pf_ctrl_atmo_initialize();
673 #endif
674     }
675     return -1;
676 }
677
678 /*
679 prepare the shutdown of the effect threads,
680 for build in filter - close the serialport after finishing the threads...
681 cleanup possible loaded DLL...
682 */
683 static void AtmoFinalize(filter_t *p_filter, int32_t what)
684 {
685     filter_sys_t *p_sys = p_filter->p_sys;
686     if(p_sys->p_atmo_config)
687     {
688         if(what == 1)
689         {
690             CAtmoDynData *p_atmo_dyndata = p_sys->p_atmo_dyndata;
691             if(p_atmo_dyndata)
692             {
693                 p_atmo_dyndata->LockCriticalSection();
694
695                 CThread *p_effect_thread = p_atmo_dyndata->getEffectThread();
696                 p_atmo_dyndata->setEffectThread(NULL);
697                 if(p_effect_thread != NULL)
698                 {
699                     /*
700                     forced the thread to die...
701                     and wait for termination of the thread
702                     */
703                     p_effect_thread->Terminate();
704                     delete p_effect_thread;
705                     msg_Dbg( p_filter, "effect thread died peacefully");
706                 }
707
708                 /*
709                 close serial port if it is open (all OS specific is inside
710                 CAtmoSerialConnection implemented / defined)
711                 */
712                 CAtmoConnection *p_atmo_connection =
713                                  p_atmo_dyndata->getAtmoConnection();
714                 p_atmo_dyndata->setAtmoConnection(NULL);
715                 if(p_atmo_connection) {
716                     p_atmo_connection->CloseConnection();
717                     delete p_atmo_connection;
718                 }
719                 p_atmo_dyndata->UnLockCriticalSection();
720             }
721         }
722 #if defined(WIN32)
723     } else if(p_sys->pf_ctrl_atmo_finalize)
724     {
725         /* on win32 with active ctrl dll */
726         p_sys->pf_ctrl_atmo_finalize(what);
727 #endif
728     }
729 }
730
731 /*
732 switch the current light effect - does only something on win32, with the
733 external  libraries - if the buildin effects are used nothing happens
734 */
735 static int32_t AtmoSwitchEffect(filter_t *p_filter, int32_t newMode)
736 {
737     filter_sys_t *p_sys = p_filter->p_sys;
738     if(p_sys->p_atmo_config)
739     {
740         /*
741         buildin driver
742
743         doesnt know different modes for effects so this
744         function call would just do nothing special
745         in this case
746         */
747
748 #if defined(WIN32)
749     } else if(p_sys->pf_ctrl_atmo_switch_effect)
750     {
751         /* on win32 with active ctrl dll */
752         return p_sys->pf_ctrl_atmo_switch_effect(newMode);
753 #endif
754     }
755     return emDisabled;
756 }
757
758 /*
759 set the current live picture source, does only something on win32,
760 with the external libraries - if the buildin effects are used nothing
761 happens...
762 */
763 static int32_t AtmoSetLiveSource(filter_t *p_filter, int32_t newSource)
764 {
765     filter_sys_t *p_sys = p_filter->p_sys;
766     if(p_sys->p_atmo_config)
767     {
768         /*
769         buildin driver
770
771         doesnt know different sources so this
772         function call would just do nothing special
773         in this case
774         */
775 #if defined(WIN32)
776     } else if(p_sys->pf_ctrl_atmo_set_live_source)
777     {
778         /* on win32 with active ctrl dll */
779         return p_sys->pf_ctrl_atmo_set_live_source(newSource);
780 #endif
781     }
782     return lvsGDI;
783 }
784
785 /*
786 setup the pixel transferbuffers which is used to transfer pixeldata from
787 the filter to the effect thread, and possible accross the process
788 boundaries on win32, with the external DLL
789 */
790 static void AtmoCreateTransferBuffers(filter_t *p_filter,
791                                       int32_t FourCC,
792                                       int32_t bytePerPixel,
793                                       int32_t width,
794                                       int32_t height)
795 {
796     filter_sys_t *p_sys = p_filter->p_sys;
797     if(p_sys->p_atmo_config)
798     {
799         /*
800         we need a buffer where the image is stored (only for transfer
801         to the processing thread)
802         */
803         if(p_sys->p_atmo_transfer_buffer)
804             free(p_sys->p_atmo_transfer_buffer);
805
806         p_sys->p_atmo_transfer_buffer = (uint8_t *)malloc(bytePerPixel *
807                                                           width *  height);
808
809         memset(&p_sys->mini_image_format,0,sizeof(BITMAPINFOHEADER));
810
811         p_sys->mini_image_format.biSize = sizeof(BITMAPINFOHEADER);
812         p_sys->mini_image_format.biWidth = width;
813         p_sys->mini_image_format.biHeight = height;
814         p_sys->mini_image_format.biBitCount = bytePerPixel*8;
815         p_sys->mini_image_format.biCompression = FourCC;
816
817 #if defined(WIN32)
818     } else if(p_sys->pf_ctrl_atmo_create_transfer_buffers)
819     {
820         /* on win32 with active ctrl dll */
821         p_sys->pf_ctrl_atmo_create_transfer_buffers(FourCC,
822             bytePerPixel,
823             width,
824             height);
825 #endif
826     }
827 }
828
829 /*
830 acquire the transfer buffer pointer the buildin version only
831 returns the pointer to the allocated buffer ... the
832 external version on win32 has to do some COM stuff to lock the
833 Variant Byte array which is behind the buffer
834 */
835 static uint8_t* AtmoLockTransferBuffer(filter_t *p_filter)
836 {
837     filter_sys_t *p_sys = p_filter->p_sys;
838     if(p_sys->p_atmo_config)
839     {
840         return p_sys->p_atmo_transfer_buffer;
841 #if defined(WIN32)
842     } else if(p_sys->pf_ctrl_atmo_lock_transfer_buffer)
843     {
844         /* on win32 with active ctrl dll */
845         return p_sys->pf_ctrl_atmo_lock_transfer_buffer();
846 #endif
847     }
848     return NULL;
849 }
850
851 /*
852 send the content of current pixel buffer got with AtmoLockTransferBuffer
853 to the processing threads
854 - build in version - will forward the data to AtmoExternalCaptureInput Thread
855 - win32 external - will do the same, but across the process boundaries via
856 COM to the AtmoWinA.exe Process
857 */
858 static void AtmoSendPixelData(filter_t *p_filter)
859 {
860     filter_sys_t *p_sys = p_filter->p_sys;
861     if(p_sys->p_atmo_config && p_sys->p_atmo_transfer_buffer)
862     {
863         CAtmoDynData *p_atmo_dyndata = p_sys->p_atmo_dyndata;
864         if(p_atmo_dyndata)
865         {
866             /*
867             the cast will go Ok because we are inside videolan there is only
868             this kind of effect thread implemented!
869             */
870
871             CAtmoLiveView *p_atmo_live_view_thread =
872                 (CAtmoLiveView *)p_atmo_dyndata->getEffectThread();
873             if(p_atmo_live_view_thread)
874             {
875                 /*
876                 the same as above inside videolan only this single kind of
877                 input exists so we can cast without further tests!
878                 */
879                 CAtmoExternalCaptureInput *p_atmo_external_capture_input_thread =
880                     (CAtmoExternalCaptureInput *)p_atmo_live_view_thread->getAtmoInput();
881                 if(p_atmo_external_capture_input_thread)
882                 {
883                     /*
884                     this call will do a 1:1 copy of this buffer, and wakeup
885                     the thread from normal sleeping
886                     */
887                     p_atmo_external_capture_input_thread->
888                         DeliverNewSourceDataPaket(&p_sys->mini_image_format,
889                         p_sys->p_atmo_transfer_buffer);
890                 }
891             }
892         }
893 #if defined(WIN32)
894     } else if(p_sys->pf_ctrl_atmo_send_pixel_data)
895     {
896         /* on win32 with active ctrl dll */
897         p_sys->pf_ctrl_atmo_send_pixel_data();
898 #endif
899     }
900 }
901
902 /*
903     Shutdown AtmoLight finally - is call from DestroyFilter
904     does the cleanup restores the effectmode on the external Software
905     (only win32) and possible setup the final light ...
906 */
907 static void Atmo_Shutdown(filter_t *p_filter)
908 {
909     filter_sys_t *p_sys = p_filter->p_sys;
910
911     if(p_sys->b_enabled == true)
912     {
913         /*
914         if there is a still running show pause color thread kill him!
915         */
916         CheckAndStopFadeThread(p_filter);
917
918         if(p_sys->p_atmo_config || (p_sys->i_AtmoOldEffect == emStaticColor))
919         {
920             /*
921             fade to end color (in case of external AtmoWin Software
922             assume that the static color will equal to this
923             one to get a soft change and no flash!
924             */
925             p_sys->b_pause_live = true;
926
927             // perpare spawn fadeing thread
928             vlc_mutex_lock( &p_sys->filter_lock );
929
930             p_sys->p_fadethread = (fadethread_t *)vlc_object_create( p_filter,
931                                                         sizeof(fadethread_t) );
932
933             p_sys->p_fadethread->p_filter = p_filter;
934             p_sys->p_fadethread->ui_red   = p_sys->ui_endcolor_red;
935             p_sys->p_fadethread->ui_green = p_sys->ui_endcolor_green;
936             p_sys->p_fadethread->ui_blue  = p_sys->ui_endcolor_blue;
937             p_sys->p_fadethread->i_steps  = p_sys->i_endfadesteps;
938
939             if( vlc_thread_create( p_sys->p_fadethread,
940                 "AtmoLight fadeing",
941                 FadeToColorThread,
942                 VLC_THREAD_PRIORITY_LOW,
943                 false ) )
944             {
945                 msg_Err( p_filter, "cannot create FadeToColorThread" );
946                 vlc_object_release( p_sys->p_fadethread );
947                 p_sys->p_fadethread = NULL;
948                 vlc_mutex_unlock( &p_sys->filter_lock );
949
950             } else {
951
952                 vlc_mutex_unlock( &p_sys->filter_lock );
953
954                 /* wait for the thread... */
955                 vlc_thread_join(p_sys->p_fadethread);
956
957                 vlc_object_release(p_sys->p_fadethread);
958
959                 p_sys->p_fadethread = NULL;
960             }
961         }
962
963         if(p_sys->i_AtmoOldEffect != emLivePicture)
964             AtmoSwitchEffect(p_filter, p_sys->i_AtmoOldEffect);
965         else
966             AtmoSetLiveSource(p_filter, lvsGDI);
967
968         AtmoFinalize(p_filter, 1);
969
970         /* disable filter method .. */
971         p_sys->b_enabled = false;
972     }
973 }
974
975 /*
976 initialize the filter_sys_t structure with the data from the settings
977 variables - if the external filter on win32 is enabled try loading the DLL,
978 if this fails fallback to the buildin software
979 */
980 static void Atmo_SetupParameters(filter_t *p_filter)
981 {
982     bool b_use_buildin_driver = true;
983     char *psz_path;
984     filter_sys_t *p_sys =  p_filter->p_sys;
985
986
987     /* default filter disabled until DLL loaded and Init Success!*/
988     p_sys->b_enabled             = false;
989
990     /* setup default mini image size (may be later a user option) */
991     p_sys->i_atmo_width          = 64;
992     p_sys->i_atmo_height         = 48;
993
994
995     vlc_mutex_init( &p_sys->filter_lock );
996
997
998 #if defined(WIN32)
999     /*
1000     only on WIN32 the user has the choice between
1001     internal driver and external
1002     */
1003     b_use_buildin_driver = var_CreateGetBoolCommand( p_filter,
1004         CFG_PREFIX "usebuildin" );
1005
1006     if(b_use_buildin_driver == false) {
1007
1008         /* Load the Com Wrapper Library (source available) */
1009         p_sys->h_AtmoCtrl = LoadLibraryA("AtmoCtrlLib.dll");
1010         if(p_sys->h_AtmoCtrl != NULL)
1011         {
1012             msg_Dbg( p_filter, "LoadLibrary('AtmoCtrlLib.dll'); Success");
1013
1014             /* importing all required functions I hope*/
1015             p_sys->pf_ctrl_atmo_initialize =
1016                 (int32_t (*)(void))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoInitialize");
1017             if(!p_sys->pf_ctrl_atmo_initialize)
1018                 msg_Err( p_filter, "export AtmoInitialize missing.");
1019
1020             p_sys->pf_ctrl_atmo_finalize =
1021                 (void (*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoFinalize");
1022             if(!p_sys->pf_ctrl_atmo_finalize)
1023                 msg_Err( p_filter, "export AtmoFinalize missing.");
1024
1025             p_sys->pf_ctrl_atmo_switch_effect =
1026                 (int32_t(*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoSwitchEffect");
1027             if(!p_sys->pf_ctrl_atmo_switch_effect)
1028                 msg_Err( p_filter, "export AtmoSwitchEffect missing.");
1029
1030             p_sys->pf_ctrl_atmo_set_live_source =
1031                 (int32_t(*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoSetLiveSource");
1032             if(!p_sys->pf_ctrl_atmo_set_live_source)
1033                 msg_Err( p_filter, "export AtmoSetLiveSource missing.");
1034
1035             p_sys->pf_ctrl_atmo_create_transfer_buffers =
1036                 (void (*)(int32_t, int32_t, int32_t , int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoCreateTransferBuffers");
1037             if(!p_sys->pf_ctrl_atmo_create_transfer_buffers)
1038                 msg_Err( p_filter, "export AtmoCreateTransferBuffers missing.");
1039
1040             p_sys->pf_ctrl_atmo_lock_transfer_buffer=
1041                 (uint8_t*(*) (void))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoLockTransferBuffer");
1042             if(!p_sys->pf_ctrl_atmo_lock_transfer_buffer)
1043                 msg_Err( p_filter, "export AtmoLockTransferBuffer missing.");
1044
1045             p_sys->pf_ctrl_atmo_send_pixel_data =
1046                 (void (*)(void))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoSendPixelData");
1047             if(!p_sys->pf_ctrl_atmo_send_pixel_data)
1048                 msg_Err( p_filter, "export AtmoSendPixelData missing.");
1049         } else {
1050             /* the DLL is missing try internal filter ...*/
1051             msg_Warn( p_filter, "AtmoCtrlLib.dll missing fallback to internal driver");
1052             b_use_buildin_driver = true;
1053         }
1054     }
1055 #endif
1056
1057
1058     if(b_use_buildin_driver == true) {
1059         msg_Dbg( p_filter, "use buildin driver");
1060         /*
1061         now we have to read a lof of options from the config dialog
1062         most important the serial device if not set ... we can skip
1063         the rest and disable the filter...
1064         */
1065         char *psz_serialdev = var_CreateGetStringCommand( p_filter,
1066                                                       CFG_PREFIX "serialdev" );
1067         if(psz_serialdev && (strlen(psz_serialdev)>0)) {
1068             msg_Dbg( p_filter, "use buildin driver on port %s",psz_serialdev);
1069
1070             p_sys->p_atmo_config = new CAtmoConfig();
1071
1072             p_sys->p_atmo_config->setSerialDevice(psz_serialdev);
1073
1074             p_sys->p_atmo_config->setLiveViewFilterMode(
1075                 (AtmoFilterMode)var_CreateGetIntegerCommand( p_filter,
1076                                                        CFG_PREFIX "filtermode")
1077                 );
1078
1079             p_sys->p_atmo_config->setLiveViewFilter_PercentNew(
1080                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "PercentNew")
1081                 );
1082             p_sys->p_atmo_config->setLiveViewFilter_MeanLength(
1083                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "MeanLength")
1084                 );
1085             p_sys->p_atmo_config->setLiveViewFilter_MeanThreshold(
1086                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "MeanThreshold")
1087                 );
1088
1089             p_sys->p_atmo_config->setLiveView_EdgeWeighting(
1090                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "EdgeWeightning")
1091                 );
1092             p_sys->p_atmo_config->setLiveView_BrightCorrect(
1093                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "Brightness")
1094                 );
1095             p_sys->p_atmo_config->setLiveView_DarknessLimit(
1096                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "DarknessLimit")
1097                 );
1098             p_sys->p_atmo_config->setLiveView_HueWinSize(
1099                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "HueWinSize")
1100                 );
1101             p_sys->p_atmo_config->setLiveView_SatWinSize(
1102                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "SatWinSize")
1103                 );
1104
1105             /* currently not required inside vlc */
1106             p_sys->p_atmo_config->setLiveView_WidescreenMode( 0 );
1107
1108             p_sys->p_atmo_config->setLiveView_FrameDelay(
1109                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "FrameDelay")
1110                 );
1111
1112
1113             p_sys->p_atmo_config->setUseSoftwareWhiteAdj(
1114                 var_CreateGetBoolCommand( p_filter, CFG_PREFIX "whiteadj")
1115                 );
1116             p_sys->p_atmo_config->setWhiteAdjustment_Red(
1117                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-red")
1118                 );
1119             p_sys->p_atmo_config->setWhiteAdjustment_Green(
1120                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-green")
1121                 );
1122             p_sys->p_atmo_config->setWhiteAdjustment_Blue(
1123                 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-blue")
1124                 );
1125
1126             tChannelAssignment *p_channel_assignment =
1127                                  p_sys->p_atmo_config->getChannelAssignment(0);
1128
1129             p_channel_assignment->mappings[0] = var_CreateGetIntegerCommand(
1130                                              p_filter, CFG_PREFIX "channel_0");
1131
1132             p_channel_assignment->mappings[1] = var_CreateGetIntegerCommand(
1133                                              p_filter, CFG_PREFIX "channel_1");
1134
1135             p_channel_assignment->mappings[2] = var_CreateGetIntegerCommand(
1136                                              p_filter, CFG_PREFIX "channel_2");
1137
1138             p_channel_assignment->mappings[3] = var_CreateGetIntegerCommand(
1139                                              p_filter, CFG_PREFIX "channel_3");
1140
1141             p_channel_assignment->mappings[4] = var_CreateGetIntegerCommand(
1142                                              p_filter, CFG_PREFIX "channel_4");
1143
1144             for(int i=0;i<ATMO_NUM_CHANNELS;i++)
1145                 msg_Dbg( p_filter, "map software channel %d to hardware channel %d",
1146                 p_channel_assignment->mappings[i],
1147                 i
1148                 );
1149
1150             // gradient_zone_0
1151             char psz_gradient_var_name[30];
1152             char *psz_gradient_file;
1153             for(int i=0;i<ATMO_NUM_CHANNELS;i++)
1154             {
1155                 sprintf(psz_gradient_var_name, CFG_PREFIX "gradient_zone_%d", i);
1156                 psz_gradient_file = var_CreateGetStringCommand(
1157                     p_filter,
1158                     psz_gradient_var_name
1159                     );
1160                 if(psz_gradient_file && strlen(psz_gradient_file)>0)
1161                 {
1162                     msg_Dbg( p_filter, "loading gradientfile %s for "\
1163                                        "zone %d", psz_gradient_file, i);
1164
1165                     int i_res = p_sys->p_atmo_config->getZoneDefinition(i)->
1166                                 LoadGradientFromBitmap(psz_gradient_file);
1167
1168                     if(i_res != ATMO_LOAD_GRADIENT_OK)
1169                     {
1170                         msg_Err( p_filter,"failed to load gradient '%s' with "\
1171                                           "error %d",psz_gradient_file,i_res);
1172                     }
1173                 }
1174                 delete psz_gradient_file;
1175             }
1176
1177             p_sys->p_atmo_dyndata = new CAtmoDynData((vlc_object_t *)p_filter,
1178                 p_sys->p_atmo_config
1179                 );
1180
1181             msg_Dbg( p_filter, "buildin driver initialized");
1182
1183             free(psz_serialdev);
1184         } else {
1185             msg_Err(p_filter,"no serial devicename set");
1186         }
1187     }
1188
1189     switch( p_filter->fmt_in.video.i_chroma )
1190     {
1191     case VLC_FOURCC('I','4','2','0'):
1192     case VLC_FOURCC('I','Y','U','V'):
1193     case VLC_FOURCC('Y','V','1','2'):
1194     case VLC_FOURCC('Y','V','1','6'):
1195     case VLC_FOURCC('Y','V','U','9'):
1196         // simple enough? Dionoea?
1197         p_sys->pf_extract_mini_image = ExtractMiniImage_YUV;
1198         break;
1199     default:
1200         msg_Dbg( p_filter, "InitFilter-unsupported chroma: %4.4s",
1201                             (char *)&p_filter->fmt_in.video.i_chroma);
1202         p_sys->pf_extract_mini_image = NULL;
1203     }
1204
1205     p_sys->i_crop_x_offset  = 0;
1206     p_sys->i_crop_y_offset  = 0;
1207     p_sys->i_crop_width     = p_filter->fmt_in.video.i_visible_width;
1208     p_sys->i_crop_height    = p_filter->fmt_in.video.i_visible_height;
1209
1210     msg_Dbg( p_filter, "set default crop %d,%d %dx%d",p_sys->i_crop_x_offset,
1211         p_sys->i_crop_y_offset,
1212         p_sys->i_crop_width,
1213         p_sys->i_crop_height );
1214
1215
1216 #if defined(__ATMO_DEBUG__)
1217     /* save debug images to a folder as Bitmap files ? */
1218     p_sys->b_saveframes  = var_CreateGetBoolCommand( p_filter,
1219         CFG_PREFIX "saveframes"
1220         );
1221     msg_Dbg(p_filter,"saveframes = %d", (int)p_sys->b_saveframes);
1222
1223     /*
1224     read debug image folder from config
1225     */
1226     psz_path = var_CreateGetStringCommand( p_filter, CFG_PREFIX "framepath" );
1227     if(psz_path != NULL)
1228     {
1229         strcpy(p_sys->sz_framepath, psz_path);
1230 #if defined( WIN32 )
1231         size_t i_strlen = strlen(p_sys->sz_framepath);
1232         if((i_strlen>0) && (p_sys->sz_framepath[i_strlen-1] != '\\'))
1233         {
1234             p_sys->sz_framepath[i_strlen] = '\\';
1235             p_sys->sz_framepath[i_strlen+1] = 0;
1236         }
1237 #endif
1238         free(psz_path);
1239     }
1240     msg_Dbg(p_filter,"saveframesfolder %s",p_sys->sz_framepath);
1241 #endif
1242
1243     /*
1244        size of extracted image by default 64x48 (other imagesizes are
1245        currently ignored by AtmoWin)
1246     */
1247     p_sys->i_atmo_width  = var_CreateGetIntegerCommand( p_filter,
1248         CFG_PREFIX "width");
1249     p_sys->i_atmo_height = var_CreateGetIntegerCommand( p_filter,
1250         CFG_PREFIX "height");
1251     msg_Dbg(p_filter,"mini image size %d * %d pixels", p_sys->i_atmo_width,
1252         p_sys->i_atmo_height);
1253
1254     /*
1255     because atmowin could also be used for lighten up the room - I think if you
1256     pause the video it would be useful to get a little bit more light into to
1257     your living room? - instead switching on a lamp?
1258     */
1259     p_sys->b_usepausecolor = var_CreateGetBoolCommand( p_filter,
1260         CFG_PREFIX "usepausecolor" );
1261     p_sys->ui_pausecolor_red = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1262         CFG_PREFIX "pcolor-red");
1263     p_sys->ui_pausecolor_green = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1264         CFG_PREFIX "pcolor-green");
1265     p_sys->ui_pausecolor_blue = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1266         CFG_PREFIX "pcolor-blue");
1267     p_sys->i_fadesteps = var_CreateGetIntegerCommand( p_filter,
1268         CFG_PREFIX "fadesteps");
1269     if(p_sys->i_fadesteps < 1)
1270         p_sys->i_fadesteps = 1;
1271     msg_Dbg(p_filter,"use pause color %d, RGB: %d, %d, %d, Fadesteps: %d",
1272         (int)p_sys->b_usepausecolor,
1273         p_sys->ui_pausecolor_red,
1274         p_sys->ui_pausecolor_green,
1275         p_sys->ui_pausecolor_blue,
1276         p_sys->i_fadesteps);
1277
1278     /*
1279     this color is use on shutdown of the filter - the define the
1280     final light after playback... may be used to dim up the light -
1281     how it happens in the cinema...
1282     */
1283     p_sys->ui_endcolor_red = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1284         CFG_PREFIX "ecolor-red");
1285     p_sys->ui_endcolor_green = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1286         CFG_PREFIX "ecolor-green");
1287     p_sys->ui_endcolor_blue = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1288         CFG_PREFIX "ecolor-blue");
1289     p_sys->i_endfadesteps = var_CreateGetIntegerCommand( p_filter,
1290         CFG_PREFIX "efadesteps");
1291     if(p_sys->i_endfadesteps < 1)
1292         p_sys->i_endfadesteps = 1;
1293     msg_Dbg(p_filter,"use ende color RGB: %d, %d, %d, Fadesteps: %d",
1294         p_sys->ui_endcolor_red,
1295         p_sys->ui_endcolor_green,
1296         p_sys->ui_endcolor_blue,
1297         p_sys->i_endfadesteps);
1298
1299     /* if the external DLL was loaded successfully call AtmoInitialize -
1300     (must be done for each thread where you wan't to use AtmoLight!
1301     */
1302     int i = AtmoInitialize(p_filter, false);
1303 #if defined( WIN32 )
1304     if((i != 1) && !b_use_buildin_driver)
1305     {
1306         /* COM Server for AtmoLight not running ?
1307         if the exe path is configured try to start the "userspace" driver
1308         */
1309         psz_path = var_CreateGetStringCommand( p_filter,
1310                                                CFG_PREFIX "atmowinexe" );
1311         if(psz_path != NULL)
1312         {
1313             STARTUPINFO startupinfo;
1314             PROCESS_INFORMATION pinfo;
1315             memset(&startupinfo, 0, sizeof(STARTUPINFO));
1316             startupinfo.cb = sizeof(STARTUPINFO);
1317             if(CreateProcess(psz_path, NULL, NULL, NULL,
1318                 FALSE, 0, NULL, NULL, &startupinfo, &pinfo) == TRUE)
1319             {
1320                 msg_Dbg(p_filter,"launched AtmoWin from %s",psz_path);
1321                 WaitForInputIdle(pinfo.hProcess, 5000);
1322                 /*
1323                 retry to initialize the library COM ... functionality
1324                 after the server was launched
1325                 */
1326                 i = AtmoInitialize(p_filter, false);
1327             } else {
1328                 msg_Err(p_filter,"failed to launch AtmoWin from %s", psz_path);
1329             }
1330             free(psz_path);
1331         }
1332     }
1333 #endif
1334
1335     if(i == 1) /* Init Atmolight success... */
1336     {
1337         msg_Dbg( p_filter, "AtmoInitialize Ok!");
1338
1339         /* Setup Transferbuffers for 64 x 48 , RGB with 32bit Per Pixel */
1340         AtmoCreateTransferBuffers(p_filter, BI_RGB, 4,
1341             p_sys->i_atmo_width,
1342             p_sys->i_atmo_height
1343             );
1344
1345         /* say the userspace driver that a live mode should be activated
1346         the functions returns the old mode for later restore!
1347         */
1348         p_sys->i_AtmoOldEffect = AtmoSwitchEffect(p_filter, emLivePicture);
1349
1350         /*
1351         live view can have two differnt source the AtmoWinA
1352         internal GDI Screencapture and the external one - which we
1353         need here...
1354         */
1355         AtmoSetLiveSource(p_filter, lvsExternal);
1356
1357         /* enable other parts only if everything is fine */
1358         p_sys->b_enabled = true;
1359     }
1360
1361 }
1362
1363
1364 /*****************************************************************************
1365 * CreateFilter: allocates AtmoLight video thread output method
1366 *****************************************************************************
1367 * This function allocates and initializes a AtmoLight vout method.
1368 *****************************************************************************/
1369 static int CreateFilter( vlc_object_t *p_this )
1370 {
1371     filter_t *p_filter = (filter_t *)p_this;
1372     filter_sys_t *p_sys;
1373
1374     /* Allocate structure */
1375     p_sys = (filter_sys_t *)malloc( sizeof( filter_sys_t ) );
1376     p_filter->p_sys = p_sys;
1377     if( p_filter->p_sys == NULL )
1378     {
1379         msg_Err( p_filter, "out of memory for p_sys structure" );
1380         return VLC_ENOMEM;
1381     }
1382     /* set all entries to zero */
1383     memset(p_sys, 0, sizeof( filter_sys_t ));
1384
1385     /* further Setup Function pointers for videolan for calling my filter */
1386     p_filter->pf_video_filter = Filter;
1387
1388     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
1389                        p_filter->p_cfg );
1390
1391     AddStateVariableCallback(p_filter);
1392
1393     AddCropVariableCallback(p_filter);
1394
1395     AddAtmoSettingsVariablesCallbacks(p_filter);
1396
1397     Atmo_SetupParameters(p_filter);
1398
1399
1400     return VLC_SUCCESS;
1401 }
1402
1403
1404
1405 /*****************************************************************************
1406 * DestroyFilter: destroy AtmoLight video thread output method
1407 *****************************************************************************
1408 * Terminate an output method created by CreateFilter
1409 *****************************************************************************/
1410
1411 static void DestroyFilter( vlc_object_t *p_this )
1412 {
1413     filter_t *p_filter = (filter_t *)p_this;
1414     filter_sys_t *p_sys =  p_filter->p_sys;
1415
1416     DelStateVariableCallback(p_filter);
1417     DelCropVariableCallback(p_filter);
1418     DelAtmoSettingsVariablesCallbacks(p_filter);
1419
1420     Atmo_Shutdown(p_filter);
1421
1422 #if defined( WIN32 )
1423     if(p_sys->h_AtmoCtrl != NULL)
1424     {
1425         FreeLibrary(p_sys->h_AtmoCtrl);
1426     }
1427 #endif
1428
1429     delete p_sys->p_atmo_dyndata;
1430     delete p_sys->p_atmo_config;
1431
1432     vlc_mutex_destroy( &p_sys->filter_lock );
1433
1434     free( p_sys );
1435 }
1436
1437
1438 /*
1439 function stolen from some other videolan source filter ;-)
1440 for the moment RGB is OK... but better would be a direct transformation
1441 from YUV --> HSV
1442 */
1443 static inline void yuv_to_rgb( uint8_t *r, uint8_t *g, uint8_t *b,
1444                               uint8_t y1, uint8_t u1, uint8_t v1 )
1445 {
1446     /* macros used for YUV pixel conversions */
1447 #   define SCALEBITS 10
1448 #   define ONE_HALF  (1 << (SCALEBITS - 1))
1449 #   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
1450 #   define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));
1451
1452     int y, cb, cr, r_add, g_add, b_add;
1453
1454     cb = u1 - 128;
1455     cr = v1 - 128;
1456     r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
1457     g_add = - FIX(0.34414*255.0/224.0) * cb
1458         - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
1459     b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
1460     y = (y1 - 16) * FIX(255.0/219.0);
1461     *r = CLAMP((y + r_add) >> SCALEBITS);
1462     *g = CLAMP((y + g_add) >> SCALEBITS);
1463     *b = CLAMP((y + b_add) >> SCALEBITS);
1464 }
1465 /******************************************************************************
1466 * ExtractMiniImage_YUV: extract a small image from the picture as 24-bit RGB
1467 *******************************************************************************
1468 * p_sys is a pointer to
1469 * p_inpic is the source frame
1470 * p_transfer_dest is the target buffer for the picture must be big enough!
1471 * (in win32 enviroment this buffer comes from the external DLL where it is
1472 * create as "variant array" and returned through the AtmoLockTransferbuffer
1473 */
1474 static void ExtractMiniImage_YUV(filter_sys_t *p_sys,
1475                                  picture_t *p_inpic,
1476                                  uint8_t *p_transfer_dest)
1477 {
1478     int i_col;
1479     int i_row;
1480     uint8_t *p_src_y;
1481     uint8_t *p_src_u;
1482     uint8_t *p_src_v;
1483     uint8_t *p_rgb_dst_line_red;
1484     uint8_t *p_rgb_dst_line_green;
1485     uint8_t *p_rgb_dst_line_blue;
1486     int i_xpos_y;
1487     int i_xpos_u;
1488     int i_xpos_v;
1489
1490     /* calcute Pointers for Storage of B G R (A) */
1491     p_rgb_dst_line_blue      = p_transfer_dest;
1492     p_rgb_dst_line_green     = p_transfer_dest + 1;
1493     p_rgb_dst_line_red       = p_transfer_dest + 2 ;
1494
1495     int i_row_count = p_sys->i_atmo_height + 1;
1496     int i_col_count = p_sys->i_atmo_width + 1;
1497     int i_y_row,i_u_row,i_v_row,i_pixel_row;
1498     int i_pixel_col;
1499
1500
1501     /*  these two ugly loops extract the small image - goes it faster? how?
1502     the loops are so designed that there is a small border around the extracted
1503     image so we wont get column and row - zero from the frame, and not the most
1504     right and bottom pixels --- which may be clipped on computers useing TV out
1505     - through overscan!
1506
1507     TODO: try to find out if the output is clipped through VLC - and try here
1508     to ingore the clipped away area for a better result!
1509
1510     TODO: performance improvement in InitFilter percalculated the offsets of
1511     the lines inside the planes so I can save (i_row_count * 3) 2xMUL and
1512     one time DIV the same could be done for the inner loop I think...
1513     */
1514     for(i_row = 1; i_row < i_row_count; i_row++)
1515     {
1516         // calcute the current Lines in the source planes for this outputrow
1517         /*  Adresscalcuation  pointer to plane  Length of one pixelrow in bytes
1518         calculate row now number
1519         */
1520         /*
1521            p_inpic->format? transform Pixel row into row of plane...
1522            how? simple? fast? good?
1523         */
1524
1525         /* compute the source pixel row and respect the active cropping */
1526         i_pixel_row = (i_row * p_sys->i_crop_height) / i_row_count
1527             + p_sys->i_crop_y_offset;
1528
1529         /*
1530         trans for these Pixel row into the row of each plane ..
1531         because planesize can differ from image size
1532         */
1533         i_y_row = (i_pixel_row * p_inpic->p[Y_PLANE].i_visible_lines) /
1534             p_inpic->format.i_visible_height;
1535
1536         i_u_row = (i_pixel_row * p_inpic->p[U_PLANE].i_visible_lines) /
1537             p_inpic->format.i_visible_height;
1538
1539         i_v_row = (i_pixel_row * p_inpic->p[V_PLANE].i_visible_lines) /
1540             p_inpic->format.i_visible_height;
1541
1542         /* calculate  the pointers to the pixeldata for this row
1543            in each plane
1544         */
1545         p_src_y = p_inpic->p[Y_PLANE].p_pixels +
1546             p_inpic->p[Y_PLANE].i_pitch * i_y_row;
1547         p_src_u = p_inpic->p[U_PLANE].p_pixels +
1548             p_inpic->p[U_PLANE].i_pitch * i_u_row;
1549         p_src_v = p_inpic->p[V_PLANE].p_pixels +
1550             p_inpic->p[V_PLANE].i_pitch * i_v_row;
1551
1552         for(i_col = 1; i_col < i_col_count; i_col++)
1553         {
1554             i_pixel_col = (i_col * p_sys->i_crop_width) / i_col_count +
1555                 p_sys->i_crop_x_offset;
1556             /*
1557             trans for these Pixel row into the row of each plane ..
1558             because planesize can differ from image size
1559             */
1560             i_xpos_y = (i_pixel_col * p_inpic->p[Y_PLANE].i_visible_pitch) /
1561                 p_inpic->format.i_visible_width;
1562             i_xpos_u = (i_pixel_col * p_inpic->p[U_PLANE].i_visible_pitch) /
1563                 p_inpic->format.i_visible_width;
1564             i_xpos_v = (i_pixel_col * p_inpic->p[V_PLANE].i_visible_pitch) /
1565                 p_inpic->format.i_visible_width;
1566
1567             yuv_to_rgb(p_rgb_dst_line_red,
1568                 p_rgb_dst_line_green,
1569                 p_rgb_dst_line_blue,
1570
1571                 p_src_y[i_xpos_y],
1572                 p_src_u[i_xpos_u],
1573                 p_src_v[i_xpos_v]);
1574
1575             /* +4 because output image should be RGB32 with dword alignment! */
1576             p_rgb_dst_line_red   += 4;
1577             p_rgb_dst_line_green += 4;
1578             p_rgb_dst_line_blue  += 4;
1579         }
1580     }
1581 }
1582
1583
1584 /******************************************************************************
1585 * SaveBitmap: Saves the content of a transferbuffer as Bitmap to disk
1586 *******************************************************************************
1587 * just for debugging
1588 * p_sys -> configuration if Atmo from there the function will get height and
1589 *          width
1590 * p_pixels -> should be the dword aligned BGR(A) image data
1591 * psz_filename -> filename where to store
1592 */
1593 #if defined(__ATMO_DEBUG__)
1594 void SaveBitmap(filter_sys_t *p_sys, uint8_t *p_pixels, char *psz_filename)
1595 {
1596     /* for debug out only used*/
1597     BITMAPINFO bmp_info;
1598     BITMAPFILEHEADER  bmp_fileheader;
1599     FILE *fp_bitmap;
1600
1601     memset(&bmp_info, 0, sizeof(BITMAPINFO));
1602     bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1603     bmp_info.bmiHeader.biSizeImage   = p_sys->i_atmo_height *
1604                                        p_sys->i_atmo_width * 4;
1605     bmp_info.bmiHeader.biCompression = BI_RGB;
1606     bmp_info.bmiHeader.biWidth        = p_sys->i_atmo_width;
1607     bmp_info.bmiHeader.biHeight       = -p_sys->i_atmo_height;
1608     bmp_info.bmiHeader.biBitCount     = 32;
1609     bmp_info.bmiHeader.biPlanes       = 1;
1610
1611     bmp_fileheader.bfReserved1 = 0;
1612     bmp_fileheader.bfReserved2 = 0;
1613     bmp_fileheader.bfSize = sizeof(BITMAPFILEHEADER) +
1614                             sizeof(BITMAPINFOHEADER) +
1615                             bmp_info.bmiHeader.biSizeImage;
1616     bmp_fileheader.bfType = VLC_TWOCC('M','B');
1617     bmp_fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) +
1618                                sizeof(BITMAPINFOHEADER);
1619
1620     fp_bitmap = fopen(psz_filename,"wb");
1621     if( fp_bitmap != NULL)
1622     {
1623         fwrite(&bmp_fileheader, sizeof(BITMAPFILEHEADER), 1, fp_bitmap);
1624         fwrite(&bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp_bitmap);
1625         fwrite(p_pixels, bmp_info.bmiHeader.biSizeImage, 1, fp_bitmap);
1626         fclose(fp_bitmap);
1627     }
1628 }
1629 #endif
1630
1631
1632 /****************************************************************************
1633 * CreateMiniImage: extracts a 64x48 pixel image from the frame
1634 * (there is a small border arround thats why the loops starts with one
1635 * instead zero) without any interpolation
1636 *****************************************************************************/
1637 static void CreateMiniImage( filter_t *p_filter, picture_t *p_inpic)
1638 {
1639     filter_sys_t *p_sys = p_filter->p_sys;
1640     /*
1641     pointer to RGB Buffer created in external libary as safe array which
1642     is locked inside AtmoLockTransferBuffer
1643     */
1644     uint8_t *p_transfer = NULL;
1645 #if defined( __ATMO_DEBUG__ )
1646     /* for debug out only used*/
1647     char sz_filename[MAX_PATH];
1648 #endif
1649
1650     /*
1651     Lock the before created VarArray (AtmoCreateTransferBuffers)
1652     inside my wrapper library and give me a pointer to the buffer!
1653     below linux a global buffer may be used and protected with a mutex?
1654     */
1655     p_transfer = AtmoLockTransferBuffer(p_filter);
1656     if(p_transfer == NULL)
1657     {
1658         msg_Err( p_filter, "AtmoLight no transferbuffer available. "\
1659                            "AtmoLight will be disabled!");
1660         p_sys->b_enabled = false;
1661         return;
1662     }
1663
1664     /*
1665     do the call via pointer to function instead of having a
1666     case structure here
1667     */
1668     p_sys->pf_extract_mini_image(p_sys, p_inpic, p_transfer);
1669
1670
1671 #if defined( __ATMO_DEBUG__ )
1672     /*
1673     if debugging enabled save every 128th image to disk
1674     */
1675     if((p_sys->b_saveframes == true) && (p_sys->sz_framepath[0] != 0 ))
1676     {
1677
1678         if((p_sys->i_framecounter & 127) == 0)
1679         {
1680             sprintf(sz_filename,"%satmo_dbg_%06d.bmp",p_sys->sz_framepath,
1681                 p_sys->i_framecounter);
1682             msg_Dbg(p_filter, "SaveFrame %s",sz_filename);
1683
1684             SaveBitmap(p_sys, p_transfer, sz_filename);
1685         }
1686         p_sys->i_framecounter++;
1687     }
1688 #endif
1689
1690     /* show the colors on the wall */
1691     AtmoSendPixelData(p_filter);
1692 }
1693
1694
1695
1696
1697 /*****************************************************************************
1698 * Filter: calls the extract method and forwards the incomming picture 1:1
1699 *****************************************************************************
1700 *
1701 *****************************************************************************/
1702
1703 static picture_t * Filter( filter_t *p_filter, picture_t *p_pic )
1704 {
1705     filter_sys_t *p_sys = p_filter->p_sys;
1706     if( !p_pic ) return NULL;
1707
1708     if((p_sys->b_enabled == true) &&
1709         (p_sys->pf_extract_mini_image != NULL) &&
1710         (p_sys->b_pause_live == false))
1711     {
1712         CreateMiniImage(p_filter, p_pic);
1713     }
1714
1715     return p_pic;
1716 }
1717
1718
1719 /*****************************************************************************
1720 * FadeToColorThread: Threadmethod which changes slowly the color
1721 * to a target color defined in p_fadethread struct
1722 * use for: Fade to Pause Color,  and Fade to End Color
1723 *****************************************************************************/
1724 static void FadeToColorThread(fadethread_t *p_fadethread)
1725 {
1726     filter_sys_t *p_sys = (filter_sys_t *)p_fadethread->p_filter->p_sys;
1727     int i_steps_done = 0;
1728     int i_index;
1729     int i_pause_red;
1730     int i_pause_green;
1731     int i_pause_blue;
1732
1733     int i_src_red;
1734     int i_src_green;
1735     int i_src_blue;
1736
1737     vlc_thread_ready( p_fadethread );
1738
1739     uint8_t *p_source = NULL;
1740
1741     /* initialize AtmoWin for this thread! */
1742     AtmoInitialize(p_fadethread->p_filter , true);
1743
1744     uint8_t *p_transfer = AtmoLockTransferBuffer( p_fadethread->p_filter );
1745     if(p_transfer != NULL) {
1746         /* safe colors as "32bit" Integers to avoid overflows*/
1747         i_pause_red   = p_fadethread->ui_red;
1748         i_pause_blue  = p_fadethread->ui_blue;
1749         i_pause_green = p_fadethread->ui_green;
1750
1751         /*
1752         allocate a temporary buffer for the last send
1753         image size less then 15kb
1754         */
1755         int i_size = 4 * p_sys->i_atmo_width * p_sys->i_atmo_height;
1756         p_source = (uint8_t *)malloc( i_size );
1757         if(p_source != NULL)
1758         {
1759             /*
1760             get a copy of the last transfered image as orign for the
1761             fading steps...
1762             */
1763             memcpy(p_source, p_transfer, i_size);
1764             /* send the same pixel data again... to unlock the buffer! */
1765             AtmoSendPixelData( p_fadethread->p_filter );
1766
1767             while( (!p_fadethread->b_die) &&
1768                 (i_steps_done < p_fadethread->i_steps))
1769             {
1770                 p_transfer = AtmoLockTransferBuffer( p_fadethread->p_filter );
1771                 if(!p_transfer) break; /* should not happen if it worked
1772                                        one time in the code above! */
1773                 i_steps_done++;
1774                 /*
1775                 move all pixels in the mini image (64x48) one step closer to
1776                 the desired color these loop takes the most time of this
1777                 thread improvements wellcome!
1778                 */
1779                 for(i_index = 0;
1780                     (i_index < i_size) && (!p_fadethread->b_die);
1781                     i_index+=4)
1782                 {
1783                     i_src_blue  = p_source[i_index+0];
1784                     i_src_green = p_source[i_index+1];
1785                     i_src_red   = p_source[i_index+2];
1786                     p_transfer[i_index+0] = (uint8_t) (((
1787                         (i_pause_blue  - i_src_blue)
1788                         * i_steps_done)/p_fadethread->i_steps)
1789                         + i_src_blue);
1790
1791                     p_transfer[i_index+1] = (uint8_t) (((
1792                         (i_pause_green - i_src_green)
1793                         * i_steps_done)/p_fadethread->i_steps)
1794                         + i_src_green);
1795
1796                     p_transfer[i_index+2] = (uint8_t) (((
1797                         (i_pause_red   - i_src_red)
1798                         * i_steps_done)/p_fadethread->i_steps)
1799                         + i_src_red);
1800                 }
1801
1802                 /* send image to lightcontroller */
1803                 AtmoSendPixelData( p_fadethread->p_filter );
1804                 /* is there something like and interruptable sleep inside
1805                 the VLC libaries? inside native win32 I would use an Event
1806                 (CreateEvent) and here an WaitForSingleObject?
1807                 */
1808                 if(p_fadethread->b_die) break;
1809                 msleep(10000);
1810                 if(p_fadethread->b_die) break;
1811                 msleep(10000);
1812                 if(p_fadethread->b_die) break;
1813                 msleep(10000);
1814                 if(p_fadethread->b_die) break;
1815                 msleep(10000);
1816             }
1817             free(p_source);
1818         } else {
1819             /* in failure of malloc also unlock buffer  */
1820             AtmoSendPixelData(p_fadethread->p_filter);
1821         }
1822     }
1823     /* call indirect to OleUnitialize() for this thread */
1824     AtmoFinalize(p_fadethread->p_filter, 0);
1825 }
1826
1827 /*****************************************************************************
1828 * CheckAndStopFadeThread: if there is a fadethread structure left, or running.
1829 ******************************************************************************
1830 * this function will stop the thread ... and waits for its termination
1831 * before removeing the objects from vout_sys_t ...
1832 ******************************************************************************/
1833 static void CheckAndStopFadeThread(filter_t *p_filter)
1834 {
1835     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
1836     vlc_mutex_lock( &p_sys->filter_lock );
1837     if(p_sys->p_fadethread != NULL)
1838     {
1839         msg_Dbg(p_filter, "kill still running fadeing thread...");
1840
1841         p_sys->p_fadethread->b_die = true;
1842
1843         vlc_thread_join(p_sys->p_fadethread);
1844
1845         vlc_object_release(p_sys->p_fadethread);
1846         p_sys->p_fadethread = NULL;
1847     }
1848     vlc_mutex_unlock( &p_sys->filter_lock );
1849 }
1850
1851 /*****************************************************************************
1852 * StateCallback: Callback for the inputs variable "State" to get notified
1853 * about Pause and Continue Playback events.
1854 *****************************************************************************/
1855 static int StateCallback( vlc_object_t *p_this, char const *psz_cmd,
1856                          vlc_value_t oldval, vlc_value_t newval,
1857                          void *p_data )
1858 {
1859     filter_t *p_filter = (filter_t *)p_data;
1860     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
1861
1862     if((p_sys->b_usepausecolor == true) && (p_sys->b_enabled == true))
1863     {
1864         msg_Dbg(p_filter, "state change from: %d to %d", oldval.i_int,
1865             newval.i_int);
1866
1867         if((newval.i_int == PAUSE_S) && (oldval.i_int == PLAYING_S))
1868         {
1869             /* tell the other thread to stop sending images to light
1870                controller */
1871             p_sys->b_pause_live = true;
1872
1873             // ggf. alten Thread abräumen should not happen....
1874             CheckAndStopFadeThread(p_filter);
1875
1876             // perpare spawn fadeing thread
1877             vlc_mutex_lock( &p_sys->filter_lock );
1878             /*
1879             launch only a new thread if there is none active!
1880             or waiting for cleanup
1881             */
1882             if(p_sys->p_fadethread == NULL)
1883             {
1884                 p_sys->p_fadethread = (fadethread_t *)vlc_object_create(
1885                      p_filter,
1886                      sizeof(fadethread_t) );
1887
1888                 p_sys->p_fadethread->p_filter = p_filter;
1889                 p_sys->p_fadethread->ui_red   = p_sys->ui_pausecolor_red;
1890                 p_sys->p_fadethread->ui_green = p_sys->ui_pausecolor_green;
1891                 p_sys->p_fadethread->ui_blue  = p_sys->ui_pausecolor_blue;
1892                 p_sys->p_fadethread->i_steps  = p_sys->i_fadesteps;
1893
1894                 if( vlc_thread_create( p_sys->p_fadethread,
1895                     "AtmoLight fadeing",
1896                     FadeToColorThread,
1897                     VLC_THREAD_PRIORITY_LOW,
1898                     false) )
1899                 {
1900                     msg_Err( p_filter, "cannot create FadeToColorThread" );
1901                     vlc_object_release( p_sys->p_fadethread );
1902                     p_sys->p_fadethread = NULL;
1903                 }
1904             }
1905             vlc_mutex_unlock( &p_sys->filter_lock );
1906         }
1907
1908         if((newval.i_int == PLAYING_S) && (oldval.i_int == PAUSE_S))
1909         {
1910             /* playback continues check thread state */
1911             CheckAndStopFadeThread(p_filter);
1912             /* reactivate the Render function... to do its normal work */
1913             p_sys->b_pause_live = false;
1914         }
1915     }
1916
1917     return VLC_SUCCESS;
1918 }
1919
1920 /*****************************************************************************
1921 * AddPlaylistInputThreadStateCallback: Setup call back on "State" Variable
1922 *****************************************************************************
1923 * Add Callback function to the "state" variable of the input thread..
1924 * first find the PlayList and get the input thread from there to attach
1925 * my callback? is vlc_object_find the right way for this??
1926 *****************************************************************************/
1927 static void AddStateVariableCallback(filter_t *p_filter)
1928 {
1929     playlist_t *p_playlist = pl_Yield( p_filter );
1930     input_thread_t *p_input = p_playlist->p_input;
1931     if(p_input)
1932     {
1933         var_AddCallback( p_input, "state", StateCallback, p_filter );
1934     }
1935     vlc_object_release( p_playlist );
1936 }
1937
1938 /*****************************************************************************
1939 * DelPlaylistInputThreadStateCallback: Remove call back on "State" Variable
1940 *****************************************************************************
1941 * Delete the callback function to the "state" variable of the input thread...
1942 * first find the PlayList and get the input thread from there to attach
1943 * my callback? is vlc_object_find the right way for this??
1944 *****************************************************************************/
1945 static void DelStateVariableCallback( filter_t *p_filter )
1946 {
1947     playlist_t *p_playlist = pl_Yield( p_filter );
1948     input_thread_t *p_input = p_playlist->p_input;
1949     if(p_input)
1950     {
1951         var_DelCallback( p_input, "state", StateCallback, p_filter );
1952     }
1953     vlc_object_release( p_playlist );
1954 }
1955
1956
1957 static int CropCallback(vlc_object_t *p_this, char const *psz_cmd,
1958                         vlc_value_t oldval, vlc_value_t newval,
1959                         void *p_data)
1960 {
1961     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1962     filter_t *p_filter = (filter_t *)p_data;
1963     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
1964
1965     /*
1966     //if the handler is attache to crop variable directly!
1967     int i_visible_width, i_visible_height, i_x_offset, i_y_offset;
1968     atmo_parse_crop(newval.psz_string, p_vout->fmt_render,
1969     p_vout->fmt_render,
1970     i_visible_width, i_visible_height,
1971     i_x_offset, i_y_offset);
1972     p_sys->i_crop_x_offset  = i_x_offset;
1973     p_sys->i_crop_y_offset  = i_y_offset;
1974     p_sys->i_crop_width     = i_visible_width;
1975     p_sys->i_crop_height    = i_visible_height;
1976     */
1977
1978     p_sys->i_crop_x_offset  = p_vout->fmt_in.i_x_offset;
1979     p_sys->i_crop_y_offset  = p_vout->fmt_in.i_y_offset;
1980     p_sys->i_crop_width     = p_vout->fmt_in.i_visible_width;
1981     p_sys->i_crop_height    = p_vout->fmt_in.i_visible_height;
1982
1983     msg_Dbg(p_filter, "cropping picture %ix%i to %i,%i,%ix%i",
1984         p_vout->fmt_in.i_width,
1985         p_vout->fmt_in.i_height,
1986         p_sys->i_crop_x_offset,
1987         p_sys->i_crop_y_offset,
1988         p_sys->i_crop_width,
1989         p_sys->i_crop_height
1990         );
1991
1992     return VLC_SUCCESS;
1993 }
1994
1995
1996 static void AddCropVariableCallback( filter_t *p_filter)
1997 {
1998     vout_thread_t *p_vout = (vout_thread_t *)vlc_object_find( p_filter,
1999         VLC_OBJECT_VOUT,
2000         FIND_ANYWHERE );
2001     if( p_vout )
2002     {
2003         var_AddCallback( p_vout, "crop-update", CropCallback, p_filter );
2004         vlc_object_release( p_vout );
2005     }
2006 }
2007
2008 static void DelCropVariableCallback( filter_t *p_filter)
2009 {
2010     vout_thread_t *p_vout = (vout_thread_t *)vlc_object_find( p_filter,
2011         VLC_OBJECT_VOUT,
2012         FIND_ANYWHERE );
2013     if( p_vout )
2014     {
2015         var_DelCallback( p_vout, "crop-update", CropCallback, p_filter );
2016         vlc_object_release( p_vout );
2017     }
2018 }
2019
2020
2021 /****************************************************************************
2022 * StateCallback: Callback for the inputs variable "State" to get notified
2023 * about Pause and Continue Playback events.
2024 *****************************************************************************/
2025 static int AtmoSettingsCallback( vlc_object_t *p_this, char const *psz_var,
2026                                  vlc_value_t oldval, vlc_value_t newval,
2027                                  void *p_data )
2028 {
2029     filter_t *p_filter = (filter_t *)p_data;
2030     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
2031
2032     CAtmoConfig *p_atmo_config = p_sys->p_atmo_config;
2033     if(p_atmo_config)
2034     {
2035
2036        msg_Dbg(p_filter, "apply AtmoSettingsCallback %s (int: %d -> %d)",
2037              psz_var,
2038              oldval.i_int,
2039              newval.i_int
2040        );
2041
2042         if( !strcmp( psz_var, CFG_PREFIX "filtermode" ))
2043             p_atmo_config->setLiveViewFilterMode( (AtmoFilterMode)newval.i_int);
2044
2045         else if( !strcmp( psz_var, CFG_PREFIX "PercentNew" ))
2046                  p_atmo_config->setLiveViewFilter_PercentNew( newval.i_int );
2047
2048         else if( !strcmp( psz_var, CFG_PREFIX "MeanLength" ))
2049                  p_atmo_config->setLiveViewFilter_MeanLength( newval.i_int );
2050
2051         else if( !strcmp( psz_var, CFG_PREFIX "MeanThreshold" ))
2052                  p_atmo_config->setLiveViewFilter_MeanThreshold( newval.i_int );
2053
2054         else if( !strcmp( psz_var, CFG_PREFIX "EdgeWeightning" ))
2055                  p_atmo_config->setLiveView_EdgeWeighting( newval.i_int );
2056
2057         else if( !strcmp( psz_var, CFG_PREFIX "Brightness" ))
2058                  p_atmo_config->setLiveView_BrightCorrect( newval.i_int );
2059
2060         else if( !strcmp( psz_var, CFG_PREFIX "DarknessLimit" ))
2061                  p_atmo_config->setLiveView_DarknessLimit( newval.i_int );
2062
2063         else if( !strcmp( psz_var, CFG_PREFIX "HueWinSize" ))
2064                  p_atmo_config->setLiveView_HueWinSize( newval.i_int );
2065
2066         else if( !strcmp( psz_var, CFG_PREFIX "SatWinSize" ))
2067                  p_atmo_config->setLiveView_SatWinSize( newval.i_int );
2068
2069         else if( !strcmp( psz_var, CFG_PREFIX "FrameDelay" ))
2070                  p_atmo_config->setLiveView_FrameDelay( newval.i_int );
2071
2072         else if( !strcmp( psz_var, CFG_PREFIX "whiteadj" ))
2073                  p_atmo_config->setUseSoftwareWhiteAdj( newval.b_bool );
2074
2075         else if( !strcmp( psz_var, CFG_PREFIX "white-red" ))
2076                  p_atmo_config->setWhiteAdjustment_Red( newval.i_int );
2077
2078         else if( !strcmp( psz_var, CFG_PREFIX "white-green" ))
2079                  p_atmo_config->setWhiteAdjustment_Green( newval.i_int );
2080
2081         else if( !strcmp( psz_var, CFG_PREFIX "white-blue" ))
2082                  p_atmo_config->setWhiteAdjustment_Blue( newval.i_int );
2083
2084     }
2085     return VLC_SUCCESS;
2086 }
2087
2088 static void AddAtmoSettingsVariablesCallbacks(filter_t *p_filter)
2089 {
2090    var_AddCallback( p_filter, CFG_PREFIX "filtermode",
2091                     AtmoSettingsCallback, p_filter );
2092    var_AddCallback( p_filter, CFG_PREFIX "PercentNew",
2093                     AtmoSettingsCallback, p_filter );
2094
2095
2096    var_AddCallback( p_filter, CFG_PREFIX "MeanLength",
2097                     AtmoSettingsCallback, p_filter );
2098    var_AddCallback( p_filter, CFG_PREFIX "MeanThreshold",
2099                     AtmoSettingsCallback, p_filter );
2100
2101    var_AddCallback( p_filter, CFG_PREFIX "EdgeWeightning",
2102                     AtmoSettingsCallback, p_filter );
2103    var_AddCallback( p_filter, CFG_PREFIX "Brightness",
2104                     AtmoSettingsCallback, p_filter );
2105    var_AddCallback( p_filter, CFG_PREFIX "DarknessLimit",
2106                     AtmoSettingsCallback, p_filter );
2107
2108    var_AddCallback( p_filter, CFG_PREFIX "HueWinSize",
2109                     AtmoSettingsCallback, p_filter );
2110    var_AddCallback( p_filter, CFG_PREFIX "SatWinSize",
2111                     AtmoSettingsCallback, p_filter );
2112    var_AddCallback( p_filter, CFG_PREFIX "FrameDelay",
2113                     AtmoSettingsCallback, p_filter );
2114
2115
2116    var_AddCallback( p_filter, CFG_PREFIX "whiteadj",
2117                     AtmoSettingsCallback, p_filter );
2118    var_AddCallback( p_filter, CFG_PREFIX "white-red",
2119                     AtmoSettingsCallback, p_filter );
2120    var_AddCallback( p_filter, CFG_PREFIX "white-green",
2121                     AtmoSettingsCallback, p_filter );
2122    var_AddCallback( p_filter, CFG_PREFIX "white-blue",
2123                     AtmoSettingsCallback, p_filter );
2124 }
2125
2126 static void DelAtmoSettingsVariablesCallbacks( filter_t *p_filter )
2127 {
2128
2129    var_DelCallback( p_filter, CFG_PREFIX "filtermode",
2130                     AtmoSettingsCallback, p_filter );
2131
2132    var_DelCallback( p_filter, CFG_PREFIX "PercentNew",
2133                     AtmoSettingsCallback, p_filter );
2134    var_DelCallback( p_filter, CFG_PREFIX "MeanLength",
2135                     AtmoSettingsCallback, p_filter );
2136    var_DelCallback( p_filter, CFG_PREFIX "MeanThreshold",
2137                     AtmoSettingsCallback, p_filter );
2138
2139    var_DelCallback( p_filter, CFG_PREFIX "EdgeWeightning",
2140                     AtmoSettingsCallback, p_filter );
2141    var_DelCallback( p_filter, CFG_PREFIX "Brightness",
2142                     AtmoSettingsCallback, p_filter );
2143    var_DelCallback( p_filter, CFG_PREFIX "DarknessLimit",
2144                     AtmoSettingsCallback, p_filter );
2145
2146    var_DelCallback( p_filter, CFG_PREFIX "HueWinSize",
2147                     AtmoSettingsCallback, p_filter );
2148    var_DelCallback( p_filter, CFG_PREFIX "SatWinSize",
2149                     AtmoSettingsCallback, p_filter );
2150    var_DelCallback( p_filter, CFG_PREFIX "FrameDelay",
2151                     AtmoSettingsCallback, p_filter );
2152
2153
2154    var_DelCallback( p_filter, CFG_PREFIX "whiteadj",
2155                     AtmoSettingsCallback, p_filter );
2156    var_DelCallback( p_filter, CFG_PREFIX "white-red",
2157                     AtmoSettingsCallback, p_filter );
2158    var_DelCallback( p_filter, CFG_PREFIX "white-green",
2159                     AtmoSettingsCallback, p_filter );
2160    var_DelCallback( p_filter, CFG_PREFIX "white-blue",
2161                     AtmoSettingsCallback, p_filter );
2162
2163 }
2164
2165
2166 #if defined(__ATMO_DEBUG__)
2167 static void atmo_parse_crop(char *psz_cropconfig,
2168                             video_format_t fmt_in,
2169                             video_format_t fmt_render,
2170                             int &i_visible_width, int &i_visible_height,
2171                             int &i_x_offset, int &i_y_offset )
2172 {
2173     int64_t i_aspect_num, i_aspect_den;
2174     unsigned int i_width, i_height;
2175
2176     i_visible_width  = fmt_in.i_visible_width;
2177     i_visible_height = fmt_in.i_visible_height;
2178     i_x_offset       = fmt_in.i_x_offset;
2179     i_y_offset       = fmt_in.i_y_offset;
2180
2181     char *psz_end = NULL, *psz_parser = strchr( psz_cropconfig, ':' );
2182     if( psz_parser )
2183     {
2184         /* We're using the 3:4 syntax */
2185         i_aspect_num = strtol( psz_cropconfig, &psz_end, 10 );
2186         if( psz_end == psz_cropconfig || !i_aspect_num ) return;
2187
2188         i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
2189         if( psz_end == psz_parser || !i_aspect_den ) return;
2190
2191         i_width = fmt_in.i_sar_den * fmt_render.i_visible_height *
2192             i_aspect_num / i_aspect_den / fmt_in.i_sar_num;
2193
2194         i_height = fmt_render.i_visible_width*fmt_in.i_sar_num *
2195             i_aspect_den / i_aspect_num / fmt_in.i_sar_den;
2196
2197         if( i_width < fmt_render.i_visible_width )
2198         {
2199             i_x_offset = fmt_render.i_x_offset +
2200                 (fmt_render.i_visible_width - i_width) / 2;
2201             i_visible_width = i_width;
2202         }
2203         else
2204         {
2205             i_y_offset = fmt_render.i_y_offset +
2206                 (fmt_render.i_visible_height - i_height) / 2;
2207             i_visible_height = i_height;
2208         }
2209     }
2210     else
2211     {
2212         psz_parser = strchr( psz_cropconfig, 'x' );
2213         if( psz_parser )
2214         {
2215             /* Maybe we're using the <width>x<height>+<left>+<top> syntax */
2216             unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
2217
2218             i_crop_width = strtol( psz_cropconfig, &psz_end, 10 );
2219             if( psz_end != psz_parser ) return;
2220
2221             psz_parser = strchr( ++psz_end, '+' );
2222             i_crop_height = strtol( psz_end, &psz_end, 10 );
2223             if( psz_end != psz_parser ) return;
2224
2225             psz_parser = strchr( ++psz_end, '+' );
2226             i_crop_left = strtol( psz_end, &psz_end, 10 );
2227             if( psz_end != psz_parser ) return;
2228
2229             psz_end++;
2230             i_crop_top = strtol( psz_end, &psz_end, 10 );
2231             if( *psz_end != '\0' ) return;
2232
2233             i_width = i_crop_width;
2234             i_visible_width = i_width;
2235
2236             i_height = i_crop_height;
2237             i_visible_height = i_height;
2238
2239             i_x_offset = i_crop_left;
2240             i_y_offset = i_crop_top;
2241         }
2242         else
2243         {
2244             /* Maybe we're using the <left>+<top>+<right>+<bottom> syntax */
2245             unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
2246
2247             psz_parser = strchr( psz_cropconfig, '+' );
2248             i_crop_left = strtol( psz_cropconfig, &psz_end, 10 );
2249             if( psz_end != psz_parser ) return;
2250
2251             psz_parser = strchr( ++psz_end, '+' );
2252             i_crop_top = strtol( psz_end, &psz_end, 10 );
2253             if( psz_end != psz_parser ) return;
2254
2255             psz_parser = strchr( ++psz_end, '+' );
2256             i_crop_right = strtol( psz_end, &psz_end, 10 );
2257             if( psz_end != psz_parser ) return;
2258
2259             psz_end++;
2260             i_crop_bottom = strtol( psz_end, &psz_end, 10 );
2261             if( *psz_end != '\0' ) return;
2262
2263             i_width = fmt_render.i_visible_width - i_crop_left - i_crop_right;
2264             i_visible_width = i_width;
2265
2266             i_height = fmt_render.i_visible_height - i_crop_top - i_crop_bottom;
2267             i_visible_height = i_height;
2268
2269             i_x_offset = i_crop_left;
2270             i_y_offset = i_crop_top;
2271         }
2272     }
2273 }
2274 #endif