151e1c9f0891243b549c497a22c74174dcbdf04c
[vlc.git] / 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 #define __STDC_FORMAT_MACROS 1
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30 #include <math.h>                                            /* sin(), cos() */
31 #include <assert.h>
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 // #define __ATMO_DEBUG__
38
39 // [:Zs]+$
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_vout.h>
43
44 #include <vlc_playlist.h>
45 #include <vlc_filter.h>
46
47 #include "AtmoDefs.h"
48 #include "AtmoDynData.h"
49 #include "AtmoLiveView.h"
50 #include "AtmoTools.h"
51 #include "AtmoExternalCaptureInput.h"
52 #include "AtmoConfig.h"
53 #include "AtmoConnection.h"
54 #include "AtmoClassicConnection.h"
55
56
57 /*****************************************************************************
58 * Local prototypes
59 *****************************************************************************/
60 /* directly to vlc related functions required that the module is accepted */
61 static int  CreateFilter    ( vlc_object_t * );
62 static void DestroyFilter   ( vlc_object_t * );
63 static picture_t * Filter( filter_t *, picture_t *);
64
65 /* callback for global variable state pause / continue / stop events */
66 static void AddStateVariableCallback( filter_t *);
67 static void DelStateVariableCallback( filter_t *);
68 static int StateCallback(vlc_object_t *, char const *,
69                          vlc_value_t, vlc_value_t, void *);
70
71 /* callback for atmo settings variables whose change
72    should be immediately realized and applied to output
73 */
74 static void DelAtmoSettingsVariablesCallbacks(filter_t *);
75 static void AddAtmoSettingsVariablesCallbacks(filter_t *);
76 static int AtmoSettingsCallback(vlc_object_t *, char const *,
77                                 vlc_value_t, vlc_value_t, void *);
78
79
80 #if defined(__ATMO_DEBUG__)
81 static void atmo_parse_crop(char *psz_cropconfig,
82                             video_format_t fmt_in,
83                             video_format_t fmt_render,
84                             int &i_visible_width,
85                             int &i_visible_height,
86                             int &i_x_offset,
87                             int &i_y_offset );
88 #endif
89
90
91 /* function to shutdown the fade thread which is started on pause*/
92 static void CheckAndStopFadeThread(filter_t *);
93
94 /* extracts a small RGB (BGR) Image from an YUV image */
95 static void ExtractMiniImage_YUV(filter_sys_t *, picture_t *, uint8_t *);
96
97 #if defined(__ATMO_DEBUG__)
98 void SaveBitmap(filter_sys_t *p_sys, uint8_t *p_pixels, char *psz_filename);
99 #endif
100
101 /*****************************************************************************
102 * External Prototypes for the AtmoCtrlLib.DLL
103 *****************************************************************************/
104 /*
105 * if effectmode = emLivePicture then the source could be GDI (Screencapture)
106 * or External - this means another application delivers Pixeldata to AtmoWin
107 * Clientsoftware through  AtmoCtrlLib.DLL and the COM Api
108 */
109 #define lvsGDI           0
110 #define lvsExternal      1
111
112 #define CLASSIC_ATMO_NUM_ZONES  5
113
114
115 /*
116 strings for settings menus and hints
117 */
118 #define MODULE_DESCRIPTION N_ ( \
119  "This module allows to control an so called AtmoLight device "\
120  "connected to your computer.\n"\
121  "AtmoLight is the homegrown version of what Philips calls AmbiLight.\n"\
122  "If you need further information feel free to visit us at\n\n"\
123  "http://www.vdr-wiki.de/wiki/index.php/Atmo-plugin\n"\
124  "http://www.vdr-wiki.de/wiki/index.php/AtmoWin\n\n"\
125  "You can find there detailed descriptions on how to build it for yourself "\
126  "and where to get the required parts.\n" \
127  "You can also have a look at pictures and some movies showing such a device " \
128  "in live action.")
129
130 #define DRIVER_TEXT            N_("Device type")
131 #define DRIVER_LONGTEXT        N_("Choose your preferred hardware from " \
132                                   "the list, or choose AtmoWin Software " \
133                                   "to delegate processing to the external " \
134                                   "process - with more options")
135
136 static const int pi_device_type_values[] = {
137 #if defined( WIN32 )
138      0, /* use AtmoWinA.exe userspace driver */
139 #endif
140      1, /* AtmoLight classic */
141      2, /* Quattro AtmoLight */
142      3, /* DMX Device */
143      4, /* MoMoLight device */
144      5  /* fnordlicht */
145 };
146 static const char *const ppsz_device_type_descriptions[] = {
147 #if defined( WIN32 )
148         N_("AtmoWin Software"),
149 #endif
150         N_("Classic AtmoLight"),
151         N_("Quattro AtmoLight"),
152         N_("DMX"),
153         N_("MoMoLight"),
154         N_("fnordlicht")
155 };
156
157 #define DMX_CHANNELS_TEXT      N_("Count of AtmoLight channels")
158 #define DMX_CHANNELS_LONGTEXT  N_("How many AtmoLight channels, should be " \
159                                   "emulated with that DMX device")
160 #define DMX_CHBASE_TEXT        N_("DMX address for each channel")
161 #define DMX_CHBASE_LONGTEXT    N_("Define here the DMX base address for each " \
162                                   "channel use , or ; to separate the values")
163
164 #define MOMO_CHANNELS_TEXT      N_("Count of channels")
165 #define MOMO_CHANNELS_LONGTEXT  N_("Depending on your MoMoLight hardware " \
166                                    "choose 3 or 4 channels")
167
168 #define FNORDLICHT_AMOUNT_TEXT      N_("Count of fnordlicht's")
169 #define FNORDLICHT_AMOUNT_LONGTEXT  N_("Depending on the amount your " \
170                                    "fnordlicht hardware " \
171                                    "choose 1 to 4 channels")
172
173 #if defined( WIN32 )
174 #  define DEFAULT_DEVICE   0
175 #else
176 #  define DEFAULT_DEVICE   1
177 #endif
178
179 #if defined( __ATMO_DEBUG__ )
180 #   define SAVEFRAMES_TEXT     N_("Save Debug Frames")
181 #   define SAVEFRAMES_LONGTEXT N_("Write every 128th miniframe to a folder.")
182 #   define FRAMEPATH_TEXT      N_("Debug Frame Folder")
183 #   define FRAMEPATH_LONGTEXT  N_("The path where the debugframes " \
184                                   "should be saved")
185 #endif
186
187 #define WIDTH_TEXT             N_("Extracted Image Width")
188 #define WIDTH_LONGTEXT         N_("The width of the mini image for " \
189                                   "further processing (64 is default)")
190
191 #define HEIGHT_TEXT            N_("Extracted Image Height")
192 #define HEIGHT_LONGTEXT        N_("The height of the mini image for " \
193                                   "further processing (48 is default)")
194
195 #define SHOW_DOTS_TEXT         N_("Mark analyzed pixels")
196 #define SHOW_DOTS_LONGTEXT     N_("makes the sample grid visible on screen as "\
197                                   "white pixels")
198
199 #define PCOLOR_TEXT            N_("Color when paused")
200 #define PCOLOR_LONGTEXT        N_("Set the color to show if the user " \
201                                   "pauses the video. (Have light to get " \
202                                   "another beer?)")
203 #define PCOLOR_RED_TEXT        N_("Pause-Red")
204 #define PCOLOR_RED_LONGTEXT    N_("Red component of the pause color")
205 #define PCOLOR_GREEN_TEXT      N_("Pause-Green")
206 #define PCOLOR_GREEN_LONGTEXT  N_("Green component of the pause color")
207 #define PCOLOR_BLUE_TEXT       N_("Pause-Blue")
208 #define PCOLOR_BLUE_LONGTEXT   N_("Blue component of the pause color")
209 #define FADESTEPS_TEXT         N_("Pause-Fadesteps")
210 #define FADESTEPS_LONGTEXT     N_("Number of steps to change current color " \
211                                   "to pause color (each step takes 40ms)")
212
213 #define ECOLOR_RED_TEXT        N_("End-Red")
214 #define ECOLOR_RED_LONGTEXT    N_("Red component of the shutdown color")
215 #define ECOLOR_GREEN_TEXT      N_("End-Green")
216 #define ECOLOR_GREEN_LONGTEXT  N_("Green component of the shutdown color")
217 #define ECOLOR_BLUE_TEXT       N_("End-Blue")
218 #define ECOLOR_BLUE_LONGTEXT   N_("Blue component of the shutdown color")
219 #define EFADESTEPS_TEXT        N_("End-Fadesteps")
220 #define EFADESTEPS_LONGTEXT  N_("Number of steps to change current color to " \
221                              "end color for dimming up the light in cinema " \
222                              "style... (each step takes 40ms)")
223
224 #define ZONE_TOP_TEXT          N_("Number of zones on top")
225 #define ZONE_TOP_LONGTEXT      N_("Number of zones on the top of the screen")
226 #define ZONE_BOTTOM_TEXT       N_("Number of zones on bottom")
227 #define ZONE_BOTTOM_LONGTEXT   N_("Number of zones on the bottom of the screen")
228 #define ZONE_LR_TEXT           N_("Zones on left / right side")
229 #define ZONE_LR_LONGTEXT       N_("left and right side having allways the " \
230                                   "same number of zones")
231 #define ZONE_SUMMARY_TEXT      N_("Calculate a average zone")
232 #define ZONE_SUMMARY_LONGTEXT  N_("it contains the average of all pixels " \
233                                   "in the sample image (only useful for " \
234                                   "single channel AtmoLight)")
235
236
237 #define USEWHITEADJ_TEXT       N_("Use Software White adjust")
238 #define USEWHITEADJ_LONGTEXT   N_("Should the buildin driver do a white " \
239                                   "adjust or your LED stripes? recommend.")
240 #define WHITE_RED_TEXT         N_("White Red")
241 #define WHITE_RED_LONGTEXT     N_("Red value of a pure white on your "\
242                                   "LED stripes.")
243 #define WHITE_GREEN_TEXT       N_("White Green")
244 #define WHITE_GREEN_LONGTEXT   N_("Green value of a pure white on your "\
245                                   "LED stripes.")
246 #define WHITE_BLUE_TEXT        N_("White Blue")
247 #define WHITE_BLUE_LONGTEXT    N_("Blue value of a pure white on your "\
248                                   "LED stripes.")
249
250 #define SERIALDEV_TEXT         N_("Serial Port/Device")
251 #define SERIALDEV_LONGTEXT   N_("Name of the serial port where the AtmoLight "\
252                                 "controller is attached to.\n" \
253                                 "On Windows usually something like COM1 or " \
254                                 "COM2. On Linux /dev/ttyS01 f.e.")
255
256 #define EDGE_TEXT            N_("Edge Weightning")
257 #define EDGE_LONGTEXT        N_("Increasing this value will result in color "\
258                                 "more depending on the border of the frame.")
259 #define BRIGHTNESS_TEXT     N_("Brightness")
260 #define BRIGHTNESS_LONGTEXT N_("Overall brightness of your LED stripes")
261 #define DARKNESS_TEXT       N_("Darkness Limit")
262 #define DARKNESS_LONGTEXT   N_("Pixels with a saturation lower than this will "\
263                                "be ignored. Should be greater than one for "\
264                                "letterboxed videos.")
265 #define HUEWINSIZE_TEXT     N_("Hue windowing")
266 #define HUEWINSIZE_LONGTEXT N_("Used for statistics.")
267 #define SATWINSIZE_TEXT     N_("Sat windowing")
268 #define SATWINSIZE_LONGTEXT N_("Used for statistics.")
269
270 #define MEANLENGTH_TEXT     N_("Filter length (ms)")
271 #define MEANLENGTH_LONGTEXT N_("Time it takes until a color is completely "\
272                                 "changed. This prevents flickering.")
273 #define MEANTHRESHOLD_TEXT     N_("Filter threshold")
274 #define MEANTHRESHOLD_LONGTEXT N_("How much a color has to be changed for an "\
275                                   "immediate color change.")
276 #define MEANPERCENTNEW_TEXT     N_("Filter Smoothness (in %)")
277 #define MEANPERCENTNEW_LONGTEXT N_("Filter Smoothness")
278
279 #define FILTERMODE_TEXT        N_("Output Color filter mode")
280 #define FILTERMODE_LONGTEXT    N_("defines the how the output color should " \
281                                   "be calculated based on previous color")
282
283 static const int pi_filtermode_values[] = {
284        (int)afmNoFilter,
285        (int)afmCombined,
286        (int)afmPercent
287 };
288 static const char *const ppsz_filtermode_descriptions[] = {
289         N_("No Filtering"),
290         N_("Combined"),
291         N_("Percent")
292 };
293
294 #define FRAMEDELAY_TEXT       N_("Frame delay (ms)")
295 #define FRAMEDELAY_LONGTEXT   N_("Helps to get the video output and the light "\
296                                  "effects in sync. Values around 20ms should " \
297                                  "do the trick.")
298
299
300 #define CHANNEL_0_ASSIGN_TEXT N_("Channel 0: summary")
301 #define CHANNEL_1_ASSIGN_TEXT N_("Channel 1: left")
302 #define CHANNEL_2_ASSIGN_TEXT N_("Channel 2: right")
303 #define CHANNEL_3_ASSIGN_TEXT N_("Channel 3: top")
304 #define CHANNEL_4_ASSIGN_TEXT N_("Channel 4: bottom")
305
306 #define CHANNELASSIGN_LONGTEXT N_("Maps the hardware channel X to logical "\
307                                   "zone Y to fix wrong wiring :-)")
308 static const int pi_zone_assignment_values[] = {
309     -1,
310      4,
311      3,
312      1,
313      0,
314      2
315 };
316 static const char *const ppsz_zone_assignment_descriptions[] = {
317         N_("disabled"),
318         N_("Zone 4:summary"),
319         N_("Zone 3:left"),
320         N_("Zone 1:right"),
321         N_("Zone 0:top"),
322         N_("Zone 2:bottom")
323 };
324 #define CHANNELS_ASSIGN_TEXT        N_("Channel / Zone Assignment")
325 #define CHANNELS_ASSIGN_LONGTEXT N_("for devices with more than five " \
326                   "channels / zones write down here for each channel " \
327                   "the zone number to show and separate the values with " \
328                   ", or ; and use -1 to not use some channels. For the " \
329                   "classic AtmoLight the sequence 4,3,1,0,2 would set the " \
330                   "default channel/zone mapping. " \
331                   "Having only two zones on top, and one zone on left and " \
332                   "right and no summary zone the mapping for classic " \
333                   "AtmoLight would be -1,3,2,1,0")
334
335 #define ZONE_0_GRADIENT_TEXT N_("Zone 0: Top gradient")
336 #define ZONE_1_GRADIENT_TEXT N_("Zone 1: Right gradient")
337 #define ZONE_2_GRADIENT_TEXT N_("Zone 2: Bottom gradient")
338 #define ZONE_3_GRADIENT_TEXT N_("Zone 3: Left gradient")
339 #define ZONE_4_GRADIENT_TEXT N_("Zone 4: Summary gradient")
340 #define ZONE_X_GRADIENT_LONG_TEXT N_("Defines a small bitmap with 64x48 "\
341                                      "pixels, containing a grayscale gradient")
342
343 #define GRADIENT_PATH_TEXT      N_("Gradient bitmap searchpath")
344 #define GRADIENT_PATH_LONGTEXT  N_("Now preferred option to assign gradient "\
345     "bitmaps, put them as zone_0.bmp, zone_1.bmp etc. into one folder and "\
346     "set the foldername here")
347
348 #if defined( WIN32 )
349 #   define ATMOWINEXE_TEXT      N_("Filename of AtmoWin*.exe")
350 #   define ATMOWINEXE_LONGTEXT  N_("if you want the AtmoLight control "\
351                                    "software to be launched by VLC, enter the "\
352                                    "complete path of AtmoWinA.exe here.")
353 #endif
354
355 #define CFG_PREFIX "atmo-"
356
357 /*****************************************************************************
358 * Module descriptor
359 *****************************************************************************/
360 vlc_module_begin ()
361 set_description( N_("AtmoLight Filter") )
362 set_help( MODULE_DESCRIPTION )
363 set_shortname( N_( "AtmoLight" ))
364 set_category( CAT_VIDEO )
365 set_subcategory( SUBCAT_VIDEO_VFILTER )
366
367 set_capability( "video filter2", 0 )
368
369
370 set_section( N_("Choose Devicetype and Connection" ), 0 )
371
372 add_integer( CFG_PREFIX "device", DEFAULT_DEVICE, NULL,
373             DRIVER_TEXT, DRIVER_LONGTEXT, false )
374 change_integer_list( pi_device_type_values,
375                      ppsz_device_type_descriptions )
376
377 #if defined(WIN32)
378 add_string(CFG_PREFIX "serialdev", "COM1",
379            SERIALDEV_TEXT, SERIALDEV_LONGTEXT, false )
380 /*
381     on win32 the executeable external driver application
382     for automatic start if needed
383 */
384 add_loadfile(CFG_PREFIX "atmowinexe", NULL, NULL,
385              ATMOWINEXE_TEXT, ATMOWINEXE_LONGTEXT, false )
386 #else
387 add_string(CFG_PREFIX "serialdev", "/dev/ttyUSB0",
388            SERIALDEV_TEXT, SERIALDEV_LONGTEXT, false )
389 #endif
390
391 /*
392     color which is showed if you want durring pausing
393     your movie ... used for both buildin / external
394 */
395 set_section( N_("Illuminate the room with this color on pause" ), 0 )
396 add_bool(CFG_PREFIX "usepausecolor", false, NULL,
397          PCOLOR_TEXT, PCOLOR_LONGTEXT, false)
398 add_integer_with_range(CFG_PREFIX "pcolor-red",   0, 0, 255, NULL,
399                        PCOLOR_RED_TEXT, PCOLOR_RED_LONGTEXT, false)
400 add_integer_with_range(CFG_PREFIX "pcolor-green", 0, 0, 255, NULL,
401                        PCOLOR_GREEN_TEXT, PCOLOR_GREEN_LONGTEXT, false)
402 add_integer_with_range(CFG_PREFIX "pcolor-blue",  192, 0, 255, NULL,
403                        PCOLOR_BLUE_TEXT, PCOLOR_BLUE_LONGTEXT, false)
404 add_integer_with_range(CFG_PREFIX "fadesteps", 50, 1, 250, NULL,
405                        FADESTEPS_TEXT, FADESTEPS_LONGTEXT, false)
406
407 /*
408     color which is showed if you finished watching your movie ...
409     used for both buildin / external
410 */
411 set_section( N_("Illuminate the room with this color on shutdown" ), 0 )
412 add_integer_with_range(CFG_PREFIX "ecolor-red",   192, 0, 255, NULL,
413                        ECOLOR_RED_TEXT,   ECOLOR_RED_LONGTEXT,   false)
414 add_integer_with_range(CFG_PREFIX "ecolor-green", 192, 0, 255, NULL,
415                        ECOLOR_GREEN_TEXT, ECOLOR_GREEN_LONGTEXT, false)
416 add_integer_with_range(CFG_PREFIX "ecolor-blue",  192, 0, 255, NULL,
417                        ECOLOR_BLUE_TEXT,  ECOLOR_BLUE_LONGTEXT,  false)
418 add_integer_with_range(CFG_PREFIX "efadesteps",    50, 1, 250, NULL,
419                        EFADESTEPS_TEXT,   EFADESTEPS_LONGTEXT,    false)
420
421
422 set_section( N_("DMX options" ), 0 )
423 add_integer_with_range(CFG_PREFIX "dmx-channels",   5, 1, 64, NULL,
424                        DMX_CHANNELS_TEXT, DMX_CHANNELS_LONGTEXT, false)
425 add_string(CFG_PREFIX "dmx-chbase", "0,3,6,9,12",
426                        DMX_CHBASE_TEXT, DMX_CHBASE_LONGTEXT, false )
427
428 set_section( N_("MoMoLight options" ), 0 )
429 add_integer_with_range(CFG_PREFIX "momo-channels",   3, 3, 4, NULL,
430                        MOMO_CHANNELS_TEXT, MOMO_CHANNELS_LONGTEXT, false)
431
432 /* 2,2,4 means 2 is the default value, 1 minimum amount,
433    4 maximum amount
434 */
435 set_section( N_("fnordlicht options" ), 0 )
436 add_integer_with_range(CFG_PREFIX "fnordlicht-amount",   2, 1, 4, NULL,
437                        FNORDLICHT_AMOUNT_TEXT,
438                        FNORDLICHT_AMOUNT_LONGTEXT, false)
439
440
441 /*
442   instead of redefining the original AtmoLight zones with gradient
443   bitmaps, we can now define the layout of the zones useing these
444   parameters - the function with the gradient bitmaps would still
445   work (but for most cases its no longer required)
446
447   short description whats this means - f.e. the classic atmo would
448   have this layout
449   zones-top    = 1  - zone 0
450   zones-lr     = 1  - zone 1 und zone 3
451   zones-bottom = 1  - zone 2
452   zone-summary = true - zone 4
453          Z0
454    ,------------,
455    |            |
456  Z3|     Z4     | Z1
457    |____________|
458          Z2
459
460   the zone numbers will be counted clockwise starting at top / left
461   if you want to split the light at the top, without having a bottom zone
462   (which is my private config)
463
464   zones-top    = 2  - zone 0, zone 1
465   zones-lr     = 1  - zone 2 und zone 3
466   zones-bottom = 0
467   zone-summary = false
468
469       Z0    Z1
470    ,------------,
471    |            |
472  Z3|            | Z2
473    |____________|
474
475 */
476
477 set_section( N_("Zone Layout for the build-in Atmo" ), 0 )
478 add_integer_with_range(CFG_PREFIX "zones-top",   1, 0, 16, NULL,
479                        ZONE_TOP_TEXT, ZONE_TOP_LONGTEXT, false)
480 add_integer_with_range(CFG_PREFIX "zones-bottom",   1, 0, 16, NULL,
481                        ZONE_BOTTOM_TEXT, ZONE_BOTTOM_LONGTEXT, false)
482 add_integer_with_range(CFG_PREFIX "zones-lr",   1, 0, 16, NULL,
483                        ZONE_LR_TEXT, ZONE_LR_LONGTEXT, false)
484 add_bool(CFG_PREFIX "zone-summary", false, NULL,
485          ZONE_SUMMARY_TEXT, ZONE_SUMMARY_LONGTEXT, false)
486
487 /*
488  settings only for the buildin driver (if external driver app is used
489  these parameters are ignored.)
490
491  definition of parameters for the buildin filter ...
492 */
493 set_section( N_("Settings for the built-in Live Video Processor only" ), 0 )
494
495 add_integer_with_range(CFG_PREFIX "edgeweightning",   3, 1, 30, NULL,
496                        EDGE_TEXT, EDGE_LONGTEXT, false)
497
498 add_integer_with_range(CFG_PREFIX "brightness",   100, 50, 300, NULL,
499                        BRIGHTNESS_TEXT, BRIGHTNESS_LONGTEXT, false)
500
501 add_integer_with_range(CFG_PREFIX "darknesslimit",   3, 0, 10, NULL,
502                        DARKNESS_TEXT, DARKNESS_LONGTEXT, false)
503
504 add_integer_with_range(CFG_PREFIX "huewinsize",   3, 0, 5, NULL,
505                        HUEWINSIZE_TEXT, HUEWINSIZE_LONGTEXT, false)
506
507 add_integer_with_range(CFG_PREFIX "satwinsize",   3, 0, 5, NULL,
508                        SATWINSIZE_TEXT, SATWINSIZE_LONGTEXT, false)
509
510 add_integer(CFG_PREFIX "filtermode", (int)afmCombined, NULL,
511             FILTERMODE_TEXT, FILTERMODE_LONGTEXT, false )
512
513 change_integer_list(pi_filtermode_values, ppsz_filtermode_descriptions )
514
515 add_integer_with_range(CFG_PREFIX "meanlength",    300, 300, 5000, NULL,
516                        MEANLENGTH_TEXT, MEANLENGTH_LONGTEXT, false)
517
518 add_integer_with_range(CFG_PREFIX "meanthreshold",  40, 1, 100, NULL,
519                        MEANTHRESHOLD_TEXT, MEANTHRESHOLD_LONGTEXT, false)
520
521 add_integer_with_range(CFG_PREFIX "percentnew", 50, 1, 100, NULL,
522                       MEANPERCENTNEW_TEXT, MEANPERCENTNEW_LONGTEXT, false)
523
524 add_integer_with_range(CFG_PREFIX "framedelay", 18, 0, 200, NULL,
525                        FRAMEDELAY_TEXT, FRAMEDELAY_LONGTEXT, false)
526
527 /*
528   output channel reordering
529 */
530 set_section( N_("Change channel assignment (fixes wrong wiring)" ), 0 )
531 add_integer( CFG_PREFIX "channel_0", 4, NULL,
532             CHANNEL_0_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false )
533 change_integer_list( pi_zone_assignment_values,
534                      ppsz_zone_assignment_descriptions )
535
536 add_integer( CFG_PREFIX "channel_1", 3, NULL,
537             CHANNEL_1_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false )
538 change_integer_list( pi_zone_assignment_values,
539                      ppsz_zone_assignment_descriptions )
540
541 add_integer( CFG_PREFIX "channel_2", 1, NULL,
542             CHANNEL_2_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false )
543 change_integer_list( pi_zone_assignment_values,
544                      ppsz_zone_assignment_descriptions )
545
546 add_integer( CFG_PREFIX "channel_3", 0, NULL,
547             CHANNEL_3_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false )
548 change_integer_list( pi_zone_assignment_values,
549                      ppsz_zone_assignment_descriptions )
550
551 add_integer( CFG_PREFIX "channel_4", 2, NULL,
552             CHANNEL_4_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false )
553 change_integer_list( pi_zone_assignment_values,
554                      ppsz_zone_assignment_descriptions )
555
556 add_string(CFG_PREFIX "channels", "",
557            CHANNELS_ASSIGN_TEXT, CHANNELS_ASSIGN_LONGTEXT, false )
558
559
560 /*
561   LED color white calibration
562 */
563 set_section( N_("Adjust the white light to your LED stripes" ), 0 )
564 add_bool(CFG_PREFIX "whiteadj", true, NULL,
565          USEWHITEADJ_TEXT, USEWHITEADJ_LONGTEXT, false)
566 add_integer_with_range(CFG_PREFIX "white-red",   255, 0, 255, NULL,
567                        WHITE_RED_TEXT,   WHITE_RED_LONGTEXT,   false)
568
569 add_integer_with_range(CFG_PREFIX "white-green", 255, 0, 255, NULL,
570                        WHITE_GREEN_TEXT, WHITE_GREEN_LONGTEXT, false)
571
572 add_integer_with_range(CFG_PREFIX "white-blue",  255, 0, 255, NULL,
573                        WHITE_BLUE_TEXT,  WHITE_BLUE_LONGTEXT,  false)
574 /* end of definition of parameter for the buildin filter ... part 1 */
575
576
577 /*
578 only for buildin (external has own definition) per default the calucation
579 used linear gradients for assigning a priority to the pixel - depending
580 how near they are to the border ...for changing this you can create 64x48
581 Pixel BMP files - which contain your own grayscale... (you can produce funny
582 effects with this...) the images MUST not compressed, should have 24-bit per
583 pixel, or a simple 256 color grayscale palette
584 */
585 set_section( N_("Change gradients" ), 0 )
586 add_loadfile(CFG_PREFIX "gradient_zone_0", NULL, NULL,
587              ZONE_0_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true )
588 add_loadfile(CFG_PREFIX "gradient_zone_1", NULL, NULL,
589              ZONE_1_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true )
590 add_loadfile(CFG_PREFIX "gradient_zone_2", NULL, NULL,
591              ZONE_2_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true )
592 add_loadfile(CFG_PREFIX "gradient_zone_3", NULL, NULL,
593              ZONE_3_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true )
594 add_loadfile(CFG_PREFIX "gradient_zone_4", NULL, NULL,
595              ZONE_4_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true )
596 add_directory(CFG_PREFIX "gradient_path", NULL, NULL,
597            GRADIENT_PATH_TEXT, GRADIENT_PATH_LONGTEXT, false )
598
599 #if defined(__ATMO_DEBUG__)
600 add_bool(CFG_PREFIX "saveframes", false, NULL,
601          SAVEFRAMES_TEXT, SAVEFRAMES_LONGTEXT, false)
602 add_string(CFG_PREFIX "framepath", "",
603            FRAMEPATH_TEXT, FRAMEPATH_LONGTEXT, false )
604 #endif
605 /*
606    may be later if computers gets more power ;-) than now we increase
607    the samplesize from which we do the stats for output color calculation
608 */
609 add_integer_with_range(CFG_PREFIX "width",  64, 64, 512, NULL,
610                        WIDTH_TEXT,  WIDTH_LONGTEXT, true)
611 add_integer_with_range(CFG_PREFIX "height", 48, 48, 384, NULL,
612                        HEIGHT_TEXT,  HEIGHT_LONGTEXT, true)
613 add_bool(CFG_PREFIX "showdots", false, NULL,
614                    SHOW_DOTS_TEXT, SHOW_DOTS_LONGTEXT, false)
615 add_shortcut( "atmo" )
616 set_callbacks( CreateFilter, DestroyFilter  )
617 vlc_module_end ()
618
619
620 static const char *const ppsz_filter_options[] = {
621         "device",
622
623         "serialdev",
624
625
626         "edgeweightning",
627         "brightness",
628         "darknesslimit",
629         "huewinsize",
630         "satwinsize",
631
632         "filtermode",
633
634         "meanlength",
635         "meanthreshold",
636         "percentnew",
637         "framedelay",
638
639         "zones-top",
640         "zones-bottom",
641         "zones-lr",
642         "zone-summary",
643
644         "channel_0",
645         "channel_1",
646         "channel_2",
647         "channel_3",
648         "channel_4",
649         "channels",
650
651         "whiteadj",
652         "white-red",
653         "white-green",
654         "white-blue",
655
656         "usepausecolor",
657         "pcolor-red",
658         "pcolor-green",
659         "pcolor-blue",
660         "fadesteps",
661
662         "ecolor-red",
663         "ecolor-green",
664         "ecolor-blue",
665         "efadesteps",
666
667         "dmx-channels",
668         "dmx-chbase",
669         "momo-channels",
670         "fnordlicht-amount",
671
672 #if defined(WIN32 )
673         "atmowinexe",
674 #endif
675 #if defined(__ATMO_DEBUG__)
676         "saveframes" ,
677         "framepath",
678 #endif
679         "width",
680         "height",
681         "showdots",
682         "gradient_zone_0",
683         "gradient_zone_1",
684         "gradient_zone_2",
685         "gradient_zone_3",
686         "gradient_zone_4",
687         "gradient_path",
688         NULL
689 };
690
691
692 /*****************************************************************************
693 * fadethread_t: Color Fading Thread
694 *****************************************************************************
695 * changes slowly the color of the output if videostream gets paused...
696 *****************************************************************************
697 */
698 typedef struct
699 {
700     VLC_COMMON_MEMBERS
701         filter_t *p_filter;
702     /* tell the thread which color should be the target of fading */
703     uint8_t ui_red;
704     uint8_t ui_green;
705     uint8_t ui_blue;
706     /* how many steps should happen until this */
707     int i_steps;
708
709 } fadethread_t;
710
711 static void *FadeToColorThread(vlc_object_t *);
712
713
714 /*****************************************************************************
715 * filter_sys_t: AtmoLight filter method descriptor
716 *****************************************************************************
717 * It describes the AtmoLight specific properties of an video filter.
718 *****************************************************************************/
719 struct filter_sys_t
720 {
721     /*
722     special for the access of the p_fadethread member all other members
723     need no special protection so far!
724     */
725     vlc_mutex_t filter_lock;
726
727     bool b_enabled;
728     int32_t i_AtmoOldEffect;
729     bool b_pause_live;
730     bool b_show_dots;
731     int32_t i_device_type;
732
733     int32_t i_atmo_width;
734     int32_t i_atmo_height;
735     /* used to disable fadeout if less than 50 frames are processed
736        used to avoid long time waiting when switch quickly between
737        deinterlaceing modes, where the output filter chains is rebuild
738        on each switch
739     */
740     int32_t i_frames_processed;
741
742 #if defined(__ATMO_DEBUG__)
743     bool  b_saveframes;
744     uint32_t ui_frame_counter;
745     char sz_framepath[MAX_PATH];
746 #endif
747
748     /* light color durring movie pause ... */
749     bool  b_usepausecolor;
750     uint8_t ui_pausecolor_red;
751     uint8_t ui_pausecolor_green;
752     uint8_t ui_pausecolor_blue;
753     int i_fadesteps;
754
755     /* light color on movie finish ... */
756     uint8_t ui_endcolor_red;
757     uint8_t ui_endcolor_green;
758     uint8_t ui_endcolor_blue;
759     int i_endfadesteps;
760
761     fadethread_t *p_fadethread;
762
763     /* Variables for buildin driver only... */
764
765     /* is only present and initialized if the internal driver is used*/
766     CAtmoConfig *p_atmo_config;
767     /* storage for temporal settings "volatile" */
768     CAtmoDynData *p_atmo_dyndata;
769     /* initialized for buildin driver with AtmoCreateTransferBuffers */
770     BITMAPINFOHEADER mini_image_format;
771     /* is only use buildin driver! */
772     uint8_t *p_atmo_transfer_buffer;
773     /* end buildin driver */
774
775     /*
776     contains the real output size of the video calculated on
777     change event of the variable "crop" from vout
778     */
779     int32_t i_crop_x_offset;
780     int32_t i_crop_y_offset;
781     int32_t i_crop_width;
782     int32_t i_crop_height;
783
784     void (*pf_extract_mini_image) (filter_sys_t *p_sys,
785         picture_t *p_inpic,
786         uint8_t *p_transfer_dest);
787
788 #if defined( WIN32 )
789     /* External Library as wrapper arround COM Stuff */
790     HINSTANCE h_AtmoCtrl;
791     int32_t (*pf_ctrl_atmo_initialize) (void);
792     void (*pf_ctrl_atmo_finalize) (int32_t what);
793     int32_t (*pf_ctrl_atmo_switch_effect) (int32_t);
794     int32_t (*pf_ctrl_atmo_set_live_source) (int32_t);
795     void (*pf_ctrl_atmo_create_transfer_buffers) (int32_t, int32_t,
796                                                   int32_t , int32_t);
797     uint8_t* (*pf_ctrl_atmo_lock_transfer_buffer) (void);
798     void (*pf_ctrl_atmo_send_pixel_data) (void);
799     void (*pf_ctrl_atmo_get_image_size)(int32_t *,int32_t *);
800 #endif
801 };
802
803 /*
804 initialize previously configured Atmo Light environment
805 - if internal is enabled try to access the device on the serial port
806 - if not internal is enabled and we are on win32 try to initialize
807 the previously loaded DLL ...
808
809 Return Values may be: -1 (failed for some reason - filter will be disabled)
810 1 Ok. lets rock
811 */
812 static int32_t AtmoInitialize(filter_t *p_filter, bool b_for_thread)
813 {
814     filter_sys_t *p_sys = p_filter->p_sys;
815     if(p_sys->p_atmo_config)
816     {
817         if(b_for_thread == false)
818         {
819             /* open com port */
820             /* setup Output Threads ... */
821             msg_Dbg( p_filter, "open atmo device...");
822             if(CAtmoTools::RecreateConnection(p_sys->p_atmo_dyndata)
823                == ATMO_TRUE)
824             {
825                 return 1;
826             } else {
827                 msg_Err( p_filter,"failed to open atmo device, "\
828                                   "some other software/driver may use it?");
829             }
830         }
831 #if defined(WIN32)
832     } else if(p_sys->pf_ctrl_atmo_initialize)
833     {
834         /* on win32 with active ctrl dll */
835         return p_sys->pf_ctrl_atmo_initialize();
836 #endif
837     }
838     return -1;
839 }
840
841 /*
842 prepare the shutdown of the effect threads,
843 for build in filter - close the serialport after finishing the threads...
844 cleanup possible loaded DLL...
845 */
846 static void AtmoFinalize(filter_t *p_filter, int32_t what)
847 {
848     filter_sys_t *p_sys = p_filter->p_sys;
849     if(p_sys->p_atmo_config)
850     {
851         if(what == 1)
852         {
853             CAtmoDynData *p_atmo_dyndata = p_sys->p_atmo_dyndata;
854             if(p_atmo_dyndata)
855             {
856                 p_atmo_dyndata->LockCriticalSection();
857
858                 CAtmoInput *p_input = p_atmo_dyndata->getLiveInput();
859                 p_atmo_dyndata->setLiveInput( NULL );
860                 if(p_input != NULL)
861                 {
862                     p_input->Terminate();
863                     delete p_input;
864                     msg_Dbg( p_filter, "input thread died peacefully");
865                 }
866
867                 CThread *p_effect_thread = p_atmo_dyndata->getEffectThread();
868                 p_atmo_dyndata->setEffectThread(NULL);
869                 if(p_effect_thread != NULL)
870                 {
871                     /*
872                     forced the thread to die...
873                     and wait for termination of the thread
874                     */
875                     p_effect_thread->Terminate();
876                     delete p_effect_thread;
877                     msg_Dbg( p_filter, "effect thread died peacefully");
878                 }
879
880                 CAtmoPacketQueue *p_queue =
881                                            p_atmo_dyndata->getLivePacketQueue();
882                 p_atmo_dyndata->setLivePacketQueue( NULL );
883                 if(p_queue != NULL)
884                 {
885                    delete p_queue;
886                    msg_Dbg( p_filter, "packetqueue removed");
887                 }
888
889                 /*
890                 close serial port if it is open (all OS specific is inside
891                 CAtmoSerialConnection implemented / defined)
892                 */
893                 CAtmoConnection *p_atmo_connection =
894                                  p_atmo_dyndata->getAtmoConnection();
895                 p_atmo_dyndata->setAtmoConnection(NULL);
896                 if(p_atmo_connection) {
897                     p_atmo_connection->CloseConnection();
898                     delete p_atmo_connection;
899                 }
900                 p_atmo_dyndata->UnLockCriticalSection();
901             }
902         }
903 #if defined(WIN32)
904     } else if(p_sys->pf_ctrl_atmo_finalize)
905     {
906         /* on win32 with active ctrl dll */
907         p_sys->pf_ctrl_atmo_finalize(what);
908 #endif
909     }
910 }
911
912 /*
913   switch the current light effect to LiveView
914 */
915 static int32_t AtmoSwitchEffect(filter_t *p_filter, int32_t newMode)
916 {
917     filter_sys_t *p_sys = p_filter->p_sys;
918
919     msg_Dbg( p_filter, "AtmoSwitchEffect %d", newMode );
920
921     if(p_sys->p_atmo_config)
922     {
923        return CAtmoTools::SwitchEffect(p_sys->p_atmo_dyndata, emLivePicture);
924 #if defined(WIN32)
925     } else if(p_sys->pf_ctrl_atmo_switch_effect)
926     {
927         /* on win32 with active ctrl dll */
928         return p_sys->pf_ctrl_atmo_switch_effect( newMode );
929 #endif
930     }
931     return emDisabled;
932 }
933
934 /*
935 set the current live picture source, does only something on win32,
936 with the external libraries - if the buildin effects are used nothing
937 happens...
938 */
939 static int32_t AtmoSetLiveSource(filter_t *p_filter, int32_t newSource)
940 {
941     filter_sys_t *p_sys = p_filter->p_sys;
942
943     msg_Dbg( p_filter, "AtmoSetLiveSource %d", newSource );
944
945     if(p_sys->p_atmo_config)
946     {
947         /*
948         buildin driver
949
950         doesnt know different sources so this
951         function call would just do nothing special
952         in this case
953         */
954 #if defined(WIN32)
955     } else if(p_sys->pf_ctrl_atmo_set_live_source)
956     {
957         /* on win32 with active ctrl dll */
958         return p_sys->pf_ctrl_atmo_set_live_source(newSource);
959 #endif
960     }
961     return lvsGDI;
962 }
963
964 /*
965 setup the pixel transferbuffers which is used to transfer pixeldata from
966 the filter to the effect thread, and possible accross the process
967 boundaries on win32, with the external DLL
968 */
969 static void AtmoCreateTransferBuffers(filter_t *p_filter,
970                                       int32_t FourCC,
971                                       int32_t bytePerPixel,
972                                       int32_t width,
973                                       int32_t height)
974 {
975     filter_sys_t *p_sys = p_filter->p_sys;
976     if(p_sys->p_atmo_config)
977     {
978         /*
979         we need a buffer where the image is stored (only for transfer
980         to the processing thread)
981         */
982         free( p_sys->p_atmo_transfer_buffer );
983
984         p_sys->p_atmo_transfer_buffer = (uint8_t *)malloc(bytePerPixel *
985                                                           width *  height);
986
987         memset(&p_sys->mini_image_format,0,sizeof(BITMAPINFOHEADER));
988
989         p_sys->mini_image_format.biSize = sizeof(BITMAPINFOHEADER);
990         p_sys->mini_image_format.biWidth = width;
991         p_sys->mini_image_format.biHeight = height;
992         p_sys->mini_image_format.biBitCount = bytePerPixel*8;
993         p_sys->mini_image_format.biCompression = FourCC;
994
995 #if defined(WIN32)
996     } else if(p_sys->pf_ctrl_atmo_create_transfer_buffers)
997     {
998         /* on win32 with active ctrl dll */
999         p_sys->pf_ctrl_atmo_create_transfer_buffers(FourCC,
1000             bytePerPixel,
1001             width,
1002             height);
1003 #endif
1004     }
1005 }
1006
1007 /*
1008 acquire the transfer buffer pointer the buildin version only
1009 returns the pointer to the allocated buffer ... the
1010 external version on win32 has to do some COM stuff to lock the
1011 Variant Byte array which is behind the buffer
1012 */
1013 static uint8_t* AtmoLockTransferBuffer(filter_t *p_filter)
1014 {
1015     filter_sys_t *p_sys = p_filter->p_sys;
1016     if(p_sys->p_atmo_config)
1017     {
1018         return p_sys->p_atmo_transfer_buffer;
1019 #if defined(WIN32)
1020     } else if(p_sys->pf_ctrl_atmo_lock_transfer_buffer)
1021     {
1022         /* on win32 with active ctrl dll */
1023         return p_sys->pf_ctrl_atmo_lock_transfer_buffer();
1024 #endif
1025     }
1026     return NULL;
1027 }
1028
1029 /*
1030 send the content of current pixel buffer got with AtmoLockTransferBuffer
1031 to the processing threads
1032 - build in version - will forward the data to AtmoExternalCaptureInput Thread
1033 - win32 external - will do the same, but across the process boundaries via
1034 COM to the AtmoWinA.exe Process
1035 */
1036 static void AtmoSendPixelData(filter_t *p_filter)
1037 {
1038     filter_sys_t *p_sys = p_filter->p_sys;
1039     if(p_sys->p_atmo_config && p_sys->p_atmo_transfer_buffer)
1040     {
1041         CAtmoDynData *p_atmo_dyndata = p_sys->p_atmo_dyndata;
1042         if(p_atmo_dyndata &&
1043           (p_atmo_dyndata->getLivePictureSource() == lpsExtern))
1044         {
1045             /*
1046             the cast will go Ok because we are inside videolan there is only
1047             this kind of effect thread implemented!
1048             */
1049             CAtmoExternalCaptureInput *p_atmo_external_capture_input_thread =
1050                 (CAtmoExternalCaptureInput *)p_atmo_dyndata->getLiveInput();
1051
1052             if(p_atmo_external_capture_input_thread)
1053             {
1054                 /*
1055                 the same as above inside videolan only this single kind of
1056                 input exists so we can cast without further tests!
1057
1058                 this call will do a 1:1 copy of this buffer, and wakeup
1059                 the thread from normal sleeping
1060                 */
1061                 p_atmo_external_capture_input_thread->
1062                      DeliverNewSourceDataPaket(&p_sys->mini_image_format,
1063                                                p_sys->p_atmo_transfer_buffer);
1064             }
1065         }
1066 #if defined(WIN32)
1067     } else if(p_sys->pf_ctrl_atmo_send_pixel_data)
1068     {
1069         /* on win32 with active ctrl dll */
1070         p_sys->pf_ctrl_atmo_send_pixel_data();
1071 #endif
1072     } else
1073     {
1074        msg_Warn( p_filter, "AtmoSendPixelData no method");
1075     }
1076 }
1077
1078 /*
1079     Shutdown AtmoLight finally - is call from DestroyFilter
1080     does the cleanup restores the effectmode on the external Software
1081     (only win32) and possible setup the final light ...
1082 */
1083 static void Atmo_Shutdown(filter_t *p_filter)
1084 {
1085     filter_sys_t *p_sys = p_filter->p_sys;
1086
1087     if(p_sys->b_enabled == true)
1088     {
1089         msg_Dbg( p_filter, "shut down atmo!");
1090         /*
1091         if there is a still running show pause color thread kill him!
1092         */
1093         CheckAndStopFadeThread(p_filter);
1094
1095         // perpare spawn fadeing thread
1096         vlc_mutex_lock( &p_sys->filter_lock );
1097
1098         /*
1099         fade to end color (in case of external AtmoWin Software
1100         assume that the static color will equal to this
1101         one to get a soft change and no flash!
1102         */
1103         p_sys->b_pause_live = true;
1104
1105
1106         p_sys->p_fadethread = (fadethread_t *)vlc_object_create( p_filter,
1107                                                     sizeof(fadethread_t) );
1108
1109         p_sys->p_fadethread->p_filter = p_filter;
1110         p_sys->p_fadethread->ui_red   = p_sys->ui_endcolor_red;
1111         p_sys->p_fadethread->ui_green = p_sys->ui_endcolor_green;
1112         p_sys->p_fadethread->ui_blue  = p_sys->ui_endcolor_blue;
1113         if(p_sys->i_frames_processed < 50)
1114           p_sys->p_fadethread->i_steps  = 1;
1115         else
1116           p_sys->p_fadethread->i_steps  = p_sys->i_endfadesteps;
1117
1118         if( vlc_thread_create( p_sys->p_fadethread,
1119             "AtmoLight fadeing",
1120             FadeToColorThread,
1121             VLC_THREAD_PRIORITY_LOW ) )
1122         {
1123             msg_Err( p_filter, "cannot create FadeToColorThread" );
1124             vlc_object_release( p_sys->p_fadethread );
1125             p_sys->p_fadethread = NULL;
1126             vlc_mutex_unlock( &p_sys->filter_lock );
1127
1128         } else {
1129
1130             vlc_mutex_unlock( &p_sys->filter_lock );
1131
1132             /* wait for the thread... */
1133             vlc_thread_join(p_sys->p_fadethread);
1134
1135             vlc_object_release(p_sys->p_fadethread);
1136
1137             p_sys->p_fadethread = NULL;
1138         }
1139
1140         /*
1141            the following happens only useing the
1142            external AtmoWin Device Software
1143         */
1144         if( !p_sys->p_atmo_config )
1145         {
1146            if(p_sys->i_AtmoOldEffect != emLivePicture)
1147               AtmoSwitchEffect( p_filter, p_sys->i_AtmoOldEffect);
1148            else
1149               AtmoSetLiveSource( p_filter, lvsGDI );
1150         }
1151
1152         /* close device connection etc. */
1153         AtmoFinalize(p_filter, 1);
1154
1155         /* disable filter method .. */
1156         p_sys->b_enabled = false;
1157     }
1158 }
1159
1160 /*
1161 depending on mode setup imagesize to 64x48(classic), or defined
1162 resolution of external atmowin.exe on windows
1163 */
1164 static void Atmo_SetupImageSize(filter_t *p_filter)
1165 {
1166     filter_sys_t *p_sys = p_filter->p_sys;
1167     /*
1168        size of extracted image by default 64x48 (other imagesizes are
1169        currently ignored by AtmoWin)
1170     */
1171     p_sys->i_atmo_width  = var_CreateGetIntegerCommand( p_filter,
1172         CFG_PREFIX "width");
1173     p_sys->i_atmo_height = var_CreateGetIntegerCommand( p_filter,
1174         CFG_PREFIX "height");
1175
1176     if(p_sys->p_atmo_config)
1177     {
1178 #if defined(WIN32)
1179     } else if(p_sys->pf_ctrl_atmo_get_image_size)
1180     {
1181         /* on win32 with active ctrl dll */
1182         p_sys->pf_ctrl_atmo_get_image_size( &p_sys->i_atmo_width,
1183                                             &p_sys->i_atmo_height );
1184 #endif
1185     }
1186
1187     msg_Dbg(p_filter,"sample image size %d * %d pixels", p_sys->i_atmo_width,
1188         p_sys->i_atmo_height);
1189 }
1190
1191 /*
1192 initialize the zone and channel mapping for the buildin atmolight adapter
1193 */
1194 static void Atmo_SetupBuildZones(filter_t *p_filter)
1195 {
1196     filter_sys_t *p_sys = p_filter->p_sys;
1197
1198     p_sys->p_atmo_dyndata->LockCriticalSection();
1199
1200     CAtmoConfig *p_atmo_config = p_sys->p_atmo_config;
1201
1202
1203     CAtmoChannelAssignment *p_channel_assignment =
1204                             p_atmo_config->getChannelAssignment(0);
1205
1206     // channel 0 - zone 4
1207     p_channel_assignment->setZoneIndex( 0, var_CreateGetIntegerCommand(
1208                                         p_filter, CFG_PREFIX "channel_0")
1209                                         );
1210
1211     // channel 1 - zone 3
1212     p_channel_assignment->setZoneIndex( 1, var_CreateGetIntegerCommand(
1213                                         p_filter, CFG_PREFIX "channel_1")
1214                                         );
1215
1216     // channel 2 - zone 1
1217     p_channel_assignment->setZoneIndex( 2, var_CreateGetIntegerCommand(
1218                                         p_filter, CFG_PREFIX "channel_2")
1219                                         );
1220
1221     // channel 3 - zone 0
1222     p_channel_assignment->setZoneIndex( 3, var_CreateGetIntegerCommand(
1223                                         p_filter, CFG_PREFIX "channel_3")
1224                                         );
1225
1226     // channel 4 - zone 2
1227     p_channel_assignment->setZoneIndex( 4, var_CreateGetIntegerCommand(
1228                                         p_filter, CFG_PREFIX "channel_4")
1229                                         );
1230
1231     char *psz_channels = var_CreateGetStringCommand(
1232               p_filter,
1233               CFG_PREFIX "channels"
1234             );
1235     if( psz_channels && strlen(psz_channels) > 0 )
1236     {
1237         msg_Dbg( p_filter, "deal with new zone mapping %s", psz_channels );
1238         int channel = 0;
1239         char *psz_temp = psz_channels;
1240         char *psz_start = psz_temp;
1241         while( *psz_temp )
1242         {
1243             if(*psz_temp == ',' || *psz_temp == ';')
1244             {
1245                 *psz_temp = 0;
1246                 if(*psz_start)
1247                 {
1248                     int zone = atoi( psz_start );
1249                     if( zone < -1 ||
1250                         zone >= p_channel_assignment->getSize()) {
1251                          msg_Warn( p_filter, "Zone %d out of range -1..%d",
1252                                 zone, p_channel_assignment->getSize()-1 );
1253                     } else {
1254                         p_channel_assignment->setZoneIndex( channel, zone );
1255                         channel++;
1256                     }
1257                 }
1258                 psz_start = psz_temp;
1259                 psz_start++;
1260             }
1261
1262             psz_temp++;
1263         }
1264
1265         /*
1266           process the rest of the string
1267         */
1268         if( *psz_start && !*psz_temp )
1269         {
1270             int zone = atoi( psz_start );
1271             if( zone < -1 ||
1272                 zone >= p_channel_assignment->getSize()) {
1273                 msg_Warn( p_filter, "Zone %d out of range -1..%d",
1274                             zone, p_channel_assignment->getSize()-1 );
1275             } else {
1276                 p_channel_assignment->setZoneIndex( channel, zone );
1277             }
1278         }
1279     }
1280     free( psz_channels );
1281
1282     for(int i=0;i< p_channel_assignment->getSize() ;i++)
1283         msg_Info( p_filter, "map zone %d to hardware channel %d",
1284         p_channel_assignment->getZoneIndex( i ),
1285         i
1286         );
1287     p_sys->p_atmo_dyndata->getAtmoConnection()
1288          ->SetChannelAssignment( p_channel_assignment );
1289
1290
1291
1292
1293
1294     /*
1295       calculate the default gradients for each zone!
1296       depending on the zone layout set before, this now
1297       supports also multiple gradients on each side
1298       (older versions could do this only with external
1299       gradient bitmaps)
1300     */
1301     p_sys->p_atmo_dyndata->CalculateDefaultZones();
1302
1303
1304     /*
1305       first try to load the old style defined gradient bitmaps
1306       this could only be done for the first five zones
1307       - should be deprecated -
1308     */
1309     CAtmoZoneDefinition *p_zone;
1310     char psz_gradient_var_name[30];
1311     char *psz_gradient_file;
1312     for(int i=0;i<CLASSIC_ATMO_NUM_ZONES;i++)
1313     {
1314         sprintf(psz_gradient_var_name, CFG_PREFIX "gradient_zone_%d", i);
1315         psz_gradient_file = var_CreateGetStringCommand(
1316             p_filter,
1317             psz_gradient_var_name
1318             );
1319         if(psz_gradient_file && strlen(psz_gradient_file)>0)
1320         {
1321             msg_Dbg( p_filter, "loading gradientfile %s for "\
1322                                 "zone %d", psz_gradient_file, i);
1323
1324             p_zone = p_atmo_config->getZoneDefinition(i);
1325             if( p_zone )
1326             {
1327                 int i_res = p_zone->LoadGradientFromBitmap(psz_gradient_file);
1328
1329                 if(i_res != ATMO_LOAD_GRADIENT_OK)
1330                 {
1331                     msg_Err( p_filter,"failed to load gradient '%s' with "\
1332                                     "error %d",psz_gradient_file,i_res);
1333                 }
1334             }
1335         }
1336         free( psz_gradient_file );
1337     }
1338
1339
1340     /*
1341       the new approach try to load a gradient bitmap for each zone
1342       from a previously defined folder containing
1343       zone_0.bmp
1344       zone_1.bmp
1345       zone_2.bmp etc.
1346     */
1347     char *psz_gradient_path = var_CreateGetStringCommand(
1348               p_filter,
1349               CFG_PREFIX "gradient_path"
1350             );
1351     if( psz_gradient_path && strlen(psz_gradient_path) > 0 )
1352     {
1353         char *psz_file_name = (char *)malloc( strlen(psz_gradient_path) + 16 );
1354         assert( psz_file_name );
1355
1356         for(int i=0; i < p_atmo_config->getZoneCount(); i++ )
1357         {
1358             p_zone = p_atmo_config->getZoneDefinition(i);
1359
1360             if( p_zone )
1361             {
1362                 sprintf(psz_file_name, "%s%szone_%d.bmp",
1363                                             psz_gradient_path, DIR_SEP, i );
1364
1365                 int i_res = p_zone->LoadGradientFromBitmap( psz_file_name );
1366
1367                 if( i_res == ATMO_LOAD_GRADIENT_OK )
1368                 {
1369                 msg_Dbg( p_filter, "loaded gradientfile %s for "\
1370                                    "zone %d", psz_file_name, i);
1371                 }
1372
1373                 if( (i_res != ATMO_LOAD_GRADIENT_OK) &&
1374                     (i_res != ATMO_LOAD_GRADIENT_FILENOTFOND) )
1375                 {
1376                     msg_Err( p_filter,"failed to load gradient '%s' with "\
1377                                     "error %d",psz_file_name,i_res);
1378                 }
1379             }
1380         }
1381
1382         free( psz_file_name );
1383     }
1384     free( psz_gradient_path );
1385
1386
1387     p_sys->p_atmo_dyndata->UnLockCriticalSection();
1388
1389 }
1390
1391 static void Atmo_SetupConfig(filter_t *p_filter, CAtmoConfig *p_atmo_config)
1392 {
1393     /*
1394        figuring out the device ports (com-ports, ttys)
1395     */
1396     char *psz_serialdev = var_CreateGetStringCommand( p_filter,
1397                                                       CFG_PREFIX "serialdev" );
1398     char *psz_temp = psz_serialdev;
1399
1400     if( psz_temp && strlen(psz_temp) > 0 )
1401     {
1402         char *psz_token;
1403         int i_port = 0;
1404         int i;
1405         int j;
1406
1407         msg_Dbg( p_filter, "use port(s) %s",psz_serialdev);
1408
1409         /*
1410           psz_serialdev - may contain up to 4 COM ports for the quattro device
1411           the quattro device is just hack of useing 4 classic devices as one
1412           logical device - thanks that usb-com-ports exists :)
1413           as Seperator I defined , or ; with the hope that these
1414           characters are never part of a device name
1415         */
1416         while( (psz_token = strsep(&psz_temp, ",;")) != NULL && i_port < 4 )
1417         {
1418             /*
1419               psz_token may contain spaces we have to trim away
1420             */
1421             i = 0;
1422             j = 0;
1423             /*
1424               find first none space in string
1425             */
1426             while( psz_token[i] == 32 ) i++;
1427             /*
1428               contains string only spaces or is empty? skip it
1429             */
1430             if( !psz_token[i] )
1431                 continue;
1432
1433             /*
1434               trim
1435             */
1436             while( psz_token[i] && psz_token[i] != 32 )
1437                 psz_token[ j++ ] = psz_token[ i++ ];
1438             psz_token[j++] = 0;
1439
1440             msg_Dbg( p_filter, "Serial Device [%d]: %s", i_port, psz_token );
1441
1442             p_atmo_config->setSerialDevice( i_port, psz_token );
1443
1444             i_port++;
1445         }
1446     }
1447     else
1448     {
1449        msg_Err(p_filter,"no serial devicename(s) set");
1450     }
1451     free( psz_serialdev );
1452
1453     /*
1454       configuration of light source layout arround the display
1455     */
1456     p_atmo_config->setZonesTopCount(
1457         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "zones-top")
1458         );
1459     p_atmo_config->setZonesBottomCount(
1460         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "zones-bottom")
1461         );
1462     p_atmo_config->setZonesLRCount(
1463         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "zones-lr")
1464         );
1465     p_atmo_config->setZoneSummary(
1466         var_CreateGetBoolCommand( p_filter, CFG_PREFIX "zone-summary")
1467         );
1468
1469
1470     p_atmo_config->setLiveViewFilterMode(
1471         (AtmoFilterMode)var_CreateGetIntegerCommand( p_filter,
1472                                                 CFG_PREFIX "filtermode")
1473         );
1474
1475     p_atmo_config->setLiveViewFilter_PercentNew(
1476         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "percentnew")
1477         );
1478     p_atmo_config->setLiveViewFilter_MeanLength(
1479         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "meanlength")
1480         );
1481     p_atmo_config->setLiveViewFilter_MeanThreshold(
1482         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "meanthreshold")
1483         );
1484
1485     p_atmo_config->setLiveView_EdgeWeighting(
1486         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "edgeweightning")
1487         );
1488     p_atmo_config->setLiveView_BrightCorrect(
1489         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "brightness")
1490         );
1491     p_atmo_config->setLiveView_DarknessLimit(
1492         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "darknesslimit")
1493         );
1494     p_atmo_config->setLiveView_HueWinSize(
1495         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "huewinsize")
1496         );
1497     p_atmo_config->setLiveView_SatWinSize(
1498         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "satwinsize")
1499         );
1500
1501     /* currently not required inside vlc */
1502     p_atmo_config->setLiveView_WidescreenMode( 0 );
1503
1504     p_atmo_config->setLiveView_FrameDelay(
1505         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "framedelay")
1506         );
1507
1508
1509     p_atmo_config->setUseSoftwareWhiteAdj(
1510         var_CreateGetBoolCommand( p_filter, CFG_PREFIX "whiteadj")
1511         );
1512     p_atmo_config->setWhiteAdjustment_Red(
1513         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-red")
1514         );
1515     p_atmo_config->setWhiteAdjustment_Green(
1516         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-green")
1517         );
1518     p_atmo_config->setWhiteAdjustment_Blue(
1519         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-blue")
1520         );
1521
1522     /*
1523       settings for DMX device only
1524     */
1525     p_atmo_config->setDMX_RGB_Channels(
1526         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "dmx-channels")
1527         );
1528
1529     char *psz_chbase = var_CreateGetStringCommand( p_filter,
1530                                                    CFG_PREFIX "dmx-chbase" );
1531     if( psz_chbase && strlen(psz_chbase) > 0 )
1532         p_atmo_config->setDMX_BaseChannels( psz_chbase );
1533
1534     free( psz_chbase );
1535
1536     /*
1537       momolight options
1538     */
1539     p_atmo_config->setMoMo_Channels(
1540         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "momo-channels")
1541        );
1542
1543     /*
1544       fnordlicht options
1545     */
1546     p_atmo_config->setFnordlicht_Amount(
1547         var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "fnordlicht-amount")
1548        );
1549
1550 }
1551
1552
1553 /*
1554 initialize the filter_sys_t structure with the data from the settings
1555 variables - if the external filter on win32 is enabled try loading the DLL,
1556 if this fails fallback to the buildin software
1557 */
1558 static void Atmo_SetupParameters(filter_t *p_filter)
1559 {
1560     char *psz_path;
1561     filter_sys_t *p_sys =  p_filter->p_sys;
1562
1563
1564     /* default filter disabled until DLL loaded and Init Success!*/
1565     p_sys->b_enabled             = false;
1566
1567     /* setup default mini image size (may be later a user option) */
1568     p_sys->i_atmo_width          = 64;
1569     p_sys->i_atmo_height         = 48;
1570
1571     p_sys->i_device_type = var_CreateGetIntegerCommand( p_filter,
1572                                                         CFG_PREFIX "device");
1573
1574     /*
1575       i_device_type
1576        0 => use AtmoWin Software (only win32)
1577        1 => use AtmoClassicConnection (direct)
1578        2 => use AtmoMultiConnection (direct up to four serial ports required)
1579        3 => use AtmoDmxConnection (simple serial DMX Device up to 255 channels)
1580     */
1581
1582
1583 #if defined(WIN32)
1584     /*
1585     only on WIN32 the user has the choice between
1586     internal driver and external
1587     */
1588
1589     if(p_sys->i_device_type == 0) {
1590
1591         /* Load the Com Wrapper Library (source available) */
1592         p_sys->h_AtmoCtrl = LoadLibraryA("AtmoCtrlLib.dll");
1593         if(p_sys->h_AtmoCtrl == NULL)
1594         {
1595             /*
1596               be clever if the location of atmowina.exe is set
1597               try to load the dll from the same folder :-)
1598             */
1599             char *psz_path = var_CreateGetStringCommand( p_filter,
1600                                                CFG_PREFIX "atmowinexe" );
1601             if( psz_path && strlen(psz_path) > 0 )
1602             {
1603                 char *psz_bs = strrchr( psz_path , '\\');
1604                 if( psz_bs )
1605                 {
1606                     *psz_bs = 0;
1607                     /*
1608                       now format a new dll filename with complete path
1609                     */
1610                     char *psz_dllname = NULL;
1611                     asprintf( &psz_dllname, "%s\\AtmoCtrlLib.dll", psz_path );
1612                     if( psz_dllname )
1613                     {
1614                         msg_Dbg( p_filter, "Try Loading '%s'", psz_dllname );
1615                         p_sys->h_AtmoCtrl = LoadLibraryA( psz_dllname );
1616                     }
1617                     free( psz_dllname );
1618                 }
1619             }
1620             free( psz_path );
1621         }
1622
1623
1624         if(p_sys->h_AtmoCtrl != NULL)
1625         {
1626             msg_Dbg( p_filter, "Load Library ok!");
1627
1628             /* importing all required functions I hope*/
1629             p_sys->pf_ctrl_atmo_initialize =
1630                 (int32_t (*)(void))GetProcAddress(p_sys->h_AtmoCtrl,
1631                             "AtmoInitialize");
1632             if(!p_sys->pf_ctrl_atmo_initialize)
1633                 msg_Err( p_filter, "export AtmoInitialize missing.");
1634
1635             p_sys->pf_ctrl_atmo_finalize =
1636                 (void (*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,
1637                             "AtmoFinalize");
1638             if(!p_sys->pf_ctrl_atmo_finalize)
1639                 msg_Err( p_filter, "export AtmoFinalize missing.");
1640
1641             p_sys->pf_ctrl_atmo_switch_effect =
1642                 (int32_t(*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,
1643                             "AtmoSwitchEffect");
1644             if(!p_sys->pf_ctrl_atmo_switch_effect)
1645                 msg_Err( p_filter, "export AtmoSwitchEffect missing.");
1646
1647             p_sys->pf_ctrl_atmo_set_live_source =
1648                 (int32_t(*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,
1649                             "AtmoSetLiveSource");
1650             if(!p_sys->pf_ctrl_atmo_set_live_source)
1651                 msg_Err( p_filter, "export AtmoSetLiveSource missing.");
1652
1653             p_sys->pf_ctrl_atmo_create_transfer_buffers =
1654                 (void (*)(int32_t, int32_t, int32_t , int32_t))
1655                     GetProcAddress(p_sys->h_AtmoCtrl,"AtmoCreateTransferBuffers");
1656             if(!p_sys->pf_ctrl_atmo_create_transfer_buffers)
1657                 msg_Err( p_filter, "export AtmoCreateTransferBuffers missing.");
1658
1659             p_sys->pf_ctrl_atmo_lock_transfer_buffer=
1660                 (uint8_t*(*) (void))GetProcAddress(p_sys->h_AtmoCtrl,
1661                             "AtmoLockTransferBuffer");
1662             if(!p_sys->pf_ctrl_atmo_lock_transfer_buffer)
1663                 msg_Err( p_filter, "export AtmoLockTransferBuffer missing.");
1664
1665             p_sys->pf_ctrl_atmo_send_pixel_data =
1666                 (void (*)(void))GetProcAddress(p_sys->h_AtmoCtrl,
1667                             "AtmoSendPixelData");
1668             if(!p_sys->pf_ctrl_atmo_send_pixel_data)
1669                 msg_Err( p_filter, "export AtmoSendPixelData missing.");
1670
1671             p_sys->pf_ctrl_atmo_get_image_size =
1672                 (void (*)(int32_t*,int32_t*))GetProcAddress(p_sys->h_AtmoCtrl,
1673                             "AtmoWinGetImageSize");
1674             if(!p_sys->pf_ctrl_atmo_get_image_size)
1675                 msg_Err( p_filter, "export AtmoWinGetImageSize missing.");
1676
1677         } else {
1678             /* the DLL is missing try internal filter ...*/
1679             msg_Warn( p_filter,
1680                 "AtmoCtrlLib.dll missing fallback to internal atmo classic driver");
1681             p_sys->i_device_type = 1;
1682         }
1683     }
1684 #endif
1685
1686     if(p_sys->i_device_type >= 1) {
1687         msg_Dbg( p_filter, "try use buildin driver %d ", p_sys->i_device_type);
1688         /*
1689         now we have to read a lof of options from the config dialog
1690         most important the serial device if not set ... we can skip
1691         the rest and disable the filter...
1692         */
1693
1694         p_sys->p_atmo_config = new CAtmoConfig();
1695
1696         p_sys->p_atmo_dyndata = new CAtmoDynData(
1697                    (vlc_object_t *)p_filter,
1698                    p_sys->p_atmo_config
1699         );
1700
1701         Atmo_SetupConfig( p_filter, p_sys->p_atmo_config );
1702         switch(p_sys->i_device_type)
1703         {
1704             case 1:
1705                 p_sys->p_atmo_config->setConnectionType( actClassicAtmo );
1706                 break;
1707
1708             case 2:
1709                 p_sys->p_atmo_config->setConnectionType( actMultiAtmo );
1710                 break;
1711
1712             case 3:
1713                 p_sys->p_atmo_config->setConnectionType( actDMX );
1714                 break;
1715
1716             case 4:
1717                 p_sys->p_atmo_config->setConnectionType( actMoMoLight );
1718                 break;
1719
1720             case 5:
1721                 p_sys->p_atmo_config->setConnectionType( actFnordlicht );
1722                 break;
1723
1724             default:
1725                 msg_Warn( p_filter, "invalid device type %d found",
1726                                     p_sys->i_device_type );
1727         }
1728
1729         msg_Dbg( p_filter, "buildin driver config set");
1730
1731     }
1732
1733     switch( p_filter->fmt_in.video.i_chroma )
1734     {
1735     case VLC_CODEC_I420:
1736     case VLC_CODEC_YV12:
1737         p_sys->pf_extract_mini_image = ExtractMiniImage_YUV;
1738         break;
1739     default:
1740         msg_Warn( p_filter, "InitFilter-unsupported chroma: %4.4s",
1741                             (char *)&p_filter->fmt_in.video.i_chroma);
1742         p_sys->pf_extract_mini_image = NULL;
1743     }
1744
1745     /*
1746     for debugging purpose show the samplinggrid on each frame as
1747     white dots
1748     */
1749     p_sys->b_show_dots = var_CreateGetBoolCommand( p_filter,
1750         CFG_PREFIX "showdots"
1751         );
1752
1753 #if defined(__ATMO_DEBUG__)
1754     /* save debug images to a folder as Bitmap files ? */
1755     p_sys->b_saveframes  = var_CreateGetBoolCommand( p_filter,
1756         CFG_PREFIX "saveframes"
1757         );
1758     msg_Dbg(p_filter,"saveframes = %d", (int)p_sys->b_saveframes);
1759
1760     /*
1761     read debug image folder from config
1762     */
1763     psz_path = var_CreateGetStringCommand( p_filter, CFG_PREFIX "framepath" );
1764     if(psz_path != NULL)
1765     {
1766         strcpy(p_sys->sz_framepath, psz_path);
1767 #if defined( WIN32 )
1768         size_t i_strlen = strlen(p_sys->sz_framepath);
1769         if((i_strlen>0) && (p_sys->sz_framepath[i_strlen-1] != '\\'))
1770         {
1771             p_sys->sz_framepath[i_strlen] = '\\';
1772             p_sys->sz_framepath[i_strlen+1] = 0;
1773         }
1774 #endif
1775         free(psz_path);
1776     }
1777     msg_Dbg(p_filter,"saveframesfolder %s",p_sys->sz_framepath);
1778 #endif
1779
1780
1781     /*
1782     because atmowin could also be used for lighten up the room - I think if you
1783     pause the video it would be useful to get a little bit more light into to
1784     your living room? - instead switching on a lamp?
1785     */
1786     p_sys->b_usepausecolor = var_CreateGetBoolCommand( p_filter,
1787         CFG_PREFIX "usepausecolor" );
1788     p_sys->ui_pausecolor_red = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1789         CFG_PREFIX "pcolor-red");
1790     p_sys->ui_pausecolor_green = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1791         CFG_PREFIX "pcolor-green");
1792     p_sys->ui_pausecolor_blue = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1793         CFG_PREFIX "pcolor-blue");
1794     p_sys->i_fadesteps = var_CreateGetIntegerCommand( p_filter,
1795         CFG_PREFIX "fadesteps");
1796     if(p_sys->i_fadesteps < 1)
1797         p_sys->i_fadesteps = 1;
1798     msg_Dbg(p_filter,"use pause color %d, RGB: %d, %d, %d, Fadesteps: %d",
1799         (int)p_sys->b_usepausecolor,
1800         p_sys->ui_pausecolor_red,
1801         p_sys->ui_pausecolor_green,
1802         p_sys->ui_pausecolor_blue,
1803         p_sys->i_fadesteps);
1804
1805     /*
1806     this color is use on shutdown of the filter - the define the
1807     final light after playback... may be used to dim up the light -
1808     how it happens in the cinema...
1809     */
1810     p_sys->ui_endcolor_red = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1811         CFG_PREFIX "ecolor-red");
1812     p_sys->ui_endcolor_green = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1813         CFG_PREFIX "ecolor-green");
1814     p_sys->ui_endcolor_blue = (uint8_t)var_CreateGetIntegerCommand( p_filter,
1815         CFG_PREFIX "ecolor-blue");
1816     p_sys->i_endfadesteps = var_CreateGetIntegerCommand( p_filter,
1817         CFG_PREFIX "efadesteps");
1818     if(p_sys->i_endfadesteps < 1)
1819         p_sys->i_endfadesteps = 1;
1820     msg_Dbg(p_filter,"use ende color RGB: %d, %d, %d, Fadesteps: %d",
1821         p_sys->ui_endcolor_red,
1822         p_sys->ui_endcolor_green,
1823         p_sys->ui_endcolor_blue,
1824         p_sys->i_endfadesteps);
1825
1826
1827
1828     /*
1829       if the external DLL was loaded successfully call AtmoInitialize -
1830       (must be done for each thread where you want to use AtmoLight!)
1831     */
1832     int i = AtmoInitialize(p_filter, false);
1833
1834 #if defined( WIN32 )
1835     if((i != 1) && (p_sys->i_device_type == 0))
1836     {
1837         /*
1838           COM Server for AtmoLight not running ?
1839           if the exe path is configured try to start the "userspace" driver
1840         */
1841         psz_path = var_CreateGetStringCommand( p_filter,
1842                                                CFG_PREFIX "atmowinexe" );
1843         if(psz_path != NULL)
1844         {
1845             STARTUPINFO startupinfo;
1846             PROCESS_INFORMATION pinfo;
1847             memset(&startupinfo, 0, sizeof(STARTUPINFO));
1848             startupinfo.cb = sizeof(STARTUPINFO);
1849             if(CreateProcess(psz_path, NULL, NULL, NULL,
1850                 FALSE, 0, NULL, NULL, &startupinfo, &pinfo) == TRUE)
1851             {
1852                 msg_Dbg(p_filter,"launched AtmoWin from %s",psz_path);
1853                 WaitForInputIdle(pinfo.hProcess, 5000);
1854                 /*
1855                   retry to initialize the library COM ... functionality
1856                   after the server was launched
1857                 */
1858                 i = AtmoInitialize(p_filter, false);
1859             } else {
1860                 msg_Err(p_filter,"failed to launch AtmoWin from %s", psz_path);
1861             }
1862             free(psz_path);
1863         }
1864     }
1865 #endif
1866
1867     if(i == 1) /* Init Atmolight success... */
1868     {
1869         msg_Dbg( p_filter, "AtmoInitialize Ok!");
1870         /*
1871         configure
1872            p_sys->i_atmo_width and p_sys->i_atmo_height
1873            if the external AtmoWinA.exe is used, it may require
1874            a other sample image size than 64 x 48
1875            (this overrides the settings of the filter)
1876         */
1877         Atmo_SetupImageSize( p_filter );
1878
1879
1880         if( p_sys->i_device_type >= 1 )
1881         {
1882            /*
1883              AtmoConnection class initialized now we can initialize
1884              the default zone and channel mappings
1885            */
1886            Atmo_SetupBuildZones( p_filter );
1887         }
1888
1889         /* Setup Transferbuffers for 64 x 48 , RGB with 32bit Per Pixel */
1890         AtmoCreateTransferBuffers(p_filter, BI_RGB, 4,
1891             p_sys->i_atmo_width,
1892             p_sys->i_atmo_height
1893             );
1894
1895         /* say the userspace driver that a live mode should be activated
1896         the functions returns the old mode for later restore!
1897         - the buildin driver launches the live view thread in that case
1898         */
1899         p_sys->i_AtmoOldEffect = AtmoSwitchEffect(p_filter, emLivePicture);
1900
1901         /*
1902         live view can have two differnt source the AtmoWinA
1903         internal GDI Screencapture and the external one - which we
1904         need here...
1905         */
1906         AtmoSetLiveSource(p_filter, lvsExternal);
1907
1908         /* enable other parts only if everything is fine */
1909         p_sys->b_enabled = true;
1910
1911         msg_Dbg( p_filter, "Atmo Filter Enabled Ok!");
1912     }
1913
1914 }
1915
1916
1917 /*****************************************************************************
1918 * CreateFilter: allocates AtmoLight video thread output method
1919 *****************************************************************************
1920 * This function allocates and initializes a AtmoLight vout method.
1921 *****************************************************************************/
1922 static int CreateFilter( vlc_object_t *p_this )
1923 {
1924     filter_t *p_filter = (filter_t *)p_this;
1925     filter_sys_t *p_sys;
1926
1927     /* Allocate structure */
1928     p_sys = (filter_sys_t *)malloc( sizeof( filter_sys_t ) );
1929     p_filter->p_sys = p_sys;
1930     if( p_filter->p_sys == NULL )
1931         return VLC_ENOMEM;
1932     /* set all entries to zero */
1933     memset(p_sys, 0, sizeof( filter_sys_t ));
1934     vlc_mutex_init( &p_sys->filter_lock );
1935
1936     msg_Dbg( p_filter, "Create Atmo Filter");
1937
1938     /* further Setup Function pointers for videolan for calling my filter */
1939     p_filter->pf_video_filter = Filter;
1940
1941     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
1942                        p_filter->p_cfg );
1943
1944     AddStateVariableCallback(p_filter);
1945
1946     AddAtmoSettingsVariablesCallbacks(p_filter);
1947
1948     Atmo_SetupParameters(p_filter);
1949
1950
1951     return VLC_SUCCESS;
1952 }
1953
1954
1955
1956 /*****************************************************************************
1957 * DestroyFilter: destroy AtmoLight video thread output method
1958 *****************************************************************************
1959 * Terminate an output method created by CreateFilter
1960 *****************************************************************************/
1961
1962 static void DestroyFilter( vlc_object_t *p_this )
1963 {
1964     filter_t *p_filter = (filter_t *)p_this;
1965     filter_sys_t *p_sys =  p_filter->p_sys;
1966
1967     msg_Dbg( p_filter, "Destroy Atmo Filter");
1968
1969     DelStateVariableCallback(p_filter);
1970
1971     DelAtmoSettingsVariablesCallbacks(p_filter);
1972
1973     Atmo_Shutdown(p_filter);
1974
1975 #if defined( WIN32 )
1976     if(p_sys->h_AtmoCtrl != NULL)
1977     {
1978         FreeLibrary(p_sys->h_AtmoCtrl);
1979     }
1980 #endif
1981
1982     delete p_sys->p_atmo_dyndata;
1983     delete p_sys->p_atmo_config;
1984
1985     vlc_mutex_destroy( &p_sys->filter_lock );
1986
1987     free( p_sys );
1988 }
1989
1990
1991 /*
1992 function stolen from some other videolan source filter ;-)
1993 for the moment RGB is OK... but better would be a direct transformation
1994 from YUV --> HSV
1995 */
1996 static inline void yuv_to_rgb( uint8_t *r, uint8_t *g, uint8_t *b,
1997                               uint8_t y1, uint8_t u1, uint8_t v1 )
1998 {
1999     /* macros used for YUV pixel conversions */
2000 #   define SCALEBITS 10
2001 #   define ONE_HALF  (1 << (SCALEBITS - 1))
2002 #   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
2003 #   define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));
2004
2005     int y, cb, cr, r_add, g_add, b_add;
2006
2007     cb = u1 - 128;
2008     cr = v1 - 128;
2009     r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
2010     g_add = - FIX(0.34414*255.0/224.0) * cb
2011         - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
2012     b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
2013     y = (y1 - 16) * FIX(255.0/219.0);
2014     *r = CLAMP((y + r_add) >> SCALEBITS);
2015     *g = CLAMP((y + g_add) >> SCALEBITS);
2016     *b = CLAMP((y + b_add) >> SCALEBITS);
2017 }
2018 /******************************************************************************
2019 * ExtractMiniImage_YUV: extract a small image from the picture as 24-bit RGB
2020 *******************************************************************************
2021 * p_sys is a pointer to
2022 * p_inpic is the source frame
2023 * p_transfer_dest is the target buffer for the picture must be big enough!
2024 * (in win32 enviroment this buffer comes from the external DLL where it is
2025 * create as "variant array" and returned through the AtmoLockTransferbuffer
2026 */
2027 static void ExtractMiniImage_YUV(filter_sys_t *p_sys,
2028                                  picture_t *p_inpic,
2029                                  uint8_t *p_transfer_dest)
2030 {
2031     int i_col;
2032     int i_row;
2033     uint8_t *p_src_y;
2034     uint8_t *p_src_u;
2035     uint8_t *p_src_v;
2036     uint8_t *p_rgb_dst_line_red;
2037     uint8_t *p_rgb_dst_line_green;
2038     uint8_t *p_rgb_dst_line_blue;
2039     int i_xpos_y;
2040     int i_xpos_u;
2041     int i_xpos_v;
2042
2043     /* calcute Pointers for Storage of B G R (A) */
2044     p_rgb_dst_line_blue      = p_transfer_dest;
2045     p_rgb_dst_line_green     = p_transfer_dest + 1;
2046     p_rgb_dst_line_red       = p_transfer_dest + 2 ;
2047
2048     int i_row_count = p_sys->i_atmo_height + 1;
2049     int i_col_count = p_sys->i_atmo_width + 1;
2050     int i_y_row,i_u_row,i_v_row,i_pixel_row;
2051     int i_pixel_col;
2052
2053
2054     /*  these two ugly loops extract the small image - goes it faster? how?
2055     the loops are so designed that there is a small border around the extracted
2056     image so we wont get column and row - zero from the frame, and not the most
2057     right and bottom pixels --- which may be clipped on computers useing TV out
2058     - through overscan!
2059
2060     TODO: try to find out if the output is clipped through VLC - and try here
2061     to ingore the clipped away area for a better result!
2062
2063     TODO: performance improvement in InitFilter percalculated the offsets of
2064     the lines inside the planes so I can save (i_row_count * 3) 2xMUL and
2065     one time DIV the same could be done for the inner loop I think...
2066     */
2067     for(i_row = 1; i_row < i_row_count; i_row++)
2068     {
2069         // calcute the current Lines in the source planes for this outputrow
2070         /*  Adresscalcuation  pointer to plane  Length of one pixelrow in bytes
2071         calculate row now number
2072         */
2073         /*
2074            p_inpic->format? transform Pixel row into row of plane...
2075            how? simple? fast? good?
2076         */
2077
2078         /* compute the source pixel row and respect the active cropping */
2079         i_pixel_row = (i_row * p_sys->i_crop_height) / i_row_count
2080             + p_sys->i_crop_y_offset;
2081
2082         /*
2083         trans for these Pixel row into the row of each plane ..
2084         because planesize can differ from image size
2085         */
2086         i_y_row = (i_pixel_row * p_inpic->p[Y_PLANE].i_visible_lines) /
2087             p_inpic->format.i_visible_height;
2088
2089         i_u_row = (i_pixel_row * p_inpic->p[U_PLANE].i_visible_lines) /
2090             p_inpic->format.i_visible_height;
2091
2092         i_v_row = (i_pixel_row * p_inpic->p[V_PLANE].i_visible_lines) /
2093             p_inpic->format.i_visible_height;
2094
2095         /* calculate  the pointers to the pixeldata for this row
2096            in each plane
2097         */
2098         p_src_y = p_inpic->p[Y_PLANE].p_pixels +
2099             p_inpic->p[Y_PLANE].i_pitch * i_y_row;
2100         p_src_u = p_inpic->p[U_PLANE].p_pixels +
2101             p_inpic->p[U_PLANE].i_pitch * i_u_row;
2102         p_src_v = p_inpic->p[V_PLANE].p_pixels +
2103             p_inpic->p[V_PLANE].i_pitch * i_v_row;
2104
2105         for(i_col = 1; i_col < i_col_count; i_col++)
2106         {
2107             i_pixel_col = (i_col * p_sys->i_crop_width) / i_col_count +
2108                 p_sys->i_crop_x_offset;
2109             /*
2110             trans for these Pixel row into the row of each plane ..
2111             because planesize can differ from image size
2112             */
2113             i_xpos_y = (i_pixel_col * p_inpic->p[Y_PLANE].i_visible_pitch) /
2114                 p_inpic->format.i_visible_width;
2115             i_xpos_u = (i_pixel_col * p_inpic->p[U_PLANE].i_visible_pitch) /
2116                 p_inpic->format.i_visible_width;
2117             i_xpos_v = (i_pixel_col * p_inpic->p[V_PLANE].i_visible_pitch) /
2118                 p_inpic->format.i_visible_width;
2119
2120             yuv_to_rgb(p_rgb_dst_line_red,
2121                 p_rgb_dst_line_green,
2122                 p_rgb_dst_line_blue,
2123
2124                 p_src_y[i_xpos_y],
2125                 p_src_u[i_xpos_u],
2126                 p_src_v[i_xpos_v]);
2127
2128             /* +4 because output image should be RGB32 with dword alignment! */
2129             p_rgb_dst_line_red   += 4;
2130             p_rgb_dst_line_green += 4;
2131             p_rgb_dst_line_blue  += 4;
2132         }
2133    }
2134
2135    if(p_sys->b_show_dots)
2136    {
2137        for(i_row = 1; i_row < i_row_count; i_row++)
2138        {
2139            i_pixel_row = (i_row * p_sys->i_crop_height) / i_row_count
2140                    + p_sys->i_crop_y_offset;
2141
2142            i_y_row = (i_pixel_row * p_inpic->p[Y_PLANE].i_visible_lines) /
2143                    p_inpic->format.i_visible_height;
2144
2145            p_src_y = p_inpic->p[Y_PLANE].p_pixels +
2146                    p_inpic->p[Y_PLANE].i_pitch * i_y_row;
2147
2148            for(i_col = 1; i_col < i_col_count; i_col++)
2149            {
2150               i_pixel_col = (i_col * p_sys->i_crop_width) / i_col_count +
2151                             p_sys->i_crop_x_offset;
2152               i_xpos_y = (i_pixel_col * p_inpic->p[Y_PLANE].i_visible_pitch) /
2153                          p_inpic->format.i_visible_width;
2154
2155               p_src_y[i_xpos_y] = 255;
2156            }
2157        }
2158    }
2159
2160 }
2161
2162
2163 /******************************************************************************
2164 * SaveBitmap: Saves the content of a transferbuffer as Bitmap to disk
2165 *******************************************************************************
2166 * just for debugging
2167 * p_sys -> configuration if Atmo from there the function will get height and
2168 *          width
2169 * p_pixels -> should be the dword aligned BGR(A) image data
2170 * psz_filename -> filename where to store
2171 */
2172 #if defined(__ATMO_DEBUG__)
2173 void SaveBitmap(filter_sys_t *p_sys, uint8_t *p_pixels, char *psz_filename)
2174 {
2175     /* for debug out only used*/
2176     BITMAPINFO bmp_info;
2177     BITMAPFILEHEADER  bmp_fileheader;
2178     FILE *fp_bitmap;
2179
2180     memset(&bmp_info, 0, sizeof(BITMAPINFO));
2181     bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
2182     bmp_info.bmiHeader.biSizeImage   = p_sys->i_atmo_height *
2183                                        p_sys->i_atmo_width * 4;
2184     bmp_info.bmiHeader.biCompression = BI_RGB;
2185     bmp_info.bmiHeader.biWidth        = p_sys->i_atmo_width;
2186     bmp_info.bmiHeader.biHeight       = -p_sys->i_atmo_height;
2187     bmp_info.bmiHeader.biBitCount     = 32;
2188     bmp_info.bmiHeader.biPlanes       = 1;
2189
2190     bmp_fileheader.bfReserved1 = 0;
2191     bmp_fileheader.bfReserved2 = 0;
2192     bmp_fileheader.bfSize = sizeof(BITMAPFILEHEADER) +
2193                             sizeof(BITMAPINFOHEADER) +
2194                             bmp_info.bmiHeader.biSizeImage;
2195     bmp_fileheader.bfType = VLC_TWOCC('B','M');
2196     bmp_fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) +
2197                                sizeof(BITMAPINFOHEADER);
2198
2199     fp_bitmap = fopen(psz_filename,"wb");
2200     if( fp_bitmap != NULL)
2201     {
2202         fwrite(&bmp_fileheader, sizeof(BITMAPFILEHEADER), 1, fp_bitmap);
2203         fwrite(&bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp_bitmap);
2204         fwrite(p_pixels, bmp_info.bmiHeader.biSizeImage, 1, fp_bitmap);
2205         fclose(fp_bitmap);
2206     }
2207 }
2208 #endif
2209
2210
2211 /****************************************************************************
2212 * CreateMiniImage: extracts a 64x48 pixel image from the frame
2213 * (there is a small border arround thats why the loops starts with one
2214 * instead zero) without any interpolation
2215 *****************************************************************************/
2216 static void CreateMiniImage( filter_t *p_filter, picture_t *p_inpic)
2217 {
2218     filter_sys_t *p_sys = p_filter->p_sys;
2219     /*
2220     pointer to RGB Buffer created in external libary as safe array which
2221     is locked inside AtmoLockTransferBuffer
2222     */
2223     uint8_t *p_transfer;
2224 #if defined( __ATMO_DEBUG__ )
2225     /* for debug out only used*/
2226     char sz_filename[MAX_PATH];
2227 #endif
2228
2229     /*
2230     Lock the before created VarArray (AtmoCreateTransferBuffers)
2231     inside my wrapper library and give me a pointer to the buffer!
2232     below linux a global buffer may be used and protected with a mutex?
2233     */
2234     p_transfer = AtmoLockTransferBuffer(p_filter);
2235     if(p_transfer == NULL)
2236     {
2237         msg_Err( p_filter, "AtmoLight no transferbuffer available. "\
2238                            "AtmoLight will be disabled!");
2239         p_sys->b_enabled = false;
2240         return;
2241     }
2242
2243     /*
2244     do the call via pointer to function instead of having a
2245     case structure here
2246     */
2247     p_sys->pf_extract_mini_image(p_sys, p_inpic, p_transfer);
2248
2249
2250 #if defined( __ATMO_DEBUG__ )
2251     /*
2252     if debugging enabled save every 128th image to disk
2253     */
2254     if((p_sys->b_saveframes == true) && (p_sys->sz_framepath[0] != 0 ))
2255     {
2256
2257         if((p_sys->ui_frame_counter & 127) == 0)
2258         {
2259             sprintf(sz_filename,"%satmo_dbg_%06u.bmp",p_sys->sz_framepath,
2260                 p_sys->ui_frame_counter);
2261             msg_Dbg(p_filter, "SaveFrame %s",sz_filename);
2262
2263             SaveBitmap(p_sys, p_transfer, sz_filename);
2264         }
2265     }
2266
2267     msg_Dbg( p_filter, "AtmoFrame %u Time: %d ms", p_sys->ui_frame_counter,
2268                 mdate() / 1000);
2269     p_sys->ui_frame_counter++;
2270 #endif
2271
2272     p_sys->i_frames_processed++;
2273
2274
2275     /* show the colors on the wall */
2276     AtmoSendPixelData( p_filter );
2277 }
2278
2279
2280
2281
2282 /*****************************************************************************
2283 * Filter: calls the extract method and forwards the incomming picture 1:1
2284 *****************************************************************************
2285 *
2286 *****************************************************************************/
2287
2288 static picture_t * Filter( filter_t *p_filter, picture_t *p_pic )
2289 {
2290     filter_sys_t *p_sys = p_filter->p_sys;
2291     if( !p_pic ) return NULL;
2292
2293     vlc_mutex_lock( &p_sys->filter_lock );
2294
2295     if((p_sys->b_enabled == true) &&
2296         (p_sys->pf_extract_mini_image != NULL) &&
2297         (p_sys->b_pause_live == false))
2298     {
2299         p_sys->i_crop_x_offset  = p_filter->fmt_in.video.i_x_offset;
2300         p_sys->i_crop_y_offset  = p_filter->fmt_in.video.i_y_offset;
2301         p_sys->i_crop_width     = p_filter->fmt_in.video.i_visible_width;
2302         p_sys->i_crop_height    = p_filter->fmt_in.video.i_visible_height;
2303
2304         CreateMiniImage(p_filter, p_pic);
2305     }
2306
2307     vlc_mutex_unlock( &p_sys->filter_lock );
2308
2309
2310
2311     return p_pic;
2312 }
2313
2314
2315 /*****************************************************************************
2316 * FadeToColorThread: Threadmethod which changes slowly the color
2317 * to a target color defined in p_fadethread struct
2318 * use for: Fade to Pause Color,  and Fade to End Color
2319 *****************************************************************************/
2320 static void *FadeToColorThread(vlc_object_t *obj)
2321 {
2322     fadethread_t *p_fadethread = (fadethread_t *)obj;
2323     filter_sys_t *p_sys = (filter_sys_t *)p_fadethread->p_filter->p_sys;
2324     int i_steps_done = 0;
2325     int i_index;
2326     int i_pause_red;
2327     int i_pause_green;
2328     int i_pause_blue;
2329
2330     int i_src_red;
2331     int i_src_green;
2332     int i_src_blue;
2333
2334     uint8_t *p_source = NULL;
2335
2336     int canc = vlc_savecancel ();
2337     /* initialize AtmoWin for this thread! */
2338     AtmoInitialize(p_fadethread->p_filter , true);
2339
2340     uint8_t *p_transfer = AtmoLockTransferBuffer( p_fadethread->p_filter );
2341     if(p_transfer != NULL) {
2342         /* safe colors as "32bit" Integers to avoid overflows*/
2343         i_pause_red   = p_fadethread->ui_red;
2344         i_pause_blue  = p_fadethread->ui_blue;
2345         i_pause_green = p_fadethread->ui_green;
2346
2347         /*
2348         allocate a temporary buffer for the last send
2349         image size less then 15kb
2350         */
2351         int i_size = 4 * p_sys->i_atmo_width * p_sys->i_atmo_height;
2352         p_source = (uint8_t *)malloc( i_size );
2353         if(p_source != NULL)
2354         {
2355             /*
2356             get a copy of the last transfered image as orign for the
2357             fading steps...
2358             */
2359             memcpy(p_source, p_transfer, i_size);
2360             /* send the same pixel data again... to unlock the buffer! */
2361             AtmoSendPixelData( p_fadethread->p_filter );
2362
2363             while( (vlc_object_alive (p_fadethread)) &&
2364                 (i_steps_done < p_fadethread->i_steps))
2365             {
2366                 p_transfer = AtmoLockTransferBuffer( p_fadethread->p_filter );
2367                 if(!p_transfer) break; /* should not happen if it worked
2368                                        one time in the code above! */
2369                 i_steps_done++;
2370                 /*
2371                 move all pixels in the mini image (64x48) one step closer to
2372                 the desired color these loop takes the most time of this
2373                 thread improvements wellcome!
2374                 */
2375                 for(i_index = 0;
2376                     (i_index < i_size) && (vlc_object_alive (p_fadethread));
2377                     i_index+=4)
2378                 {
2379                     i_src_blue  = p_source[i_index+0];
2380                     i_src_green = p_source[i_index+1];
2381                     i_src_red   = p_source[i_index+2];
2382                     p_transfer[i_index+0] = (uint8_t) (((
2383                         (i_pause_blue  - i_src_blue)
2384                         * i_steps_done)/p_fadethread->i_steps)
2385                         + i_src_blue);
2386
2387                     p_transfer[i_index+1] = (uint8_t) (((
2388                         (i_pause_green - i_src_green)
2389                         * i_steps_done)/p_fadethread->i_steps)
2390                         + i_src_green);
2391
2392                     p_transfer[i_index+2] = (uint8_t) (((
2393                         (i_pause_red   - i_src_red)
2394                         * i_steps_done)/p_fadethread->i_steps)
2395                         + i_src_red);
2396                 }
2397
2398                 /* send image to lightcontroller */
2399                 AtmoSendPixelData( p_fadethread->p_filter );
2400                 /* is there something like and interruptable sleep inside
2401                 the VLC libaries? inside native win32 I would use an Event
2402                 (CreateEvent) and here an WaitForSingleObject?
2403                 */
2404                 msleep(40000);
2405             }
2406             free(p_source);
2407         } else {
2408             /* in failure of malloc also unlock buffer  */
2409             AtmoSendPixelData(p_fadethread->p_filter);
2410         }
2411     }
2412     /* call indirect to OleUnitialize() for this thread */
2413     AtmoFinalize(p_fadethread->p_filter, 0);
2414     vlc_restorecancel (canc);
2415     return NULL;
2416 }
2417
2418 /*****************************************************************************
2419 * CheckAndStopFadeThread: if there is a fadethread structure left, or running.
2420 ******************************************************************************
2421 * this function will stop the thread ... and waits for its termination
2422 * before removeing the objects from vout_sys_t ...
2423 ******************************************************************************/
2424 static void CheckAndStopFadeThread(filter_t *p_filter)
2425 {
2426     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
2427     vlc_mutex_lock( &p_sys->filter_lock );
2428     if(p_sys->p_fadethread != NULL)
2429     {
2430         msg_Dbg(p_filter, "kill still running fadeing thread...");
2431
2432         p_sys->p_fadethread->b_die = true;
2433
2434         vlc_thread_join(p_sys->p_fadethread);
2435
2436         vlc_object_release(p_sys->p_fadethread);
2437         p_sys->p_fadethread = NULL;
2438     }
2439     vlc_mutex_unlock( &p_sys->filter_lock );
2440 }
2441
2442 /*****************************************************************************
2443 * StateCallback: Callback for the inputs variable "State" to get notified
2444 * about Pause and Continue Playback events.
2445 *****************************************************************************/
2446 static int StateCallback( vlc_object_t *p_this, char const *psz_cmd,
2447                          vlc_value_t oldval, vlc_value_t newval,
2448                          void *p_data )
2449 {
2450     filter_t *p_filter = (filter_t *)p_data;
2451     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
2452
2453     if((p_sys->b_usepausecolor == true) && (p_sys->b_enabled == true))
2454     {
2455         msg_Dbg(p_filter, "state change from: %"PRId64" to %"PRId64, oldval.i_int,
2456             newval.i_int);
2457
2458         if((newval.i_int == PAUSE_S) && (oldval.i_int == PLAYING_S))
2459         {
2460             /* tell the other thread to stop sending images to light
2461                controller */
2462             p_sys->b_pause_live = true;
2463
2464             // clean up old thread - should not happen....
2465             CheckAndStopFadeThread( p_filter );
2466
2467             // perpare spawn fadeing thread
2468             vlc_mutex_lock( &p_sys->filter_lock );
2469             /*
2470             launch only a new thread if there is none active!
2471             or waiting for cleanup
2472             */
2473             if(p_sys->p_fadethread == NULL)
2474             {
2475                 p_sys->p_fadethread = (fadethread_t *)vlc_object_create(
2476                      p_filter,
2477                      sizeof(fadethread_t) );
2478
2479                 p_sys->p_fadethread->p_filter = p_filter;
2480                 p_sys->p_fadethread->ui_red   = p_sys->ui_pausecolor_red;
2481                 p_sys->p_fadethread->ui_green = p_sys->ui_pausecolor_green;
2482                 p_sys->p_fadethread->ui_blue  = p_sys->ui_pausecolor_blue;
2483                 p_sys->p_fadethread->i_steps  = p_sys->i_fadesteps;
2484
2485                 if( vlc_thread_create( p_sys->p_fadethread,
2486                     "AtmoLight fadeing",
2487                     FadeToColorThread,
2488                     VLC_THREAD_PRIORITY_LOW ) )
2489                 {
2490                     msg_Err( p_filter, "cannot create FadeToColorThread" );
2491                     vlc_object_release( p_sys->p_fadethread );
2492                     p_sys->p_fadethread = NULL;
2493                 }
2494             }
2495             vlc_mutex_unlock( &p_sys->filter_lock );
2496         }
2497
2498         if((newval.i_int == PLAYING_S) && (oldval.i_int == PAUSE_S))
2499         {
2500             /* playback continues check thread state */
2501             CheckAndStopFadeThread( p_filter );
2502             /* reactivate the Render function... to do its normal work */
2503             p_sys->b_pause_live = false;
2504         }
2505     }
2506
2507     return VLC_SUCCESS;
2508 }
2509
2510 /*****************************************************************************
2511 * AddPlaylistInputThreadStateCallback: Setup call back on "State" Variable
2512 *****************************************************************************
2513 * Add Callback function to the "state" variable of the input thread..
2514 * first find the PlayList and get the input thread from there to attach
2515 * my callback?
2516 *****************************************************************************/
2517 static void AddStateVariableCallback(filter_t *p_filter)
2518 {
2519     input_thread_t *p_input = playlist_CurrentInput( pl_Get( p_filter ) );
2520     if(p_input)
2521     {
2522         var_AddCallback( p_input, "state", StateCallback, p_filter );
2523         vlc_object_release( p_input );
2524     }
2525 }
2526
2527 /*****************************************************************************
2528 * DelPlaylistInputThreadStateCallback: Remove call back on "State" Variable
2529 *****************************************************************************
2530 * Delete the callback function to the "state" variable of the input thread...
2531 * first find the PlayList and get the input thread from there to attach
2532 * my callback? is vlc_object_find the right way for this??
2533 *****************************************************************************/
2534 static void DelStateVariableCallback( filter_t *p_filter )
2535 {
2536     input_thread_t *p_input = playlist_CurrentInput( pl_Get ( p_filter ) );
2537     if(p_input)
2538     {
2539         var_DelCallback( p_input, "state", StateCallback, p_filter );
2540         vlc_object_release( p_input );
2541     }
2542 }
2543
2544 /****************************************************************************
2545 * StateCallback: Callback for the inputs variable "State" to get notified
2546 * about Pause and Continue Playback events.
2547 *****************************************************************************/
2548 static int AtmoSettingsCallback( vlc_object_t *p_this, char const *psz_var,
2549                                  vlc_value_t oldval, vlc_value_t newval,
2550                                  void *p_data )
2551 {
2552     filter_t *p_filter = (filter_t *)p_data;
2553     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
2554
2555     vlc_mutex_lock( &p_sys->filter_lock );
2556
2557     if( !strcmp( psz_var, CFG_PREFIX "showdots" ))
2558     {
2559         p_sys->b_show_dots = newval.b_bool;
2560     }
2561
2562     CAtmoConfig *p_atmo_config = p_sys->p_atmo_config;
2563     if(p_atmo_config)
2564     {
2565
2566        msg_Dbg(p_filter, "apply AtmoSettingsCallback %s (int: %"PRId64" -> %"PRId64")",
2567              psz_var,
2568              oldval.i_int,
2569              newval.i_int
2570        );
2571
2572         if( !strcmp( psz_var, CFG_PREFIX "filtermode" ))
2573             p_atmo_config->setLiveViewFilterMode( (AtmoFilterMode)newval.i_int);
2574
2575         else if( !strcmp( psz_var, CFG_PREFIX "percentnew" ))
2576                  p_atmo_config->setLiveViewFilter_PercentNew( newval.i_int );
2577
2578         else if( !strcmp( psz_var, CFG_PREFIX "meanlength" ))
2579                  p_atmo_config->setLiveViewFilter_MeanLength( newval.i_int );
2580
2581         else if( !strcmp( psz_var, CFG_PREFIX "meanthreshold" ))
2582                  p_atmo_config->setLiveViewFilter_MeanThreshold( newval.i_int );
2583
2584         else if( !strcmp( psz_var, CFG_PREFIX "edgeweightning" ))
2585                  p_atmo_config->setLiveView_EdgeWeighting( newval.i_int );
2586
2587         else if( !strcmp( psz_var, CFG_PREFIX "brightness" ))
2588                  p_atmo_config->setLiveView_BrightCorrect( newval.i_int );
2589
2590         else if( !strcmp( psz_var, CFG_PREFIX "darknesslimit" ))
2591                  p_atmo_config->setLiveView_DarknessLimit( newval.i_int );
2592
2593         else if( !strcmp( psz_var, CFG_PREFIX "huewinsize" ))
2594                  p_atmo_config->setLiveView_HueWinSize( newval.i_int );
2595
2596         else if( !strcmp( psz_var, CFG_PREFIX "satwinsize" ))
2597                  p_atmo_config->setLiveView_SatWinSize( newval.i_int );
2598
2599         else if( !strcmp( psz_var, CFG_PREFIX "framedelay" ))
2600                  p_atmo_config->setLiveView_FrameDelay( newval.i_int );
2601
2602         else if( !strcmp( psz_var, CFG_PREFIX "whiteadj" ))
2603                  p_atmo_config->setUseSoftwareWhiteAdj( newval.b_bool );
2604
2605         else if( !strcmp( psz_var, CFG_PREFIX "white-red" ))
2606                  p_atmo_config->setWhiteAdjustment_Red( newval.i_int );
2607
2608         else if( !strcmp( psz_var, CFG_PREFIX "white-green" ))
2609                  p_atmo_config->setWhiteAdjustment_Green( newval.i_int );
2610
2611         else if( !strcmp( psz_var, CFG_PREFIX "white-blue" ))
2612                  p_atmo_config->setWhiteAdjustment_Blue( newval.i_int );
2613
2614     }
2615
2616     vlc_mutex_unlock( &p_sys->filter_lock );
2617
2618     return VLC_SUCCESS;
2619 }
2620
2621 static void AddAtmoSettingsVariablesCallbacks(filter_t *p_filter)
2622 {
2623    var_AddCallback( p_filter, CFG_PREFIX "filtermode",
2624                     AtmoSettingsCallback, p_filter );
2625    var_AddCallback( p_filter, CFG_PREFIX "percentnew",
2626                     AtmoSettingsCallback, p_filter );
2627
2628
2629    var_AddCallback( p_filter, CFG_PREFIX "meanlength",
2630                     AtmoSettingsCallback, p_filter );
2631    var_AddCallback( p_filter, CFG_PREFIX "meanthreshold",
2632                     AtmoSettingsCallback, p_filter );
2633
2634    var_AddCallback( p_filter, CFG_PREFIX "edgeweightning",
2635                     AtmoSettingsCallback, p_filter );
2636    var_AddCallback( p_filter, CFG_PREFIX "brightness",
2637                     AtmoSettingsCallback, p_filter );
2638    var_AddCallback( p_filter, CFG_PREFIX "darknesslimit",
2639                     AtmoSettingsCallback, p_filter );
2640
2641    var_AddCallback( p_filter, CFG_PREFIX "huewinsize",
2642                     AtmoSettingsCallback, p_filter );
2643    var_AddCallback( p_filter, CFG_PREFIX "satwinsize",
2644                     AtmoSettingsCallback, p_filter );
2645    var_AddCallback( p_filter, CFG_PREFIX "framedelay",
2646                     AtmoSettingsCallback, p_filter );
2647
2648
2649    var_AddCallback( p_filter, CFG_PREFIX "whiteadj",
2650                     AtmoSettingsCallback, p_filter );
2651    var_AddCallback( p_filter, CFG_PREFIX "white-red",
2652                     AtmoSettingsCallback, p_filter );
2653    var_AddCallback( p_filter, CFG_PREFIX "white-green",
2654                     AtmoSettingsCallback, p_filter );
2655    var_AddCallback( p_filter, CFG_PREFIX "white-blue",
2656                     AtmoSettingsCallback, p_filter );
2657
2658    var_AddCallback( p_filter, CFG_PREFIX "showdots",
2659                     AtmoSettingsCallback, p_filter );
2660
2661 }
2662
2663 static void DelAtmoSettingsVariablesCallbacks( filter_t *p_filter )
2664 {
2665
2666    var_DelCallback( p_filter, CFG_PREFIX "filtermode",
2667                     AtmoSettingsCallback, p_filter );
2668
2669    var_DelCallback( p_filter, CFG_PREFIX "percentnew",
2670                     AtmoSettingsCallback, p_filter );
2671    var_DelCallback( p_filter, CFG_PREFIX "meanlength",
2672                     AtmoSettingsCallback, p_filter );
2673    var_DelCallback( p_filter, CFG_PREFIX "meanthreshold",
2674                     AtmoSettingsCallback, p_filter );
2675
2676    var_DelCallback( p_filter, CFG_PREFIX "edgeweightning",
2677                     AtmoSettingsCallback, p_filter );
2678    var_DelCallback( p_filter, CFG_PREFIX "brightness",
2679                     AtmoSettingsCallback, p_filter );
2680    var_DelCallback( p_filter, CFG_PREFIX "darknesslimit",
2681                     AtmoSettingsCallback, p_filter );
2682
2683    var_DelCallback( p_filter, CFG_PREFIX "huewinsize",
2684                     AtmoSettingsCallback, p_filter );
2685    var_DelCallback( p_filter, CFG_PREFIX "satwinsize",
2686                     AtmoSettingsCallback, p_filter );
2687    var_DelCallback( p_filter, CFG_PREFIX "framedelay",
2688                     AtmoSettingsCallback, p_filter );
2689
2690
2691    var_DelCallback( p_filter, CFG_PREFIX "whiteadj",
2692                     AtmoSettingsCallback, p_filter );
2693    var_DelCallback( p_filter, CFG_PREFIX "white-red",
2694                     AtmoSettingsCallback, p_filter );
2695    var_DelCallback( p_filter, CFG_PREFIX "white-green",
2696                     AtmoSettingsCallback, p_filter );
2697    var_DelCallback( p_filter, CFG_PREFIX "white-blue",
2698                     AtmoSettingsCallback, p_filter );
2699
2700    var_DelCallback( p_filter, CFG_PREFIX "showdots",
2701                     AtmoSettingsCallback, p_filter );
2702
2703 }
2704
2705
2706 #if defined(__ATMO_DEBUG__)
2707 static void atmo_parse_crop(char *psz_cropconfig,
2708                             video_format_t fmt_in,
2709                             video_format_t fmt_render,
2710                             int &i_visible_width, int &i_visible_height,
2711                             int &i_x_offset, int &i_y_offset )
2712 {
2713     int64_t i_aspect_num, i_aspect_den;
2714     unsigned int i_width, i_height;
2715
2716     i_visible_width  = fmt_in.i_visible_width;
2717     i_visible_height = fmt_in.i_visible_height;
2718     i_x_offset       = fmt_in.i_x_offset;
2719     i_y_offset       = fmt_in.i_y_offset;
2720
2721     char *psz_end = NULL, *psz_parser = strchr( psz_cropconfig, ':' );
2722     if( psz_parser )
2723     {
2724         /* We're using the 3:4 syntax */
2725         i_aspect_num = strtol( psz_cropconfig, &psz_end, 10 );
2726         if( psz_end == psz_cropconfig || !i_aspect_num ) return;
2727
2728         i_aspect_den = strtol( ++psz_parser, &psz_end, 10 );
2729         if( psz_end == psz_parser || !i_aspect_den ) return;
2730
2731         i_width = fmt_in.i_sar_den * fmt_render.i_visible_height *
2732             i_aspect_num / i_aspect_den / fmt_in.i_sar_num;
2733
2734         i_height = fmt_render.i_visible_width*fmt_in.i_sar_num *
2735             i_aspect_den / i_aspect_num / fmt_in.i_sar_den;
2736
2737         if( i_width < fmt_render.i_visible_width )
2738         {
2739             i_x_offset = fmt_render.i_x_offset +
2740                 (fmt_render.i_visible_width - i_width) / 2;
2741             i_visible_width = i_width;
2742         }
2743         else
2744         {
2745             i_y_offset = fmt_render.i_y_offset +
2746                 (fmt_render.i_visible_height - i_height) / 2;
2747             i_visible_height = i_height;
2748         }
2749     }
2750     else
2751     {
2752         psz_parser = strchr( psz_cropconfig, 'x' );
2753         if( psz_parser )
2754         {
2755             /* Maybe we're using the <width>x<height>+<left>+<top> syntax */
2756             unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left;
2757
2758             i_crop_width = strtol( psz_cropconfig, &psz_end, 10 );
2759             if( psz_end != psz_parser ) return;
2760
2761             psz_parser = strchr( ++psz_end, '+' );
2762             i_crop_height = strtol( psz_end, &psz_end, 10 );
2763             if( psz_end != psz_parser ) return;
2764
2765             psz_parser = strchr( ++psz_end, '+' );
2766             i_crop_left = strtol( psz_end, &psz_end, 10 );
2767             if( psz_end != psz_parser ) return;
2768
2769             psz_end++;
2770             i_crop_top = strtol( psz_end, &psz_end, 10 );
2771             if( *psz_end != '\0' ) return;
2772
2773             i_width = i_crop_width;
2774             i_visible_width = i_width;
2775
2776             i_height = i_crop_height;
2777             i_visible_height = i_height;
2778
2779             i_x_offset = i_crop_left;
2780             i_y_offset = i_crop_top;
2781         }
2782         else
2783         {
2784             /* Maybe we're using the <left>+<top>+<right>+<bottom> syntax */
2785             unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right;
2786
2787             psz_parser = strchr( psz_cropconfig, '+' );
2788             i_crop_left = strtol( psz_cropconfig, &psz_end, 10 );
2789             if( psz_end != psz_parser ) return;
2790
2791             psz_parser = strchr( ++psz_end, '+' );
2792             i_crop_top = strtol( psz_end, &psz_end, 10 );
2793             if( psz_end != psz_parser ) return;
2794
2795             psz_parser = strchr( ++psz_end, '+' );
2796             i_crop_right = strtol( psz_end, &psz_end, 10 );
2797             if( psz_end != psz_parser ) return;
2798
2799             psz_end++;
2800             i_crop_bottom = strtol( psz_end, &psz_end, 10 );
2801             if( *psz_end != '\0' ) return;
2802
2803             i_width = fmt_render.i_visible_width -
2804                         i_crop_left -
2805                         i_crop_right;
2806             i_visible_width = i_width;
2807
2808             i_height = fmt_render.i_visible_height -
2809                         i_crop_top -
2810                         i_crop_bottom;
2811             i_visible_height = i_height;
2812
2813             i_x_offset = i_crop_left;
2814             i_y_offset = i_crop_top;
2815         }
2816     }
2817 }
2818 #endif