atmo.cpp 82.9 KB
Newer Older
1
2
3
4
5
6
/*****************************************************************************
* atmo.cpp : "Atmo Light" video filter
*****************************************************************************
* Copyright (C) 2000-2006 the VideoLAN team
* $Id$
*
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
7
* Authors: André Weber (WeberAndre@gmx.de)
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/

/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>
#include <math.h>                                            /* sin(), cos() */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

// #define __ATMO_DEBUG__
// [:Zs]+$
37
#include <vlc_common.h>
38
#include <vlc_plugin.h>
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <vlc_vout.h>

#include <vlc_playlist.h>
#include "vlc_filter.h"

#include "AtmoDefs.h"
#include "AtmoDynData.h"
#include "AtmoLiveView.h"
#include "AtmoTools.h"
#include "AtmoExternalCaptureInput.h"
#include "AtmoConfig.h"
#include "AtmoConnection.h"
#include "AtmoSerialConnection.h"


/*****************************************************************************
* Local prototypes
*****************************************************************************/
/* directly to vlc related functions required that the module is accepted */
static int  CreateFilter    ( vlc_object_t * );
static void DestroyFilter   ( vlc_object_t * );
static picture_t * Filter( filter_t *, picture_t *);

/* callback for global variable state pause / continue / stop events */
static void AddStateVariableCallback( filter_t *);
static void DelStateVariableCallback( filter_t *);
static int StateCallback(vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void *);

/* callback for variable crop-update */
static void AddCropVariableCallback( filter_t *);
static void DelCropVariableCallback( filter_t *);
static int CropCallback(vlc_object_t *, char const *,
                        vlc_value_t, vlc_value_t, void *);

/* callback for atmo settings variables whose change
   should be immediately realized and applied to output
*/
static void DelAtmoSettingsVariablesCallbacks(filter_t *);
static void AddAtmoSettingsVariablesCallbacks(filter_t *);
static int AtmoSettingsCallback(vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void *);


#if defined(__ATMO_DEBUG__)
static void atmo_parse_crop(char *psz_cropconfig,
                            video_format_t fmt_in,
                            video_format_t fmt_render,
                            int &i_visible_width,
                            int &i_visible_height,
                            int &i_x_offset,
                            int &i_y_offset );
#endif


/* function to shutdown the fade thread which is started on pause*/
static void CheckAndStopFadeThread(filter_t *);

/* extracts a small RGB (BGR) Image from an YUV image */
static void ExtractMiniImage_YUV(filter_sys_t *, picture_t *, uint8_t *);

#if defined(__ATMO_DEBUG__)
void SaveBitmap(filter_sys_t *p_sys, uint8_t *p_pixels, char *psz_filename);
#endif

/*****************************************************************************
* External Prototypes for the AtmoCtrlLib.DLL
*****************************************************************************/
/*
* if effectmode = emLivePicture then the source could be GDI (Screencapture)
* or External - this means another application delivers Pixeldata to AtmoWin
* Clientsoftware through  AtmoCtrlLib.DLL and the COM Api
*/
#define lvsGDI           0
#define lvsExternal      1


/*
strings for settings menus and hints
*/
#define MODULE_DESCRIPTION N_ ( \
120
121
122
123
 "This module allows to control an so called AtmoLight device "\
 "connected to your computer.\n"\
 "AtmoLight is the homegrown version of what Philips calls AmbiLight.\n"\
 "If you need further information feel free to visit us at\n\n"\
124
 "http://www.vdr-wiki.de/wiki/index.php/Atmo-plugin\n "\
125
 "http://www.vdr-wiki.de/wiki/index.php/AtmoWin\n\n"\
126
127
128
 "You can find there detailed descriptions on how to build it for yourself "\
 "and where to get the required parts.\n" \
 "You can also have a look at pictures and some movies showing such a device " \
129
 "in live action.")
130
131
132
133
134



#if defined( __ATMO_DEBUG__ )
#   define SAVEFRAMES_TEXT     N_("Save Debug Frames")
135
#   define SAVEFRAMES_LONGTEXT N_("Write every 128th miniframe to a folder.")
136
#   define FRAMEPATH_TEXT      N_("Debug Frame Folder")
137
#   define FRAMEPATH_LONGTEXT  N_("The path where the debugframes " \
138
139
140
141
                                  "should be saved")
#endif

#define WIDTH_TEXT             N_("Extracted Image Width")
142
#define WIDTH_LONGTEXT         N_("The width of the mini image for " \
143
144
145
                                  "further processing (64 is default)")

#define HEIGHT_TEXT            N_("Extracted Image Height")
146
#define HEIGHT_LONGTEXT        N_("The height of the mini image for " \
147
148
                                  "further processing (48 is default)")

149
150
151
152
#define PCOLOR_TEXT            N_("Color when paused")
#define PCOLOR_LONGTEXT        N_("Set the color to show if the user " \
                                  "pauses the video. (Have light to get " \
                                  "another beer?)")
153
#define PCOLOR_RED_TEXT        N_("Pause-Red")
154
#define PCOLOR_RED_LONGTEXT    N_("Red component of the pause color")
155
#define PCOLOR_GREEN_TEXT      N_("Pause-Green")
156
#define PCOLOR_GREEN_LONGTEXT  N_("Green component of the pause color")
157
#define PCOLOR_BLUE_TEXT       N_("Pause-Blue")
158
#define PCOLOR_BLUE_LONGTEXT   N_("Blue component of the pause color")
159
160
161
162
163
#define FADESTEPS_TEXT         N_("Pause-Fadesteps")
#define FADESTEPS_LONGTEXT     N_("Number of steps to change current color " \
                                  "to pause color (each step takes 40ms)")

#define ECOLOR_RED_TEXT        N_("End-Red")
164
#define ECOLOR_RED_LONGTEXT    N_("Red component of the shutdown color")
165
#define ECOLOR_GREEN_TEXT      N_("End-Green")
166
#define ECOLOR_GREEN_LONGTEXT  N_("Green component of the shutdown color")
167
#define ECOLOR_BLUE_TEXT       N_("End-Blue")
168
#define ECOLOR_BLUE_LONGTEXT   N_("Blue component of the shutdown color")
169
170
171
172
173
174
175
#define EFADESTEPS_TEXT        N_("End-Fadesteps")
#define EFADESTEPS_LONGTEXT  N_("Number of steps to change current color to " \
                             "end color for dimming up the light in cinema " \
                             "style... (each step takes 40ms)")

#define USEWHITEADJ_TEXT       N_("Use Software White adjust")
#define USEWHITEADJ_LONGTEXT   N_("Should the buildin driver do a white " \
176
                                  "adjust or your LED stripes? recommend.")
177
178
179
180
181
182
183
184
185
186
187
188
#define WHITE_RED_TEXT         N_("White Red")
#define WHITE_RED_LONGTEXT     N_("Red value of a pure white on your "\
                                  "LED stripes.")
#define WHITE_GREEN_TEXT       N_("White Green")
#define WHITE_GREEN_LONGTEXT   N_("Green value of a pure white on your "\
                                  "LED stripes.")
#define WHITE_BLUE_TEXT        N_("White Blue")
#define WHITE_BLUE_LONGTEXT    N_("Blue value of a pure white on your "\
                                  "LED stripes.")

#define SERIALDEV_TEXT         N_("Serial Port/Device")
#define SERIALDEV_LONGTEXT   N_("Name of the serial port where the AtmoLight "\
189
190
                                "controller is attached to.\n" \
                                "On Windows usually something like COM1 or " \
191
                                "COM2. On Linux /dev/ttyS01 f.e.")
192
193

#define EDGE_TEXT            N_("Edge Weightning")
194
195
#define EDGE_LONGTEXT        N_("Increasing this value will result in color "\
                                "more depending on the border of the frame.")
196
#define BRIGHTNESS_TEXT     N_("Brightness")
197
#define BRIGHTNESS_LONGTEXT N_("Overall brightness of your LED stripes")
198
#define DARKNESS_TEXT       N_("Darkness Limit")
199
200
201
#define DARKNESS_LONGTEXT   N_("Pixels with a saturation lower than this will "\
                               "be ignored. Should be greater than one for "\
                               "letterboxed videos.")
202
#define HUEWINSIZE_TEXT     N_("Hue windowing")
203
#define HUEWINSIZE_LONGTEXT N_("Used for statistics.")
204
#define SATWINSIZE_TEXT     N_("Sat windowing")
205
#define SATWINSIZE_LONGTEXT N_("Used for statistics.")
206

Rafaël Carré's avatar
Rafaël Carré committed
207
#define MEANLENGTH_TEXT     N_("Filter length (ms)")
208
209
#define MEANLENGTH_LONGTEXT N_("Time it takes until a color is completely "\
                                "changed. This prevents flickering.")
210
#define MEANTHRESHOLD_TEXT     N_("Filter threshold")
211
212
213
#define MEANTHRESHOLD_LONGTEXT N_("How much a color has to be changed for an "\
                                  "immediate color change.")
#define MEANPERCENTNEW_TEXT     N_("Filter Smoothness (in %)")
214
215
#define MEANPERCENTNEW_LONGTEXT N_("Filter Smoothness")

