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