1 /*****************************************************************************
2  * lwinput.c
3  *****************************************************************************
4  * Copyright (C) 2011-2015 L-SMASH Works project
5  *
6  * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  *****************************************************************************/
20 
21 /* This file is available under an ISC license.
22  * However, when distributing its binary file, it will be under LGPL or GPL.
23  * Don't distribute it if its license is GPL. */
24 
25 #include "lwinput.h"
26 #include "resource.h"
27 
28 #include "config.h"
29 
30 #include <commctrl.h>
31 
32 #include <libavutil/channel_layout.h>
33 /* Version */
34 #include <libavutil/version.h>
35 #include <libavcodec/version.h>
36 #include <libavformat/version.h>
37 #include <libswscale/version.h>
38 #include <libavresample/version.h>
39 /* License */
40 #include <libavutil/avutil.h>
41 #include <libavcodec/avcodec.h>
42 #include <libavformat/avformat.h>
43 #include <libswscale/swscale.h>
44 #include <libavresample/avresample.h>
45 
46 #define MAX_AUTO_NUM_THREADS 16
47 
48 #define MPEG4_FILE_EXT      "*.mp4;*.m4v;*.m4a;*.mov;*.qt;*.3gp;*.3g2;*.f4v;*.ismv;*.isma"
49 #define INDEX_FILE_EXT      "*.lwi"
50 #define ANY_FILE_EXT        "*.*"
51 
52 static char plugin_information[512] = { 0 };
53 
get_plugin_information(void)54 static void get_plugin_information( void )
55 {
56     sprintf( plugin_information,
57              "L-SMASH Works File Reader r%s\n"
58              "    libavutil %s: %s / libavcodec %s: %s\n"
59              "    libavformat %s: %s / libswscale %s: %s\n"
60              "    libavresample %s: %s",
61              LSMASHWORKS_REV,
62              AV_STRINGIFY( LIBAVUTIL_VERSION     ), avutil_license    (),
63              AV_STRINGIFY( LIBAVCODEC_VERSION    ), avcodec_license   (),
64              AV_STRINGIFY( LIBAVFORMAT_VERSION   ), avformat_license  (),
65              AV_STRINGIFY( LIBSWSCALE_VERSION    ), swscale_license   (),
66              AV_STRINGIFY( LIBAVRESAMPLE_VERSION ), avresample_license() );
67 }
68 
69 INPUT_PLUGIN_TABLE input_plugin_table =
70 {
71     INPUT_PLUGIN_FLAG_VIDEO | INPUT_PLUGIN_FLAG_AUDIO,              /* INPUT_PLUGIN_FLAG_VIDEO : support images
72                                                                      * INPUT_PLUGIN_FLAG_AUDIO : support audio */
73     "L-SMASH Works File Reader",                                    /* Name of plugin */
74     "MPEG-4 File (" MPEG4_FILE_EXT ")\0" MPEG4_FILE_EXT "\0"        /* Filter for Input file */
75     "LW-Libav Index File (" INDEX_FILE_EXT ")\0" INDEX_FILE_EXT "\0"
76     "Any File (" ANY_FILE_EXT ")\0" ANY_FILE_EXT "\0",
77     "L-SMASH Works File Reader r" LSMASHWORKS_REV "\0",             /* Information of plugin */
78     NULL,                                                           /* Pointer to function called when opening DLL (If NULL, won't be called.) */
79     NULL,                                                           /* Pointer to function called when closing DLL (If NULL, won't be called.) */
80     func_open,                                                      /* Pointer to function to open input file */
81     func_close,                                                     /* Pointer to function to close input file */
82     func_info_get,                                                  /* Pointer to function to get information of input file */
83     func_read_video,                                                /* Pointer to function to read image data */
84     func_read_audio,                                                /* Pointer to function to read audio data */
85     func_is_keyframe,                                               /* Pointer to function to check if it is a keyframe or not (If NULL, all is keyframe.) */
86     func_config,                                                    /* Pointer to function called when configuration dialog is required */
87 };
88 
GetInputPluginTable(void)89 EXTERN_C INPUT_PLUGIN_TABLE __declspec(dllexport) * __stdcall GetInputPluginTable( void )
90 {
91     return &input_plugin_table;
92 }
93 
94 static reader_option_t reader_opt = { 0 };
95 static video_option_t *video_opt = &reader_opt.video_opt;
96 static audio_option_t *audio_opt = &reader_opt.audio_opt;
97 static int reader_disabled[5] = { 0 };
98 static int audio_delay = 0;
99 static char *settings_path = NULL;
100 static const char *settings_path_list[] = { "lsmash.ini", "plugins/lsmash.ini" };
101 static const char *seek_mode_list[] = { "Normal", "Unsafe", "Aggressive" };
102 static const char *dummy_colorspace_list[] = { "YUY2", "RGB", "YC48" };
103 static const char *scaler_list[] = { "Fast bilinear", "Bilinear", "Bicubic", "Experimental", "Nearest neighbor", "Area averaging",
104                                      "L-bicubic/C-bilinear", "Gaussian", "Sinc", "Lanczos", "Bicubic spline" };
105 static const char *field_dominance_list[] = { "Obey source flags", "Top -> Bottom", "Bottom -> Top" };
106 static const char *avs_bit_depth_list[] = { "8", "9", "10", "16" };
107 
au_message_box_desktop(lw_log_handler_t * lhp,lw_log_level level,const char * message)108 void au_message_box_desktop
109 (
110     lw_log_handler_t *lhp,
111     lw_log_level      level,
112     const char       *message
113 )
114 {
115     UINT uType = *(UINT *)lhp->priv;
116     MessageBox( HWND_DESKTOP, message, "lwinput", uType );
117 }
118 
open_settings(void)119 static FILE *open_settings( void )
120 {
121     FILE *ini = NULL;
122     for( int i = 0; i < 2; i++ )
123     {
124         ini = fopen( settings_path_list[i], "rb" );
125         if( ini )
126         {
127             settings_path = (char *)settings_path_list[i];
128             return ini;
129         }
130     }
131     return NULL;
132 }
133 
get_auto_threads(void)134 static int get_auto_threads( void )
135 {
136     int n = atoi( getenv( "NUMBER_OF_PROCESSORS" ) );
137     if( n > MAX_AUTO_NUM_THREADS )
138         n = MAX_AUTO_NUM_THREADS;
139     return n;
140 }
141 
clean_preferred_decoder_names(void)142 static inline void clean_preferred_decoder_names( void )
143 {
144     lw_freep( &reader_opt.preferred_decoder_names );
145     memset( reader_opt.preferred_decoder_names_buf, 0, PREFERRED_DECODER_NAMES_BUFSIZE );
146 }
147 
set_preferred_decoder_names_on_buf(const char * preferred_decoder_names)148 static inline void set_preferred_decoder_names_on_buf
149 (
150     const char *preferred_decoder_names
151 )
152 {
153     clean_preferred_decoder_names();
154     memcpy( reader_opt.preferred_decoder_names_buf, preferred_decoder_names,
155             MIN( PREFERRED_DECODER_NAMES_BUFSIZE - 1, strlen(preferred_decoder_names) ) );
156     reader_opt.preferred_decoder_names = lw_tokenize_string( reader_opt.preferred_decoder_names_buf, ',', NULL );
157 }
158 
get_settings(void)159 static void get_settings( void )
160 {
161     FILE *ini = open_settings();
162     char buf[512];
163     if( ini )
164     {
165         /* threads */
166         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "threads=%d", &reader_opt.threads ) != 1 )
167             reader_opt.threads = 0;
168         /* av_sync */
169         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "av_sync=%d", &reader_opt.av_sync ) != 1 )
170             reader_opt.av_sync = 1;
171         /* no_create_index */
172         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "no_create_index=%d", &reader_opt.no_create_index ) != 1 )
173             reader_opt.no_create_index = 0;
174         /* force stream index */
175         if( !fgets( buf, sizeof(buf), ini )
176          || sscanf( buf, "force_video_index=%d:%d",
177                     &reader_opt.force_video, &reader_opt.force_video_index ) != 2 )
178         {
179             reader_opt.force_video       = 0;
180             reader_opt.force_video_index = -1;
181         }
182         else
183             reader_opt.force_video_index = MAX( reader_opt.force_video_index, -1 );
184         if( !fgets( buf, sizeof(buf), ini )
185          || sscanf( buf, "force_audio_index=%d:%d",
186                     &reader_opt.force_audio, &reader_opt.force_audio_index ) != 2 )
187         {
188             reader_opt.force_audio       = 0;
189             reader_opt.force_audio_index = -1;
190         }
191         else
192             reader_opt.force_audio_index = MAX( reader_opt.force_audio_index, -1 );
193         /* seek_mode */
194         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "seek_mode=%d", &video_opt->seek_mode ) != 1 )
195             video_opt->seek_mode = 0;
196         else
197             video_opt->seek_mode = CLIP_VALUE( video_opt->seek_mode, 0, 2 );
198         /* forward_seek_threshold */
199         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "forward_threshold=%d", &video_opt->forward_seek_threshold ) != 1 )
200             video_opt->forward_seek_threshold = 10;
201         else
202             video_opt->forward_seek_threshold = CLIP_VALUE( video_opt->forward_seek_threshold, 1, 999 );
203         /* scaler */
204         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "scaler=%d", &video_opt->scaler ) != 1 )
205             video_opt->scaler = 0;
206         else
207             video_opt->scaler = CLIP_VALUE( video_opt->scaler, 0, 10 );
208         /* apply_repeat_flag */
209         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "apply_repeat_flag=%d", &video_opt->apply_repeat_flag ) != 1 )
210             video_opt->apply_repeat_flag = 0;
211         /* field_dominance */
212         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "field_dominance=%d", &video_opt->field_dominance ) != 1 )
213             video_opt->field_dominance = 0;
214         else
215             video_opt->field_dominance = CLIP_VALUE( video_opt->field_dominance, 0, 2 );
216         /* VFR->CFR */
217         if( !fgets( buf, sizeof(buf), ini )
218          || sscanf( buf, "vfr2cfr=%d:%d:%d",
219                     &video_opt->vfr2cfr.active,
220                     &video_opt->vfr2cfr.framerate_num,
221                     &video_opt->vfr2cfr.framerate_den ) != 3 )
222         {
223             video_opt->vfr2cfr.active        = 0;
224             video_opt->vfr2cfr.framerate_num = 60000;
225             video_opt->vfr2cfr.framerate_den = 1001;
226         }
227         else
228         {
229             video_opt->vfr2cfr.framerate_num = MAX( video_opt->vfr2cfr.framerate_num, 1 );
230             video_opt->vfr2cfr.framerate_den = MAX( video_opt->vfr2cfr.framerate_den, 1 );
231         }
232         /* LW48 output */
233         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "colorspace=%d", (int *)&video_opt->colorspace ) != 1 )
234             video_opt->colorspace = 0;
235         else
236             video_opt->colorspace = video_opt->colorspace ? OUTPUT_LW48 : 0;
237         /* AVS bit-depth */
238         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "avs_bit_depth=%d", &video_opt->avs.bit_depth ) != 1 )
239             video_opt->avs.bit_depth = 8;
240         else
241         {
242             video_opt->avs.bit_depth = CLIP_VALUE( video_opt->avs.bit_depth, 8, 16 );
243             if( video_opt->avs.bit_depth > 10 )
244                 video_opt->avs.bit_depth = 16;
245         }
246         /* audio_delay */
247         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "audio_delay=%d", &audio_delay ) != 1 )
248             audio_delay = 0;
249         /* channel_layout */
250         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "channel_layout=0x%"SCNx64, &audio_opt->channel_layout ) != 1 )
251             audio_opt->channel_layout = 0;
252         /* sample_rate */
253         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "sample_rate=%d", &audio_opt->sample_rate ) != 1 )
254             audio_opt->sample_rate = 0;
255         /* mix_level */
256         if( !fgets( buf, sizeof(buf), ini )
257          || sscanf( buf, "mix_level=%d:%d:%d",
258                     &audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ],
259                     &audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND],
260                     &audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] ) != 3 )
261         {
262             audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ] = 71;
263             audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND] = 71;
264             audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] = 0;
265         }
266         else
267         {
268             audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ] = CLIP_VALUE( audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ], 0, 10000 );
269             audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND] = CLIP_VALUE( audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND], 0, 10000 );
270             audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] = CLIP_VALUE( audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ], 0, 30000 );
271         }
272         /* readers */
273         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "libavsmash_disabled=%d", &reader_disabled[0] ) != 1 )
274             reader_disabled[0] = 0;
275         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "avs_disabled=%d",        &reader_disabled[1] ) != 1 )
276             reader_disabled[1] = 0;
277         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "vpy_disabled=%d",        &reader_disabled[2] ) != 1 )
278             reader_disabled[2] = 0;
279         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "libav_disabled=%d",      &reader_disabled[3] ) != 1 )
280             reader_disabled[3] = 0;
281         /* dummy reader */
282         if( !fgets( buf, sizeof(buf), ini )
283          || sscanf( buf, "dummy_resolution=%dx%d", &video_opt->dummy.width, &video_opt->dummy.height ) != 2 )
284         {
285             video_opt->dummy.width  = 720;
286             video_opt->dummy.height = 480;
287         }
288         else
289         {
290             video_opt->dummy.width  = MAX( video_opt->dummy.width,  32 );
291             video_opt->dummy.height = MAX( video_opt->dummy.height, 32 );
292         }
293         if( !fgets( buf, sizeof(buf), ini )
294          || sscanf( buf, "dummy_framerate=%d/%d", &video_opt->dummy.framerate_num, &video_opt->dummy.framerate_den ) != 2 )
295         {
296             video_opt->dummy.framerate_num = 24;
297             video_opt->dummy.framerate_den = 1;
298         }
299         else
300         {
301             video_opt->dummy.framerate_num = MAX( video_opt->dummy.framerate_num, 1 );
302             video_opt->dummy.framerate_den = MAX( video_opt->dummy.framerate_den, 1 );
303         }
304         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "dummy_colorspace=%d", (int *)&video_opt->dummy.colorspace ) != 1 )
305             video_opt->dummy.colorspace = OUTPUT_YUY2;
306         else
307             video_opt->dummy.colorspace = CLIP_VALUE( video_opt->dummy.colorspace, 0, 2 );
308         /* preferred decoders settings */
309         char preferred_decoder_names[512] = { 0 };
310         if( !fgets( buf, sizeof(buf), ini ) || sscanf( buf, "preferred_decoders=%s", preferred_decoder_names ) != 1 )
311             clean_preferred_decoder_names();
312         else
313             set_preferred_decoder_names_on_buf( preferred_decoder_names );
314         fclose( ini );
315     }
316     else
317     {
318         /* Set up defalut values. */
319         clean_preferred_decoder_names();
320         reader_opt.threads                = 0;
321         reader_opt.av_sync                = 1;
322         reader_opt.no_create_index        = 0;
323         reader_opt.force_video            = 0;
324         reader_opt.force_video_index      = -1;
325         reader_opt.force_audio            = 0;
326         reader_opt.force_audio_index      = -1;
327         reader_disabled[0]                = 0;
328         reader_disabled[1]                = 0;
329         reader_disabled[2]                = 0;
330         reader_disabled[3]                = 0;
331         video_opt->seek_mode              = 0;
332         video_opt->forward_seek_threshold = 10;
333         video_opt->scaler                 = 0;
334         video_opt->apply_repeat_flag      = 0;
335         video_opt->field_dominance        = 0;
336         video_opt->vfr2cfr.active         = 0;
337         video_opt->vfr2cfr.framerate_num  = 60000;
338         video_opt->vfr2cfr.framerate_den  = 1001;
339         video_opt->colorspace             = 0;
340         video_opt->dummy.width            = 720;
341         video_opt->dummy.height           = 480;
342         video_opt->dummy.framerate_num    = 24;
343         video_opt->dummy.framerate_den    = 1;
344         video_opt->dummy.colorspace       = OUTPUT_YUY2;
345         video_opt->avs.bit_depth          = 8;
346         audio_delay                       = 0;
347         audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ] = 71;
348         audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND] = 71;
349         audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] = 0;
350     }
351 }
352 
func_open(LPSTR file)353 INPUT_HANDLE func_open( LPSTR file )
354 {
355     lsmash_handler_t *hp = (lsmash_handler_t *)lw_malloc_zero( sizeof(lsmash_handler_t) );
356     if( !hp )
357         return NULL;
358     hp->video_reader = READER_NONE;
359     hp->audio_reader = READER_NONE;
360     get_settings();
361     if( reader_opt.threads <= 0 )
362         reader_opt.threads = get_auto_threads();
363     extern lsmash_reader_t libavsmash_reader;
364     extern lsmash_reader_t avs_reader;
365     extern lsmash_reader_t vpy_reader;
366     extern lsmash_reader_t libav_reader;
367     extern lsmash_reader_t dummy_reader;
368     enum
369     {
370         AU_VIDEO_READER  = 1,
371         AU_SCRIPT_READER = 2,
372         AU_DUMMY_READER  = 3
373     };
374     static const struct
375     {
376         lsmash_reader_t *reader;
377         int              attribute;
378     } lsmash_reader_table[] =
379         {
380             { &libavsmash_reader, AU_VIDEO_READER  },
381             { &avs_reader       , AU_SCRIPT_READER },
382             { &vpy_reader       , AU_SCRIPT_READER },
383             { &libav_reader     , AU_VIDEO_READER  },
384             { &dummy_reader     , AU_DUMMY_READER  },
385             { NULL              , 0                }
386         };
387     for( int i = 0; lsmash_reader_table[i].reader; i++ )
388     {
389         if( reader_disabled[lsmash_reader_table[i].reader->type - 1] )
390             continue;
391         int video_none = 1;
392         int audio_none = 1;
393         lsmash_reader_t reader      = *lsmash_reader_table[i].reader;
394         int             reader_attr =  lsmash_reader_table[i].attribute;
395         void *private_stuff = reader.open_file( file, &reader_opt );
396         if( private_stuff )
397         {
398             if( !hp->video_private )
399             {
400                 hp->video_private = private_stuff;
401                 if( reader.get_video_track
402                  && reader.get_video_track( hp, video_opt ) == 0 )
403                 {
404                     hp->video_reader     = reader.type;
405                     hp->read_video       = reader.read_video;
406                     hp->is_keyframe      = reader.is_keyframe;
407                     hp->video_cleanup    = reader.video_cleanup;
408                     hp->close_video_file = reader.close_file;
409                     video_none = 0;
410                 }
411                 else
412                     hp->video_private = NULL;
413             }
414             if( !hp->audio_private )
415             {
416                 hp->audio_private = private_stuff;
417                 if( reader.get_audio_track
418                  && reader.get_audio_track( hp, audio_opt ) == 0 )
419                 {
420                     hp->audio_reader     = reader.type;
421                     hp->read_audio       = reader.read_audio;
422                     hp->delay_audio      = reader.delay_audio;
423                     hp->audio_cleanup    = reader.audio_cleanup;
424                     hp->close_audio_file = reader.close_file;
425                     audio_none = 0;
426                 }
427                 else
428                     hp->audio_private = NULL;
429             }
430         }
431         if( video_none && audio_none )
432         {
433             if( reader.close_file )
434                 reader.close_file( private_stuff );
435         }
436         else
437             if( reader.destroy_disposable )
438                 reader.destroy_disposable( private_stuff );
439         /* Found both video and audio reader. */
440         if( hp->video_reader != READER_NONE && hp->audio_reader != READER_NONE )
441             break;
442         if( reader_attr == AU_SCRIPT_READER )
443         {
444             if( hp->video_reader == reader.type )
445                 break;
446             if( hp->audio_reader == reader.type )
447                 while( lsmash_reader_table[i + 1].attribute != AU_DUMMY_READER )
448                     i++;
449         }
450     }
451     if( hp->video_reader == hp->audio_reader )
452     {
453         hp->global_private = hp->video_private;
454         hp->close_file     = hp->close_video_file;
455         hp->close_video_file = NULL;
456         hp->close_audio_file = NULL;
457     }
458     if( hp->video_reader == READER_NONE && hp->audio_reader == READER_NONE )
459     {
460         DEBUG_MESSAGE_BOX_DESKTOP( MB_OK, "No readable video and/or audio stream" );
461         func_close( hp );
462         return NULL;
463     }
464     return hp;
465 }
466 
func_close(INPUT_HANDLE ih)467 BOOL func_close( INPUT_HANDLE ih )
468 {
469     lw_freep( &reader_opt.preferred_decoder_names );
470     lsmash_handler_t *hp = (lsmash_handler_t *)ih;
471     if( !hp )
472         return TRUE;
473     if( hp->video_cleanup )
474         hp->video_cleanup( hp );
475     if( hp->audio_cleanup )
476         hp->audio_cleanup( hp );
477     if( hp->close_file )
478         hp->close_file( hp->global_private );
479     else
480     {
481         if( hp->close_video_file )
482             hp->close_video_file( hp->video_private );
483         if( hp->close_audio_file )
484             hp->close_audio_file( hp->audio_private );
485     }
486     lw_free( hp );
487     return TRUE;
488 }
489 
func_info_get(INPUT_HANDLE ih,INPUT_INFO * iip)490 BOOL func_info_get( INPUT_HANDLE ih, INPUT_INFO *iip )
491 {
492     lsmash_handler_t *hp = (lsmash_handler_t *)ih;
493     memset( iip, 0, sizeof(INPUT_INFO) );
494     if( hp->video_reader != READER_NONE )
495     {
496         iip->flag             |= INPUT_INFO_FLAG_VIDEO | INPUT_INFO_FLAG_VIDEO_RANDOM_ACCESS;
497         iip->rate              = hp->framerate_num;
498         iip->scale             = hp->framerate_den;
499         iip->n                 = hp->video_sample_count;
500         iip->format            = &hp->video_format;
501         iip->format_size       = hp->video_format.biSize;
502         iip->handler           = 0;
503     }
504     if( hp->audio_reader != READER_NONE )
505     {
506         iip->flag             |= INPUT_INFO_FLAG_AUDIO;
507         iip->audio_n           = hp->audio_pcm_sample_count + audio_delay;
508         iip->audio_format      = &hp->audio_format.Format;
509         iip->audio_format_size = sizeof( WAVEFORMATEX ) + hp->audio_format.Format.cbSize;
510     }
511     return TRUE;
512 }
513 
func_read_video(INPUT_HANDLE ih,int sample_number,void * buf)514 int func_read_video( INPUT_HANDLE ih, int sample_number, void *buf )
515 {
516     lsmash_handler_t *hp = (lsmash_handler_t *)ih;
517     return hp->read_video ? hp->read_video( hp, sample_number, buf ) : 0;
518 }
519 
func_read_audio(INPUT_HANDLE ih,int start,int length,void * buf)520 int func_read_audio( INPUT_HANDLE ih, int start, int length, void *buf )
521 {
522     lsmash_handler_t *hp = (lsmash_handler_t *)ih;
523     if( hp->read_audio && hp->delay_audio( hp, &start, length, audio_delay ) )
524         return hp->read_audio( hp, start, length, buf );
525     uint8_t silence = hp->audio_format.Format.wBitsPerSample == 8 ? 128 : 0;
526     memset( buf, silence, length * hp->audio_format.Format.nBlockAlign );
527     return length;
528 }
529 
func_is_keyframe(INPUT_HANDLE ih,int sample_number)530 BOOL func_is_keyframe( INPUT_HANDLE ih, int sample_number )
531 {
532     lsmash_handler_t *hp = (lsmash_handler_t *)ih;
533     if( sample_number >= hp->video_sample_count )
534         return FALSE;   /* In reading as double framerate, keyframe detection doesn't work at all
535                          * since sample_number exceeds the number of video samples. */
536     return hp->is_keyframe ? hp->is_keyframe( hp, sample_number ) : TRUE;
537 }
538 
set_buddy_window_for_updown_control(HWND hwnd,int spin_idc,int buddy_idc)539 static inline void set_buddy_window_for_updown_control
540 (
541     HWND hwnd,
542     int  spin_idc,
543     int  buddy_idc
544 )
545 {
546     SendMessage( GetDlgItem( hwnd, spin_idc ), UDM_SETBUDDY, (WPARAM)GetDlgItem( hwnd, buddy_idc ), 0 );
547 }
548 
set_check_state(HWND hwnd,int idc,int value)549 static inline void set_check_state
550 (
551     HWND hwnd,
552     int  idc,   /* identifier for control */
553     int  value
554 )
555 {
556     SendMessage( GetDlgItem( hwnd, idc ), BM_SETCHECK, (WPARAM) value ? BST_CHECKED : BST_UNCHECKED, 0 );
557 }
558 
get_check_state(HWND hwnd,int idc)559 static inline int get_check_state
560 (
561     HWND hwnd,
562     int  idc    /* identifier for control */
563 )
564 {
565     return (BST_CHECKED == SendMessage( GetDlgItem( hwnd, idc ), BM_GETCHECK, 0, 0 ));
566 }
567 
set_int_to_dlg(HWND hwnd,int idc,int value)568 static void set_int_to_dlg
569 (
570     HWND hwnd,
571     int  idc,   /* identifier for control */
572     int  value  /* message value */
573 )
574 {
575     char edit_buf[512];
576     sprintf( edit_buf, "%d", value );
577     SetDlgItemText( hwnd, idc, (LPCTSTR)edit_buf );
578 }
579 
get_int_from_dlg(HWND hwnd,int idc)580 static int get_int_from_dlg
581 (
582     HWND hwnd,
583     int  idc    /* identifier for control */
584 )
585 {
586     char edit_buf[512];
587     GetDlgItemText( hwnd, idc, (LPTSTR)edit_buf, sizeof(edit_buf) );
588     return atoi( edit_buf );
589 }
590 
get_int_from_dlg_with_min(HWND hwnd,int idc,int min)591 static int get_int_from_dlg_with_min
592 (
593     HWND hwnd,
594     int  idc,   /* identifier for control */
595     int  min
596 )
597 {
598     int value = get_int_from_dlg( hwnd, idc );
599     return MAX( value, min );
600 }
601 
set_string_to_dlg(HWND hwnd,int idc,char * value)602 static inline void set_string_to_dlg
603 (
604     HWND  hwnd,
605     int   idc,  /* identifier for control */
606     char *value /* message value */
607 )
608 {
609     SetDlgItemText( hwnd, idc, (LPCTSTR)value );
610 }
611 
send_mix_level(HWND hwnd,int slider_idc,int text_idc,int range_min,int range_max,int mix_level)612 static void send_mix_level
613 (
614     HWND  hwnd,
615     int   slider_idc,
616     int   text_idc,
617     int   range_min,
618     int   range_max,
619     int   mix_level
620 )
621 {
622     char edit_buf[512];
623     HWND hslider = GetDlgItem( hwnd, slider_idc );
624     SendMessage( hslider, TBM_SETRANGE,    TRUE, MAKELPARAM( range_min, range_max ) );
625     SendMessage( hslider, TBM_SETTICFREQ,  1,    0 );
626     SendMessage( hslider, TBM_SETPOS,      TRUE, mix_level );
627     SendMessage( hslider, TBM_SETLINESIZE, 0,    1 );
628     SendMessage( hslider, TBM_SETPAGESIZE, 0,    1 );
629     sprintf( edit_buf, "%.2f", mix_level / 100.0 );
630     SetWindowText( GetDlgItem( hwnd, text_idc ), (LPCTSTR)edit_buf );
631 }
632 
get_mix_level(HWND hwnd,int slider_idc,int text_idc,int * mix_level)633 static void get_mix_level
634 (
635     HWND  hwnd,
636     int   slider_idc,
637     int   text_idc,
638     int  *mix_level
639 )
640 {
641     char edit_buf[512];
642     HWND hslider = GetDlgItem( hwnd, slider_idc );
643     *mix_level = SendMessage( hslider, TBM_GETPOS, 0, 0 );
644     sprintf( edit_buf, "%.2f", *mix_level / 100.0 );
645     SetWindowText( GetDlgItem( hwnd, text_idc ), (LPCTSTR)edit_buf );
646 }
647 
dialog_proc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)648 static BOOL CALLBACK dialog_proc
649 (
650     HWND   hwnd,
651     UINT   message,
652     WPARAM wparam,
653     LPARAM lparam
654 )
655 {
656     switch( message )
657     {
658         case WM_INITDIALOG :
659             InitCommonControls();
660             get_settings();
661             /* threads */
662             set_int_to_dlg( hwnd, IDC_EDIT_THREADS, reader_opt.threads );
663             set_buddy_window_for_updown_control( hwnd, IDC_SPIN_THREADS, IDC_EDIT_THREADS );
664             /* av_sync */
665             set_check_state( hwnd, IDC_CHECK_AV_SYNC, reader_opt.av_sync );
666             /* no_create_index */
667             set_check_state( hwnd, IDC_CHECK_CREATE_INDEX_FILE, !reader_opt.no_create_index );
668             /* force stream index */
669             set_check_state( hwnd, IDC_CHECK_FORCE_VIDEO, reader_opt.force_video );
670             set_check_state( hwnd, IDC_CHECK_FORCE_AUDIO, reader_opt.force_audio );
671             set_int_to_dlg( hwnd, IDC_EDIT_FORCE_VIDEO_INDEX, reader_opt.force_video_index );
672             set_int_to_dlg( hwnd, IDC_EDIT_FORCE_AUDIO_INDEX, reader_opt.force_audio_index );
673             /* forward_seek_threshold */
674             set_int_to_dlg( hwnd, IDC_EDIT_FORWARD_THRESHOLD, video_opt->forward_seek_threshold );
675             set_buddy_window_for_updown_control( hwnd, IDC_SPIN_FORWARD_THRESHOLD, IDC_EDIT_FORWARD_THRESHOLD );
676             /* seek mode */
677             HWND hcombo = GetDlgItem( hwnd, IDC_COMBOBOX_SEEK_MODE );
678             for( int i = 0; i < 3; i++ )
679                 SendMessage( hcombo, CB_ADDSTRING, 0, (LPARAM)seek_mode_list[i] );
680             SendMessage( hcombo, CB_SETCURSEL, video_opt->seek_mode, 0 );
681             /* scaler */
682             hcombo = GetDlgItem( hwnd, IDC_COMBOBOX_SCALER );
683             for( int i = 0; i < 11; i++ )
684                 SendMessage( hcombo, CB_ADDSTRING, 0, (LPARAM)scaler_list[i] );
685             SendMessage( hcombo, CB_SETCURSEL, video_opt->scaler, 0 );
686             /* apply_repeat_flag */
687             set_check_state( hwnd, IDC_CHECK_APPLY_REPEAT_FLAG, video_opt->apply_repeat_flag );
688             /* field_dominance */
689             hcombo = GetDlgItem( hwnd, IDC_COMBOBOX_FIELD_DOMINANCE );
690             for( int i = 0; i < 3; i++ )
691                 SendMessage( hcombo, CB_ADDSTRING, 0, (LPARAM)field_dominance_list[i] );
692             SendMessage( hcombo, CB_SETCURSEL, video_opt->field_dominance, 0 );
693             /* VFR->CFR */
694             set_check_state( hwnd, IDC_CHECK_VFR_TO_CFR, video_opt->vfr2cfr.active );
695             set_int_to_dlg( hwnd, IDC_EDIT_CONST_FRAMERATE_NUM, video_opt->vfr2cfr.framerate_num );
696             set_int_to_dlg( hwnd, IDC_EDIT_CONST_FRAMERATE_DEN, video_opt->vfr2cfr.framerate_den );
697             /* LW48 output */
698             set_check_state( hwnd, IDC_CHECK_LW48_OUTPUT, video_opt->colorspace != 0 );
699             /* AVS bit-depth */
700             hcombo = GetDlgItem( hwnd, IDC_COMBOBOX_AVS_BITDEPTH );
701             for( int i = 0; i < 4; i++ )
702             {
703                 SendMessage( hcombo, CB_ADDSTRING, 0, (LPARAM)avs_bit_depth_list[i] );
704                 if( video_opt->avs.bit_depth == atoi( avs_bit_depth_list[i] ) )
705                     SendMessage( hcombo, CB_SETCURSEL, i, 0 );
706             }
707             /* audio_delay */
708             set_int_to_dlg( hwnd, IDC_EDIT_AUDIO_DELAY, audio_delay );
709             /* channel_layout */
710             if( audio_opt->channel_layout )
711             {
712                 char edit_buf[512] = { 0 };
713                 char *buf = edit_buf;
714                 for( int i = 0; i < 64; i++ )
715                 {
716                     uint64_t audio_channel = audio_opt->channel_layout & (1ULL << i);
717                     if( audio_channel )
718                     {
719                         const char *channel_name = av_get_channel_name( audio_channel );
720                         if( channel_name )
721                         {
722                             int name_length = strlen( channel_name );
723                             memcpy( buf, channel_name, name_length );
724                             buf += name_length;
725                             *(buf++) = '+';
726                         }
727                     }
728                 }
729                 if( buf > edit_buf )
730                 {
731                     *(buf - 1) = '\0';  /* Replace the last '+' with NULL terminator. */
732                     SetDlgItemText( hwnd, IDC_EDIT_CHANNEL_LAYOUT, (LPCTSTR)edit_buf );
733                 }
734                 else
735                     set_string_to_dlg( hwnd, IDC_EDIT_CHANNEL_LAYOUT, "Unspecified" );
736             }
737             else
738                 set_string_to_dlg( hwnd, IDC_EDIT_CHANNEL_LAYOUT, "Unspecified" );
739             /* sample_rate */
740             if( audio_opt->sample_rate > 0 )
741                 set_int_to_dlg( hwnd, IDC_EDIT_SAMPLE_RATE, audio_opt->sample_rate );
742             else
743             {
744                 audio_opt->sample_rate = 0;
745                 set_string_to_dlg( hwnd, IDC_EDIT_SAMPLE_RATE, "0 (Auto)" );
746             }
747             /* mix_level */
748             send_mix_level( hwnd, IDC_SLIDER_MIX_LEVEL_CENTER,   IDC_TEXT_MIX_LEVEL_CENTER,   0, 500, audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ] );
749             send_mix_level( hwnd, IDC_SLIDER_MIX_LEVEL_SURROUND, IDC_TEXT_MIX_LEVEL_SURROUND, 0, 500, audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND] );
750             send_mix_level( hwnd, IDC_SLIDER_MIX_LEVEL_LFE,      IDC_TEXT_MIX_LEVEL_LFE,      0, 500, audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] );
751             /* readers */
752             set_check_state( hwnd, IDC_CHECK_LIBAVSMASH_INPUT, !reader_disabled[0] );
753             set_check_state( hwnd, IDC_CHECK_AVS_INPUT,        !reader_disabled[1] );
754             set_check_state( hwnd, IDC_CHECK_VPY_INPUT,        !reader_disabled[2] );
755             set_check_state( hwnd, IDC_CHECK_LIBAV_INPUT,      !reader_disabled[3] );
756             /* dummy reader */
757             set_int_to_dlg( hwnd, IDC_EDIT_DUMMY_WIDTH,         video_opt->dummy.width );
758             set_int_to_dlg( hwnd, IDC_EDIT_DUMMY_HEIGHT,        video_opt->dummy.height );
759             set_int_to_dlg( hwnd, IDC_EDIT_DUMMY_FRAMERATE_NUM, video_opt->dummy.framerate_num );
760             set_int_to_dlg( hwnd, IDC_EDIT_DUMMY_FRAMERATE_DEN, video_opt->dummy.framerate_den );
761             hcombo = GetDlgItem( hwnd, IDC_COMBOBOX_DUMMY_COLORSPACE );
762             for( int i = 0; i < 3; i++ )
763                 SendMessage( hcombo, CB_ADDSTRING, 0, (LPARAM)dummy_colorspace_list[i] );
764             SendMessage( hcombo, CB_SETCURSEL, video_opt->dummy.colorspace, 0 );
765             /* preferred decoders */
766             if( reader_opt.preferred_decoder_names )
767             {
768                 char edit_buf[512] = { 0 };
769                 char *buf = edit_buf;
770                 for( const char **decoder = reader_opt.preferred_decoder_names; *decoder != NULL; decoder++ )
771                 {
772                     if( *decoder != *reader_opt.preferred_decoder_names )
773                         *(buf++) = ',';
774                     int length = strlen( *decoder );
775                     memcpy( buf, *decoder, length );
776                     buf += length;
777                 }
778                 set_string_to_dlg( hwnd, IDC_EDIT_PREFERRED_DECODERS, edit_buf );
779             }
780             else
781                 set_string_to_dlg( hwnd, IDC_EDIT_PREFERRED_DECODERS, "" );
782             /* Library informations */
783             if( plugin_information[0] == 0 )
784                 get_plugin_information();
785             SetDlgItemText( hwnd, IDC_TEXT_LIBRARY_INFO, (LPCTSTR)plugin_information );
786             HFONT hfont = (HFONT)GetStockObject( DEFAULT_GUI_FONT );
787             LOGFONT lf = { 0 };
788             GetObject( hfont, sizeof(lf), &lf );
789             lf.lfWidth  *= 0.90;
790             lf.lfHeight *= 0.90;
791             lf.lfQuality = ANTIALIASED_QUALITY;
792             SendMessage( GetDlgItem( hwnd, IDC_TEXT_LIBRARY_INFO ), WM_SETFONT, (WPARAM)CreateFontIndirect( &lf ), 1 );
793             return TRUE;
794         case WM_NOTIFY :
795             if( wparam == IDC_SPIN_THREADS )
796             {
797                 LPNMUPDOWN lpnmud = (LPNMUPDOWN)lparam;
798                 if( lpnmud->hdr.code == UDN_DELTAPOS )
799                 {
800                     reader_opt.threads = get_int_from_dlg( hwnd, IDC_EDIT_THREADS );
801                     if( lpnmud->iDelta )
802                         reader_opt.threads += lpnmud->iDelta > 0 ? -1 : 1;
803                     if( reader_opt.threads < 0 )
804                         reader_opt.threads = 0;
805                     set_int_to_dlg( hwnd, IDC_EDIT_THREADS, reader_opt.threads );
806                 }
807             }
808             else if( wparam == IDC_SPIN_FORWARD_THRESHOLD )
809             {
810                 LPNMUPDOWN lpnmud = (LPNMUPDOWN)lparam;
811                 if( lpnmud->hdr.code == UDN_DELTAPOS )
812                 {
813                     video_opt->forward_seek_threshold = get_int_from_dlg( hwnd, IDC_EDIT_FORWARD_THRESHOLD );
814                     if( lpnmud->iDelta )
815                         video_opt->forward_seek_threshold += lpnmud->iDelta > 0 ? -1 : 1;
816                     video_opt->forward_seek_threshold = CLIP_VALUE( video_opt->forward_seek_threshold, 1, 999 );
817                     set_int_to_dlg( hwnd, IDC_EDIT_FORWARD_THRESHOLD, video_opt->forward_seek_threshold );
818                 }
819             }
820             return TRUE;
821         case WM_HSCROLL :
822             if( GetDlgItem( hwnd, IDC_SLIDER_MIX_LEVEL_CENTER ) == (HWND)lparam )
823                 get_mix_level( hwnd, IDC_SLIDER_MIX_LEVEL_CENTER,   IDC_TEXT_MIX_LEVEL_CENTER,   &audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ] );
824             else if( GetDlgItem( hwnd, IDC_SLIDER_MIX_LEVEL_SURROUND ) == (HWND)lparam )
825                 get_mix_level( hwnd, IDC_SLIDER_MIX_LEVEL_SURROUND, IDC_TEXT_MIX_LEVEL_SURROUND, &audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND] );
826             else if( GetDlgItem( hwnd, IDC_SLIDER_MIX_LEVEL_LFE ) == (HWND)lparam )
827                 get_mix_level( hwnd, IDC_SLIDER_MIX_LEVEL_LFE,      IDC_TEXT_MIX_LEVEL_LFE,      &audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] );
828             return FALSE;
829         case WM_COMMAND :
830             switch( wparam )
831             {
832                 case IDCANCEL :
833                     EndDialog( hwnd, IDCANCEL );
834                     return TRUE;
835                 case IDOK :
836                 {
837                     /* Open */
838                     if( !settings_path )
839                         settings_path = (char *)settings_path_list[0];
840                     FILE *ini = fopen( settings_path, "w" );
841                     if( !ini )
842                     {
843                         MESSAGE_BOX_DESKTOP( MB_ICONERROR | MB_OK, "Failed to update configuration file" );
844                         return FALSE;
845                     }
846                     /* threads */
847                     reader_opt.threads = get_int_from_dlg_with_min( hwnd, IDC_EDIT_THREADS, 0 );
848                     if( reader_opt.threads > 0 )
849                         fprintf( ini, "threads=%d\n", reader_opt.threads );
850                     else
851                         fprintf( ini, "threads=0 (auto)\n" );
852                     /* av_sync */
853                     reader_opt.av_sync = get_check_state( hwnd, IDC_CHECK_AV_SYNC );
854                     fprintf( ini, "av_sync=%d\n", reader_opt.av_sync );
855                     /* no_create_index */
856                     reader_opt.no_create_index = !get_check_state( hwnd, IDC_CHECK_CREATE_INDEX_FILE );
857                     fprintf( ini, "no_create_index=%d\n", reader_opt.no_create_index );
858                     /* force stream index */
859                     reader_opt.force_video = get_check_state( hwnd, IDC_CHECK_FORCE_VIDEO );
860                     reader_opt.force_audio = get_check_state( hwnd, IDC_CHECK_FORCE_AUDIO );
861                     reader_opt.force_video_index = get_int_from_dlg_with_min( hwnd, IDC_EDIT_FORCE_VIDEO_INDEX, -1 );
862                     reader_opt.force_audio_index = get_int_from_dlg_with_min( hwnd, IDC_EDIT_FORCE_AUDIO_INDEX, -1 );
863                     fprintf( ini, "force_video_index=%d:%d\n", reader_opt.force_video, reader_opt.force_video_index );
864                     fprintf( ini, "force_audio_index=%d:%d\n", reader_opt.force_audio, reader_opt.force_audio_index );
865                     /* seek_mode */
866                     video_opt->seek_mode = SendMessage( GetDlgItem( hwnd, IDC_COMBOBOX_SEEK_MODE ), CB_GETCURSEL, 0, 0 );
867                     fprintf( ini, "seek_mode=%d\n", video_opt->seek_mode );
868                     /* forward_seek_threshold */
869                     video_opt->forward_seek_threshold = get_int_from_dlg( hwnd, IDC_EDIT_FORWARD_THRESHOLD );
870                     video_opt->forward_seek_threshold = CLIP_VALUE( video_opt->forward_seek_threshold, 1, 999 );
871                     fprintf( ini, "forward_threshold=%d\n", video_opt->forward_seek_threshold );
872                     /* scaler */
873                     video_opt->scaler = SendMessage( GetDlgItem( hwnd, IDC_COMBOBOX_SCALER ), CB_GETCURSEL, 0, 0 );
874                     fprintf( ini, "scaler=%d\n", video_opt->scaler );
875                     /* apply_repeat_flag */
876                     video_opt->apply_repeat_flag = get_check_state( hwnd, IDC_CHECK_APPLY_REPEAT_FLAG );
877                     fprintf( ini, "apply_repeat_flag=%d\n", video_opt->apply_repeat_flag );
878                     /* field_dominance */
879                     video_opt->field_dominance = SendMessage( GetDlgItem( hwnd, IDC_COMBOBOX_FIELD_DOMINANCE ), CB_GETCURSEL, 0, 0 );
880                     fprintf( ini, "field_dominance=%d\n", video_opt->field_dominance );
881                     /* VFR->CFR */
882                     video_opt->vfr2cfr.active = get_check_state( hwnd, IDC_CHECK_VFR_TO_CFR );
883                     video_opt->vfr2cfr.framerate_num = get_int_from_dlg_with_min( hwnd, IDC_EDIT_CONST_FRAMERATE_NUM, 1 );
884                     video_opt->vfr2cfr.framerate_den = get_int_from_dlg_with_min( hwnd, IDC_EDIT_CONST_FRAMERATE_DEN, 1 );
885                     fprintf( ini, "vfr2cfr=%d:%d:%d\n", video_opt->vfr2cfr.active, video_opt->vfr2cfr.framerate_num, video_opt->vfr2cfr.framerate_den );
886                     /* LW48 output */
887                     video_opt->colorspace = (get_check_state( hwnd, IDC_CHECK_LW48_OUTPUT ) ? OUTPUT_LW48 : 0);
888                     fprintf( ini, "colorspace=%d\n", video_opt->colorspace );
889                     /* AVS bit-depth */
890                     video_opt->avs.bit_depth = SendMessage( GetDlgItem( hwnd, IDC_COMBOBOX_AVS_BITDEPTH ), CB_GETCURSEL, 0, 0 );
891                     video_opt->avs.bit_depth = atoi( avs_bit_depth_list[ video_opt->avs.bit_depth ] );
892                     fprintf( ini, "avs_bit_depth=%d\n", video_opt->avs.bit_depth );
893                     /* audio_delay */
894                     audio_delay = get_int_from_dlg( hwnd, IDC_EDIT_AUDIO_DELAY );
895                     fprintf( ini, "audio_delay=%d\n", audio_delay );
896                     /* channel_layout */
897                     {
898                         char edit_buf[512];
899                         GetDlgItemText( hwnd, IDC_EDIT_CHANNEL_LAYOUT, (LPTSTR)edit_buf, sizeof(edit_buf) );
900                         audio_opt->channel_layout = av_get_channel_layout( edit_buf );
901                     }
902                     fprintf( ini, "channel_layout=0x%"PRIx64"\n", audio_opt->channel_layout );
903                     /* sample_rate */
904                     audio_opt->sample_rate = get_int_from_dlg_with_min( hwnd, IDC_EDIT_SAMPLE_RATE, 0 );
905                     fprintf( ini, "sample_rate=%d\n", audio_opt->sample_rate );
906                     /* mix_level */
907                     fprintf( ini, "mix_level=%d:%d:%d\n",
908                              audio_opt->mix_level[MIX_LEVEL_INDEX_CENTER  ],
909                              audio_opt->mix_level[MIX_LEVEL_INDEX_SURROUND],
910                              audio_opt->mix_level[MIX_LEVEL_INDEX_LFE     ] );
911                     /* readers */
912                     reader_disabled[0] = !get_check_state( hwnd, IDC_CHECK_LIBAVSMASH_INPUT );
913                     reader_disabled[1] = !get_check_state( hwnd, IDC_CHECK_AVS_INPUT        );
914                     reader_disabled[2] = !get_check_state( hwnd, IDC_CHECK_VPY_INPUT        );
915                     reader_disabled[3] = !get_check_state( hwnd, IDC_CHECK_LIBAV_INPUT      );
916                     fprintf( ini, "libavsmash_disabled=%d\n", reader_disabled[0] );
917                     fprintf( ini, "avs_disabled=%d\n",        reader_disabled[1] );
918                     fprintf( ini, "vpy_disabled=%d\n",        reader_disabled[2] );
919                     fprintf( ini, "libav_disabled=%d\n",      reader_disabled[3] );
920                     /* dummy reader */
921                     video_opt->dummy.width         = get_int_from_dlg_with_min( hwnd, IDC_EDIT_DUMMY_WIDTH,  32 );
922                     video_opt->dummy.height        = get_int_from_dlg_with_min( hwnd, IDC_EDIT_DUMMY_HEIGHT, 32 );
923                     video_opt->dummy.framerate_num = get_int_from_dlg_with_min( hwnd, IDC_EDIT_DUMMY_FRAMERATE_NUM, 1 );
924                     video_opt->dummy.framerate_den = get_int_from_dlg_with_min( hwnd, IDC_EDIT_DUMMY_FRAMERATE_DEN, 1 );
925                     video_opt->dummy.colorspace    = SendMessage( GetDlgItem( hwnd, IDC_COMBOBOX_DUMMY_COLORSPACE ), CB_GETCURSEL, 0, 0 );
926                     fprintf( ini, "dummy_resolution=%dx%d\n", video_opt->dummy.width, video_opt->dummy.height );
927                     fprintf( ini, "dummy_framerate=%d/%d\n",  video_opt->dummy.framerate_num, video_opt->dummy.framerate_den );
928                     fprintf( ini, "dummy_colorspace=%d\n",    video_opt->dummy.colorspace );
929                     /* preferred decoders */
930                     {
931                         char edit_buf[512];
932                         GetDlgItemText( hwnd, IDC_EDIT_PREFERRED_DECODERS, (LPTSTR)edit_buf, sizeof(edit_buf) );
933                         set_preferred_decoder_names_on_buf( edit_buf );
934                         fprintf( ini, "preferred_decoders=%s\n", edit_buf );
935                     }
936                     /* Close */
937                     fclose( ini );
938                     EndDialog( hwnd, IDOK );
939                     MESSAGE_BOX_DESKTOP( MB_OK, "Please reopen the input file for updating settings!" );
940                     return TRUE;
941                 }
942                 default :
943                     return FALSE;
944             }
945         case WM_CLOSE :
946             EndDialog( hwnd, IDOK );
947             return TRUE;
948         default :
949             return FALSE;
950     }
951 }
952 
func_config(HWND hwnd,HINSTANCE dll_hinst)953 BOOL func_config( HWND hwnd, HINSTANCE dll_hinst )
954 {
955     DialogBox( dll_hinst, "LWINPUT_CONFIG", hwnd, dialog_proc );
956     return TRUE;
957 }
958