216
217
/* FIXME: WTF?!! feepk, July 6 '08 */
#define FILTERMODE_TEXT        N_("Filter mode")
218
219
220
221
222
223
224
225
226
227
228
229
230
#define FILTERMODE_LONGTEXT    N_("kind of filtering which should be use to "\
                                  "calcuate the color output")
static int pi_filtermode_values[] = {
       (int)afmNoFilter,
       (int)afmCombined,
       (int)afmPercent
};
static const char *ppsz_filtermode_descriptions[] = {
        N_("No Filtering"),
        N_("Combined"),
        N_("Percent")
};

231
#define FRAMEDELAY_TEXT       N_("Frame delay")
232
233
#define FRAMEDELAY_LONGTEXT   N_("Helps to get the video output and the light "\
                                 "effects in sync. Values around 20ms should " \
234
                                 "do the trick.")
235
236
237
238
239
240
241
242


#define CHANNEL_0_ASSIGN_TEXT N_("Channel summary")
#define CHANNEL_1_ASSIGN_TEXT N_("Channel left")
#define CHANNEL_2_ASSIGN_TEXT N_("Channel right")
#define CHANNEL_3_ASSIGN_TEXT N_("Channel top")
#define CHANNEL_4_ASSIGN_TEXT N_("Channel bottom")

243
244
#define CHANNELASSIGN_LONGTEXT N_("Maps the hardware channel X to logical "\
                                  "channel Y to fix wrong wiring :-)")
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
static int pi_channel_assignment_values[] = {
    -1,
     0,
     1,
     2,
     3,
     4
};
static const char *ppsz_channel_assignment_descriptions[] = {
        N_("disabled"),
        N_("summary"),
        N_("left"),
        N_("right"),
        N_("top"),
        N_("bottom")
};

262
263
264
265
266
267
#define ZONE_0_GRADIENT_TEXT N_("Summary gradient")
#define ZONE_1_GRADIENT_TEXT N_("Left gradient")
#define ZONE_2_GRADIENT_TEXT N_("Right gradient")
#define ZONE_3_GRADIENT_TEXT N_("Top gradient")
#define ZONE_4_GRADIENT_TEXT N_("Bottom gradient")
#define ZONE_X_GRADIENT_LONG_TEXT N_("Defines a small bitmap with 64x48 "\
268
269
270
271
                                     "pixels, containing a grayscale gradient")

#if defined( WIN32 )
#   define ATMOWINEXE_TEXT      N_("Filename of AtmoWinA.exe")
272
273
274
275
276
#   define ATMOWINEXE_LONGTEXT  N_("if you want the AtmoLight control "\
                                   "software to be launched by VLC, enter the "\
                                   "complete path of AtmoWinA.exe here.")
#   define USEBUILDIN_TEXT      N_("Use built-in AtmoLight")
#   define USEBUILDIN_LONGTEXT N_("VLC will directly use your AtmoLight "\
277
278
279
280
281
282
283
284
285
286
                                  "hardware without running the external "\
                                  "AtmoWinA.exe Userspace driver.")
#endif

#define CFG_PREFIX "atmo-"

/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
287
set_description( N_("AtmoLight Filter") );
288
set_help( MODULE_DESCRIPTION );
289
set_shortname( N_( "AtmoLight" ));
290
291
292
293
294
295
set_capability( "video filter2", 0 );

set_category( CAT_VIDEO );
set_subcategory( SUBCAT_VIDEO_VFILTER );

#if defined(WIN32)
296
set_section( N_("Choose between the built-in AtmoLight "\
297
298
299
300
301
302
                 "driver or the external" ), 0 );

/*
    only on win32 exists the option to use the buildin driver or
    the more flexible external driver application
*/
303
304
add_bool(CFG_PREFIX "usebuildin", true, NULL,
         USEBUILDIN_TEXT, USEBUILDIN_LONGTEXT, false);
305
add_string(CFG_PREFIX "serialdev", "COM1", NULL,
306
           SERIALDEV_TEXT, SERIALDEV_LONGTEXT, false );
307
308
309
310
311
312

/*
    on win32 the executeable external driver application
    for automatic start if needed
*/
add_file(CFG_PREFIX "atmowinexe", NULL, NULL,
313
         ATMOWINEXE_TEXT, ATMOWINEXE_LONGTEXT, false );
314
#else
315
set_section( N_("Enter the connection of your AtmoLight hardware" ), 0 );
316
add_string(CFG_PREFIX "serialdev", "/dev/ttyS01", NULL,
317
           SERIALDEV_TEXT, SERIALDEV_LONGTEXT, false );
318
319
320
321
322
323
324
#endif

/*
    color which is showed if you want durring pausing
    your movie ... used for both buildin / external
*/
set_section( N_("Illuminate the room with this color on pause" ), 0 );
325
326
add_bool(CFG_PREFIX "usepausecolor", false, NULL,
         PCOLOR_TEXT, PCOLOR_LONGTEXT, false);
327
add_integer_with_range(CFG_PREFIX "pcolor-red",   0, 0, 255, NULL,
328
                       PCOLOR_RED_TEXT, PCOLOR_RED_LONGTEXT, false);
329
add_integer_with_range(CFG_PREFIX "pcolor-green", 0, 0, 255, NULL,
330
                       PCOLOR_GREEN_TEXT, PCOLOR_GREEN_LONGTEXT, false);
331
add_integer_with_range(CFG_PREFIX "pcolor-blue",  192, 0, 255, NULL,
332
                       PCOLOR_BLUE_TEXT, PCOLOR_BLUE_LONGTEXT, false);
333
add_integer_with_range(CFG_PREFIX "fadesteps", 50, 1, 250, NULL,
334
                       FADESTEPS_TEXT, FADESTEPS_LONGTEXT, false);
335
336
337
338
339
340
341

/*
    color which is showed if you finished watching your movie ...
    used for both buildin / external
*/
set_section( N_("Illuminate the room with this color on shutdown" ), 0 );
add_integer_with_range(CFG_PREFIX "ecolor-red",   192, 0, 255, NULL,
342
                       ECOLOR_RED_TEXT,   ECOLOR_RED_LONGTEXT,   false);
343
add_integer_with_range(CFG_PREFIX "ecolor-green", 192, 0, 255, NULL,
344
                       ECOLOR_GREEN_TEXT, ECOLOR_GREEN_LONGTEXT, false);
345
add_integer_with_range(CFG_PREFIX "ecolor-blue",  192, 0, 255, NULL,
346
                       ECOLOR_BLUE_TEXT,  ECOLOR_BLUE_LONGTEXT,  false);
347
add_integer_with_range(CFG_PREFIX "efadesteps",    50, 1, 250, NULL,
348
                       EFADESTEPS_TEXT,   EFADESTEPS_LONGTEXT,    false);
349
350
351
352
353
354
355

/*
 settings only for the buildin driver (if external driver app is used
 these parameters are ignored.)

 definition of parameters for the buildin filter ...
*/
356
set_section( N_("Settings for the built-in Live Video Processor only" ), 0 );
357
358

add_integer_with_range(CFG_PREFIX "EdgeWeightning",   8, 1, 30, NULL,
359
                       EDGE_TEXT, EDGE_LONGTEXT, false);
360
361

add_integer_with_range(CFG_PREFIX "Brightness",   100, 50, 300, NULL,
362
                       BRIGHTNESS_TEXT, BRIGHTNESS_LONGTEXT, false);
363
364

add_integer_with_range(CFG_PREFIX "DarknessLimit",   5, 0, 10, NULL,
365
                       DARKNESS_TEXT, DARKNESS_LONGTEXT, false);
366
367

add_integer_with_range(CFG_PREFIX "HueWinSize",   3, 0, 5, NULL,
368
                       HUEWINSIZE_TEXT, HUEWINSIZE_LONGTEXT, false);
369
370

add_integer_with_range(CFG_PREFIX "SatWinSize",   3, 0, 5, NULL,
371
                       SATWINSIZE_TEXT, SATWINSIZE_LONGTEXT, false);
372
373

add_integer(CFG_PREFIX "filtermode", (int)afmCombined, NULL,
374
            FILTERMODE_TEXT, FILTERMODE_LONGTEXT, false );
375
376
377
378

change_integer_list(pi_filtermode_values, ppsz_filtermode_descriptions, 0 );

add_integer_with_range(CFG_PREFIX "MeanLength",    300, 300, 5000, NULL,
379
                       MEANLENGTH_TEXT, MEANLENGTH_LONGTEXT, false);
380
381

add_integer_with_range(CFG_PREFIX "MeanThreshold",  40, 1, 100, NULL,
382
                       MEANTHRESHOLD_TEXT, MEANTHRESHOLD_LONGTEXT, false);
383
384

add_integer_with_range(CFG_PREFIX "PercentNew", 50, 1, 100, NULL,
385
                      MEANPERCENTNEW_TEXT, MEANPERCENTNEW_LONGTEXT, false);
386
387

add_integer_with_range(CFG_PREFIX "FrameDelay", 18, 0, 35, NULL,
388
                       FRAMEDELAY_TEXT, FRAMEDELAY_LONGTEXT, false);
