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