389
390
391
392
393
394

/*
  output channel reordering
*/
set_section( N_("Change channel assignment (fixes wrong wiring)" ), 0 );
add_integer( CFG_PREFIX "channel_0", 0, NULL,
395
            CHANNEL_0_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
396
397
398
399
change_integer_list( pi_channel_assignment_values,
                     ppsz_channel_assignment_descriptions, 0 );

add_integer( CFG_PREFIX "channel_1", 1, NULL,
400
            CHANNEL_1_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
401
402
403
404
change_integer_list( pi_channel_assignment_values,
                     ppsz_channel_assignment_descriptions, 0 );

add_integer( CFG_PREFIX "channel_2", 2, NULL,
405
            CHANNEL_2_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
406
407
408
409
change_integer_list( pi_channel_assignment_values,
                     ppsz_channel_assignment_descriptions, 0 );

add_integer( CFG_PREFIX "channel_3", 3, NULL,
410
            CHANNEL_3_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
411
412
413
414
change_integer_list( pi_channel_assignment_values,
                     ppsz_channel_assignment_descriptions, 0 );

add_integer( CFG_PREFIX "channel_4", 4, NULL,
415
            CHANNEL_4_ASSIGN_TEXT, CHANNELASSIGN_LONGTEXT, false );
416
417
418
419
420
421
422
change_integer_list( pi_channel_assignment_values,
                     ppsz_channel_assignment_descriptions, 0 );

/*
  LED color white calibration
*/
set_section( N_("Adjust the white light to your LED stripes" ), 0 );
423
424
add_bool(CFG_PREFIX "whiteadj", true, NULL,
         USEWHITEADJ_TEXT, USEWHITEADJ_LONGTEXT, false);
425
add_integer_with_range(CFG_PREFIX "white-red",   255, 0, 255, NULL,
426
                       WHITE_RED_TEXT,   WHITE_RED_LONGTEXT,   false);
427
428

add_integer_with_range(CFG_PREFIX "white-green", 255, 0, 255, NULL,
429
                       WHITE_GREEN_TEXT, WHITE_GREEN_LONGTEXT, false);
430
431

add_integer_with_range(CFG_PREFIX "white-blue",  255, 0, 255, NULL,
432
                       WHITE_BLUE_TEXT,  WHITE_BLUE_LONGTEXT,  false);
433
434
435
436
437
438
439
440
441
442
443
444
445
/* end of definition of parameter for the buildin filter ... part 1 */


/*
only for buildin (external has own definition) per default the calucation
used linear gradients for assigning a priority to the pixel - depending
how near they are to the border ...for changing this you can create 64x48
Pixel BMP files - which contain your own grayscale... (you can produce funny
effects with this...) the images MUST not compressed, should have 24-bit per
pixel, or a simple 256 color grayscale palette
*/
set_section( N_("Change gradients" ), 0 );
add_file(CFG_PREFIX "gradient_zone_0", NULL, NULL,
446
         ZONE_0_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
447
add_file(CFG_PREFIX "gradient_zone_1", NULL, NULL,
448
         ZONE_1_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
449
add_file(CFG_PREFIX "gradient_zone_2", NULL, NULL,
450
         ZONE_2_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
451
add_file(CFG_PREFIX "gradient_zone_3", NULL, NULL,
452
         ZONE_3_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
453
add_file(CFG_PREFIX "gradient_zone_4", NULL, NULL,
454
         ZONE_4_GRADIENT_TEXT, ZONE_X_GRADIENT_LONG_TEXT, true );
455
456
457


#if defined(__ATMO_DEBUG__)
458
459
add_bool(CFG_PREFIX "saveframes", false, NULL,
         SAVEFRAMES_TEXT, SAVEFRAMES_LONGTEXT, false);
460
add_string(CFG_PREFIX "framepath", "", NULL,
461
           FRAMEPATH_TEXT, FRAMEPATH_LONGTEXT, false );
462
463
464
465
466
467
#endif
/*
   may be later if computers gets more power ;-) than now we increase
   the samplesize from which we do the stats for output color calculation
*/
add_integer_with_range(CFG_PREFIX "width",  64, 64, 512, NULL,
468
                       WIDTH_TEXT,  WIDTH_LONGTEXT, true);
469
add_integer_with_range(CFG_PREFIX "height", 48, 48, 384, NULL,
470
                       HEIGHT_TEXT,  HEIGHT_LONGTEXT, true);
471
472
473
474
475
476

add_shortcut( "atmo" );
set_callbacks( CreateFilter, DestroyFilter  );
vlc_module_end();


477
static const char *const ppsz_filter_options[] = {
478
#if defined(WIN32)
479
        "usebuildin",
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
#endif
        "serialdev",


        "EdgeWeightning",
        "Brightness",
        "DarknessLimit",
        "HueWinSize",
        "SatWinSize",

        "filtermode",

        "MeanLength",
        "MeanThreshold",
        "PercentNew",
        "FrameDelay",

        "channel_0",
        "channel_1",
        "channel_2",
        "channel_3",
        "channel_4",

        "whiteadj",
        "white-red",
        "white-green",
        "white-blue",

        "usepausecolor",
        "pcolor-red",
        "pcolor-green",
        "pcolor-blue",
        "fadesteps",

        "ecolor-red",
        "ecolor-green",
        "ecolor-blue",
        "efadesteps",


#if defined(WIN32 )
        "usebuildin",
        "atmowinexe",
#endif
#if defined(__ATMO_DEBUG__)
        "saveframes" ,
        "framepath",
#endif
        "width",
        "height",
        "gradient_zone_0",
        "gradient_zone_1",
        "gradient_zone_2",
        "gradient_zone_3",
        "gradient_zone_4",
        NULL
};


/*****************************************************************************
* fadethread_t: Color Fading Thread
*****************************************************************************
* changes slowly the color of the output if videostream gets paused...
*****************************************************************************
*/
typedef struct
{
    VLC_COMMON_MEMBERS
        filter_t *p_filter;
    /* tell the thread which color should be the target of fading */
    uint8_t ui_red;
    uint8_t ui_green;
    uint8_t ui_blue;
    /* how many steps should happen until this */
    int i_steps;

} fadethread_t;

static void FadeToColorThread(fadethread_t *p_fadethread);


/*****************************************************************************
* filter_sys_t: AtmoLight filter method descriptor
*****************************************************************************
* It describes the AtmoLight specific properties of an video filter.
*****************************************************************************/
struct filter_sys_t
{
    /*
    special for the access of the p_fadethread member all other members
    need no special protection so far!
    */
    vlc_mutex_t filter_lock;

574
    bool b_enabled;
575
    int32_t i_AtmoOldEffect;
576
    bool b_pause_live;
577
578
579
580
581

    int32_t i_atmo_width;
    int32_t i_atmo_height;

#if defined(__ATMO_DEBUG__)
582
    bool  b_saveframes;
583
584
585
586
587
    int i_framecounter;
    char sz_framepath[MAX_PATH];
#endif

    /* light color durring movie pause ... */
588
    bool  b_usepausecolor;
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    uint8_t ui_pausecolor_red;
    uint8_t ui_pausecolor_green;
    uint8_t ui_pausecolor_blue;
    int i_fadesteps;

    /* light color on movie finish ... */
    uint8_t ui_endcolor_red;
    uint8_t ui_endcolor_green;
    uint8_t ui_endcolor_blue;
    int i_endfadesteps;

    fadethread_t *p_fadethread;

    /* Variables for buildin driver only... */

    /* is only present and initialized if the internal driver is used*/
    CAtmoConfig *p_atmo_config;
    /* storage for temporal settings "volatile" */
    CAtmoDynData *p_atmo_dyndata;
    /* initialized for buildin driver with AtmoCreateTransferBuffers */
    BITMAPINFOHEADER mini_image_format;
    /* is only use buildin driver! */
    uint8_t *p_atmo_transfer_buffer;
    /* end buildin driver */

    /*
    contains the real output size of the video calculated on
    change event of the variable "crop" from vout
    */
    int32_t i_crop_x_offset;
    int32_t i_crop_y_offset;
    int32_t i_crop_width;
    int32_t i_crop_height;

    void (*pf_extract_mini_image) (filter_sys_t *p_sys,
        picture_t *p_inpic,
        uint8_t *p_transfer_dest);

#if defined( WIN32 )
    /* External Library as wrapper arround COM Stuff */
    HINSTANCE h_AtmoCtrl;
    int32_t (*pf_ctrl_atmo_initialize) (void);
    void (*pf_ctrl_atmo_finalize) (int32_t what);
    int32_t (*pf_ctrl_atmo_switch_effect) (int32_t);
    int32_t (*pf_ctrl_atmo_set_live_source) (int32_t);
    void (*pf_ctrl_atmo_create_transfer_buffers) (int32_t, int32_t,
                                                  int32_t , int32_t);
    uint8_t* (*pf_ctrl_atmo_lock_transfer_buffer) (void);
    void (*pf_ctrl_atmo_send_pixel_data) (void);
#endif
};

/*
initialize previously configured Atmo Light environment
- if internal is enabled try to access the device on the serial port
- if not internal is enabled and we are on win32 try to initialize
the previously loaded DLL ...

Return Values may be: -1 (failed for some reason - filter will be disabled)
1 Ok. lets rock
*/
650
static int32_t AtmoInitialize(filter_t *p_filter, bool b_for_thread)
651
652
653
654
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config)
    {
655
        if(b_for_thread == false)
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
        {
            /* open com port */
            /* setup Output Threads ... */
            msg_Dbg( p_filter, "open serial connection %s",
                p_sys->p_atmo_config->getSerialDevice());

            if(CAtmoTools::RecreateConnection(p_sys->p_atmo_dyndata) == ATMO_TRUE)
            {
                msg_Dbg( p_filter, "start live view thread ...");
                CAtmoTools::SwitchEffect(p_sys->p_atmo_dyndata, emLivePicture);
                msg_Dbg( p_filter, "live view thread launched...");
                return 1;

            } else {
                msg_Err( p_filter,"failed to open serial device? some other software/driver may use it?");
            }
        }
#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_initialize)
    {
        /* on win32 with active ctrl dll */
        return p_sys->pf_ctrl_atmo_initialize();
#endif
    }
    return -1;
}

/*
prepare the shutdown of the effect threads,
for build in filter - close the serialport after finishing the threads...
cleanup possible loaded DLL...
*/
static void AtmoFinalize(filter_t *p_filter, int32_t what)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config)
    {
        if(what == 1)
        {
            CAtmoDynData *p_atmo_dyndata = p_sys->p_atmo_dyndata;
            if(p_atmo_dyndata)
            {
                p_atmo_dyndata->LockCriticalSection();

                CThread *p_effect_thread = p_atmo_dyndata->getEffectThread();
                p_atmo_dyndata->setEffectThread(NULL);
                if(p_effect_thread != NULL)
                {
                    /*
                    forced the thread to die...
                    and wait for termination of the thread
                    */
                    p_effect_thread->Terminate();
                    delete p_effect_thread;
                    msg_Dbg( p_filter, "effect thread died peacefully");
                }

                /*
                close serial port if it is open (all OS specific is inside
                CAtmoSerialConnection implemented / defined)
                */
                CAtmoConnection *p_atmo_connection =
                                 p_atmo_dyndata->getAtmoConnection();
                p_atmo_dyndata->setAtmoConnection(NULL);
                if(p_atmo_connection) {
                    p_atmo_connection->CloseConnection();
                    delete p_atmo_connection;
                }
                p_atmo_dyndata->UnLockCriticalSection();
            }
        }
#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_finalize)
    {
        /* on win32 with active ctrl dll */
        p_sys->pf_ctrl_atmo_finalize(what);
#endif
    }
}

/*
switch the current light effect - does only something on win32, with the
external  libraries - if the buildin effects are used nothing happens
*/
static int32_t AtmoSwitchEffect(filter_t *p_filter, int32_t newMode)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config)
    {
        /*
        buildin driver

        doesnt know different modes for effects so this
        function call would just do nothing special
        in this case
        */

#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_switch_effect)
    {
        /* on win32 with active ctrl dll */
        return p_sys->pf_ctrl_atmo_switch_effect(newMode);
#endif
    }
    return emDisabled;
}

/*
set the current live picture source, does only something on win32,
with the external libraries - if the buildin effects are used nothing
happens...
*/
static int32_t AtmoSetLiveSource(filter_t *p_filter, int32_t newSource)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config)
    {
        /*
        buildin driver

        doesnt know different sources so this
        function call would just do nothing special
        in this case
        */
#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_set_live_source)
    {
        /* on win32 with active ctrl dll */
        return p_sys->pf_ctrl_atmo_set_live_source(newSource);
#endif
    }
    return lvsGDI;
}

/*
setup the pixel transferbuffers which is used to transfer pixeldata from
the filter to the effect thread, and possible accross the process
boundaries on win32, with the external DLL
*/
static void AtmoCreateTransferBuffers(filter_t *p_filter,
                                      int32_t FourCC,
                                      int32_t bytePerPixel,
                                      int32_t width,
                                      int32_t height)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config)
    {
        /*
        we need a buffer where the image is stored (only for transfer
        to the processing thread)
        */
        if(p_sys->p_atmo_transfer_buffer)
            free(p_sys->p_atmo_transfer_buffer);

        p_sys->p_atmo_transfer_buffer = (uint8_t *)malloc(bytePerPixel *
                                                          width *  height);

        memset(&p_sys->mini_image_format,0,sizeof(BITMAPINFOHEADER));

        p_sys->mini_image_format.biSize = sizeof(BITMAPINFOHEADER);
        p_sys->mini_image_format.biWidth = width;
        p_sys->mini_image_format.biHeight = height;
        p_sys->mini_image_format.biBitCount = bytePerPixel*8;
        p_sys->mini_image_format.biCompression = FourCC;

#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_create_transfer_buffers)
    {
        /* on win32 with active ctrl dll */
        p_sys->pf_ctrl_atmo_create_transfer_buffers(FourCC,
            bytePerPixel,
            width,
            height);
#endif
    }
}

/*
acquire the transfer buffer pointer the buildin version only
returns the pointer to the allocated buffer ... the
external version on win32 has to do some COM stuff to lock the
Variant Byte array which is behind the buffer
*/
static uint8_t* AtmoLockTransferBuffer(filter_t *p_filter)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config)
    {
        return p_sys->p_atmo_transfer_buffer;
#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_lock_transfer_buffer)
    {
        /* on win32 with active ctrl dll */
        return p_sys->pf_ctrl_atmo_lock_transfer_buffer();
#endif
    }
    return NULL;
}

/*
send the content of current pixel buffer got with AtmoLockTransferBuffer
to the processing threads
- build in version - will forward the data to AtmoExternalCaptureInput Thread
- win32 external - will do the same, but across the process boundaries via
COM to the AtmoWinA.exe Process
*/
static void AtmoSendPixelData(filter_t *p_filter)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if(p_sys->p_atmo_config && p_sys->p_atmo_transfer_buffer)
    {
        CAtmoDynData *p_atmo_dyndata = p_sys->p_atmo_dyndata;
        if(p_atmo_dyndata)
        {
            /*
            the cast will go Ok because we are inside videolan there is only
            this kind of effect thread implemented!
            */

            CAtmoLiveView *p_atmo_live_view_thread =
                (CAtmoLiveView *)p_atmo_dyndata->getEffectThread();
            if(p_atmo_live_view_thread)
            {
                /*
                the same as above inside videolan only this single kind of
                input exists so we can cast without further tests!
                */
                CAtmoExternalCaptureInput *p_atmo_external_capture_input_thread =
                    (CAtmoExternalCaptureInput *)p_atmo_live_view_thread->getAtmoInput();
                if(p_atmo_external_capture_input_thread)
                {
                    /*
                    this call will do a 1:1 copy of this buffer, and wakeup
                    the thread from normal sleeping
                    */
                    p_atmo_external_capture_input_thread->
                        DeliverNewSourceDataPaket(&p_sys->mini_image_format,
                        p_sys->p_atmo_transfer_buffer);
                }
            }
        }
#if defined(WIN32)
    } else if(p_sys->pf_ctrl_atmo_send_pixel_data)
    {
        /* on win32 with active ctrl dll */
        p_sys->pf_ctrl_atmo_send_pixel_data();
#endif
    }
}

/*
    Shutdown AtmoLight finally - is call from DestroyFilter
    does the cleanup restores the effectmode on the external Software
    (only win32) and possible setup the final light ...
*/
static void Atmo_Shutdown(filter_t *p_filter)
{
    filter_sys_t *p_sys = p_filter->p_sys;

916
    if(p_sys->b_enabled == true)
917
918
919
920
921
922
923
924
925
926
927
928
929
    {
        /*
        if there is a still running show pause color thread kill him!
        */
        CheckAndStopFadeThread(p_filter);

        if(p_sys->p_atmo_config || (p_sys->i_AtmoOldEffect == emStaticColor))
        {
            /*
            fade to end color (in case of external AtmoWin Software
            assume that the static color will equal to this
            one to get a soft change and no flash!
            */
930
            p_sys->b_pause_live = true;
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947

            // perpare spawn fadeing thread
            vlc_mutex_lock( &p_sys->filter_lock );

            p_sys->p_fadethread = (fadethread_t *)vlc_object_create( p_filter,
                                                        sizeof(fadethread_t) );

            p_sys->p_fadethread->p_filter = p_filter;
            p_sys->p_fadethread->ui_red   = p_sys->ui_endcolor_red;
            p_sys->p_fadethread->ui_green = p_sys->ui_endcolor_green;
            p_sys->p_fadethread->ui_blue  = p_sys->ui_endcolor_blue;
            p_sys->p_fadethread->i_steps  = p_sys->i_endfadesteps;

            if( vlc_thread_create( p_sys->p_fadethread,
                "AtmoLight fadeing",
                FadeToColorThread,
                VLC_THREAD_PRIORITY_LOW,
948
                false ) )
949
950
            {
                msg_Err( p_filter, "cannot create FadeToColorThread" );
951
                vlc_object_release( p_sys->p_fadethread );
952
953
954
955
956
957
958
959
960
961
                p_sys->p_fadethread = NULL;
                vlc_mutex_unlock( &p_sys->filter_lock );

            } else {

                vlc_mutex_unlock( &p_sys->filter_lock );

                /* wait for the thread... */
                vlc_thread_join(p_sys->p_fadethread);

962
                vlc_object_release(p_sys->p_fadethread);
963
964
965
966
967
968
969
970
971
972
973
974
975

                p_sys->p_fadethread = NULL;
            }
        }

        if(p_sys->i_AtmoOldEffect != emLivePicture)
            AtmoSwitchEffect(p_filter, p_sys->i_AtmoOldEffect);
        else
            AtmoSetLiveSource(p_filter, lvsGDI);

        AtmoFinalize(p_filter, 1);

        /* disable filter method .. */
976
        p_sys->b_enabled = false;
977
978
979
980
981
982
983
984
985
986
    }
}

/*
initialize the filter_sys_t structure with the data from the settings
variables - if the external filter on win32 is enabled try loading the DLL,
if this fails fallback to the buildin software
*/
static void Atmo_SetupParameters(filter_t *p_filter)
{
987
    bool b_use_buildin_driver = true;
988
989
990
991
992
    char *psz_path;
    filter_sys_t *p_sys =  p_filter->p_sys;


    /* default filter disabled until DLL loaded and Init Success!*/
993
    p_sys->b_enabled             = false;
994
995
996
997
998
999

    /* setup default mini image size (may be later a user option) */
    p_sys->i_atmo_width          = 64;
    p_sys->i_atmo_height         = 48;


1000
    vlc_mutex_init( &p_sys->filter_lock );
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010


#if defined(WIN32)
    /*
    only on WIN32 the user has the choice between
    internal driver and external
    */
    b_use_buildin_driver = var_CreateGetBoolCommand( p_filter,
        CFG_PREFIX "usebuildin" );

1011
    if(b_use_buildin_driver == false) {
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056

        /* Load the Com Wrapper Library (source available) */
        p_sys->h_AtmoCtrl = LoadLibraryA("AtmoCtrlLib.dll");
        if(p_sys->h_AtmoCtrl != NULL)
        {
            msg_Dbg( p_filter, "LoadLibrary('AtmoCtrlLib.dll'); Success");

            /* importing all required functions I hope*/
            p_sys->pf_ctrl_atmo_initialize =
                (int32_t (*)(void))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoInitialize");
            if(!p_sys->pf_ctrl_atmo_initialize)
                msg_Err( p_filter, "export AtmoInitialize missing.");

            p_sys->pf_ctrl_atmo_finalize =
                (void (*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoFinalize");
            if(!p_sys->pf_ctrl_atmo_finalize)
                msg_Err( p_filter, "export AtmoFinalize missing.");

            p_sys->pf_ctrl_atmo_switch_effect =
                (int32_t(*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoSwitchEffect");
            if(!p_sys->pf_ctrl_atmo_switch_effect)
                msg_Err( p_filter, "export AtmoSwitchEffect missing.");

            p_sys->pf_ctrl_atmo_set_live_source =
                (int32_t(*)(int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoSetLiveSource");
            if(!p_sys->pf_ctrl_atmo_set_live_source)
                msg_Err( p_filter, "export AtmoSetLiveSource missing.");

            p_sys->pf_ctrl_atmo_create_transfer_buffers =
                (void (*)(int32_t, int32_t, int32_t , int32_t))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoCreateTransferBuffers");
            if(!p_sys->pf_ctrl_atmo_create_transfer_buffers)
                msg_Err( p_filter, "export AtmoCreateTransferBuffers missing.");

            p_sys->pf_ctrl_atmo_lock_transfer_buffer=
                (uint8_t*(*) (void))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoLockTransferBuffer");
            if(!p_sys->pf_ctrl_atmo_lock_transfer_buffer)
                msg_Err( p_filter, "export AtmoLockTransferBuffer missing.");

            p_sys->pf_ctrl_atmo_send_pixel_data =
                (void (*)(void))GetProcAddress(p_sys->h_AtmoCtrl,"AtmoSendPixelData");
            if(!p_sys->pf_ctrl_atmo_send_pixel_data)
                msg_Err( p_filter, "export AtmoSendPixelData missing.");
        } else {
            /* the DLL is missing try internal filter ...*/
            msg_Warn( p_filter, "AtmoCtrlLib.dll missing fallback to internal driver");
1057
            b_use_buildin_driver = true;
1058
1059
1060
1061
1062
        }
    }
#endif


1063
    if(b_use_buildin_driver == true) {
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
        msg_Dbg( p_filter, "use buildin driver");
        /*
        now we have to read a lof of options from the config dialog
        most important the serial device if not set ... we can skip
        the rest and disable the filter...
        */
        char *psz_serialdev = var_CreateGetStringCommand( p_filter,
                                                      CFG_PREFIX "serialdev" );
        if(psz_serialdev && (strlen(psz_serialdev)>0)) {
            msg_Dbg( p_filter, "use buildin driver on port %s",psz_serialdev);

            p_sys->p_atmo_config = new CAtmoConfig();

            p_sys->p_atmo_config->setSerialDevice(psz_serialdev);

            p_sys->p_atmo_config->setLiveViewFilterMode(
                (AtmoFilterMode)var_CreateGetIntegerCommand( p_filter,
                                                       CFG_PREFIX "filtermode")
                );

            p_sys->p_atmo_config->setLiveViewFilter_PercentNew(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "PercentNew")
                );
            p_sys->p_atmo_config->setLiveViewFilter_MeanLength(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "MeanLength")
                );
            p_sys->p_atmo_config->setLiveViewFilter_MeanThreshold(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "MeanThreshold")
                );

            p_sys->p_atmo_config->setLiveView_EdgeWeighting(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "EdgeWeightning")
                );
            p_sys->p_atmo_config->setLiveView_BrightCorrect(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "Brightness")
                );
            p_sys->p_atmo_config->setLiveView_DarknessLimit(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "DarknessLimit")
                );
            p_sys->p_atmo_config->setLiveView_HueWinSize(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "HueWinSize")
                );
            p_sys->p_atmo_config->setLiveView_SatWinSize(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "SatWinSize")
                );

            /* currently not required inside vlc */
            p_sys->p_atmo_config->setLiveView_WidescreenMode( 0 );

            p_sys->p_atmo_config->setLiveView_FrameDelay(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "FrameDelay")
                );


            p_sys->p_atmo_config->setUseSoftwareWhiteAdj(
                var_CreateGetBoolCommand( p_filter, CFG_PREFIX "whiteadj")
                );
            p_sys->p_atmo_config->setWhiteAdjustment_Red(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-red")
                );
            p_sys->p_atmo_config->setWhiteAdjustment_Green(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-green")
                );
            p_sys->p_atmo_config->setWhiteAdjustment_Blue(
                var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "white-blue")
                );

            tChannelAssignment *p_channel_assignment =
                                 p_sys->p_atmo_config->getChannelAssignment(0);

            p_channel_assignment->mappings[0] = var_CreateGetIntegerCommand(
                                             p_filter, CFG_PREFIX "channel_0");

            p_channel_assignment->mappings[1] = var_CreateGetIntegerCommand(
                                             p_filter, CFG_PREFIX "channel_1");

            p_channel_assignment->mappings[2] = var_CreateGetIntegerCommand(
                                             p_filter, CFG_PREFIX "channel_2");

            p_channel_assignment->mappings[3] = var_CreateGetIntegerCommand(
                                             p_filter, CFG_PREFIX "channel_3");

            p_channel_assignment->mappings[4] = var_CreateGetIntegerCommand(
                                             p_filter, CFG_PREFIX "channel_4");

            for(int i=0;i<ATMO_NUM_CHANNELS;i++)
                msg_Dbg( p_filter, "map software channel %d to hardware channel %d",
                p_channel_assignment->mappings[i],
                i
                );

            // gradient_zone_0
            char psz_gradient_var_name[30];
            char *psz_gradient_file;
            for(int i=0;i<ATMO_NUM_CHANNELS;i++)
            {
                sprintf(psz_gradient_var_name, CFG_PREFIX "gradient_zone_%d", i);
                psz_gradient_file = var_CreateGetStringCommand(
                    p_filter,
                    psz_gradient_var_name
                    );
                if(psz_gradient_file && strlen(psz_gradient_file)>0)
                {
                    msg_Dbg( p_filter, "loading gradientfile %s for "\
                                       "zone %d", psz_gradient_file, i);

                    int i_res = p_sys->p_atmo_config->getZoneDefinition(i)->
                                LoadGradientFromBitmap(psz_gradient_file);

                    if(i_res != ATMO_LOAD_GRADIENT_OK)
                    {
                        msg_Err( p_filter,"failed to load gradient '%s' with "\
                                          "error %d",psz_gradient_file,i_res);
                    }
                }
                delete psz_gradient_file;
            }

            p_sys->p_atmo_dyndata = new CAtmoDynData((vlc_object_t *)p_filter,
                p_sys->p_atmo_config
                );

            msg_Dbg( p_filter, "buildin driver initialized");

            free(psz_serialdev);
        } else {
            msg_Err(p_filter,"no serial devicename set");
        }
    }

    switch( p_filter->fmt_in.video.i_chroma )
    {
    case VLC_FOURCC('I','4','2','0'):
    case VLC_FOURCC('I','Y','U','V'):
    case VLC_FOURCC('Y','V','1','2'):
    case VLC_FOURCC('Y','V','1','6'):
    case VLC_FOURCC('Y','V','U','9'):
        // simple enough? Dionoea?
        p_sys->pf_extract_mini_image = ExtractMiniImage_YUV;
        break;
    default:
        msg_Dbg( p_filter, "InitFilter-unsupported chroma: %4.4s",
                            (char *)&p_filter->fmt_in.video.i_chroma);
        p_sys->pf_extract_mini_image = NULL;
    }

    p_sys->i_crop_x_offset  = 0;
    p_sys->i_crop_y_offset  = 0;
    p_sys->i_crop_width     = p_filter->fmt_in.video.i_visible_width;
    p_sys->i_crop_height    = p_filter->fmt_in.video.i_visible_height;

    msg_Dbg( p_filter, "set default crop %d,%d %dx%d",p_sys->i_crop_x_offset,
        p_sys->i_crop_y_offset,
        p_sys->i_crop_width,
        p_sys->i_crop_height );


#if defined(__ATMO_DEBUG__)
    /* save debug images to a folder as Bitmap files ? */
    p_sys->b_saveframes  = var_CreateGetBoolCommand( p_filter,
        CFG_PREFIX "saveframes"
        );
    msg_Dbg(p_filter,"saveframes = %d", (int)p_sys->b_saveframes);

    /*
    read debug image folder from config
    */
    psz_path = var_CreateGetStringCommand( p_filter, CFG_PREFIX "framepath" );
    if(psz_path != NULL)
    {
        strcpy(p_sys->sz_framepath, psz_path);
#if defined( WIN32 )
        size_t i_strlen = strlen(p_sys->sz_framepath);
        if((i_strlen>0) && (p_sys->sz_framepath[i_strlen-1] != '\\'))
        {
            p_sys->sz_framepath[i_strlen] = '\\';
            p_sys->sz_framepath[i_strlen+1] = 0;
        }
#endif
        free(psz_path);
    }
    msg_Dbg(p_filter,"saveframesfolder %s",p_sys->sz_framepath);
#endif

    /*
       size of extracted image by default 64x48 (other imagesizes are
       currently ignored by AtmoWin)
    */
    p_sys->i_atmo_width  = var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "width");
    p_sys->i_atmo_height = var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "height");
    msg_Dbg(p_filter,"mini image size %d * %d pixels", p_sys->i_atmo_width,
        p_sys->i_atmo_height);

    /*
    because atmowin could also be used for lighten up the room - I think if you
    pause the video it would be useful to get a little bit more light into to
    your living room? - instead switching on a lamp?
    */
    p_sys->b_usepausecolor = var_CreateGetBoolCommand( p_filter,
        CFG_PREFIX "usepausecolor" );
    p_sys->ui_pausecolor_red = (uint8_t)var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "pcolor-red");
    p_sys->ui_pausecolor_green = (uint8_t)var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "pcolor-green");
    p_sys->ui_pausecolor_blue = (uint8_t)var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "pcolor-blue");
    p_sys->i_fadesteps = var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "fadesteps");
    if(p_sys->i_fadesteps < 1)
        p_sys->i_fadesteps = 1;
    msg_Dbg(p_filter,"use pause color %d, RGB: %d, %d, %d, Fadesteps: %d",
        (int)p_sys->b_usepausecolor,
        p_sys->ui_pausecolor_red,
        p_sys->ui_pausecolor_green,
        p_sys->ui_pausecolor_blue,
        p_sys->i_fadesteps);

    /*
    this color is use on shutdown of the filter - the define the
    final light after playback... may be used to dim up the light -
    how it happens in the cinema...
    */
    p_sys->ui_endcolor_red = (uint8_t)var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "ecolor-red");
    p_sys->ui_endcolor_green = (uint8_t)var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "ecolor-green");
    p_sys->ui_endcolor_blue = (uint8_t)var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "ecolor-blue");
    p_sys->i_endfadesteps = var_CreateGetIntegerCommand( p_filter,
        CFG_PREFIX "efadesteps");
    if(p_sys->i_endfadesteps < 1)
        p_sys->i_endfadesteps = 1;
    msg_Dbg(p_filter,"use ende color RGB: %d, %d, %d, Fadesteps: %d",
        p_sys->ui_endcolor_red,
        p_sys->ui_endcolor_green,
        p_sys->ui_endcolor_blue,
        p_sys->i_endfadesteps);

    /* if the external DLL was loaded successfully call AtmoInitialize -
    (must be done for each thread where you wan't to use AtmoLight!
    */
1307
    int i = AtmoInitialize(p_filter, false);
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
#if defined( WIN32 )
    if((i != 1) && !b_use_buildin_driver)
    {
        /* COM Server for AtmoLight not running ?
        if the exe path is configured try to start the "userspace" driver
        */
        psz_path = var_CreateGetStringCommand( p_filter,
                                               CFG_PREFIX "atmowinexe" );
        if(psz_path != NULL)
        {
            STARTUPINFO startupinfo;
            PROCESS_INFORMATION pinfo;
            memset(&startupinfo, 0, sizeof(STARTUPINFO));
            startupinfo.cb = sizeof(STARTUPINFO);
            if(CreateProcess(psz_path, NULL, NULL, NULL,
                FALSE, 0, NULL, NULL, &startupinfo, &pinfo) == TRUE)
            {
                msg_Dbg(p_filter,"launched AtmoWin from %s",psz_path);
                WaitForInputIdle(pinfo.hProcess, 5000);
                /*
                retry to initialize the library COM ... functionality
                after the server was launched
                */
1331
                i = AtmoInitialize(p_filter, false);
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
            } else {
                msg_Err(p_filter,"failed to launch AtmoWin from %s", psz_path);
            }
            free(psz_path);
        }
    }
#endif

    if(i == 1) /* Init Atmolight success... */
    {
        msg_Dbg( p_filter, "AtmoInitialize Ok!");

        /* Setup Transferbuffers for 64 x 48 , RGB with 32bit Per Pixel */
        AtmoCreateTransferBuffers(p_filter, BI_RGB, 4,
            p_sys->i_atmo_width,
            p_sys->i_atmo_height
            );

        /* say the userspace driver that a live mode should be activated
        the functions returns the old mode for later restore!
        */
        p_sys->i_AtmoOldEffect = AtmoSwitchEffect(p_filter, emLivePicture);

        /*
        live view can have two differnt source the AtmoWinA
        internal GDI Screencapture and the external one - which we
        need here...
        */
        AtmoSetLiveSource(p_filter, lvsExternal);

        /* enable other parts only if everything is fine */
1363
        p_sys->b_enabled = true;
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
    }

}


/*****************************************************************************
* CreateFilter: allocates AtmoLight video thread output method
*****************************************************************************
* This function allocates and initializes a AtmoLight vout method.
*****************************************************************************/
static int CreateFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;

    /* Allocate structure */
    p_sys = (filter_sys_t *)malloc( sizeof( filter_sys_t ) );
    p_filter->p_sys = p_sys;
    if( p_filter->p_sys == NULL )
        return VLC_ENOMEM;
    /* set all entries to zero */
    memset(p_sys, 0, sizeof( filter_sys_t ));

    /* further Setup Function pointers for videolan for calling my filter */
    p_filter->pf_video_filter = Filter;

    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                       p_filter->p_cfg );

    AddStateVariableCallback(p_filter);

    AddCropVariableCallback(p_filter);

    AddAtmoSettingsVariablesCallbacks(p_filter);

    Atmo_SetupParameters(p_filter);


    return VLC_SUCCESS;
}



/*****************************************************************************
* DestroyFilter: destroy AtmoLight video thread output method
*****************************************************************************
* Terminate an output method created by CreateFilter
*****************************************************************************/

static void DestroyFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys =  p_filter->p_sys;

    DelStateVariableCallback(p_filter);
    DelCropVariableCallback(p_filter);
    DelAtmoSettingsVariablesCallbacks(p_filter);

    Atmo_Shutdown(p_filter);

#if defined( WIN32 )
    if(p_sys->h_AtmoCtrl != NULL)
    {
        FreeLibrary(p_sys->h_AtmoCtrl);
    }
#endif

    delete p_sys->p_atmo_dyndata;
    delete p_sys->p_atmo_config;

    vlc_mutex_destroy( &p_sys->filter_lock );

    free( p_sys );
}


/*
function stolen from some other videolan source filter ;-)
for the moment RGB is OK... but better would be a direct transformation
from YUV --> HSV
*/
static inline void yuv_to_rgb( uint8_t *r, uint8_t *g, uint8_t *b,
                              uint8_t y1, uint8_t u1, uint8_t v1 )
{
    /* macros used for YUV pixel conversions */
#   define SCALEBITS 10
#   define ONE_HALF  (1 << (SCALEBITS - 1))
#   define FIX(x)    ((int) ((x) * (1<<SCALEBITS) + 0.5))
#   define CLAMP( x ) (((x) > 255) ? 255 : ((x) < 0) ? 0 : (x));

    int y, cb, cr, r_add, g_add, b_add;

    cb = u1 - 128;
    cr = v1 - 128;
    r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;
    g_add = - FIX(0.34414*255.0/224.0) * cb
        - FIX(0.71414*255.0/224.0) * cr + ONE_HALF;
    b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;
    y = (y1 - 16) * FIX(255.0/219.0);
    *r = CLAMP((y + r_add) >> SCALEBITS);
    *g = CLAMP((y + g_add) >> SCALEBITS);
    *b = CLAMP((y + b_add) >> SCALEBITS);
}
/******************************************************************************
* ExtractMiniImage_YUV: extract a small image from the picture as 24-bit RGB
*******************************************************************************
* p_sys is a pointer to
* p_inpic is the source frame
* p_transfer_dest is the target buffer for the picture must be big enough!
* (in win32 enviroment this buffer comes from the external DLL where it is
* create as "variant array" and returned through the AtmoLockTransferbuffer
*/
static void ExtractMiniImage_YUV(filter_sys_t *p_sys,
                                 picture_t *p_inpic,
                                 uint8_t *p_transfer_dest)
{
    int i_col;
    int i_row;
    uint8_t *p_src_y;
    uint8_t *p_src_u;
    uint8_t *p_src_v;
    uint8_t *p_rgb_dst_line_red;
    uint8_t *p_rgb_dst_line_green;
    uint8_t *p_rgb_dst_line_blue;
    int i_xpos_y;
    int i_xpos_u;
    int i_xpos_v;

    /* calcute Pointers for Storage of B G R (A) */
    p_rgb_dst_line_blue      = p_transfer_dest;
    p_rgb_dst_line_green     = p_transfer_dest + 1;
    p_rgb_dst_line_red       = p_transfer_dest + 2 ;

    int i_row_count = p_sys->i_atmo_height + 1;
    int i_col_count = p_sys->i_atmo_width + 1;
    int i_y_row,i_u_row,i_v_row,i_pixel_row;
    int i_pixel_col;


    /*  these two ugly loops extract the small image - goes it faster? how?
    the loops are so designed that there is a small border around the extracted
    image so we wont get column and row - zero from the frame, and not the most
    right and bottom pixels --- which may be clipped on computers useing TV out
    - through overscan!

    TODO: try to find out if the output is clipped through VLC - and try here
    to ingore the clipped away area for a better result!

    TODO: performance improvement in InitFilter percalculated the offsets of
    the lines inside the planes so I can save (i_row_count * 3) 2xMUL and
    one time DIV the same could be done for the inner loop I think...
    */
    for(i_row = 1; i_row < i_row_count; i_row++)
    {
        // calcute the current Lines in the source planes for this outputrow
        /*  Adresscalcuation  pointer to plane  Length of one pixelrow in bytes
        calculate row now number
        */
        /*
           p_inpic->format? transform Pixel row into row of plane...
           how? simple? fast? good?
        */

        /* compute the source pixel row and respect the active cropping */
        i_pixel_row = (i_row * p_sys->i_crop_height) / i_row_count
            + p_sys->i_crop_y_offset;

        /*
        trans for these Pixel row into the row of each plane ..
        because planesize can differ from image size
        */
        i_y_row = (i_pixel_row * p_inpic->p[Y_PLANE].i_visible_lines) /
            p_inpic->format.i_visible_height;

        i_u_row = (i_pixel_row * p_inpic->p[U_PLANE].i_visible_lines) /
            p_inpic->format.i_visible_height;

        i_v_row = (i_pixel_row * p_inpic->p[V_PLANE].i_visible_lines) /
            p_inpic->format.i_visible_height;

        /* calculate  the pointers to the pixeldata for this row
           in each plane
        */
        p_src_y = p_inpic->p[Y_PLANE].p_pixels +
            p_inpic->p[Y_PLANE].i_pitch * i_y_row;
        p_src_u = p_inpic->p[U_PLANE].p_pixels +
            p_inpic->p[U_PLANE].i_pitch * i_u_row;
        p_src_v = p_inpic->p[V_PLANE].p_pixels +
            p_inpic->p[V_PLANE].i_pitch * i_v_row;

        for(i_col = 1; i_col < i_col_count; i_col++)
        {
            i_pixel_col = (i_col * p_sys->i_crop_width) / i_col_count +
                p_sys->i_crop_x_offset;
            /*
            trans for these Pixel row into the row of each plane ..
            because planesize can differ from image size
            */
            i_xpos_y = (i_pixel_col * p_inpic->p[Y_PLANE].i_visible_pitch) /
                p_inpic->format.i_visible_width;
            i_xpos_u = (i_pixel_col * p_inpic->p[U_PLANE].i_visible_pitch) /
                p_inpic->format.i_visible_width;
            i_xpos_v = (i_pixel_col * p_inpic->p[V_PLANE].i_visible_pitch) /
                p_inpic->format.i_visible_width;

            yuv_to_rgb(p_rgb_dst_line_red,
                p_rgb_dst_line_green,
                p_rgb_dst_line_blue,

                p_src_y[i_xpos_y],
                p_src_u[i_xpos_u],
                p_src_v[i_xpos_v]);

            /* +4 because output image should be RGB32 with dword alignment! */
            p_rgb_dst_line_red   += 4;
            p_rgb_dst_line_green += 4;
            p_rgb_dst_line_blue  += 4;
        }
    }
}


/******************************************************************************
* SaveBitmap: Saves the content of a transferbuffer as Bitmap to disk
*******************************************************************************
* just for debugging
* p_sys -> configuration if Atmo from there the function will get height and
*          width
* p_pixels -> should be the dword aligned BGR(A) image data
* psz_filename -> filename where to store
*/
#if defined(__ATMO_DEBUG__)
void SaveBitmap(filter_sys_t *p_sys, uint8_t *p_pixels, char *psz_filename)
{
    /* for debug out only used*/
    BITMAPINFO bmp_info;
    BITMAPFILEHEADER  bmp_fileheader;
    FILE *fp_bitmap;

    memset(&bmp_info, 0, sizeof(BITMAPINFO));
    bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmp_info.bmiHeader.biSizeImage   = p_sys->i_atmo_height *
                                       p_sys->i_atmo_width * 4;
    bmp_info.bmiHeader.biCompression = BI_RGB;
    bmp_info.bmiHeader.biWidth        = p_sys->i_atmo_width;
    bmp_info.bmiHeader.biHeight       = -p_sys->i_atmo_height;
    bmp_info.bmiHeader.biBitCount     = 32;
    bmp_info.bmiHeader.biPlanes       = 1;

    bmp_fileheader.bfReserved1 = 0;
    bmp_fileheader.bfReserved2 = 0;
    bmp_fileheader.bfSize = sizeof(BITMAPFILEHEADER) +
                            sizeof(BITMAPINFOHEADER) +
                            bmp_info.bmiHeader.biSizeImage;
1618
    bmp_fileheader.bfType = VLC_TWOCC('M','B');
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
    bmp_fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) +
                               sizeof(BITMAPINFOHEADER);

    fp_bitmap = fopen(psz_filename,"wb");
    if( fp_bitmap != NULL)
    {
        fwrite(&bmp_fileheader, sizeof(BITMAPFILEHEADER), 1, fp_bitmap);
        fwrite(&bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp_bitmap);
        fwrite(p_pixels, bmp_info.bmiHeader.biSizeImage, 1, fp_bitmap);
        fclose(fp_bitmap);
    }
}
#endif


/****************************************************************************
* CreateMiniImage: extracts a 64x48 pixel image from the frame
* (there is a small border arround thats why the loops starts with one
* instead zero) without any interpolation
*****************************************************************************/
static void CreateMiniImage( filter_t *p_filter, picture_t *p_inpic)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    /*
    pointer to RGB Buffer created in external libary as safe array which
    is locked inside AtmoLockTransferBuffer
    */
    uint8_t *p_transfer = NULL;
#if defined( __ATMO_DEBUG__ )
    /* for debug out only used*/
    char sz_filename[MAX_PATH];
#endif

    /*
    Lock the before created VarArray (AtmoCreateTransferBuffers)
    inside my wrapper library and give me a pointer to the buffer!
    below linux a global buffer may be used and protected with a mutex?
    */
    p_transfer = AtmoLockTransferBuffer(p_filter);
    if(p_transfer == NULL)
    {
        msg_Err( p_filter, "AtmoLight no transferbuffer available. "\
                           "AtmoLight will be disabled!");
1662
        p_sys->b_enabled = false;
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
        return;
    }

    /*
    do the call via pointer to function instead of having a
    case structure here
    */
    p_sys->pf_extract_mini_image(p_sys, p_inpic, p_transfer);


#if defined( __ATMO_DEBUG__ )
    /*
    if debugging enabled save every 128th image to disk
    */
1677
    if((p_sys->b_saveframes == true) && (p_sys->sz_framepath[0] != 0 ))
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
    {

        if((p_sys->i_framecounter & 127) == 0)
        {
            sprintf(sz_filename,"%satmo_dbg_%06d.bmp",p_sys->sz_framepath,
                p_sys->i_framecounter);
            msg_Dbg(p_filter, "SaveFrame %s",sz_filename);

            SaveBitmap(p_sys, p_transfer, sz_filename);
        }
        p_sys->i_framecounter++;
    }
#endif

    /* show the colors on the wall */
    AtmoSendPixelData(p_filter);
}




/*****************************************************************************
* Filter: calls the extract method and forwards the incomming picture 1:1
*****************************************************************************
*
*****************************************************************************/

static picture_t * Filter( filter_t *p_filter, picture_t *p_pic )
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if( !p_pic ) return NULL;

1710
    if((p_sys->b_enabled == true) &&
1711
        (p_sys->pf_extract_mini_image != NULL) &&
1712
        (p_sys->b_pause_live == false))
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
    {
        CreateMiniImage(p_filter, p_pic);
    }

    return p_pic;
}


/*****************************************************************************
* FadeToColorThread: Threadmethod which changes slowly the color
* to a target color defined in p_fadethread struct
* use for: Fade to Pause Color,  and Fade to End Color
*****************************************************************************/
static void FadeToColorThread(fadethread_t *p_fadethread)
{
    filter_sys_t *p_sys = (filter_sys_t *)p_fadethread->p_filter->p_sys;
    int i_steps_done = 0;
    int i_index;
    int i_pause_red;
    int i_pause_green;
    int i_pause_blue;

    int i_src_red;
    int i_src_green;
    int i_src_blue;

    vlc_thread_ready( p_fadethread );

    uint8_t *p_source = NULL;

    /* initialize AtmoWin for this thread! */
1744
    AtmoInitialize(p_fadethread->p_filter , true);
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768

    uint8_t *p_transfer = AtmoLockTransferBuffer( p_fadethread->p_filter );
    if(p_transfer != NULL) {
        /* safe colors as "32bit" Integers to avoid overflows*/
        i_pause_red   = p_fadethread->ui_red;
        i_pause_blue  = p_fadethread->ui_blue;
        i_pause_green = p_fadethread->ui_green;

        /*
        allocate a temporary buffer for the last send
        image size less then 15kb
        */
        int i_size = 4 * p_sys->i_atmo_width * p_sys->i_atmo_height;
        p_source = (uint8_t *)malloc( i_size );
        if(p_source != NULL)
        {
            /*
            get a copy of the last transfered image as orign for the
            fading steps...
            */
            memcpy(p_source, p_transfer, i_size);
            /* send the same pixel data again... to unlock the buffer! */
            AtmoSendPixelData( p_fadethread->p_filter );

1769
            while( (vlc_object_alive (p_fadethread)) &&
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
                (i_steps_done < p_fadethread->i_steps))
            {
                p_transfer = AtmoLockTransferBuffer( p_fadethread->p_filter );
                if(!p_transfer) break; /* should not happen if it worked
                                       one time in the code above! */
                i_steps_done++;
                /*
                move all pixels in the mini image (64x48) one step closer to
                the desired color these loop takes the most time of this
                thread improvements wellcome!
                */
                for(i_index = 0;
1782
                    (i_index < i_size) && (vlc_object_alive (p_fadethread));
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
                    i_index+=4)
                {
                    i_src_blue  = p_source[i_index+0];
                    i_src_green = p_source[i_index+1];
                    i_src_red   = p_source[i_index+2];
                    p_transfer[i_index+0] = (uint8_t) (((
                        (i_pause_blue  - i_src_blue)
                        * i_steps_done)/p_fadethread->i_steps)
                        + i_src_blue);

                    p_transfer[i_index+1] = (uint8_t) (((
                        (i_pause_green - i_src_green)
                        * i_steps_done)/p_fadethread->i_steps)
                        + i_src_green);

                    p_transfer[i_index+2] = (uint8_t) (((
                        (i_pause_red   - i_src_red)
                        * i_steps_done)/p_fadethread->i_steps)
                        + i_src_red);
                }

                /* send image to lightcontroller */
                AtmoSendPixelData( p_fadethread->p_filter );
                /* is there something like and interruptable sleep inside
                the VLC libaries? inside native win32 I would use an Event
                (CreateEvent) and here an WaitForSingleObject?
                */
1810
                if(!vlc_object_alive (p_fadethread)) break;
1811
                msleep(10000);
1812
                if(!vlc_object_alive (p_fadethread)) break;
1813
                msleep(10000);
1814
                if(!vlc_object_alive (p_fadethread)) break;
1815
                msleep(10000);
1816
                if(!vlc_object_alive (p_fadethread)) break;
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
                msleep(10000);
            }
            free(p_source);
        } else {
            /* in failure of malloc also unlock buffer  */
            AtmoSendPixelData(p_fadethread->p_filter);
        }
    }
    /* call indirect to OleUnitialize() for this thread */
    AtmoFinalize(p_fadethread->p_filter, 0);
}

/*****************************************************************************
* CheckAndStopFadeThread: if there is a fadethread structure left, or running.
******************************************************************************
* this function will stop the thread ... and waits for its termination
* before removeing the objects from vout_sys_t ...
******************************************************************************/
static void CheckAndStopFadeThread(filter_t *p_filter)
{
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
    vlc_mutex_lock( &p_sys->filter_lock );
    if(p_sys->p_fadethread != NULL)
    {
        msg_Dbg(p_filter, "kill still running fadeing thread...");

1843
        p_sys->p_fadethread->b_die = true;
1844
1845
1846

        vlc_thread_join(p_sys->p_fadethread);

1847
        vlc_object_release(p_sys->p_fadethread);
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
        p_sys->p_fadethread = NULL;
    }
    vlc_mutex_unlock( &p_sys->filter_lock );
}

/*****************************************************************************
* StateCallback: Callback for the inputs variable "State" to get notified
* about Pause and Continue Playback events.
*****************************************************************************/
static int StateCallback( vlc_object_t *p_this, char const *psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval,
                         void *p_data )
{
    filter_t *p_filter = (filter_t *)p_data;
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;

1864
    if((p_sys->b_usepausecolor == true) && (p_sys->b_enabled == true))
1865
1866
1867
1868
1869
1870
1871
1872
    {
        msg_Dbg(p_filter, "state change from: %d to %d", oldval.i_int,
            newval.i_int);

        if((newval.i_int == PAUSE_S) && (oldval.i_int == PLAYING_S))
        {
            /* tell the other thread to stop sending images to light
               controller */
1873
            p_sys->b_pause_live = true;
1874

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1875
            // ggf. alten Thread abräumen should not happen....
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
            CheckAndStopFadeThread(p_filter);

            // perpare spawn fadeing thread
            vlc_mutex_lock( &p_sys->filter_lock );
            /*
            launch only a new thread if there is none active!
            or waiting for cleanup
            */
            if(p_sys->p_fadethread == NULL)
            {
                p_sys->p_fadethread = (fadethread_t *)vlc_object_create(
                     p_filter,
                     sizeof(fadethread_t) );

                p_sys->p_fadethread->p_filter = p_filter;
                p_sys->p_fadethread->ui_red   = p_sys->ui_pausecolor_red;
                p_sys->p_fadethread->ui_green = p_sys->ui_pausecolor_green;
                p_sys->p_fadethread->ui_blue  = p_sys->ui_pausecolor_blue;
                p_sys->p_fadethread->i_steps  = p_sys->i_fadesteps;

                if( vlc_thread_create( p_sys->p_fadethread,
                    "AtmoLight fadeing",
                    FadeToColorThread,
                    VLC_THREAD_PRIORITY_LOW,
1900
                    false) )
1901
1902
                {
                    msg_Err( p_filter, "cannot create FadeToColorThread" );
1903
                    vlc_object_release( p_sys->p_fadethread );
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
                    p_sys->p_fadethread = NULL;
                }
            }
            vlc_mutex_unlock( &p_sys->filter_lock );
        }

        if((newval.i_int == PLAYING_S) && (oldval.i_int == PAUSE_S))
        {
            /* playback continues check thread state */
            CheckAndStopFadeThread(p_filter);
            /* reactivate the Render function... to do its normal work */
1915
            p_sys->b_pause_live = false;
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
        }
    }

    return VLC_SUCCESS;
}

/*****************************************************************************
* AddPlaylistInputThreadStateCallback: Setup call back on "State" Variable
*****************************************************************************
* Add Callback function to the "state" variable of the input thread..
* first find the PlayList and get the input thread from there to attach
* my callback? is vlc_object_find the right way for this??
*****************************************************************************/
static void AddStateVariableCallback(filter_t *p_filter)
{
1931
1932
1933
    playlist_t *p_playlist = pl_Yield( p_filter );
    input_thread_t *p_input = p_playlist->p_input;
    if(p_input)
1934
    {
1935
        var_AddCallback( p_input, "state", StateCallback, p_filter );
1936
    }
Felix Paul Kühne's avatar