From 4a69d1eaa961de1c6bb624cc7e40ed60fc8a42a5 Mon Sep 17 00:00:00 2001 From: sr55 Date: Sun, 24 Feb 2013 14:09:48 +0000 Subject: [PATCH] Merging Trunk into OpenCL Branch. git-svn-id: svn://svn.handbrake.fr/HandBrake/branches/opencl@5264 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- contrib/bzip2/module.defs | 6 +- contrib/ffmpeg/A05-truehd-downmix.patch | 46 + contrib/ffmpeg/module.defs | 2 +- contrib/libdvdnav/module.defs | 6 +- contrib/libsamplerate/module.defs | 3 + contrib/mp4v2/module.defs | 4 +- gtk/src/ghb.ui | 10 +- libhb/common.c | 57 + libhb/common.h | 18 + libhb/decavcodec.c | 114 +- libhb/encavcodecaudio.c | 10 + libhb/hb.c | 27 +- libhb/internal.h | 4 +- libhb/ports.c | 71 + libhb/ports.h | 8 + libhb/work.c | 46 +- macosx/Controller.m | 2 +- macosx/English.lproj/Preferences.xib | 188 ++- macosx/HBAudioController.m | 2 + macosx/HBPreviewController.m | 15 +- macosx/PictureController.h | 2 + macosx/PictureController.m | 54 +- make/variant/mingw.defs | 2 + test/test.c | 106 ++ .../Services/LibEncode.cs | 12 +- .../Services/LibScan.cs | 3 +- .../HandBrakeInterop/Converters.cs | 9 +- .../EncodeCompletedEventArgs.cs | 4 +- .../EncodeProgressEventArgs.cs | 6 +- .../{ => EventArgs}/MessageLoggedEventArgs.cs | 6 +- .../{ => EventArgs}/ScanProgressEventArgs.cs | 4 +- .../HandBrakeInterop/HandBrakeInstance.cs | 5 +- .../HandBrakeInterop/HandBrakeInterop.csproj | 8 +- .../HandBrakeInterop/HandBrakeUtils.cs | 2 + .../Interfaces/IHandBrakeInstance.cs | 1 + .../HandBrakeInterop/InteropUtilities.cs | 31 +- .../HandBrakeInterop/LanguageCodes.cs | 10 +- .../HandBrakeInterop/MarshalingConstants.cs | 5 +- .../HandBrakeInterop/Model/BitrateLimits.cs | 32 +- .../HandBrakeInterop/Model/EncodeJob.cs | 240 ++-- .../HandBrakeInterop/Model/Encoders.cs | 33 +- .../HandBrakeInterop/Model/RangeLimits.cs | 57 +- .../HandBrakeInterop/Model/Size.cs | 49 +- .../HandBrakeInterop/Model/SourceSubtitle.cs | 73 +- .../HandBrakeInterop/Model/SourceType.cs | 32 +- .../HandBrakeInterop/Model/SrtSubtitle.cs | 78 +- .../HandBrakeInterop/Model/Subtitles.cs | 24 +- .../HandBrakeInterop/Model/VideoRangeType.cs | 30 +- .../HandBrakeInterop/SourceData/AudioCodec.cs | 46 +- .../HandBrakeInterop/SourceData/AudioTrack.cs | 23 +- .../HandBrakeInterop/SourceData/Chapter.cs | 3 +- .../HandBrakeInterop/SourceData/InputType.cs | 3 + .../HandBrakeInterop/SourceData/Subtitle.cs | 5 +- .../SourceData/SubtitleSource.cs | 5 +- .../SourceData/SubtitleType.cs | 5 +- .../HandBrakeInterop/SourceData/Title.cs | 2 +- .../HandBrakeInterop/Utilities.cs | 10 +- .../HandBrakeInterop/libgcc_s_sjlj-1.dll | Bin 241655 -> 0 bytes win/CS/HandBrakeWPF/HandBrakeWPF.csproj | 26 +- .../Startup/CastleBootstrapper.cs | 2 + .../ViewModels/AdvancedViewModel.cs | 1113 +--------------- .../ViewModels/EncoderOptionsViewModel.cs | 114 ++ .../Interfaces/IEncoderOptionsViewModel.cs | 32 + .../ViewModels/Interfaces/IX264ViewModel.cs | 32 + .../HandBrakeWPF/ViewModels/MainViewModel.cs | 22 +- .../ViewModels/OptionsViewModel.cs | 6 + .../ViewModels/PreviewViewModel.cs | 27 +- .../HandBrakeWPF/ViewModels/X264ViewModel.cs | 1155 +++++++++++++++++ win/CS/HandBrakeWPF/Views/AdvancedView.xaml | 544 +------- .../Views/EncoderOptionsView.xaml | 28 + .../Views/EncoderOptionsView.xaml.cs | 27 + win/CS/HandBrakeWPF/Views/OptionsView.xaml | 3 - win/CS/HandBrakeWPF/Views/X264View.xaml | 539 ++++++++ win/CS/HandBrakeWPF/Views/X264View.xaml.cs | 27 + 74 files changed, 3331 insertions(+), 2025 deletions(-) create mode 100644 contrib/ffmpeg/A05-truehd-downmix.patch rename win/CS/HandBrake.Interop/HandBrakeInterop/{ => EventArgs}/EncodeCompletedEventArgs.cs (91%) rename win/CS/HandBrake.Interop/HandBrakeInterop/{ => EventArgs}/EncodeProgressEventArgs.cs (91%) rename win/CS/HandBrake.Interop/HandBrakeInterop/{ => EventArgs}/MessageLoggedEventArgs.cs (88%) rename win/CS/HandBrake.Interop/HandBrakeInterop/{ => EventArgs}/ScanProgressEventArgs.cs (91%) delete mode 100644 win/CS/HandBrake.Interop/HandBrakeInterop/libgcc_s_sjlj-1.dll create mode 100644 win/CS/HandBrakeWPF/ViewModels/EncoderOptionsViewModel.cs create mode 100644 win/CS/HandBrakeWPF/ViewModels/Interfaces/IEncoderOptionsViewModel.cs create mode 100644 win/CS/HandBrakeWPF/ViewModels/Interfaces/IX264ViewModel.cs create mode 100644 win/CS/HandBrakeWPF/ViewModels/X264ViewModel.cs create mode 100644 win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml create mode 100644 win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml.cs create mode 100644 win/CS/HandBrakeWPF/Views/X264View.xaml create mode 100644 win/CS/HandBrakeWPF/Views/X264View.xaml.cs diff --git a/contrib/bzip2/module.defs b/contrib/bzip2/module.defs index 728e28189..0a7463e20 100644 --- a/contrib/bzip2/module.defs +++ b/contrib/bzip2/module.defs @@ -6,10 +6,10 @@ BZIP2.EXTRACT.tarbase = bzip2 BZIP2.CONFIGURE = $(TOUCH.exe) $@ BZIP2.BUILD.extra = \ - CC=$(BZIP2.GCC.gcc) \ - CFLAGS="$(call fn.ARGS,BZIP2.GCC,*archs *sysroot *minver)" \ + PREFIX="$(BZIP2.CONFIGURE.prefix)" \ + CC="$(BZIP2.GCC.gcc)" \ + $(BZIP2.CONFIGURE.env.CFLAGS) \ RANLIB="$(RANLIB.exe)" - PREFIX=$(call fn.ABSOLUTE,$(CONTRIB.build/)) BZIP2.BUILD.ntargets = libbz2.a diff --git a/contrib/ffmpeg/A05-truehd-downmix.patch b/contrib/ffmpeg/A05-truehd-downmix.patch new file mode 100644 index 000000000..35344613f --- /dev/null +++ b/contrib/ffmpeg/A05-truehd-downmix.patch @@ -0,0 +1,46 @@ +diff --git a/libavcodec/mlpdec.c b/libavcodec/mlpdec.c +index 3852f6e..86aecf7 100644 +--- a/libavcodec/mlpdec.c ++++ b/libavcodec/mlpdec.c +@@ -394,14 +394,24 @@ static int read_restart_header(MLPDecodeContext *m, GetBitContext *gbp, + return AVERROR_INVALIDDATA; + } + +- if (m->avctx->request_channels > 0 +- && s->max_channel + 1 >= m->avctx->request_channels +- && substr < m->max_decoded_substream) { ++#if FF_API_REQUEST_CHANNELS ++ if (m->avctx->request_channels > 0 && ++ m->avctx->request_channels <= s->max_channel + 1 && ++ m->max_decoded_substream > substr) { + av_log(m->avctx, AV_LOG_DEBUG, +- "Extracting %d channel downmix from substream %d. " ++ "Extracting %d-channel downmix from substream %d. " + "Further substreams will be skipped.\n", + s->max_channel + 1, substr); + m->max_decoded_substream = substr; ++ } else ++#endif ++ if (m->avctx->request_channel_layout == s->ch_layout && ++ m->max_decoded_substream > substr) { ++ av_log(m->avctx, AV_LOG_DEBUG, ++ "Extracting %d-channel downmix (0x%"PRIx64") from substream %d. " ++ "Further substreams will be skipped.\n", ++ s->max_channel + 1, s->ch_layout, substr); ++ m->max_decoded_substream = substr; + } + + s->noise_shift = get_bits(gbp, 4); +@@ -463,8 +473,10 @@ static int read_restart_header(MLPDecodeContext *m, GetBitContext *gbp, + cp->huff_lsbs = 24; + } + +- if (substr == m->max_decoded_substream) +- m->avctx->channels = s->max_matrix_channel + 1; ++ if (substr == m->max_decoded_substream) { ++ m->avctx->channels = s->max_matrix_channel + 1; ++ m->avctx->channel_layout = s->ch_layout; ++ } + + return 0; + } diff --git a/contrib/ffmpeg/module.defs b/contrib/ffmpeg/module.defs index 435ce778b..85a8299fe 100644 --- a/contrib/ffmpeg/module.defs +++ b/contrib/ffmpeg/module.defs @@ -34,7 +34,7 @@ FFMPEG.CONFIGURE.extra = \ ## check against tuple: B-SYSTEM where B is { 0 | 1 } for cross-compiling flag ifeq (0-cygwin,$(BUILD.cross)-$(BUILD.system)) FFMPEG.CONFIGURE.extra += --enable-pthreads --enable-memalign-hack - FFMPEG.GCC.args.extra = -fno-common + FFMPEG.GCC.args.extra += -fno-common else ifeq (darwin,$(BUILD.system)) ## section for darwin-archs FFMPEG.CONFIGURE.extra += --enable-pthreads --enable-cross-compile --arch=$(BUILD.machine) --target-os=darwin diff --git a/contrib/libdvdnav/module.defs b/contrib/libdvdnav/module.defs index 6f596d409..473a76237 100644 --- a/contrib/libdvdnav/module.defs +++ b/contrib/libdvdnav/module.defs @@ -5,11 +5,7 @@ LIBDVDNAV.FETCH.url = http://download.handbrake.fr/handbrake/contrib/libdvdnav-s LIBDVDNAV.EXTRACT.tarbase = libdvdnav ifneq (max,$(LIBDVDNAV.GCC.g)) -ifeq (none,$(LIBDVDNAV.GCC.O)) - LIBDVDNAV.CONFIGURE.env.CFLAGS = CFLAGS="$(call fn.ARGS,LIBDVDNAV.GCC,*sysroot *minver ?extra .O) -DNDEBUG" -else - LIBDVDNAV.CONFIGURE.env.CFLAGS = CFLAGS="$(call fn.ARGS,LIBDVDNAV.GCC,*sysroot *minver ?extra) -DNDEBUG" -endif + LIBDVDNAV.GCC.D += NDEBUG endif LIBDVDNAV.CONFIGURE.bootstrap = rm -fr aclocal.m4 autom4te.cache; autoreconf -fiv; diff --git a/contrib/libsamplerate/module.defs b/contrib/libsamplerate/module.defs index c99ae3abf..468aa4739 100644 --- a/contrib/libsamplerate/module.defs +++ b/contrib/libsamplerate/module.defs @@ -3,3 +3,6 @@ $(eval $(call import.CONTRIB.defs,LIBSAMPLERATE)) LIBSAMPLERATE.FETCH.url = http://download.handbrake.fr/handbrake/contrib/libsamplerate-0.1.4.tar.gz LIBSAMPLERATE.EXTRACT.tarbase = libsamplerate + +# Disable to avoid Carbon.h dependency on OSX +LIBSAMPLERATE.CONFIGURE.extra = --disable-sndfile diff --git a/contrib/mp4v2/module.defs b/contrib/mp4v2/module.defs index 0254d43ff..39aa2233d 100644 --- a/contrib/mp4v2/module.defs +++ b/contrib/mp4v2/module.defs @@ -4,8 +4,8 @@ $(eval $(call import.CONTRIB.defs,MP4V2)) MP4V2.FETCH.url = http://download.handbrake.fr/handbrake/contrib/mp4v2-trunk-r355.tar.bz2 ## propagate more flags -MP4V2.CONFIGURE.env.CFLAGS = CFLAGS="$(call fn.ARGS,MP4V2.GCC,*archs *sysroot *minver *D .g .O)" -MP4V2.CONFIGURE.env.CXXFLAGS = CXXFLAGS="$(call fn.ARGS,MP4V2.GCC,*archs *sysroot *minver *D .g .O)" +MP4V2.CONFIGURE.env.CFLAGS = CFLAGS="$(call fn.ARGS,MP4V2.GCC,*archs *sysroot *minver ?extra *D .g .O)" +MP4V2.CONFIGURE.env.CXXFLAGS = CXXFLAGS="$(call fn.ARGS,MP4V2.GCC,*archs *sysroot *minver ?extra *D .g .O)" ## save some build-time by disabling utils MP4V2.CONFIGURE.extra += --disable-util diff --git a/gtk/src/ghb.ui b/gtk/src/ghb.ui index c697849fa..ba8f46856 100644 --- a/gtk/src/ghb.ui +++ b/gtk/src/ghb.ui @@ -213,10 +213,10 @@ This setting allows you to synchronize the files. 15 - 5 - 60 - 5 - 10 + 15 + 240 + 15 + 15 0 @@ -339,7 +339,7 @@ This setting allows you to synchronize the files. 0 - 30 + 60 5 10 5 diff --git a/libhb/common.c b/libhb/common.c index b5d296dee..7caae40a4 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -83,6 +83,17 @@ int hb_audio_bitrates_count = sizeof(hb_audio_bitrates) / sizeof(hb_rate_t); static hb_error_handler_t *error_handler = NULL; +hb_dither_t hb_audio_dithers[] = +{ + { "default", "auto", AV_RESAMPLE_DITHER_NONE - 1, }, + { "none", "none", AV_RESAMPLE_DITHER_NONE, }, + { "rectangular", "rectangular", AV_RESAMPLE_DITHER_RECTANGULAR, }, + { "triangular", "triangular", AV_RESAMPLE_DITHER_TRIANGULAR, }, + { "triangular with high pass", "triangular_hp", AV_RESAMPLE_DITHER_TRIANGULAR_HP, }, + { "triangular with noise shaping", "triangular_ns", AV_RESAMPLE_DITHER_TRIANGULAR_NS, }, +}; +int hb_audio_dithers_count = sizeof(hb_audio_dithers) / sizeof(hb_dither_t); + hb_mixdown_t hb_audio_mixdowns[] = { { "None", "HB_AMIXDOWN_NONE", "none", HB_AMIXDOWN_NONE }, @@ -138,6 +149,8 @@ hb_rate_t* hb_get_audio_rates() { return hb_audio_rates; } int hb_get_audio_rates_count() { return hb_audio_rates_count; } hb_rate_t* hb_get_audio_bitrates() { return hb_audio_bitrates; } int hb_get_audio_bitrates_count() { return hb_audio_bitrates_count; } +hb_dither_t* hb_get_audio_dithers() { return hb_audio_dithers; } +int hb_get_audio_dithers_count() { return hb_audio_dithers_count; } hb_mixdown_t* hb_get_audio_mixdowns() { return hb_audio_mixdowns; } int hb_get_audio_mixdowns_count() { return hb_audio_mixdowns_count; } hb_encoder_t* hb_get_video_encoders() { return hb_video_encoders; } @@ -145,6 +158,47 @@ int hb_get_video_encoders_count() { return hb_video_encoders_count; } hb_encoder_t* hb_get_audio_encoders() { return hb_audio_encoders; } int hb_get_audio_encoders_count() { return hb_audio_encoders_count; } +int hb_audio_dither_get_default() +{ + // "auto" + return hb_audio_dithers[0].method; +} + +int hb_audio_dither_get_default_method() +{ + /* + * input could be s16 (possibly already dithered) converted to flt, so + * let's use a "low-risk" dither algorithm (standard triangular). + */ + return AV_RESAMPLE_DITHER_TRIANGULAR; +} + +int hb_audio_dither_is_supported(uint32_t codec) +{ + // encoder's input sample format must be s16(p) + switch (codec) + { + case HB_ACODEC_FFFLAC: + return 1; + default: + return 0; + } +} + +const char* hb_audio_dither_get_description(int method) +{ + int i; + for (i = 0; i < hb_audio_dithers_count; i++) + { + if (hb_audio_dithers[i].method == method) + { + return hb_audio_dithers[i].description; + } + } + return ""; +} + + int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout) { return (hb_mixdown_has_codec_support(mixdown, codec) && @@ -2205,6 +2259,7 @@ void hb_audio_config_init(hb_audio_config_t * audiocfg) audiocfg->out.dynamic_range_compression = 0; audiocfg->out.gain = 0; audiocfg->out.normalize_mix_level = 0; + audiocfg->out.dither_method = hb_audio_dither_get_default(); audiocfg->out.name = NULL; } @@ -2255,6 +2310,7 @@ int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg) audio->config.out.normalize_mix_level = 0; audio->config.out.compression_level = -1; audio->config.out.quality = HB_INVALID_AUDIO_QUALITY; + audio->config.out.dither_method = AV_RESAMPLE_DITHER_NONE; } else { @@ -2268,6 +2324,7 @@ int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg) audio->config.out.mixdown = audiocfg->out.mixdown; audio->config.out.gain = audiocfg->out.gain; audio->config.out.normalize_mix_level = audiocfg->out.normalize_mix_level; + audio->config.out.dither_method = audiocfg->out.dither_method; } if (audiocfg->out.name && *audiocfg->out.name) { diff --git a/libhb/common.h b/libhb/common.h index 1f3d6fd81..9c54f7a9d 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -72,6 +72,7 @@ typedef struct hb_handle_s hb_handle_t; typedef struct hb_list_s hb_list_t; typedef struct hb_rate_s hb_rate_t; +typedef struct hb_dither_s hb_dither_t; typedef struct hb_mixdown_s hb_mixdown_t; typedef struct hb_encoder_s hb_encoder_t; typedef struct hb_job_s hb_job_t; @@ -178,6 +179,13 @@ struct hb_rate_s int rate; }; +struct hb_dither_s +{ + const char *description; + const char *short_name; + int method; +}; + struct hb_mixdown_s { const char *human_readable_name; @@ -215,6 +223,8 @@ extern int hb_audio_rates_count; extern int hb_audio_rates_default; extern hb_rate_t hb_audio_bitrates[]; extern int hb_audio_bitrates_count; +extern hb_dither_t hb_audio_dithers[]; +extern int hb_audio_dithers_count; extern hb_mixdown_t hb_audio_mixdowns[]; extern int hb_audio_mixdowns_count; extern hb_encoder_t hb_video_encoders[]; @@ -230,6 +240,8 @@ int hb_get_audio_rates_count(); int hb_get_audio_rates_default(); hb_rate_t* hb_get_audio_bitrates(); int hb_get_audio_bitrates_count(); +hb_dither_t* hb_get_audio_dithers(); +int hb_get_audio_dithers_count(); hb_mixdown_t* hb_get_audio_mixdowns(); int hb_get_audio_mixdowns_count(); hb_encoder_t* hb_get_video_encoders(); @@ -237,6 +249,11 @@ int hb_get_video_encoders_count(); hb_encoder_t* hb_get_audio_encoders(); int hb_get_audio_encoders_count(); +int hb_audio_dither_get_default(); +int hb_audio_dither_get_default_method(); +int hb_audio_dither_is_supported(uint32_t codec); +const char* hb_audio_dither_get_description(int method); + int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout); int hb_mixdown_has_codec_support(int mixdown, uint32_t codec); int hb_mixdown_has_remix_support(int mixdown, uint64_t layout); @@ -525,6 +542,7 @@ struct hb_audio_config_s double dynamic_range_compression; /* Amount of DRC applied to this track */ double gain; /* Gain (in dB), negative is quieter */ int normalize_mix_level; /* mix level normalization (boolean) */ + int dither_method; /* dither algorithm */ char * name; /* Output track name */ } out; diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index 71c089016..d748c41db 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -193,6 +193,19 @@ static int decavcodecaInit( hb_work_object_t * w, hb_job_t * job ) pv->title = w->title; pv->list = hb_list_init(); + codec = avcodec_find_decoder(w->codec_param); + pv->context = avcodec_alloc_context3(codec); + if (pv->title->opaque_priv != NULL) + { + AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv; + avcodec_copy_context(pv->context, ic->streams[w->audio->id]->codec); + } + else + { + pv->parser = av_parser_init(w->codec_param); + } + hb_ff_set_sample_fmt(pv->context, codec, AV_SAMPLE_FMT_FLT); + /* Downmixing & sample_fmt conversion */ if (!(w->audio->config.out.codec & HB_ACODEC_PASS_FLAG)) { @@ -205,26 +218,37 @@ static int decavcodecaInit( hb_work_object_t * w, hb_job_t * job ) hb_error("decavcodecaInit: hb_audio_resample_init() failed"); return 1; } + // some decoders can downmix using embedded coefficients, + // or dedicated audio substreams for a specific channel layout + switch (w->audio->config.out.mixdown) + { + case HB_AMIXDOWN_MONO: + if (w->codec_param == AV_CODEC_ID_TRUEHD) + { + // libavcodec can't decode TrueHD Mono (bug #356) + // work around it by requesting Stereo and downmixing + pv->context->request_channels = 2; + pv->context->request_channel_layout = AV_CH_LAYOUT_STEREO; + break; + } + pv->context->request_channel_layout = AV_CH_LAYOUT_MONO; + break; + // request 5.1 before downmixing to dpl1/dpl2 + case HB_AMIXDOWN_DOLBY: + case HB_AMIXDOWN_DOLBYPLII: + pv->context->request_channel_layout = AV_CH_LAYOUT_5POINT1; + break; + // request the layout corresponding to the selected mixdown + default: + pv->context->request_channel_layout = + hb_ff_mixdown_xlat(w->audio->config.out.mixdown, NULL); + break; + } } - codec = avcodec_find_decoder( w->codec_param ); - if ( pv->title->opaque_priv ) - { - AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv; - pv->context = avcodec_alloc_context3(codec); - avcodec_copy_context( pv->context, ic->streams[w->audio->id]->codec); - hb_ff_set_sample_fmt( pv->context, codec, AV_SAMPLE_FMT_FLT ); - } - else - { - pv->parser = av_parser_init( w->codec_param ); - - pv->context = avcodec_alloc_context3(codec); - hb_ff_set_sample_fmt( pv->context, codec, AV_SAMPLE_FMT_FLT ); - } - if ( hb_avcodec_open( pv->context, codec, NULL, 0 ) ) + if (hb_avcodec_open(pv->context, codec, NULL, 0)) { - hb_log( "decavcodecaInit: avcodec_open failed" ); + hb_log("decavcodecaInit: avcodec_open failed"); return 1; } @@ -437,19 +461,32 @@ static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, unsigned char *pbuffer; int pos, pbuffer_size; - while ( buf && !ret ) + while (buf != NULL && !ret) { pos = 0; - while ( pos < buf->size ) + while (pos < buf->size) { - int len; + int len, truehd_mono = 0; - if ( parser != NULL ) + if (parser != NULL) { - len = av_parser_parse2( parser, context, &pbuffer, - &pbuffer_size, buf->data + pos, - buf->size - pos, buf->s.start, - buf->s.start, 0 ); + len = av_parser_parse2(parser, context, &pbuffer, &pbuffer_size, + buf->data + pos, buf->size - pos, + buf->s.start, buf->s.start, 0); + if (context->codec_id == AV_CODEC_ID_TRUEHD && + context->channel_layout == AV_CH_LAYOUT_MONO) + { + // libavcodec can't decode TrueHD Mono (bug #356) + // work around it by requesting Stereo before decoding + truehd_mono = 1; + context->request_channels = 2; + context->request_channel_layout = AV_CH_LAYOUT_STEREO; + } + else + { + context->request_channels = 0; + context->request_channel_layout = 0; + } } else { @@ -457,7 +494,7 @@ static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, len = pbuffer_size = buf->size; } pos += len; - if ( pbuffer_size > 0 ) + if (pbuffer_size > 0) { int got_frame; AVFrame frame = { { 0 } }; @@ -466,17 +503,24 @@ static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, avp.data = pbuffer; avp.size = pbuffer_size; - len = avcodec_decode_audio4( context, &frame, &got_frame, &avp ); - if ( len > 0 && context->sample_rate > 0 ) + len = avcodec_decode_audio4(context, &frame, &got_frame, &avp); + if (len > 0 && context->sample_rate > 0) { - info->bitrate = context->bit_rate; - info->rate = context->sample_rate; - info->rate_base = 1; - info->channel_layout = - hb_ff_layout_xlat(context->channel_layout, - context->channels); + info->rate_base = 1; + info->rate = context->sample_rate; + info->bitrate = context->bit_rate; + info->samples_per_frame = frame.nb_samples; + if (truehd_mono) + { + info->channel_layout = AV_CH_LAYOUT_MONO; + } + else + { + info->channel_layout = + hb_ff_layout_xlat(context->channel_layout, + context->channels); + } ret = 1; - info->samples_per_frame = frame.nb_samples; break; } } diff --git a/libhb/encavcodecaudio.c b/libhb/encavcodecaudio.c index 669691dc1..ecd75ff98 100644 --- a/libhb/encavcodecaudio.c +++ b/libhb/encavcodecaudio.c @@ -148,6 +148,16 @@ static int encavcodecaInit(hb_work_object_t *w, hb_job_t *job) context->channel_layout, 0); av_opt_set_int(pv->avresample, "out_channel_layout", context->channel_layout, 0); + if (hb_audio_dither_is_supported(audio->config.out.codec)) + { + // dithering needs the sample rate + av_opt_set_int(pv->avresample, "in_sample_rate", + context->sample_rate, 0); + av_opt_set_int(pv->avresample, "out_sample_rate", + context->sample_rate, 0); + av_opt_set_int(pv->avresample, "dither_method", + audio->config.out.dither_method, 0); + } if (avresample_open(pv->avresample)) { hb_error("encavcodecaInit: avresample_open() failed"); diff --git a/libhb/hb.c b/libhb/hb.c index fca34bacc..518212737 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -64,7 +64,10 @@ struct hb_handle_s on multi-pass encodes where frames get dropped. */ hb_interjob_t * interjob; -}; + // Power Management opaque pointer + // For OSX, it's an IOPMAssertionID* + void * hb_system_sleep_opaque; +} ; hb_work_object_t * hb_objects = NULL; int hb_instance_counter = 0; @@ -407,6 +410,9 @@ hb_handle_t * hb_init( int verbose, int update_check ) /* Check for an update on the website if asked to */ h->build = -1; + /* Initialize opaque for PowerManagement purposes */ + h->hb_system_sleep_opaque = hb_system_sleep_opaque_init(); + if( update_check ) { hb_log( "hb_init: checking for updates" ); @@ -511,6 +517,9 @@ hb_handle_t * hb_init_dl( int verbose, int update_check ) /* Check for an update on the website if asked to */ h->build = -1; + /* Initialize opaque for PowerManagement purposes */ + h->hb_system_sleep_opaque = hb_system_sleep_opaque_init(); + if( update_check ) { hb_log( "hb_init: checking for updates" ); @@ -1541,7 +1550,7 @@ void hb_start( hb_handle_t * h ) h->paused = 0; h->work_die = 0; - h->work_thread = hb_work_init( h->jobs, &h->work_die, &h->work_error, &h->current_job ); + h->work_thread = hb_work_init( h, h->jobs, &h->work_die, &h->work_error, &h->current_job ); } /** @@ -1560,6 +1569,8 @@ void hb_pause( hb_handle_t * h ) hb_lock( h->state_lock ); h->state.state = HB_STATE_PAUSED; hb_unlock( h->state_lock ); + + hb_allow_sleep( h ); } } @@ -1571,6 +1582,8 @@ void hb_resume( hb_handle_t * h ) { if( h->paused ) { + hb_prevent_sleep( h ); + #define job hb_current_job( h ) if( job->st_pause_date != -1 ) { @@ -1876,6 +1889,16 @@ void hb_set_state( hb_handle_t * h, hb_state_t * s ) hb_unlock( h->pause_lock ); } +void hb_prevent_sleep( hb_handle_t * h ) +{ + hb_system_sleep_prevent( h->hb_system_sleep_opaque ); +} + +void hb_allow_sleep( hb_handle_t * h ) +{ + hb_system_sleep_allow( h->hb_system_sleep_opaque ); +} + /* Passes a pointer to persistent data */ hb_interjob_t * hb_interjob_get( hb_handle_t * h ) { diff --git a/libhb/internal.h b/libhb/internal.h index 81cc8680f..8bf9fa533 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -39,6 +39,8 @@ void hb_title_close( hb_title_t ** ); **********************************************************************/ int hb_get_pid( hb_handle_t * ); void hb_set_state( hb_handle_t *, hb_state_t * ); +void hb_prevent_sleep( hb_handle_t * ); +void hb_allow_sleep( hb_handle_t * ); /*********************************************************************** * fifo.c @@ -229,7 +231,7 @@ hb_thread_t * hb_scan_init( hb_handle_t *, volatile int * die, const char * path, int title_index, hb_title_set_t * title_set, int preview_count, int store_previews, uint64_t min_duration ); -hb_thread_t * hb_work_init( hb_list_t * jobs, +hb_thread_t * hb_work_init( hb_handle_t * handle, hb_list_t * jobs, volatile int * die, int * error, hb_job_t ** job ); void ReadLoop( void * _w ); hb_work_object_t * hb_muxer_init( hb_job_t * ); diff --git a/libhb/ports.c b/libhb/ports.c index f10c9d96c..11952c052 100644 --- a/libhb/ports.c +++ b/libhb/ports.c @@ -66,6 +66,10 @@ #include #endif +#ifdef __APPLE__ +#include +#endif + #include #include @@ -781,3 +785,70 @@ char *strtok_r(char *s, const char *delim, char **save_ptr) return token; } #endif + +/************************************************************************ +* OS Sleep Allow / Prevent +***********************************************************************/ + +#ifdef __APPLE__ +// 128 chars limit for IOPMAssertionCreateWithName +static CFStringRef reasonForActivity= CFSTR("HandBrake is currently scanning and/or encoding"); +#endif + +void* hb_system_sleep_opaque_init() +{ + void* opaque; +#ifdef __APPLE__ + opaque = calloc( sizeof( IOPMAssertionID ), 1); + IOPMAssertionID * assertionID = (IOPMAssertionID *)opaque; + *assertionID = -1; +#endif + + return opaque; +} + +void hb_system_sleep_opaque_close(void **_opaque) +{ +#ifdef __APPLE__ + IOPMAssertionID * assertionID = (IOPMAssertionID *) *_opaque; + free( assertionID ); +#endif + *_opaque = NULL; +} + +void hb_system_sleep_allow(void *opaque) +{ +#ifdef __APPLE__ + IOPMAssertionID * assertionID = (IOPMAssertionID *)opaque; + + if (*assertionID == -1) + return; + + IOReturn success = IOPMAssertionRelease(*assertionID); + + if (success == kIOReturnSuccess) { + hb_deep_log( 3, "osxsleep: IOPM assertion %d successfully released, sleep allowed", *assertionID ); + *assertionID = -1; + } else { + hb_log( "osxsleep: error while trying to unset power management assertion" ); + } +#endif +} + +void hb_system_sleep_prevent(void *opaque) +{ +#ifdef __APPLE__ + IOPMAssertionID * assertionID = (IOPMAssertionID *)opaque; + + if (*assertionID != -1) + return; + + IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, + kIOPMAssertionLevelOn, reasonForActivity, assertionID); + if (success == kIOReturnSuccess) { + hb_deep_log( 3, "IOPM assertion %d successfully created, prevent sleep", *assertionID); + } else { + hb_log( "osxsleep: error while trying to set power management assertion" ); + } +#endif +} diff --git a/libhb/ports.h b/libhb/ports.h index 23879fcb1..9f7da2b14 100644 --- a/libhb/ports.h +++ b/libhb/ports.h @@ -102,6 +102,14 @@ int hb_net_send( hb_net_t *, char * ); int hb_net_recv( hb_net_t *, char *, int ); void hb_net_close( hb_net_t ** ); +/************************************************************************ +* OS Sleep Allow / Prevent +***********************************************************************/ +void * hb_system_sleep_opaque_init(); +void hb_system_sleep_opaque_close( void ** opaque ); +void hb_system_sleep_allow( void * opaque ); +void hb_system_sleep_prevent( void * opaque ); + #endif /* __LIBHB__ */ #endif diff --git a/libhb/work.c b/libhb/work.c index dc50d502b..10f7fca46 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -14,9 +14,10 @@ typedef struct { - hb_list_t * jobs; - hb_job_t ** current_job; - int * error; + hb_handle_t * handle; + hb_list_t * jobs; + hb_job_t ** current_job; + int * error; volatile int * die; } hb_work_t; @@ -41,14 +42,15 @@ static void filter_loop( void * ); * @param die Handle to user inititated exit indicator. * @param error Handle to error indicator. */ -hb_thread_t * hb_work_init( hb_list_t * jobs, volatile int * die, int * error, hb_job_t ** job ) +hb_thread_t * hb_work_init( hb_handle_t * handle, hb_list_t * jobs, volatile int * die, int * error, hb_job_t ** job ) { hb_work_t * work = calloc( sizeof( hb_work_t ), 1 ); - work->jobs = jobs; + work->handle = handle; + work->jobs = jobs; work->current_job = job; - work->die = die; - work->error = error; + work->die = die; + work->error = error; return hb_thread_init( "work", work_func, work, HB_LOW_PRIORITY ); } @@ -82,16 +84,22 @@ static void work_func( void * _work ) hb_log( "%d job(s) to process", hb_list_count( work->jobs ) ); + hb_prevent_sleep( work->handle ); + while( !*work->die && ( job = hb_list_item( work->jobs, 0 ) ) ) { hb_list_rem( work->jobs, job ); job->die = work->die; *(work->current_job) = job; + InitWorkState( job->h ); do_job( job ); + *(work->current_job) = NULL; } + hb_allow_sleep( work->handle ); + *(work->error) = HB_ERROR_NONE; free( work ); @@ -479,6 +487,11 @@ void hb_display_job_info( hb_job_t * job ) { hb_log( " + dynamic range compression: %f", audio->config.out.dynamic_range_compression ); } + if (hb_audio_dither_is_supported(audio->config.out.codec)) + { + hb_log(" + dither: %s", + hb_audio_dither_get_description(audio->config.out.dither_method)); + } for( j = 0; j < hb_audio_encoders_count; j++ ) { if( hb_audio_encoders[j].encoder == audio->config.out.codec ) @@ -1005,6 +1018,25 @@ static void do_job( hb_job_t * job ) audio->config.out.bitrate = best_bitrate; } } + + /* sense-check the requested dither */ + if (hb_audio_dither_is_supported(audio->config.out.codec)) + { + if (audio->config.out.dither_method == + hb_audio_dither_get_default()) + { + /* "auto", enable with default settings */ + audio->config.out.dither_method = + hb_audio_dither_get_default_method(); + } + } + else if (audio->config.out.dither_method != + hb_audio_dither_get_default()) + { + /* specific dither requested but dithering not supported */ + hb_log("work: track %d, dithering not supported by codec", + audio->config.out.track); + } } } diff --git a/macosx/Controller.m b/macosx/Controller.m index 1a4b1fa8d..61a77076c 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -5769,7 +5769,7 @@ the user is using "Custom" settings by determining the sender*/ // width or height may have changed, unparse [self x264PresetsChangedDisplayExpandedOptions:nil]; - //[fPictureController reloadStillPreview]; + [fPictureController decombDeinterlacePreviewImage]; } diff --git a/macosx/English.lproj/Preferences.xib b/macosx/English.lproj/Preferences.xib index 0316a9982..ab9a908bf 100644 --- a/macosx/English.lproj/Preferences.xib +++ b/macosx/English.lproj/Preferences.xib @@ -2,10 +2,10 @@ 1050 - 11C74 + 11G63 1938 - 1138.23 - 567.00 + 1138.51 + 569.00 com.apple.InterfaceBuilder.CocoaPlugin 1938 @@ -67,7 +67,7 @@ - {{0, 0}, {1920, 1178}} + {{0, 0}, {1440, 878}} {213, 129} {10000000000000, 10000000000000} YES @@ -687,7 +687,7 @@ NSControl - + 256 YES @@ -696,6 +696,8 @@ 268 {{34, 42}, {432, 17}} + + YES 67239488 @@ -709,6 +711,9 @@ {496, 82} + + + NSView NSResponder @@ -1348,7 +1353,7 @@ NSResponder - + 256 YES @@ -1357,6 +1362,8 @@ 268 {{302, 172}, {48, 19}} + + YES -1804468671 @@ -1379,6 +1386,8 @@ 256 {{17, 207}, {280, 14}} + + YES 67239424 @@ -1395,6 +1404,8 @@ 256 {{48, 175}, {249, 14}} + + YES 67239424 @@ -1411,6 +1422,8 @@ 256 {{99, 145}, {198, 14}} + + YES 67239424 @@ -1427,6 +1440,8 @@ 256 {{137, 53}, {160, 15}} + + YES 67239424 @@ -1443,6 +1458,8 @@ 256 {{108, 76}, {27, 14}} + + YES 67239424 @@ -1459,6 +1476,8 @@ 256 {{70, 110}, {65, 14}} + + YES 67239424 @@ -1475,6 +1494,8 @@ 256 {{137, 74}, {304, 18}} + + YES 67239424 @@ -1496,6 +1517,8 @@ 256 {{137, 108}, {220, 18}} + + YES 67239424 @@ -1517,6 +1540,8 @@ 256 {{137, 18}, {217, 18}} + + YES 67239424 @@ -1538,6 +1563,8 @@ 268 {{299, 140}, {73, 22}} + + YES -2076049856 @@ -1550,24 +1577,23 @@ 400 75 - - - 10 - - 1048576 - 2147483647 - 1 - - - _popUpItemAction: - - + YES OtherViews YES - + + + 10 + + 1048576 + 2147483647 + + + _popUpItemAction: + + 15 @@ -1610,8 +1636,69 @@ _popUpItemAction: + + + 35 + + 2147483647 + + + _popUpItemAction: + + + + + 40 + + 2147483647 + + + _popUpItemAction: + + + + + 45 + + 2147483647 + + + _popUpItemAction: + + + + + 50 + + 2147483647 + + + _popUpItemAction: + + + + + 55 + + 2147483647 + + + _popUpItemAction: + + + + + 60 + + 2147483647 + + + _popUpItemAction: + + + -1 1 YES YES @@ -1623,6 +1710,8 @@ 268 {{299, 202}, {76, 22}} + + YES -2076049856 @@ -1698,6 +1787,8 @@ 268 {{303, 49}, {66, 22}} + + YES -2076049856 @@ -1764,6 +1855,8 @@ 256 {{89, 20}, {46, 14}} + + YES 67239424 @@ -1777,6 +1870,9 @@ {495, 241} + + + NSView @@ -2590,6 +2686,12 @@ + + + + + + @@ -3130,6 +3232,36 @@ + + 514 + + + + + 515 + + + + + 516 + + + + + 517 + + + + + 518 + + + + + 519 + + + @@ -3256,6 +3388,12 @@ 509.IBPluginDependency 510.IBPluginDependency 511.IBPluginDependency + 514.IBPluginDependency + 515.IBPluginDependency + 516.IBPluginDependency + 517.IBPluginDependency + 518.IBPluginDependency + 519.IBPluginDependency 6.IBPluginDependency 61.IBPluginDependency @@ -3383,6 +3521,12 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -3397,7 +3541,7 @@ - 513 + 519 @@ -3528,8 +3672,8 @@ YES - {9, 8} - {7, 2} + {11, 11} + {10, 3} {15, 15} diff --git a/macosx/HBAudioController.m b/macosx/HBAudioController.m index b9b80bd53..5e4a32508 100644 --- a/macosx/HBAudioController.m +++ b/macosx/HBAudioController.m @@ -165,10 +165,12 @@ NSString *HBMixdownChangedNotification = @"HBMixdownChangedNotification"; audio->out.codec = [[[anAudio codec] objectForKey: keyAudioCodec] intValue]; audio->out.compression_level = hb_get_default_audio_compression(audio->out.codec); audio->out.mixdown = [[[anAudio mixdown] objectForKey: keyAudioMixdown] intValue]; + audio->out.normalize_mix_level = 0; audio->out.bitrate = [[[anAudio bitRate] objectForKey: keyAudioBitrate] intValue]; audio->out.samplerate = [sampleRateToUse intValue]; audio->out.dynamic_range_compression = [[anAudio drc] floatValue]; audio->out.gain = [[anAudio gain] floatValue]; + audio->out.dither_method = hb_audio_dither_get_default(); hb_audio_add(aJob, audio); free(audio); diff --git a/macosx/HBPreviewController.m b/macosx/HBPreviewController.m index ba664059b..fe605450b 100644 --- a/macosx/HBPreviewController.m +++ b/macosx/HBPreviewController.m @@ -89,6 +89,14 @@ [fPreviewMovieLengthPopUp addItemWithTitle: @"90"]; [fPreviewMovieLengthPopUp addItemWithTitle: @"105"]; [fPreviewMovieLengthPopUp addItemWithTitle: @"120"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"135"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"150"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"165"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"180"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"195"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"210"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"225"]; + [fPreviewMovieLengthPopUp addItemWithTitle: @"240"]; [fMovieView setHidden:YES]; [fMovieView setDelegate:self]; @@ -1257,10 +1265,11 @@ [fPictureSlider setIntegerValue:fPicture < [fPictureSlider maxValue] ? fPicture + 1 : fPicture]; [self pictureSliderChanged:self]; } - [super keyDown:event]; + else + [super keyDown:event]; } - - [super keyDown:event]; + else + [super keyDown:event]; } #pragma mark *** QTTime Utilities *** diff --git a/macosx/PictureController.h b/macosx/PictureController.h index 56dbe5f21..f738ba24e 100644 --- a/macosx/PictureController.h +++ b/macosx/PictureController.h @@ -198,6 +198,8 @@ - (IBAction) modeDecombDeinterlaceSliderChanged: (id) sender; - (IBAction) deblockSliderChanged: (id) sender; +- (void) decombDeinterlacePreviewImage; + - (int) detelecine; - (NSString*) detelecineCustomString; - (void) setDetelecine: (int) setting; diff --git a/macosx/PictureController.m b/macosx/PictureController.m index 027523f01..b15cd9b8c 100644 --- a/macosx/PictureController.m +++ b/macosx/PictureController.m @@ -1077,22 +1077,6 @@ hb_job_t * job = fTitle->job; [fPreviewController SetTitle:fTitle]; - /* Sanity Check Here for < 16 px preview to avoid - crashing hb_get_preview. In fact, just for kicks - lets getting previews at a min limit of 32, since - no human can see any meaningful detail below that */ - if (job->width >= 64 && job->height >= 64) - { - - // Purge the existing picture previews so they get recreated the next time - // they are needed. - // [fPreviewController purgeImageCache]; - /* We actually call displayPreview now from pictureSliderChanged which keeps - * our picture preview slider in sync with the previews being shown - */ - - //[fPreviewController pictureSliderChanged:nil]; - } } @@ -1233,6 +1217,8 @@ are maintained across different sources */ fPictureFilterSettings.grayscale = [fGrayscaleCheck state]; + [self decombDeinterlacePreviewImage]; + if (sender != nil) { [fHBController pictureSettingsDidChange]; @@ -1241,7 +1227,41 @@ are maintained across different sources */ } - +- (void) decombDeinterlacePreviewImage +{ + if ([fDecombDeinterlaceSlider floatValue] < 0.50) + { + /* Since Libhb only shows a deinterlaced preview image via deinterlace .. for decomb we + do a quick switch to get libhb to show a deinterlaced image if needed even though deinterlace + has not been changed by the user. so we temporarily switch deinterace accoring to decomb then + call reloadStillPreview quickly and then switch deinterlace back to its proper state. + */ + + // decomb is chosen + if (fPictureFilterSettings.decomb > 0) + { + /* Temporarily turn on deinterlacing .... */ + fTitle->job->deinterlace = 1; + /* Grab a still preview ... */ + [self reloadStillPreview]; + /* ... then reset deinterlace back to where specified in the ui */ + fTitle->job->deinterlace = [fDeinterlacePopUp indexOfSelectedItem]; + + } + else + { + [self reloadStillPreview]; + } + + } + else + { + fTitle->job->deinterlace = [fDeinterlacePopUp indexOfSelectedItem]; + [self reloadStillPreview]; + } + + +} #pragma mark - - (IBAction) deblockSliderChanged: (id) sender diff --git a/make/variant/mingw.defs b/make/variant/mingw.defs index f521e24f5..d369e343a 100644 --- a/make/variant/mingw.defs +++ b/make/variant/mingw.defs @@ -8,3 +8,5 @@ GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 + +GCC.args.extra += -mno-ms-bitfields \ No newline at end of file diff --git a/test/test.c b/test/test.c index 57156c726..3dbbde110 100644 --- a/test/test.c +++ b/test/test.c @@ -72,6 +72,7 @@ static int allowed_audio_copy = -1; static char * mixdowns = NULL; static char * dynamic_range_compression = NULL; static char * audio_gain = NULL; +static char ** audio_dither = NULL; static char ** normalize_mix_level = NULL; static char * atracks = NULL; static char * arates = NULL; @@ -159,6 +160,7 @@ static int ParseOptions( int argc, char ** argv ); static int CheckOptions( int argc, char ** argv ); static int HandleEvents( hb_handle_t * h ); +static int get_dither_for_string(const char *dither); static int get_acodec_for_string(const char *codec); static const char* get_string_for_acodec(int acodec); @@ -364,6 +366,7 @@ int main( int argc, char ** argv ) str_vfree(abitrates); str_vfree(acompressions); str_vfree(aqualities); + str_vfree(audio_dither); free(acodecs); free(arates); free(atracks); @@ -2109,6 +2112,58 @@ static int HandleEvents( hb_handle_t * h ) } /* Audio Gain */ + /* Audio Dither */ + if (audio_dither != NULL) + { + int dither_method = hb_audio_dither_get_default(); + for (i = 0; audio_dither[i] != NULL; i++) + { + dither_method = get_dither_for_string(audio_dither[i]); + audio = hb_list_audio_config_item(job->list_audio, i); + if (audio != NULL) + { + if (hb_audio_dither_is_supported(audio->out.codec)) + { + audio->out.dither_method = dither_method; + } + else if (dither_method != hb_audio_dither_get_default()) + { + fprintf(stderr, + "Ignoring dither %s, not supported by codec\n", + audio_dither[i]); + } + } + else + { + fprintf(stderr, "Ignoring dither %s, no audio tracks\n", + audio_dither[i]); + } + } + if (i < num_audio_tracks && i == 1) + { + /* + * We have fewer inputs than audio tracks, and we only have + * one input: use that for all tracks. + */ + while (i < num_audio_tracks) + { + audio = hb_list_audio_config_item(job->list_audio, i); + if (hb_audio_dither_is_supported(audio->out.codec)) + { + audio->out.dither_method = dither_method; + } + else if (dither_method != hb_audio_dither_get_default()) + { + fprintf(stderr, + "Ignoring dither %s, not supported by codec\n", + audio_dither[0]); + } + i++; + } + } + } + /* Audio Dither */ + /* Audio Mix Normalization */ i = 0; int norm = 0; @@ -2965,6 +3020,36 @@ static void ShowHelp() " NOT work with audio passthru (copy). Values are in\n" " dB. Negative values attenuate, positive values\n" " amplify. A 1 dB difference is barely audible.\n" + " --adither Apply dithering to the audio before encoding.\n" + " Separated by commas for more than one audio track.\n" + " Only supported by some encoders ("); + for (i = j = 0; i < hb_audio_encoders_count; i++) + { + if (hb_audio_dither_is_supported(hb_audio_encoders[i].encoder)) + { + if (j) + fprintf(out, "/"); + fprintf(out, "%s", hb_audio_encoders[i].short_name); + j = 1; + } + } + fprintf(out, ").\n"); + fprintf(out, + " Options:\n"); + for (i = 0; i < hb_audio_dithers_count; i++) + { + if (hb_audio_dithers[i].method == hb_audio_dither_get_default()) + { + fprintf(out, " %s (default)\n", + hb_audio_dithers[i].short_name); + } + else + { + fprintf(out, " %s\n", + hb_audio_dithers[i].short_name); + } + } + fprintf(out, " -A, --aname Audio track name(s),\n" " Separated by commas for more than one audio track.\n" "\n" @@ -3229,6 +3314,7 @@ static int ParseOptions( int argc, char ** argv ) #define H264_LEVEL 286 #define NO_OPENCL 287 #define NORMALIZE_MIX 288 + #define AUDIO_DITHER 288 for( ;; ) { @@ -3261,6 +3347,7 @@ static int ParseOptions( int argc, char ** argv ) { "normalize-mix", required_argument, NULL, NORMALIZE_MIX }, { "drc", required_argument, NULL, 'D' }, { "gain", required_argument, NULL, AUDIO_GAIN }, + { "adither", required_argument, NULL, AUDIO_DITHER }, { "subtitle", required_argument, NULL, 's' }, { "subtitle-forced", optional_argument, NULL, 'F' }, { "subtitle-burned", optional_argument, NULL, SUB_BURNED }, @@ -3487,6 +3574,12 @@ static int ParseOptions( int argc, char ** argv ) audio_gain = strdup( optarg ); } break; + case AUDIO_DITHER: + if (optarg != NULL) + { + audio_dither = str_split(optarg, ','); + } + break; case NORMALIZE_MIX: if( optarg != NULL ) { @@ -4014,6 +4107,19 @@ static int CheckOptions( int argc, char ** argv ) return 0; } +static int get_dither_for_string(const char *dither) +{ + int i; + for (i = 0; i < hb_audio_dithers_count; i++) + { + if (!strcasecmp(hb_audio_dithers[i].short_name, dither)) + { + return hb_audio_dithers[i].method; + } + } + return hb_audio_dither_get_default(); +} + static int get_acodec_for_string(const char *codec) { int i; diff --git a/win/CS/HandBrake.ApplicationServices/Services/LibEncode.cs b/win/CS/HandBrake.ApplicationServices/Services/LibEncode.cs index 320b96f1b..0d1998a59 100644 --- a/win/CS/HandBrake.ApplicationServices/Services/LibEncode.cs +++ b/win/CS/HandBrake.ApplicationServices/Services/LibEncode.cs @@ -11,12 +11,14 @@ namespace HandBrake.ApplicationServices.Services { using System; using System.Diagnostics; + using System.Globalization; using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Services.Base; using HandBrake.ApplicationServices.Services.Interfaces; using HandBrake.ApplicationServices.Utilities; using HandBrake.Interop; + using HandBrake.Interop.EventArgs; using HandBrake.Interop.Interfaces; using HandBrake.Interop.Model; @@ -174,8 +176,8 @@ namespace HandBrake.ApplicationServices.Services { this.IsEncoding = false; this.instance.StopEncode(); - } - catch(Exception) + } + catch (Exception) { // Do Nothing. } @@ -239,7 +241,7 @@ namespace HandBrake.ApplicationServices.Services /// /// The Interop.EncodeProgressEventArgs. /// - private void InstanceEncodeProgress(object sender, Interop.EncodeProgressEventArgs e) + private void InstanceEncodeProgress(object sender, Interop.EventArgs.EncodeProgressEventArgs e) { EncodeProgressEventArgs args = new EncodeProgressEventArgs { @@ -256,7 +258,7 @@ namespace HandBrake.ApplicationServices.Services if (this.WindowsSeven.IsWindowsSeven) { int percent; - int.TryParse(Math.Round(e.FractionComplete).ToString(), out percent); + int.TryParse(Math.Round(e.FractionComplete).ToString(CultureInfo.InvariantCulture), out percent); this.WindowsSeven.SetTaskBarProgress(percent); } @@ -271,7 +273,7 @@ namespace HandBrake.ApplicationServices.Services /// /// The e. /// - private void InstanceEncodeCompleted(object sender, Interop.EncodeCompletedEventArgs e) + private void InstanceEncodeCompleted(object sender, Interop.EventArgs.EncodeCompletedEventArgs e) { this.IsEncoding = false; diff --git a/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs b/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs index c3598d922..8dfb5ca12 100644 --- a/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs +++ b/win/CS/HandBrake.ApplicationServices/Services/LibScan.cs @@ -20,10 +20,11 @@ namespace HandBrake.ApplicationServices.Services using HandBrake.ApplicationServices.Services.Interfaces; using HandBrake.ApplicationServices.Utilities; using HandBrake.Interop; + using HandBrake.Interop.EventArgs; using HandBrake.Interop.Interfaces; using AudioTrack = HandBrake.ApplicationServices.Parsing.Audio; - using ScanProgressEventArgs = HandBrake.Interop.ScanProgressEventArgs; + using ScanProgressEventArgs = HandBrake.Interop.EventArgs.ScanProgressEventArgs; using Size = System.Drawing.Size; /// diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Converters.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Converters.cs index 00fc4e4e8..1dc2a1e74 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Converters.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Converters.cs @@ -17,12 +17,15 @@ namespace HandBrake.Interop using HandBrake.Interop.SourceData; using HandBrake.Interop.Model; + /// + /// The converters. + /// public static class Converters { /// /// Video Frame Rates /// - private static Dictionary vrates = new Dictionary + private static readonly Dictionary VideoRates = new Dictionary { {5, 5400000}, {10, 2700000}, @@ -52,12 +55,12 @@ namespace HandBrake.Interop /// public static int FramerateToVrate(double framerate) { - if (!vrates.ContainsKey(framerate)) + if (!VideoRates.ContainsKey(framerate)) { throw new ArgumentException("Framerate not recognized.", "framerate"); } - return vrates[framerate]; + return VideoRates[framerate]; } /// diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/EncodeCompletedEventArgs.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/EncodeCompletedEventArgs.cs similarity index 91% rename from win/CS/HandBrake.Interop/HandBrakeInterop/EncodeCompletedEventArgs.cs rename to win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/EncodeCompletedEventArgs.cs index aa71b3a14..2169c6e4d 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/EncodeCompletedEventArgs.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/EncodeCompletedEventArgs.cs @@ -7,9 +7,9 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace HandBrake.Interop +namespace HandBrake.Interop.EventArgs { - using System; + using System; /// /// Encode Completed Event Args diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/EncodeProgressEventArgs.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/EncodeProgressEventArgs.cs similarity index 91% rename from win/CS/HandBrake.Interop/HandBrakeInterop/EncodeProgressEventArgs.cs rename to win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/EncodeProgressEventArgs.cs index 73802fafd..9e2883e3b 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/EncodeProgressEventArgs.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/EncodeProgressEventArgs.cs @@ -7,11 +7,11 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace HandBrake.Interop +namespace HandBrake.Interop.EventArgs { - using System; + using System; - /// + /// s /// Encode Progress Event Args /// public class EncodeProgressEventArgs : EventArgs diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/MessageLoggedEventArgs.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/MessageLoggedEventArgs.cs similarity index 88% rename from win/CS/HandBrake.Interop/HandBrakeInterop/MessageLoggedEventArgs.cs rename to win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/MessageLoggedEventArgs.cs index fd33c9ef3..0649c52f4 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/MessageLoggedEventArgs.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/MessageLoggedEventArgs.cs @@ -7,11 +7,11 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace HandBrake.Interop +namespace HandBrake.Interop.EventArgs { - using System; + using System; - /// + /// /// The Message Logged Event Args /// public class MessageLoggedEventArgs : EventArgs diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/ScanProgressEventArgs.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/ScanProgressEventArgs.cs similarity index 91% rename from win/CS/HandBrake.Interop/HandBrakeInterop/ScanProgressEventArgs.cs rename to win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/ScanProgressEventArgs.cs index ce20a4f9d..b342c8f12 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/ScanProgressEventArgs.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/EventArgs/ScanProgressEventArgs.cs @@ -7,9 +7,9 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace HandBrake.Interop +namespace HandBrake.Interop.EventArgs { - using System; + using System; /// /// The Scan Progress Event Args diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInstance.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInstance.cs index 06bf022f3..ecfc79ebf 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInstance.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInstance.cs @@ -17,6 +17,7 @@ namespace HandBrake.Interop using System.Runtime.InteropServices; using System.Windows.Media.Imaging; + using HandBrake.Interop.EventArgs; using HandBrake.Interop.HbLib; using HandBrake.Interop.Interfaces; using HandBrake.Interop.Model; @@ -124,7 +125,7 @@ namespace HandBrake.Interop /// /// Fires when a scan has completed. /// - public event EventHandler ScanCompleted; + public event EventHandler ScanCompleted; /// /// Fires for progress updates when encoding. @@ -785,7 +786,7 @@ namespace HandBrake.Interop if (this.ScanCompleted != null) { - this.ScanCompleted(this, new EventArgs()); + this.ScanCompleted(this, new System.EventArgs()); } } } diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInterop.csproj b/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInterop.csproj index 3fbeace49..6de4a9adb 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInterop.csproj +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeInterop.csproj @@ -125,8 +125,8 @@ - - + + @@ -144,7 +144,7 @@ - + @@ -179,7 +179,7 @@ - + diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeUtils.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeUtils.cs index 6fde37c57..568e41a31 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeUtils.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/HandBrakeUtils.cs @@ -12,6 +12,8 @@ namespace HandBrake.Interop using System; using System.Collections.Generic; using System.Runtime.InteropServices; + + using HandBrake.Interop.EventArgs; using HandBrake.Interop.HbLib; using HandBrake.Interop.Model; using HandBrake.Interop.Model.Encoding; diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Interfaces/IHandBrakeInstance.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Interfaces/IHandBrakeInstance.cs index 4d6a127bd..1b0f8b354 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Interfaces/IHandBrakeInstance.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Interfaces/IHandBrakeInstance.cs @@ -13,6 +13,7 @@ namespace HandBrake.Interop.Interfaces using System.Collections.Generic; using System.Windows.Media.Imaging; + using HandBrake.Interop.EventArgs; using HandBrake.Interop.Model; using HandBrake.Interop.SourceData; diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/InteropUtilities.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/InteropUtilities.cs index de73dd607..8bf68b2e1 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/InteropUtilities.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/InteropUtilities.cs @@ -106,12 +106,9 @@ namespace HandBrake.Interop IntPtr nativeListInternal = Marshal.AllocHGlobal(capacity * intSize); returnList.AllocatedMemory.Add(nativeListInternal); - hb_list_s nativeListStruct = new hb_list_s(); - nativeListStruct.items = nativeListInternal; - nativeListStruct.items_alloc = capacity; - nativeListStruct.items_count = 0; + hb_list_s nativeListStruct = new hb_list_s { items = nativeListInternal, items_alloc = capacity, items_count = 0 }; - IntPtr nativeListStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_list_s))); + IntPtr nativeListStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_list_s))); Marshal.StructureToPtr(nativeListStruct, nativeListStructPtr, false); returnList.ListPtr = nativeListStructPtr; @@ -135,12 +132,14 @@ namespace HandBrake.Interop Marshal.WriteIntPtr(nativeListInternal, i * intSize, list[i]); } - hb_list_s nativeListStruct = new hb_list_s(); - nativeListStruct.items = nativeListInternal; - nativeListStruct.items_alloc = list.Count; - nativeListStruct.items_count = list.Count; + hb_list_s nativeListStruct = new hb_list_s + { + items = nativeListInternal, + items_alloc = list.Count, + items_count = list.Count + }; - IntPtr nativeListStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_list_s))); + IntPtr nativeListStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_list_s))); Marshal.StructureToPtr(nativeListStruct, nativeListStructPtr, false); returnList.ListPtr = nativeListStructPtr; @@ -169,12 +168,14 @@ namespace HandBrake.Interop Marshal.WriteIntPtr(nativeListInternal, i * intSize, itemPtr); } - hb_list_s nativeListStruct = new hb_list_s(); - nativeListStruct.items = nativeListInternal; - nativeListStruct.items_alloc = list.Count; - nativeListStruct.items_count = list.Count; + hb_list_s nativeListStruct = new hb_list_s + { + items = nativeListInternal, + items_alloc = list.Count, + items_count = list.Count + }; - IntPtr nativeListStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_list_s))); + IntPtr nativeListStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_list_s))); Marshal.StructureToPtr(nativeListStruct, nativeListStructPtr, false); returnList.ListPtr = nativeListStructPtr; diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/LanguageCodes.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/LanguageCodes.cs index 373dcb816..7fbb7be1f 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/LanguageCodes.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/LanguageCodes.cs @@ -10,6 +10,7 @@ namespace HandBrake.Interop { using System.Collections.Generic; + using System.Linq; /// /// Contains utilities for converting language codes. @@ -232,14 +233,7 @@ namespace HandBrake.Interop { get { - List languages = new List(); - - foreach (string languageCode in LanguageMap.Keys) - { - languages.Add(new Language(languageCode)); - } - - return languages; + return LanguageMap.Keys.Select(languageCode => new Language(languageCode)).ToList(); } } diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/MarshalingConstants.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/MarshalingConstants.cs index 82948f05a..05261251a 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/MarshalingConstants.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/MarshalingConstants.cs @@ -9,7 +9,10 @@ namespace HandBrake.Interop { - public static class MarshalingConstants + /// + /// The marshaling constants. + /// + public static class MarshalingConstants { #if X64 public const int JobPaddingBytes = 49264; diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/BitrateLimits.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/BitrateLimits.cs index 8e6a49582..ed9905960 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/BitrateLimits.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/BitrateLimits.cs @@ -1,15 +1,27 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. -// +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// Defines the BitrateLimits type. +// // -------------------------------------------------------------------------------------------------------------------- namespace HandBrake.Interop.Model -{ - public class BitrateLimits - { - public int Low { get; set; } - - public int High { get; set; } +{ + /// + /// The bitrate limits. + /// + public class BitrateLimits + { + /// + /// Gets or sets the low. + /// + public int Low { get; set; } + + /// + /// Gets or sets the high. + /// + public int High { get; set; } } } diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/EncodeJob.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/EncodeJob.cs index 80c28c383..9b6ed825e 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/EncodeJob.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/EncodeJob.cs @@ -9,84 +9,162 @@ namespace HandBrake.Interop.Model { - using System; - using System.Collections.Generic; - using System.Xml.Serialization; - - using HandBrake.Interop.Model.Encoding; - - public class EncodeJob - { - public SourceType SourceType { get; set; } - public string SourcePath { get; set; } - - /// - /// Gets or sets the 1-based index of the title to encode. - /// - public int Title { get; set; } - - /// - /// Gets or sets the angle to encode. 0 for default, 1+ for specified angle. - /// - public int Angle { get; set; } - - public VideoRangeType RangeType { get; set; } - public int ChapterStart { get; set; } - public int ChapterEnd { get; set; } - - public double SecondsStart { get; set; } - public double SecondsEnd { get; set; } - - public int FramesStart { get; set; } - public int FramesEnd { get; set; } - - /// - /// Gets or sets the list of chosen audio tracks (1-based) - /// - public List ChosenAudioTracks { get; set; } - public Subtitles Subtitles { get; set; } - public bool UseDefaultChapterNames { get; set; } - public List CustomChapterNames { get; set; } - - public string OutputPath { get; set; } - - public EncodingProfile EncodingProfile { get; set; } - - // The length of video to encode. - [XmlIgnore] - public TimeSpan Length { get; set; } - - [XmlElement("Length")] - public string XmlLength - { - get { return this.Length.ToString(); } - set { this.Length = TimeSpan.Parse(value); } - } - - public EncodeJob Clone() - { - EncodeJob clone = new EncodeJob - { - SourceType = this.SourceType, - SourcePath = this.SourcePath, - Title = this.Title, - Angle = this.Angle, - RangeType = this.RangeType, - ChapterStart = this.ChapterStart, - ChapterEnd = this.ChapterEnd, - SecondsStart = this.SecondsStart, - SecondsEnd = this.SecondsEnd, - FramesStart = this.FramesStart, - FramesEnd = this.FramesEnd, - ChosenAudioTracks = new List(this.ChosenAudioTracks), - Subtitles = this.Subtitles, - UseDefaultChapterNames = this.UseDefaultChapterNames, - OutputPath = this.OutputPath, - EncodingProfile = this.EncodingProfile, - Length = this.Length - }; - - return clone; - } - } -} + using System; + using System.Collections.Generic; + using System.Xml.Serialization; + + using HandBrake.Interop.Model.Encoding; + + /// + /// The encode job. + /// + public class EncodeJob + { + #region Properties + + /// + /// Gets or sets the angle to encode. 0 for default, 1+ for specified angle. + /// + public int Angle { get; set; } + + /// + /// Gets or sets the chapter end. + /// + public int ChapterEnd { get; set; } + + /// + /// Gets or sets the chapter start. + /// + public int ChapterStart { get; set; } + + /// + /// Gets or sets the list of chosen audio tracks (1-based) + /// + public List ChosenAudioTracks { get; set; } + + /// + /// Gets or sets the custom chapter names. + /// + public List CustomChapterNames { get; set; } + + /// + /// Gets or sets the encoding profile. + /// + public EncodingProfile EncodingProfile { get; set; } + + /// + /// Gets or sets the frames end. + /// + public int FramesEnd { get; set; } + + /// + /// Gets or sets the frames start. + /// + public int FramesStart { get; set; } + + /// + /// Gets or sets the length. The length of video to encode. + /// + [XmlIgnore] + public TimeSpan Length { get; set; } + + /// + /// Gets or sets the output path. + /// + public string OutputPath { get; set; } + + /// + /// Gets or sets the range type. + /// + public VideoRangeType RangeType { get; set; } + + /// + /// Gets or sets the seconds end. + /// + public double SecondsEnd { get; set; } + + /// + /// Gets or sets the seconds start. + /// + public double SecondsStart { get; set; } + + /// + /// Gets or sets the source path. + /// + public string SourcePath { get; set; } + + /// + /// Gets or sets the source type. + /// + public SourceType SourceType { get; set; } + + /// + /// Gets or sets the subtitles. + /// + public Subtitles Subtitles { get; set; } + + /// + /// Gets or sets the 1-based index of the title to encode. + /// + public int Title { get; set; } + + /// + /// Gets or sets a value indicating whether use default chapter names. + /// + public bool UseDefaultChapterNames { get; set; } + + /// + /// Gets or sets the xml length. + /// + [XmlElement("Length")] + public string XmlLength + { + get + { + return this.Length.ToString(); + } + set + { + this.Length = TimeSpan.Parse(value); + } + } + + #endregion + + #region Public Methods + + /// + /// The clone. + /// + /// + /// The . + /// + public EncodeJob Clone() + { + var clone = new EncodeJob + { + SourceType = this.SourceType, + SourcePath = this.SourcePath, + Title = this.Title, + Angle = this.Angle, + RangeType = this.RangeType, + ChapterStart = this.ChapterStart, + ChapterEnd = this.ChapterEnd, + SecondsStart = this.SecondsStart, + SecondsEnd = this.SecondsEnd, + FramesStart = this.FramesStart, + FramesEnd = this.FramesEnd, + ChosenAudioTracks = new List(this.ChosenAudioTracks), + Subtitles = this.Subtitles, + UseDefaultChapterNames = this.UseDefaultChapterNames, + OutputPath = this.OutputPath, + EncodingProfile = this.EncodingProfile, + Length = this.Length + }; + + return clone; + } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Encoders.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Encoders.cs index a5406a70a..9f877cd9b 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Encoders.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Encoders.cs @@ -2,6 +2,9 @@ // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // +// +// The encoders. +// // -------------------------------------------------------------------------------------------------------------------- namespace HandBrake.Interop.Model @@ -9,17 +12,35 @@ namespace HandBrake.Interop.Model using System; using System.Collections.Generic; using System.Linq; - using System.Runtime.InteropServices; + using HandBrake.Interop.HbLib; using HandBrake.Interop.Model.Encoding; using HandBrake.Interop.SourceData; - public static class Encoders + /// + /// The encoders. + /// + public static class Encoders { - private static List audioEncoders; - private static List videoEncoders; - private static List mixdowns; - private static List audioBitrates; + /// + /// The audio encoders. + /// + private static List audioEncoders; + + /// + /// The video encoders. + /// + private static List videoEncoders; + + /// + /// The mixdowns. + /// + private static List mixdowns; + + /// + /// The audio bitrates. + /// + private static List audioBitrates; /// /// Gets a list of supported audio encoders. diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/RangeLimits.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/RangeLimits.cs index 6055e8eed..2c30b2b44 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/RangeLimits.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/RangeLimits.cs @@ -1,16 +1,41 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace HandBrake.Interop.Model -{ - public class RangeLimits - { - public float Low { get; set; } - public float High { get; set; } - public float Granularity { get; set; } - public bool Ascending { get; set; } - } -} +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The range limits. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrake.Interop.Model +{ + /// + /// The range limits. + /// + public class RangeLimits + { + #region Properties + + /// + /// Gets or sets a value indicating whether ascending. + /// + public bool Ascending { get; set; } + + /// + /// Gets or sets the granularity. + /// + public float Granularity { get; set; } + + /// + /// Gets or sets the high. + /// + public float High { get; set; } + + /// + /// Gets or sets the low. + /// + public float Low { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Size.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Size.cs index a06894cc5..dcc58e0e3 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Size.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Size.cs @@ -9,15 +9,42 @@ namespace HandBrake.Interop.Model { - public class Size - { - public Size(int width, int height) - { - this.Width = width; - this.Height = height; - } + /// + /// The size. + /// + public class Size + { + #region Constructors and Destructors - public int Width { get; set; } - public int Height { get; set; } - } -} + /// + /// Initializes a new instance of the class. + /// + /// + /// The width. + /// + /// + /// The height. + /// + public Size(int width, int height) + { + this.Width = width; + this.Height = height; + } + + #endregion + + #region Properties + + /// + /// Gets or sets the height. + /// + public int Height { get; set; } + + /// + /// Gets or sets the width. + /// + public int Width { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceSubtitle.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceSubtitle.cs index 13344dd58..2912dec1e 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceSubtitle.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceSubtitle.cs @@ -9,25 +9,54 @@ namespace HandBrake.Interop.Model { - public class SourceSubtitle - { - /// - /// Gets or sets the 1-based subtitle track number. 0 means foreign audio search. - /// - public int TrackNumber { get; set; } - public bool Default { get; set; } - public bool Forced { get; set; } - public bool BurnedIn { get; set; } - - public SourceSubtitle Clone() - { - return new SourceSubtitle - { - TrackNumber = this.TrackNumber, - Default = this.Default, - Forced = this.Forced, - BurnedIn = this.BurnedIn - }; - } - } -} + /// + /// The source subtitle. + /// + public class SourceSubtitle + { + #region Properties + + /// + /// Gets or sets a value indicating whether burned in. + /// + public bool BurnedIn { get; set; } + + /// + /// Gets or sets a value indicating whether default. + /// + public bool Default { get; set; } + + /// + /// Gets or sets a value indicating whether forced. + /// + public bool Forced { get; set; } + + /// + /// Gets or sets the 1-based subtitle track number. 0 means foreign audio search. + /// + public int TrackNumber { get; set; } + + #endregion + + #region Public Methods + + /// + /// The clone. + /// + /// + /// The . + /// + public SourceSubtitle Clone() + { + return new SourceSubtitle + { + TrackNumber = this.TrackNumber, + Default = this.Default, + Forced = this.Forced, + BurnedIn = this.BurnedIn + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceType.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceType.cs index 9ea7a07af..e5a49bcd1 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceType.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SourceType.cs @@ -9,11 +9,29 @@ namespace HandBrake.Interop.Model { - public enum SourceType - { - None = 0, - File, - VideoFolder, - Dvd - } + /// + /// The source type. + /// + public enum SourceType + { + /// + /// The none. + /// + None = 0, + + /// + /// The file. + /// + File, + + /// + /// The video folder. + /// + VideoFolder, + + /// + /// The dvd. + /// + Dvd + } } \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SrtSubtitle.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SrtSubtitle.cs index 945cc236a..46cc45f57 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SrtSubtitle.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/SrtSubtitle.cs @@ -9,24 +9,60 @@ namespace HandBrake.Interop.Model { - public class SrtSubtitle - { - public bool Default { get; set; } - public string FileName { get; set; } - public string LanguageCode { get; set; } - public string CharacterCode { get; set; } - public int Offset { get; set; } - - public SrtSubtitle Clone() - { - return new SrtSubtitle - { - Default = this.Default, - FileName = this.FileName, - LanguageCode = this.LanguageCode, - CharacterCode = this.CharacterCode, - Offset = this.Offset - }; - } - } -} + /// + /// The srt subtitle. + /// + public class SrtSubtitle + { + #region Properties + + /// + /// Gets or sets the character code. + /// + public string CharacterCode { get; set; } + + /// + /// Gets or sets a value indicating whether default. + /// + public bool Default { get; set; } + + /// + /// Gets or sets the file name. + /// + public string FileName { get; set; } + + /// + /// Gets or sets the language code. + /// + public string LanguageCode { get; set; } + + /// + /// Gets or sets the offset. + /// + public int Offset { get; set; } + + #endregion + + #region Public Methods + + /// + /// The clone. + /// + /// + /// The . + /// + public SrtSubtitle Clone() + { + return new SrtSubtitle + { + Default = this.Default, + FileName = this.FileName, + LanguageCode = this.LanguageCode, + CharacterCode = this.CharacterCode, + Offset = this.Offset + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Subtitles.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Subtitles.cs index 4289375c0..843d0d442 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Subtitles.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/Subtitles.cs @@ -11,9 +11,23 @@ namespace HandBrake.Interop.Model { using System.Collections.Generic; + /// + /// The subtitles. + /// public class Subtitles - { - public List SrtSubtitles { get; set; } - public List SourceSubtitles { get; set; } - } -} + { + #region Properties + + /// + /// Gets or sets the source subtitles. + /// + public List SourceSubtitles { get; set; } + + /// + /// Gets or sets the srt subtitles. + /// + public List SrtSubtitles { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/VideoRangeType.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/VideoRangeType.cs index 2a42928a5..a99005e77 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Model/VideoRangeType.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Model/VideoRangeType.cs @@ -11,15 +11,27 @@ namespace HandBrake.Interop.Model { using System.ComponentModel.DataAnnotations; + /// + /// The video range type. + /// public enum VideoRangeType - { - [Display(Name = "Chapters")] - Chapters, + { + /// + /// The chapters. + /// + [Display(Name = "Chapters")] + Chapters, - [Display(Name = "Seconds")] - Seconds, + /// + /// The seconds. + /// + [Display(Name = "Seconds")] + Seconds, - [Display(Name = "Frames")] - Frames - } -} + /// + /// The frames. + /// + [Display(Name = "Frames")] + Frames + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioCodec.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioCodec.cs index ad797c53d..dce947311 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioCodec.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioCodec.cs @@ -9,21 +9,45 @@ namespace HandBrake.Interop.SourceData { - // Only contains 2 real codecs at the moment as those are what we care about. More will be added later. - public enum AudioCodec - { - Ac3, + /// + /// The audio codec. + /// Only contains 2 real codecs at the moment as those are what we care about. More will be added later. + /// + public enum AudioCodec + { + /// + /// The ac 3. + /// + Ac3, - Dts, + /// + /// The dts. + /// + Dts, - DtsHD, + /// + /// The dts hd. + /// + DtsHD, - Mp3, + /// + /// The mp 3. + /// + Mp3, - Aac, + /// + /// The aac. + /// + Aac, - Other, + /// + /// The other. + /// + Other, + /// + /// The flac. + /// Flac - } -} + } +} \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioTrack.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioTrack.cs index 2e78951da..e20ff3003 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioTrack.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/AudioTrack.cs @@ -90,16 +90,23 @@ namespace HandBrake.Interop.SourceData return this.GetDisplayString(true); } - private string GetDisplayString(bool includeTrackNumber) - { - if (includeTrackNumber) + /// + /// The get display string. + /// + /// + /// The include track number. + /// + /// + /// The . + /// + private string GetDisplayString(bool includeTrackNumber) + { + if (includeTrackNumber) { return this.TrackNumber + " " + this.Description; } - else - { - return this.Description; - } - } + + return this.Description; + } } } \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Chapter.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Chapter.cs index 49d2e2b85..efec79d75 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Chapter.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Chapter.cs @@ -10,6 +10,7 @@ namespace HandBrake.Interop.SourceData { using System; + using System.Globalization; /// /// An object representing a Chapter aosciated with a Title, in a DVD @@ -32,7 +33,7 @@ namespace HandBrake.Interop.SourceData /// A string formatted as: {chapter #} public override string ToString() { - return this.ChapterNumber.ToString(); + return this.ChapterNumber.ToString(CultureInfo.InvariantCulture); } } } \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/InputType.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/InputType.cs index a00e084bb..aec194533 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/InputType.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/InputType.cs @@ -11,6 +11,9 @@ namespace HandBrake.Interop.SourceData { using System.ComponentModel.DataAnnotations; + /// + /// The input type. + /// public enum InputType { [Display(Name = "File")] diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Subtitle.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Subtitle.cs index a8ddd8b2f..570bc174e 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Subtitle.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Subtitle.cs @@ -48,7 +48,10 @@ namespace HandBrake.Interop.SourceData return string.Format("{0} {1} ({2})", this.TrackNumber, this.Language, this.SubtitleSource); } - public string Display + /// + /// Gets the display. + /// + public string Display { get { diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleSource.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleSource.cs index f0a0b0a38..e169e8882 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleSource.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleSource.cs @@ -9,7 +9,10 @@ namespace HandBrake.Interop.SourceData { - public enum SubtitleSource + /// + /// The subtitle source. + /// + public enum SubtitleSource { VobSub, SRT, diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleType.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleType.cs index e419c4451..422f9001d 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleType.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/SubtitleType.cs @@ -9,7 +9,10 @@ namespace HandBrake.Interop.SourceData { - public enum SubtitleType + /// + /// The subtitle type. + /// + public enum SubtitleType { Picture, Text diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Title.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Title.cs index fbe8e8532..57201e847 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Title.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/SourceData/Title.cs @@ -140,7 +140,7 @@ namespace HandBrake.Interop.SourceData { get { - return (int)Math.Ceiling(((double)this.Duration.TotalSeconds) * this.Framerate); + return (int)Math.Ceiling(this.Duration.TotalSeconds * this.Framerate); } } diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/Utilities.cs b/win/CS/HandBrake.Interop/HandBrakeInterop/Utilities.cs index 084005d68..11246230c 100644 --- a/win/CS/HandBrake.Interop/HandBrakeInterop/Utilities.cs +++ b/win/CS/HandBrake.Interop/HandBrakeInterop/Utilities.cs @@ -11,6 +11,9 @@ namespace HandBrake.Interop { using HandBrake.Interop.Model.Encoding; + /// + /// The utilities. + /// public static class Utilities { /// @@ -23,6 +26,7 @@ namespace HandBrake.Interop /// The b. /// /// + /// The greatest common factor /// public static int GreatestCommonFactor(int a, int b) { @@ -40,10 +44,8 @@ namespace HandBrake.Interop { return GreatestCommonFactor(a % b, b); } - else - { - return GreatestCommonFactor(a, b % a); - } + + return GreatestCommonFactor(a, b % a); } /// diff --git a/win/CS/HandBrake.Interop/HandBrakeInterop/libgcc_s_sjlj-1.dll b/win/CS/HandBrake.Interop/HandBrakeInterop/libgcc_s_sjlj-1.dll deleted file mode 100644 index b86e5dfe90ed8267b2ba2595ca568c546f74a726..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 241655 zcmeEv4M0>?+W)=700WNAsA!m)SmbIS#-OGnnkY`@2W9GLrG$usQX{Z1qvUE~1Lk$& zUHaU%wqE<%w{~mSZCkUpHV_j~GZfcmM2r+|Xjmh2fy_7lzvrBL=L4{8&EoCP=;5Ao zp67f&&vTx0&U5ZfzOzIK5d=XE*WNA&+wi1cBK!O8e+mG;Wa!(M2yaAuFl?JR`-5Tg zS7heJ8~ShRD4xG?rWq!m{y+t^BvR?^WgFx#& zYSu16h~eod0j0-}JvIx%7?dS!5rll|fOGvyQ9qRPL;ptEEcrHM8oL;Os6y0MUnAhh zZ;3Ex$*5&%wlw&!rHC9js>@Oe_ju=I4DS)HAmca%55``s+qn767QPdLp zQ$l|_{|YES3(t@B(PrR8!mS9#MY)NiGC3aWL-ZvpH@6cW>SwWLr^|g=D&x`E2KRm>ER0Df_{3{^`pnK7|j$=Ni)1t@q8=@ z2?5}Z8OI_Q&%z@Soje=QgTM{?0q+I_i}`IL9woq=isu!02K|6HZZwM-aVZ}4fJfuG ztRr5W>iY3FFuW^RQ5ZZzS=$j0`5AfrqSFA^18*sw_jQ41h#OBCCicdk0^nVOCzT)c z)B7UK(s4&qw9}-im=>#1mo!=NR10`Y>R8XmcM3vPdGS_CTl~&1=<%)ieetv zyrlSz;WGX_M^luuKjm4p-!{fMgi3LE)y6}s`#VF5N^M`ZS936nT;-LbVm&L7Q$;AH zp-d{&vGnkyBG$1ZYEeqL)Dl>q@lwiZXNp#`w>j+^Y28`pI=$m-(Uu^sjnzntW7X1_ zSkJLLkhV!%RBP|=tIP_?)Zn?Zy}jLOYH%V8q+fSdYD(Fzr6LQc$kRwxRQdwd#FkWn z7(sY}8sJF>z;hqCj0&MnqXDFX*2o2w+F$j2^G$oZq15{V+<)_K^!yRv3PB5GL~bhK z{rTL89Q019Z34Bl0J*;lENQ!=NnNy`Nn~3;Dxx<))I0yfiqIfI1QJl%_UbBhxotR& zPfB@-|94yQ>{!ht;*Q+@>l^9mh!C!$CyQL|9a)A5sb1RS)HoB>b0{q}Qu(p^X{Euj z&J>L^Sufepn`p3;de+tYiFD9uj?(=(S*!bVjdO6UxJx|cB$Otl5-h!jk~xz#PCMmf zsFf;Pj}%qu9xs;|_Q*r6E+l4&7`~p3r=G^s#KzN%akIrr729G^uaT~zQpwzWU#xUc za@U-c%4&{EZmC*2Rr7g7Ti`I37q1dpU4|B5d`58Z(GuR@ygjTjQ~3+@n|9Sck3ZTDNs%z_fM$T1Ny6T{==l5z82hf zmGlwvZ|BvoIT>*ZRqR+oMoFs+Xk}&XLu<}>kMR8Gp|Va>6dw@d_O-G1UuN8$dpW1C z)t77|LIjKO@HXOfgbbzl(`EYH*wx=Jy)v}aT;AUC#iqj&=u;LWgHa@j56wc(a;3K6 z#inK;pxPdiGl_)2@DCH7X93~qtI9o3z{j%n{V{JNS|y_9?J>X25<_T>aGLavws6~E zM_cIYF}D7WHudXz+a->+kk{3=a7dxoRd!VDv@{>2%F5Qtb)+(FD2W(MZzzRSihz{k z@@gBTL3>qsQK=lZpL|{PzU8Y9Z?6Ut1We~tfdemfY*$Mx$i$c&ZEy7R;{pgky>p1( z3bFTfyX`uVa=+p+&rmp{q#)3PO}EQNsL-lCGC}J^7~6JuqUT@Vol!vt+kGW0=T#3-g=K`zC0g5GEn-W zs$zAY{NBcD-S4&+?zi`$fzwo>RTv0Ya{O;Y^)*v%8T->M^fY7`&*fOTdOwKtZwR1HUIGV+lwkm)gvp5s`9n04yaHh z#)MVQd&+ZdP<^N}sD&v??gFVs+Ivlkevmzi*XRDowQ_w{MR~rMNA+3D>vKQqllxuu ziKfa_wyxAj=F`&lvNJ;y@H92sL!G8(Q2l0Ps5F7~&&^;YFu7j}+M^`XHpx_4);3hy zXWWh{PBOn^A1-|i!C?GGvhT24ou<-OuT+MbAHqB)RgxfNlUcdcEFCHPW~i}QSF{~e zL5RmBlS?w~kt!wg;nrgqaC3vST{6|@-&a(dZ*|%a8`?wc_5Id0NX;nH*rqG`BTKnQ z`cf+MJ`<3I)3k%~u#`I1?GTUyCx1U4!?< zqW$@?7?Vegxi{&j-B#-aOIYN_%X6Xu|Xk*@s5 zQRps~mjikE=})Q#r|r+z6_tVkKzzL+VnELr{?CSbKli+Pn~xtL9@yjv1*Q_>{*Yro zW)+OXWl~Mq*Fz=u$g+VolDn*NXknGTw_}Hz)SrC_B9{y~7lnGjUzm=L6EE(!h3Vee z?kE+5#5L8A6Bk5RKWY10BqxBEB35$!fyCik!w_k2ROSoayCzMFqj@M(E6j%dl!F$t zIeJwsRlXjhd$*LpVzoTK=-vT7L+1QWH7W6dCabUB zEI)nSh2%G_Q+`pk5M6<_bC-UP`rX_iKdd+0CNwz;t#&2r-6?0?Wv(iBN*jgoJnc5= zUAx>V&2H0Kx2X-lPuwO%Hp5ruHaEF<*VTVq|LmH{_9=Bo>Ri5mZtoS$U(5cwy-|3& z>)c)~`x{1oGfLbk9;dkpdPr5ddpEjwcb$jwQrUJ;>GeuTj}Z5V=?WhOlel-fK9wVE z1KoR(axbq%q^{5lGUZY3z3vYT`>FoEoX|==PZ&w`t}A?!aw~Ox;>-SPM6kScMa}T) zHho4BzLvSGkOMM3{&9Vst7V7E7Wy{IE0D~3w+ApK+Wm=5<*uh{Q|IV5jl^Cq#)uuRa_@E}YuvjKe`LFR*Aa|NQ{%gk zalq}WI+`x4qvyRe!9E_<_<#a$qj2HbHR#s^lo_kPVSix5JPgHc@-)8QC5HP0G~WAH z)B`Uhg`4 zj2+77!>6*z4UCZWMO%a$`8Hap;>fk6aZ#5|kjx%xUlD+X`*lUxgtWtTT%oW*?mZZl zJvEh271pe2vsGgr&bVYwagl-f4CF1f9mU-|H{CegmUsxSD5#{+vawA%y zD|!HbeI&Ui#lDCbgfaAZsI~Udepm#;y28}|Md4jh2|5rxJ+(`)8!C<7yX{=*yw1)L zN}_4ZAadwb8i&1!_Cmfon#R|ev~{MaI@91fQ>?oj6P9aN9pu+`S5+OvRyjR92qCh* z&eT?CYOXU^x*F?Dp1PFkI#YdJimT34eb}Th=9=mD8Flny!pu7gWO!KilSpHJbm)Kp zf27}EcbQaOcc-RmyBajY29x!s%C;>~Xk8Y-25Wlh3f}`8y7!gZwuSrd@ns+*J@3vbf_@fo@YTChbD;7sRe|?_%XJdAn&j z^0y6bToPQ&Zs0|_cca79^p^$;((eP3esj3=+jHatchwQF+>tWKzN*H@iAVSHDo_PZ z+O%@*)Fk#%zYpd5?dA0ggE;1F%*F#E(7l`bo7I^qUesTZgMadg2%c{k&zGSzKHLFV z%7N%#nYt;B$3|&9C@^3s2L=o!4a))|Z9a@AQP;hV$t3aoJ@1=I{J8}Pipv6N3-oh0 z)vJoyQ-vYg)wm?Mp1Q(U5W(x(_$<7IRl1_JgtOgsOd+-?d9c_N+}n09<)^75*+C-7 zROz2Z!ETj2mCAM(c*hTcu2U`N$K>7YI+II*YBEZ6g?}e0WpXhbUC~=ia=k}!U@Jy( zyP4j=~;;g17*?;=;b?l5MhUGCD_bsl*3)UG>SyYjH>v)YxXk8I~Md!~Ih2`*V$ z83H8NRelNO9T?#6?e{~z*G`bCYu9Ky3$MQg3a=r7!s`-U;dU^ydoPVQD5Xb!112>Ik!?MjayqDLr5eu23ZD3f1*1IHpxmS3cb={AuR z^UJ3H@8y>UIwo6EV%ZkuE^~iE?ocDGgEl-(vbxOm1u18)?U)~Tx!y-;m+J%9i6dY5 zRz{CA!Tu=zqFB+3I+Iz|snLp_PbK?Aj~=mm&E-KPH` zfw-HZ>JB6Fc@{~3WmRs?c>4s_7`_Y$V-nl#pF~1$htROmoL%KdB3;bIhzko%&=r>u|1->o$=Vsw;Y$^8bLf-yWuO zK1o5+{CGD#M>v}pj=w`5LL{xHSoeV@d5^5^sz(AewCF%Jht^}ZsXPYhcFTRTi}VcM zm)l)?*d9Wen-LWHWf!#QT}O7Ke~ywi-FOy~3q3-LcPZ+n;6PU!lBYnQ1K3{*Iub1LOe*`SP*nZ}>@^gAu z{LJ*VADo|;`1pC*dGIqWUVlh_E@S*$)`6cTBGW*Aet;1wXY)F&lW85GD=J{Ig@o)U zUt)dE&*Wp&Th+b!xz5ZQ+m}%kP!O@SB_$vA>i7)>Q{R9R5GxUEG&L)QA7jc^q zSrRlqUrc@`g8I++nV-+m5oB~(eRM>nv~T??=(~W!X_p1UnAo`wQIO zM#~Isl9@3OjB;?&c@-tYA@*m{>px{_JsRDs-$X}Lmwg@`(JY-YwCJ{ejU(r8$l^x# z>X(`bm$bX=q-OmwOzQ0srvQKPh^FkACgK!9D7sx>k6i4p;n%)#T>cv8z}EMIa>G4 zkx$M6ybe_>P)r)a7gUqUOne5`j5 zj`ci&U{$M&om~6d?%Q9Art?T8eI2EL-=Dq?>9>AIdTO7NzIGkbANQxPofzvm_#Nq~ z{_9$xoAq9cPLk|rK_qXQTBMXVHoT|ZD^GunEv}UMObzsScJebyuJ4oF;N9;EEkTvMJ|&;=8`+yO`xa`FROTpamulA6 zw6_lx)_>jJK3T|(UN!lZ_HgXm>!h9QPY7-8?b4S8AHw1gc7KK{lwE6TcJ+RY`r727 zdL%cYRTw8aJU&gea(wm8s{P)#VGjX&1=wHJnzXLoPa*+|CMz6M^T#WjkCee8DEJqE z4>TaVdjF0UL-{L9{8Xn`$IO1*C5Z$MWt#)0w5#uvC(`p^j~FxSJ5qpxxIs&Udj z=?sh&+FQvYNMo#AHQmr|s48lKP0<-!xGW2TtTCCqfc`4hJ^{dpz%Dk5h?d&iF^hd@=4Xua%ft6IdW1rCKMir$L1X@$- zTMs(S4MJ;5L*r~j$mdI2lMhRWV8&^3G1JRyV5tamP#vOsnO!xE7IOEHq! zE0eA_*4AWa(rFPZ3{whhQkKmikGvV2xP` zBOecs?`lBUdP?r?YWw1Zb$fIh_9HiArS5@UtO9$aJhc?!{aaDHf7Du~3JK&)71-0- z+GuBeU+pj-K+hj&oKC?*Y-G?b1lk)%;t%?)TG}p;Ks5|@O?wcvM{29_5GVrHxsvG} z7?ag}%Hr3RI@j%B!&F^!f?{O6=2rVV(!rWfVQ^+Vm^;;2kvp2wkE|GYP-?FEoMF-c zRYz1v2{7EvQ?s#fChr;f)xe!n@}9VMZ-w_tWAo}k3Jo{d4aTo^8zkz~X59np@iep;+NCyF zRtDIc!wwo;(8AImL+u-wnbbAr1~NY;t2Y;GenT%)Jt%gclyX@5l2I&d?Z7{vdd>xMg5YRv@%|A-9P++G^@ez9tvUKu3{qW# zP;xyR-Ujd`>>piH1)~ehONXT+H78j^sq5+)(KJNVkkuD)FC&)2AQmHt#XxwRSjb$Q z;S;$uM6}g>)_6U7VdNJBziyBp;CAVtZ9CafB9CzIwywLnoP=mgV;UWX>u(nqoi)_eoG1@RAhUWYg=iHL2>IF%fCBM6 z0Sdf5!BoHcR;rd0wJjWu0wMC5MPnr^`4B6{RdZA>EWE~x`7%}ANLHMX_f9Yakt@cf zTuN?eybL)Q57}RHtZWcRHV-l%@IJ4wE_<4p0k?Dk;4j;@uI>K-fAZ7r=T_x291kFPw+@^U0#*E-0|GJkmIY}J%)J#N|rp#?;j6Jc#~4DJzO3Uf61(C{)tOP;67CBW5VByAS;NCf*MTe&#xkcYAjEX@~|Q| zx_Lw6sG-}AlsgN{7mPL_KPgE|_WDE)pubM}eMb({SPp6>2YG!!`feg?z_qLasHvYG z+5M})y9s)RLyc4$Kv7kVzd%F~K3@!YbW|AFPeW956su|UD?|%ArVarTA*7^Jq|bgI z?cw*mD9L*~2@29sK_tJc8sA1F8Xk1N09mptJa*XZYds~DtgYR4sT3w*azNXBw*(p= ziUmvfgDWXBTHOWY$LY;Z*11J3wp(x=Vy3enUBWPw%EVHf#QJKyQa$^i_JfAfiV&h< zZ!O)C!p}2|gV`N2uXnp#jHPYc06>Wpi4oCW+!G;XcMfL^<87K%s+979rMS_6_5!|; zDs(LbHHmOjL$R%|m|)kgo$Sq{ZkYHN=}TvL@tB7t$VrfmGmPINX+Q_IBTe#Q+^3k~ zycF>Rifw~L>5vg7$Km(gP<)qYOwr!=52yOKl!@7;WDp*l0d!8vBfq!wrT2*v$Bt{6 z8FKN&-+DK4`dcV{$ZF{Pr5X~pcna1phfz=I%Y9y49LXOhL0W$VIf>gt5X5|>Jh7mC zK=1w3xz6P{DHgTd7hk;iO-+fjpL06Qu^Xg?Sb-guLXZU4?r3m1at$QGGiAb*hGFh_lkqb>U)D?b#dg!)d+0o!Qs&kytf__$odh(F8J@)Zev9hE5oP8XnxFl%96_WhuH-PSYG z*N$WN*Bo<{g-0AI+@Jrc1Ged>bX!wuS3m6dG|~1DTY=S_h&bh4&-0Kzruy*oQcY0e z&!CKPt*S)#9!gg+t6SC|x;I&P0tS)yPicsw{;U&~P&12M@CH=B#_JR~`fI^Fsoc>N zQ;;9A#-?}dh{4r1x-0btgduu0CxR|vnq%Od77|&)>w^Zg@b>z{1;5gWwj6&L{0e^a zhjm*Qh{g8P1!v~k`+LV1oSAKZ)KM<7&gq9p+f@Z;=GZ;OH>0yK4FiO79rgEnt_Jhy zwi1fH4)$kTabq=xRgj56r1w5f9p$Ibg$tye^Uz~Wvz@96(++9{)QQ4FcvhJ9P!O`u zR81aljKj1;ObF9CXn_dv+ve<7yttp*nNZXYyGz75=MgnDwjL^8+fR*)JB7~@Vw-0N zYDH_T>W!w|&{Lq&c?>W*qjlJuH|eGA8S9U>g=efkg9~+QGs-+6z%iPtZR&zs^y*^V zjy3OrymOik6wlIlZbm|QJ)3cs`!+E@7;1H&V(t4VN?_Vr%~8i^B6Pu`?Ybf|uy*#7 z+I;max}9Q7I~+S^Gf}78YN|$kyiaoaG4IZ}D!i@iXh=kLhIEtOyIvOTzWc`ur3#S1 zZuFOAuGGDmL$xnE8M6KaCUi`Co;y%2w2S5iT_N>fh3Np*%o~UCb+PJQmKK+29NR@{j@lVgyf{QnlMzhzkOC?q=ElaB>3X&8>Q-0r>=3mv`G5_VG^FGm zo?lY`osn=arPOmA`&Q269a8cEl#E&J$zt4tYQ|k%bR6L5HFUj4hnhL@Tn_AS>9`_u zgRbaK>Lh=CigFM}{gqDZpufdnGS$7Aq?ZoKgXfvYYNRbTpT=buRFZ&EvC=68j;o06 z6j^L;rhB~#h~11S^;jsixtZx!M)T=a&>9&LmkccRo|XsXJ?d27{E3S;xOOcyUvrZh zyz7}yO*P||jZ1QQD$bzp=7!>1G*r%Z#DpS$sqLES40l8z?$Zpn9*D)aY8b8?G1zvI z>(a1F8#pbYKFuBL!{`J2G!e;@tBFE_=)*|)^}_CpsZv+igf_@rCapW*z0R3LI)HN? zGe7s`zLbPv2v^xz+l!oO0O(}&Z1pk*pZ618p;T?2ad^Cq@&8svE4+&+ zCb=42#cJ%hg%a$TD7m;o!R4c&HXsUONEyGy;6Us$>9)?%w`>=6g}+Dh97olG{p>$E+o@zF#g z8^{A_G1~N3Y>uM2KALFre8FNGf;ylMaZphbOgmOT6Jlz>Q}^H|%+n-pQ}#68izZ5C z&P2UbUgqtE&FvJhyh()`n@WoZ7tu^k+VJ(4kw~SLZQ*|bSdmCGJ%jX@A%|_@ax4T} zOQK$-E8HtL{7x+qi!Hhsn+YG4ILg(uS>v(M;36Iu3)i`@YS*H~gz0KsQGeu({z7LZ z-qK_xy08yGp#CP@_HCW2hkOl3urwJv(e}edExN+}R8FdzDi3;m4x!@d8pst>wW0Ly zTBu3I`J!=MrL=g1W)=2p7C)d_|20M1W?>r%n<3u8lIt2%by=Ir36zRy8>rRTNM|_1 zoFPnUo%S2L1C48j2PAE*WPYRFyvJ#N16pS}HYLtT6%fz9QdU1LePpP`q+aTcDsj$! zP=h&3n*AG+kw#PHszJ`#8z4jN$9~uF;87Qdp#oGVfuhG`JLk1aYYlIx@(xHOh|SAEb&eg2p(}Ex28?8Q#w%^%_QhD7V?!GuxSq@fTsod_+=kh$KjP|1eHr)k&={f3I);rs^_i#I5TDXEA0;0vG}z| zV8P4PS60{;o;qMZ$JRz*mSXaQZUBp3J+2SNw~wVD3Lp48?c{1IW9$;X#WI{_UCAMM>`5*85~;|y27ahmy-?W z$r?um#PwCO%HKfSF(}x#z2?jAU=$UnZA?QtKFb?w+1i0uo$ta;0aD{wV0ae7*LnYq zc555SoQ(8K^grMKaFPte`w*zs%V|taz-sA-=JSctub=b%LI`+0-%`ScA)OB-X|VB9 zk%YHX2*S=Jy((k6P1A>(H6@IUTTuuraB% zRV%2+Q8Cu<`R|^yvt9uX+XvHP5#4cMe#bJPJ*CH#JRjzHdL13~C%zwr9DG}4)I=sr z`A(Au-Gv1*ThH+Rq%48s+1WP;@y{3(1<~K1e7^+@;Q9P>Fp9htfV-+B>=a5Km@qoG zcVihcHICM8AhKk%USe-nI6_yLLJ82br(R%v?DAX>rgrux7I=#>#C|CH2?-Ncg?TSA zPRNZco*Ctw8wFvF7XnD2>w&hvN)^ujtgrXm?U==9LZhRDX%(q~Q`JniLhONO)R23+ zA~))x+uDpR@n+8{RG#!WMNw_`#5JRMS}SR#sn`xZE#D}olL*gaEhIst3SHqubc#}_ z&8ZBA8a>AuYa`v}Ip-kn=)*bURv-~QE9teq^o83nJe|j%rDKdq*1}4%Viv7t?BUbL zpF}$v`=bDyWYz(>1AQ=lz@}dcbYxqYQ;!af;M=9BkJGGh$vpHVxI`pbmGLA@b3eIUInl8>0 zXfv6=wkE_mb%gR_7C(OxJXudB34%1c1g~P~Fn)kj=80H)iREYyObsefCGe6j<1p-6 z0g5eD2B-Qs1uW^YM)qarTaPRG4QBi0^!|y!_j7&UFqhAGAInr4gWh<1j~aajP8Zla zu6X}@dZYVj3p)0NSa3THA~k?yc`v;14AT!gjd?+vL%WWiPdmp2te;8VDf^L8j!Al? z5%5}{Nt9TiSLRfNGA{~XyOU9f_xz4!IoB2~C$N8rT2dW6n*f>P^?2qIA%;?JRC({8 zee!al^bzgnPNa3F$~zfKh5?pwQP?h69IJQsa#X6Ei_}CIa=RtG!3!X5`+j=e(Ktt& z#(o`ihZ~&X7~pWj{-V;iLQ#S()Hx?Q4#qJJ2}yd^{^qh1Dy&?dWqX)d_OW%KCmwHx z!$yLiUa=Ui2JNINC#Xh0jpZbq64GM>|8H^ZRmuDuCp~ zUTVr-8GG{qG+wH(t)S-Lh5C$tjs^uQ7!y^?jLv?JNC7>`&JOl~IoUyxu4pn~o(6@PE^9Or!k5tHSeD*DzXvTJT96{YW%lvWJ2szcpusU@XFO^!2_ouJ zX)Noge#KK`oHwhTcgHxFXxcw1p`CSJG0^5XrBe2fJoBhRuxI(ebFZ)DH|70t-Zpmj z4yzp65Jo8$lrr`?Dg=a6jAMQ}ZKao+B#~-@H3epwQII@DB&OgE-$~Rno=XWt?xcwde?vhfIH&$l~Y-3no$r zKrcwWjMa`d8X7P-wwp0X-Vhq5#At}JIEBP<1T#xb^i6U>;chehfBE|s;2|9=7S8r?3RbD3O~tmfFFZ*DJ7jVM_D7Z^>%9 zCqb#A_Zg}mZ_WbHmtWqp(=$F^>o8vVzd~K6`#Gt>+uQMVsO>6J-2{vsUL6O<8dyvF z`Nu*@5VwDX@vHqi^$Upwc|(oqzs|9Yw`i*g_)@uN5qvZp>=deJ^we@`Gm)b zr6liCQYZL%SeN`x^q$0ONo#*LZqT#_Q1{`*Y@WX&A7b?Zo;2(*D6!X}H)vQtJq=zzh2Hr>&_7>v3}gO(VK8JdIxXdeXn5Gc zs)FBf{-^m})83yF(4iWmCxj^-3p47InQX9Q9nNU5-#eI*BaLs1Nvoo_U=3!+betnh zgK6T=h4K&H+i5%MG!4dh^}>VV??$|C>*bD}VgcU1w}dRpvA0TRce=uKu%Pb0F2P?% zTcWOT8S~7sU4|1qU6BSMIggu=2d5_-(b@VrKGiwewYmpSfFRmq->t`4oMUH*-J87jmO1wJ&Oe(@I@o1U#Oz$Xd5G4CPT<8V4XQAQ(=#JJo1k<5`-5cB&dr zv!|x9iB*n`gdSxy{@ow0MimP_uR(>98qLMtxp{q_0S10?c@`Ixj@J z8GEI+aHHNHBHga_z35V*H>N+5)<|Q&6SZbqfDQH}Sc$HJeZwKd!Q;p0=ZSh!h}+bw zZ^VXTDNFnn_#JeVCE-v}&86IQY^RQi4eV%7FDhaO4nAndB%>q*sY9q}k`^TOX+P7Z z4{1F#?4WehS(c6MEoe*-78ozmlssF2>dV6O2tq;igMNwN`_4w3qZ-itkS)Lz(oXBVX93Vz~?jiBk!Ucu0(7pxl4)x7%X< z%#I1M1jRd?Nvtq?&Ccsl0&rYwChc~*Y5$xn~`JS{()FKZHE+~1t5ryyxhi$8WqD?v&61mBP0P!(Kv0{4p!E&<^{_;=F$&kTWmdk1a8XD-;c zbm)^b8g!8j)dZVScZIR~zJ0n)+c zn33lwgcDHjs{xM(?h3?ZAU~vS9*8=?Eg6q^xI`oT_=HM5`~sev5iX@C!u1o7ADkX} zmB1xZp5$K!hk7lUgt`nvKJe!ZM%<-{gA<11iMRrkGl$$D;J{U*o+WVA@E1@V(kxko zJjmUNa&AZ5LX?B2fPAV4puT9Melp^w;F%~02hisy;J$$y{GuRS2R9LJ9$Xq+Hr#r+ z=is)%eF#?v=Y

Nf3s?jf0y7cQ@Pva1X&f0k;|M4{&e5{R6HF?f~3TI4|63xc`L< z`#skDa96-hfV&+o4eoBZJh-)R55xTdt_*G$Tn$_u+^2A-;l%&JCuraX!Hs|$3pW{V z7TiL(bhux_-49m`_X6B&a9iQtgR6ww2UiPs9PT7sE1dYUAoPN}6z)nm1Keb|8F2I9 zQsA=S$S8I*+&geb;QkHQ4j1u?AoPP90(UhWQNBtD5!6Dc5GI5R8X*Fo9_ob;we-d( zbaeO#MIWKB&`*dG`h#>Y#piqm3YQ6kgu%iP;c{W95F-o|t`M#iVuh2m{-X3Pl=&B)5g4vd+dmbKiT zw%jr=$C93zk(o}J2Es`-$(Od=B8!lbfj{itXyAKscB2|RxQhn z7gCuokNcJ_L)>VJgD;Q!@>W>oxV#m)+_yAuby^Nj%)C5xy48}Fo0^`Mz5>-sO}DP$ z81TsOm2#SNn*y@Qpk)@NW~mwYiW{Zx7?qmI2CnP))TN+-JW7?hYFRopfIO`J@Z>2m z`OITkX}4MPQkPq7)XZE9zD<^zDo6UNk)FMBCDCfCB`a;I)skw-x1`%`mef2O=o`@) z8F{>U@bKnkWacl+Se8k!%=|o`&z_a%k7j{983%zNw7iS}NPb`j`96peZOu-zEz4Y% zL8WnjUVuN}=eK7CVo}JCM`Uc7A|LM~d%D6E zb|1--kM;Q&lxCNEpJt1n%P(<@za?>h6@(oKmjuLF5f}9rKTb#AG{EM zobvmr>o8nM!pv#8IW21$sPc9pDc6!_v&^%sOv_o3ooj(ccezMYrad>;l4YB}0^nsc zmI;E1VrQr2*-W{)*}2$*rEqfgGP_mI3n{G>HzzkceTpm}g=TR^7S`wZn&Lf{Wz#I_ zxt5j471~x{{ERGL{3;bDM+=0quR_rHRG1P2O8#6m&uX#c2w$t_Tl1z^*_Rs6lK-Y$ zi$(Zf@}U|F(yVri@C}8KoQEzCS&*4)v!_{6N1G)b^%$na+-A4r-V5z2WMNvS4fE|h z=oVJXtxNAFG~y?0Ib>&9@-uD1Rw31ro12v_2-8(*OS3V*&r_wR@p>#(Wn@_Gc`KkN zsxqiL!i%bvGVBkkm6nz1IVj~-^5t1j{@W__FH5>lm1oPfTCy0O5KaF8ZkB28EvDI{ zrR9ai+M;xV*THanHoH#QnlB#_*uw z5yNYSa>Mws)5qRA_U^H3#ukr#b!_F>17lyD@TUoXpYZ;KvlGe_`b|uq*#AbfBZhzG zvft>Mahi#JC*E?&%cEZ({nglBpcf$>A*(h2WO z_}7Fh6LJ&&m~bQkh>1L(#PKV}=Z}A2{Nv-djjtTP%?Kq}_#MED3@Z$)4gYP}YB*$g zZ0z%6|1`E@Z1vcX8?L@#`VDC}*l&2^hC9YBMH#EdZ5;RTxZjL>eq8yu`f)Ae9OEAz z|Hk;8<7>vd#)la5jSl0>#&To5@r*Hl!ukoDCOk3We3kLlZF#a4>%$I~~2EASjb|7J1ji?7QnHcHnJ<-GohfCJh0RaN*Lf(!URq;N>p?e1tD|rG77BNI1L%aQ1#88Hbl&22F^==M3zs{u`z#@B+Z6 zcFKR)Oa)#IIJO6UKc0WXYz5v7IK9GA`Ej^vo&t}#tjVfXfOvBF0dyI!UmV~=I>8s* zp}^+=9?=OtVzB~W0(fjE_>!dxyae#XPVgLPMZElKz|mR0AFrPS+7pMz3~I7syVUpN z@C2N;bNC#ly-Y5Da98PHfCk6mIe?dPFDr%D zukShq9yhGXx}y`kpg@5O!<(#cbb_aW<#=T2)lJszkY>t{=l`&zz)MDT1uuI@fiH>g z3T}Bsfydp@6@6azxB_1S`0td$@#N)y_!|W-jBB!DYuNYW@F$*9;7huMr#z#;OS^=} zZdTwi}!bC>W>{-D6?0mnAC@5l2${h9(#G{H)ulz)QP?U%5wtHv@jR=&vBJ|EPTme95FHD}6sp`EmHzg9=>=u1qfJP zzc>4eGX0b+YO?-Xsnmsqe?(Z(pszO920Or~2&~=Y!)cuXjNGd>Qq4 zy6gd3ZmAfAbi5_-rpjyb68N!=LCbRyplMcXa$06q(%kuM1K!RDX%I_J>R&c+KBDBQ z*lVO6N&0axX@7}6u)G6FU_Q8{{QVx_Z&+$&uhS1Fj`@M;zck<)`JL`lqlHrj4_ZIS)2X~Hr zYC^t-A(JvV0Eo+x;c|;D2PYnRnQJV9gz(B7Wg7*kCWcPBp%du2to&u!smra|OVg~W z%WT=XdBR^h#HO=Y9AwczV7!op($AzsmI8eROG1YZw1ex1lM-1T1gHI8Kb(}v{eHN7 zEa8Wf5*Z9v$bgSCDUp7-vhV6oPfBDkJUBfmksqV{RjmHX-nKtIDUm}!i9RZ1JG_24 zDUt8{>aXm2`{ATS2EQ!l85S)h1YGv>`tO%>>ZRch+AfEdb^ zQy;0G)T@~cM?RwPgGuvTiP1WtfR!?NpWQY`U_<-}1LOQA6+76JTF#vmQ|hzT3S1dd zUO4Hc4V%@@Gz3X+UazR7Buma4O*ssW_%Z#=LIIserG7F${?` zS3PQw&uX9`DYfJ{q`i{aaWIZj)yr8vq`^`*^KSGXP5_AWSE>%_u*BxPt^T77!r+}r zT5K)y;0%zH&5tYmaMEIf;dHR!hm#f?45vd5`tj~0EtbTfk3i`>#t$YXHY*TJ2RMFk zK>?=(UurtA@qVA5cNz`>c50viNYG9Q90R08R*fAjT>?;w$~=Ln?0 zc0f?Z2ci!-kOoWU)0_}-?8c7NaEeAhjzUUoN0eoBoaRR%J+>nXJ8>fvK8UfKIY04! zP|o-Ka8hN7-+XX(|t@G8ne9$eCMCnFCo zBLWWh2*oW@Hl*sdL177FjaZJe`&1ZHjE;v|17#@HmU}Oqya?+$f^%^wotu}r$|57$ zvlw1=M?9X54DE-yq^1TG(lHmMxTn7hkrj5jBNi`=!N#KJzqP2$tV~;KhGP3s*b$v) z6^vBXHPSK*&Rw$a6*OTTip2R$o^0D8+|dyTRmcbYM@Qg2mV04tK*lRVdsi@=c0pGa zoEW&EMrLQh`h|X2U|?`*ow9V8AdCh{fkHc&Ip@=`RRt zuv$|}#UUU}dP;+1$P}n8APQ%LK)N|1C>iW;?&}!C?0;$kVv!Pf{jfd_D6mVbphdwr za|0}co(ceUZP)a100vqVXuC8NYmtDmgG`G8KppL<+&X`<&gMmL={rCJtbeq9I>4wY zWPdF@(Fqc4nDkQLPANN@99jGMQT)bWPAt!rHU`)#v7vtve^ml4$LB(n?nDUel$B-! zuVkdzt+rGqOT3-qWq}5oQBq`P!9d4qWhwFm3kN`);_y2)4F+bAlsQ6iP;`K}+TR60 zE`usM0!}5+wm{$BSf&yUrFLaIL%7ozE^QG^1l~p_v(8~-q-FA>3#%l`=V$r*C*~)N zG=6Gv`Yq;|q?j?I#*B)KiI0mj#>E@P#f-4z!yqY>Yz)$@BLxwM_Bg~gj7k@-Uuw^^ z!W=1=d#;~qo-upc^(%48zdF};-RMz!8mIQT5ggP9plKYM|P957(X3tr0>x^ka=z<}j63xw1ZkdMK zhCW6il}5cR+fIg9nw(U=Q^hML{$w95lWh@{a_~J2Mp!_H2_iTk&tfi!YY}lTu?pr9zvohJc$fd z!YF{%q8olXxb1_((@~?Y=l{9oFa;nuuTRg;%E(-Pedg%#H(Z~7?{cWTqtfzLo`c7c z;5sHIu47_iw81EgfXo}l-*8<{y2AC>^D+byEK}yi#E&u?>m+oL zTPLA&j!N_F_hk#4l-bsCBZWAwMLIT9!0ylD$%` zRwNAvYt)LQ;czX&T+;Bc9?5-@hDSvq3q{iKm}qsNq~RG47DCngKmlR0q#vbNo-pZe$DcYq(EQ@g8=waa~B=#jM}MZlK*R*-bu75@#}Q zQwT~A^kE=+;OQaG}Xa~4rRq>M3Imb%3BOS?SvAeJ;NXy5p&5>3ek=;?M*Yj zdtM|o{nC|!_<~4hN@@y%4__3iCRZTTCmMz8($jKtGjV@Nd`)~1kzh-L_odAG5+}1+ z?l*w=XF1Zs!KHKrtP*~y?rvO47K^iCRd_wsuSco;$!58qKbI<|5#n2%OBKqS3Cb&i zBQAZ5ap@`wflJ?FTzVWK#-;xd32hKqNJpIdp129Yc@zYvmNHHy{?ZQtIbR3Y$H%Qz z;;(_)HMjmZAbxIr3?XnU8GNXOB5cNWuQ`?MhhKv`NcHRC)*f#Cy?Bx4RB$UZR1*Km zxmBUO--5%|gZhYD|H-)ZeZ&yA{*!U589c+d^^iztY)-8eH^A!zgyz)4j8k7hsL!uJ z%1$`{ym~?;W1_A(^=Ux-oLYhqIFEYC$PW!z$n>Asu z)5G;%n>{y6)^@kN2&xU%cHe_ScwXA>5g>AHx0rnA*u$ahNZXZ71jYN+Y|BR>w0&Tx zX<&v+(y)!JCS~-05krj0L!-bVx-@X@dNrZxmcsi0^9p*ZKp$FQ&IbhVk&qXdGY|rc z(XNe3cpQgM-HXMV!Cu$l!pcyzvxmicSPVpb&TZ1~$6|_^t@sG%F@@4n!9yl+1M%1+ zjK`daAs*wQcN89bOigHv#~x?i03M@lPd|^5_6r{SD^*^27sX(@W1NEweco;5pFS4b3j*r0Ij0X0Q?ZqP7Fn(ema-3ef>pM3K<7e{f1;aoi z8g)c!vMKr2xp$;aoicAmQtFIbZn|}D@|5{AZoNeqtTsn}45UbUF_ZEJ3H5@=D8xaM zM^3TAo+pi7_Qc-Yq+TdP)BzCX#SZG*Bfp_qT&ana&8_Itg8B|QRvU?XAuz#H-zi7v zBYi0s$x%^}^XZgNy;u%M3%$-+b#+SQl~hZgk--nfuP{mI`c*>W*Wsc<*8`!9(Z)q@ zdM}Xx2=%oIku(>59ZsF9dU!q#>d+;$^N9C@$&5>>z_@)GULZ~*X}=tdlZfBna(cObmp7EE&?Njk?A_Kw+XrCZV+3jItPC> zVg#gp$4hgfStHVjFM`Vi`Tmi302F+uhJ2wRLQazv8PJWt;N<*votUc;mmuj@xW|#4 zZrk;W!&93D%R-!8t}9?AWzDag#hfrSiNfx&ZGW>0UCLUc`15ZbuJA|Hx{FV(rQ9D*T{o zvhq$8aV?{0Wr*toD0(fEzfgLKqFLY;1c;*VBZ|Hkf%B8mMqF9#UKGt<9f;TerJ}h# zGWzEsqm}opi;pvkR)+XDAX7y~KTZ@~E>koM421eX6#W}QF(U9?6kQ8Em2j%tQ8c~m zawFW?9*X`cW%NZ{7%Fd}5}#-Etqk~WAlgz8C27^q6Me5k1W9Zbct)o07YRj$H>vd^ zVS7SOMO{fOY~#T@3*7 zQQy@_wSfVO2_W>RdujbiI1Hc__a%@6`0|vz;toLw!l4XMm2ecdLc5o6pcgf-gk$q=nQ-_)*C)z56UBGgY^MzI-$1O&HvYRb+kJuvQsCaB*efw_1t@S^5#^uZ=)BEG z#82amHr22cM;Rb`ldx-eQB;t#=ZA3)O_VN!hB36cY3dmF;;)6uQD-l6N?4sDF z^B`h+Jsz&ve<3qQqkY-)bRl;u#0O)k1W3!?#-eV6xY5+n@@zZ6K{+ z9moz}s$^P$_*5=Jq8LF|0Z+qe%YY%`=rq_g;LjQ-;f03i@%e86gA4?=D8Rw=ZE$|f zN}ng~epKB6s-BFwrbjgYn4&rOBJB@G);bt^#2)@@*+e5-X>Gn`CLH{Vr={G^c=XG=K zAQ1V@HF^cYx!%)2+0o54`BSLkWt@K$0?P)s+yW+`)%0bmQ9yngF>L)jSVaK+>g-&Y zoDE@bCTb^`%v(H02m4S3D)gZ$v6rRXg_Qo6rO>#wc$hEvhqW(eilUyO%RX3^&;h` z9XD#|3&`xf^dWDn*oreWB(VUB&jUb`jc}&pBN5$?V6O$is=$sHMzE>?wOtjgwyQd* z?WzuHyQ+iQuIj9|t2(LesxH)aRhMeJsynLf!FG^g!L!2;N4^l(^7pvKU0hs~c%l&> zf_)IOr-{fH0HW&&314+PijG6ulHq9b z>7nQ!Q)Lf6F9@dSV0|adtn9oz(eQ&x>!8zwokZQ|^YX>xK;-JrW8~u(_ep^XdQBB& z$1d*k58aC&aeh+hZ86v-9XxRf_~|3nL?CZO3{!vHDgwyr&mqQZuK}iK%vQ^ot(3w{ z0pj5rgoQp-F{T9NA-!J-T7eLhAa2;H#v84=*PJp3+;<1uM6{uY1G@_c{;+cj|B8?J z8Rr3I#J|9ZD|54eAslgK4@GwuivBsxDg3inVk_q- zh2G-8F3OzJ%I1{Ch+%WeSAlcN8OCdm0LJH(uNkwwL}50k@bET-ziUo;9W#zfC>z+_ z=9Cg}AGut#;lenuXJP$+Zegu_(2cLIH6dF-q&QYcT(7LIwNQvuLds>`k!q_!wXGLS zwLPor|BPxw*mlsxTwW{0NnaZjqeIw{0!bSeiePrmRaZ30L^c^K*>c>OS?^9!0GsI6TUQ|l*^goZ(<}{6!iH7NP>>>!z6qW zt$VEus89L~qIbGns1)zvhYRC}i?sg6c9TMv%=%$C=Lcon`(xa9gFuNNhBJPcf*4|m z5sV?k+c-<`&;ve}pu+$^OHd9nOPq%H+$_N9>0ycQ$r8$b6?>uj54t?|e>qkh!#O}1=Ep(P&w><*1IC1qR4*ecXRqd>!)O1i zxd;oJJp9F|+q)gz&H>G?gWKCfx83$w`Tz7btN+`|;tbA13dwoFG3*to;Gr2Iw9|Si zh>)#{XN3?zp9RS55IWPC2uQ%1mtKtO-$#{SjQSDqRKf@Nux9s~8VbN(Rd882W9gY1 ze$-v3oA|dcHnVrp{a~MlV%ib^9=P~^7&i=+$JhcoB_n&97v2w}dEsHiMBx4S?}yP+ zfxeL%iMM4{g#8gAaZcpDf>_D$#6^5Vbq~_`59uf`%pRPy@5d)yyBBF!quB!o<1)gf zJ)}+Fq`Wxqhf^t^Sp1eZVTDbTY5m1;kj#Jl)ias2=~MPZ+E*YiWsIwXNSh#MgGhTY z#9L?5CcN_?Z38|`+`ULU24tNHx3!0~e_X}rJT&;Q+ht;Zc`6b6`F+q&ChdHgw8tQ& zGR6rYUBA>O$a2K^rS^kek~ZOeD{0d^HVeqhZ_Y+W{LmEm*HZ6_>2Bhb=q+v-K9_UBG}p5~^1Wy-Y+j%e95yeBtHjS; zN5bicREcrBJuiK@UpNJhYX zn8V@`icAA=Bul8WuZ>&^pc2Z*t7!cpTjbIJ%$!JQ;2cq0L|V3z$cn8=ymj;Ha9UPs zBZqLZ?44^#PqQNZ#Bi$YTojD%5ml*Ev$L(ZN(^?R?{4S>y?mio{#xqYh*SwF-dY6#w5AyU(D5}9JWZ| z=0w9#3%~6I)d>m_=a(U|-Hl&L&~y%Mb7iTYi9FNVQi6 zd<x5@&HMR6o_q|rFpBC9sF>ahMO!W}g~h<9$1Vbw zy>uzvFMA0KvX_%_d$%LwB_QE9;kNdW@sDdk)`5&W5pM92?1ktLoQ#(-GX6CuV`bFi zK(q=O|2oV7QeAvLWc>Vy?k0^(LBf}gZ?Z0ckwcFP@gwd{d>0vaCS3nnC#~zx^DqV) z1t;TtM#lGYGFC=?Z2%eH8p)Q zWh{e2F*B4YzsiVLf?(MTVWCjW3?;L#(>@Y@10lacL0C{II0<{Y9SJW1`92Ew*cCmJ z_{Y_YgGl(|>J!Tt2@9Nrl@X7^xqw2#LMR<_BqE0E#NR@~&a1nd6y6N-t$~Zj5r2;| z@nb3z-%i5crc2BLxqUBPBA=1)F*TFI%7`xsAmL-`smSx8Zzo}X_Au)IvG*;2Srpg) zJ3E`OkN^n;2=7RE1jvhkJd+Rz&k!K;knqqiAuoa-Nlfw)Q3M3>Z4IKU+BUXa)Ye*i`~UrBcE8=c5dJ<$dkO6BJkB|D=FFKhXJ%*i z8{N@V_)VxUucIGZh0nixIIAVKR(7|w(w?E3wtIR)e{ki?9a86}Ov1PA@#gxx+59Zc zW{c0CHvi!1M>w;e^g4s~Zo5PXE{f3vQ6=`liv9(0E|+zQNTaRBw~0#25sisphFH~Q z4V>a$5$`gxC@P5UNBva9h+cps^-ZF#>=G6>6?HSZJ0~3ePHi|JALxyx6qScD3YZbTD8S@oYH;OY1ah(X7- zbN!DFr{guyZ(f7(CjH~-_<85LqdC_1FoO-#Ye5wFKToOw=uYYWvJMRyimCm*Ji0fy!bWDR9s?z>$Or%JFV$$eP32ui; zRllwUe>X;bh4N{n&_kFKIW=rke}s_dArKxOg8tE}uXZ^7ZbY@7!nF*$H$I+CJ+I#% zN55N{*iUdB*wtvdywK<|&`7u0--G_DU#<#k^aTl8qvO;bOzkCb&gN+};lmnToYB!N z_919<6Ry9X*XYmK=WYJ7ua!o(b{agV_af-j8NQ6>{!3S*>GrOU5L~-;n|%@V*VX9N z%I)1mjm9a^iU`7;Mx(Xg8#}h6Y4k~GGq3-`_N0$zqtE-jXJw<$K!wjxY1`dF(J2CL zYTVKxb#6sgQ}FGNt#jx7g0s@O=BGDX>}hYY0({m6_YAf5Q~x>HKp=zz{_CnsODo42 zqD%_@nt?pV{S^4vdzt!igKjX{PN=qqL1uTaLVksOjlog?tkJ>RT0>My{%mab(sVv`CSmmWw`BL%KZU^>q09>ZgE(d@U#M{E4PF#(T(m26}218tEk%gG<>j2x+ z;vVVl>p(#V4I{#0h4k;(V!YzVKPvI5&rD0CBAvFJed}=yO#fryml@!EWm?SnB~p6g z#iWishpqQ<_?SVqdQEv8G4*2)Q!Q1|^?=J!!8kB9P2rnE45Qb3AT&I)DR>12JCSGg z9tuu-_v0-Fw?2E39G`|z{AAu{;?}nSw|>CwyApw12Fek*m4V9<_$C7z5hzSV;Cciu zW8iiKZbTsdUIgO5gj?UQ;r1mY^*M}E`aO-987l011kWELm4E$d3O_`x2N(mLoimgH z97?H7;i+*QZTKxhQ`u9Sg0Etm+s7Mz&zr*Yrf|lxP*r7hl_9?>xHQo9|ClLMHXo*N zi8h69-+CL?;cdqhE>Whe?@(_bZ!9}i2^mzJmcRV~+38wAWc$mh%2_4R~!K@`u!{-;V zzwf+hJ8#;m8w~kH{Xv*Sqw|`|iZx!`GP>CEF~KQVRGlFo7k4le3>tF3n%o)D|HnPN zkn7@I=9o68bG;4o@FGncX1kXu)7BSjOPuZM*nNm`Cc94MeHSq`lkE%iw`l09Tg_x+ zAo;dtvYjzeQ#08N259J3GC)K38Ur+Rv*E?4nd}M%Xy~>f;LT)}q3bgdCG@3{yNggX za!(+@zkW1w$55UA#sJ62v2rkS)C|+;JgK7@xpWw~$+(`zhSKv!?z1#<%@2Tla*N7X zS)v~iXb9BdVTiAo%4WF@{R&j|2$Zoiou#js3}OdR=VB~bJz{dRrn$rFT#Qw#eNN2g zw=y4fBGFjHB9Qut-d`Z#9sBxcdNl8Fik<@{e|9=Pz=G{~=N0|4RP_G~6BVb^ zwOG4$b?$%B=lY>vc+1$M;8S`_*HcLSo`+gk39(aYWBX)j=6xjiw)TlRjE zWW1&CUnA-+fA`=>*puUAI*{XxUd}i<3^ACGcRQhb8c$FDxZ`9=byay~MOx+9iR05M z>etk!G*niN%}6aV(pHBmgLuPGs{?7%m(IQCO1KW;F#b!gfegM3$^6t~KP0WeXS0FyslYa+_AC zV0BeR0=~~ySy5G1>ino_g!*k$VTY3yrfGJnQ*Y(N$@(WyE%i#q_1jKJhCdMolS2QEDI!vattfqdAVV1bu zbg6ErVnL;<9%iEL6jbIE6zyjhKVlZDt*cyLhF^zriYyl+QBstaOQ3Sx#PNY(b;)Ic zl65tKD@xW>80HqC%94@r2BYzAr>qW~ZV_?d6j2w3!a=m8dRH-yPK&jo)7RX}|1A5FZF#IEAufbD% zBlb6V#PSel>~pyFxB<7Gf5mM)u<>VEY>^hlwiHCDN|*nkD*q&iUzFPvoC^vspiuk{ zIZFDF!4QdB7F(wUNi9*A&{K*~5r{mD>s!F;scZrXJ;jRrmV{c6XvtN*E0#YgYWqRy zeO%qJsqPD5YJVbIlabVGT^SCy9%Km@DH%P((!r?sb4wW=LaLI{M&NxkA6hmqD<+H? zO~Ea=?g8ezVJ2QC6aS5*-elq^nHY?XQY|Z}Wu(bHsK}Aj4{^-_^o0?ws2`++>Peta zb9HGcp~|Yt`apT8ssulpffc0Z#Zk~2I1}Gg1oE|@r&Z^9vIo^^Nlx?Ag2?AZ0tf_f z{RTvigo%9K(Z9Bduv^qt<97siiz1+Iz|{?#9PCJ!hutoEi$;aHJtpQ`7W+Jt8xLfjKQy1(R8tQrhsz zjrDJ$qP+fzp8N4;4q|H^q+kV<)@)-KgQpcJ!Dub6{Sw54A^33?lz}9ghJzoR8HZ?0LIEC&ht82wx$*(hxW5lz1ypzpkdds+e}?CQWz% zW^zA4k&(0J=Fc?5&9AZeRt|&D=P>=wMVsyood^->_&eIMY31qI+AUHpNps(&_0O~0 z8z3R>qVRs7H;$HCMEUfGHH7|LdI8use)tLMPfB@d*(c^NQhz?T61y*8EFB$^Xd${q zk;e{E^sQXEHeEu;)wJj|qsbE@wr+=vB9ul&Y-1Q26`?dL;@U?@H_?jF8Wpi!XWJ24 zqav=;`O%0|qav=?@fe^x8WnMaPKpBwtx*v->Xi5hPopAk(u4=V<@_CrluDx_ZvHci z?+6+dX$^te*qyCNYcv8~xNzusyE3p;*^EE|28>Q)53vSr(p>tdRmDM_cOmnl0(HT% z)nhUyqUkzCiibffDiHFLI*IQh$%66()|Zvg8rxP9za9`#U0YcZC@ropHms2Ykbsto z#t>jwqrQea8>+arws?bKjV3spRHG~3;T%V#+cD5Iy5kiIsEuZ7M6j$19-?7={s8<7 zHhS*dnH+3L{_aOrniY)?dKlJ!&B5qQa*@_3V8)ONiN;#i*b*f4sKeX_3Sf;R;T{eX z7wRm0A<(eKS0e-Hm3Ty2#ErCW!@Z|vO@0e$adiNL`)^HAb;oJoL?f=ewt8K_A(pAr z2QhtJQa1SOwoDxnu^Nv0=B)UL;B!-fF?ej>@OpHUNS+Z5yP+VIcC|jetRh}c!ONodS)&q~OfwyNh{zP$aM-+!D)ZnR{Kx+r-n2DW@t|U$k(K@E0%fe9!JvOXe zZ1Ucm{oRI~KJM&Kn*yVcZ}!LTz`!BXkTd&p#O9|G zo{9y;!@!PW|Kc>NxRy3lghT{XCDMw*JzkKpVF>kQ%k@wzk+bD|bj_QTWTVz|_pC)U706slA?(92Xl zk(prXjORgnP(Vq;K%x^>ih<}4kUJRsQN9Tx(s~i`3p`S7x!`7GeKFon-N3YAb!#fi>kWC+ArKg*bWYw(wHQ7R z3G$YW00&_{G-1aA8gjQLe83^R+atW2X)fWtKEn5Tgzt9Z_yC6s;w@fh|2zAjE=&kQKt{E}eP%5Js>|B`CxL^|s_|8Xt;Nz8|VFs#R^6r}vP z0=P8%_@rnY)D+`(rQ zFeN-2NL@%*F}tp;wgTDlP_nPO%*LA@v+<^4c0JY#`+$|ttdCCfw#+eE5dC=@~yZh-_6?txUoHdj`mG@ zfw!l5`#NqTlHbKW8E&eP+=I6PybZ@KtH-BP1!MGeEbo^r z>Sf$UB>!GjZuDJ99*GxA$4u7AfR-7eNr$0fUq)J_Me{pwb!Am??S?=dOl`@UKy#mf zO^PY&)lzruwHQlPU%6UR{RY)wW_h*b7%>tamLWDvs^3U{k#CDsI>%3z>EHOqS~hm+b4&) zfWzFuu8v3p9&-U7bH$1|Err!e3Zw8F+=eJoGE<^1^_bQ{#q^pm)0I9^ul0#qhbijI zl&B3O6{}aJWwm7$m0Vy6l-FXK3X5Eh_IzEinZ?^+H?0R5c%8M!7V+>1tVc#?LD78B z8Eg}}xx?avYFHfH8bz(InOhu$)nSA!3Snd*;H;dOTTN=gU?ZI-+-q&e`7lJTwp*Ox zeTeIOSnl`*sEwg@&sEkPt4A4Fahaji`OnDDz(SRdb;X9xkyq#ifmwohV>js7Y!wrW zj4o8uaat;+SjS#dRt(|2E`%|=DR?ig6wtT^OL!S9@pV<=awJ{2RtZdvDO(ZGz$edM z7X-YMF?N?X)Yy20QFa*}eHYR3i2Yh+#$mQF9^;3OWg#}j~)Dsz%pyLVZnaoC)u;C{gMi*fjTU-9`Gk~E+1D?b0i_jCY%g27Nf?PtC46J6Mg1ZucYH7g-8cORk#`fb1|5j z^kZ;89tlBQB0K3H94p$u)efAPE@}Yx3a+O>;4u)Gte3!M(1VMRRt>%@dIM;z2MG7y ziJ3`HAe!`$qMNDa{?(X8a2f>L0cR{!X2|*C5K4U$IHTucbswagKsqZGiE=(2r#QS< zH!`_Ggnokgl{<=@6x5P*AYF+o15jgQ9Yk@Gh?gQMn+kH0HguyRqnyNM9N>LJy`I+? z@__Q~wt(Ctd05E?#$j|p&C3Cj{BI;>#gnxIEWAJ7*=57KJV*mN36ZJF!#qeV^njdc zfVAb)?}4u#TmX~Luy6MWWf=0%QRMXqjWgtT52?^ZLw@h?D56Ila-V%dg^>Fkdt#3| z4#QLn}-B>WIkwqh{C6*L+GoJHH?}ne??bwN;mpqsLUMX$=8#{ zAexkf+k_${!b?gyLs|=NjD*ybPaAZ z-Nf8G5&Jf2-jBdb|2)G?l{L` zb8d*v+Uo2Fh0wgF;8Y;bk3-+03yn;A&zB)>JZ61{h*C}Y6~@tPb?kM-#+HWGt%EI! zR@!6k2#yS?e%)12TqEmLq?=$}XH#}>qO_B^L@%+cC+Zp&;7m6snL<&*}ygwuP5R&$S zuDHZZvf$vrc4O(mspPGu*pJFRIUlQ*AiKD?Q^8wR1rH!8L;I+=D#!X&B36P}E>eTrkC4Q1{~|R?cFI8veND$kAU0QtV6p0Z zvk@Qb_yIb_4Ar}?fSXXGeUKK;a+srH%~1}xc@9?@NPe%6({RO#qaEY{92F~$zRDOl z(y`xb4!j9%i5mY!0k~)*(c!gJTaHkv%d4AEG`kAzvk-i&!X?VZBVa{&(#9O-T!qvd zn6p;sR3lx_d)+C4_NGnkupR~EhYRu25*G1Qh)uaVrt)3qB1!9>sXWdb5YONg!Bqb2 zR}s~!XsIOybSJHf5z4K7j@hamB9)JdGepx*LJPi&G_8tV6q=k4IMunL9JtcFVguUZ zMvJD~7+1dnG)KWkD7a+c(dH&8U$+P`Lv_mesLWzqqL*|!;Oj~yOcooAV~>emlD9}T=bI1qc9Kx<$JiqWAc%{Xp280 zE$I#1X1X~k?;!>WqzFtaAgEj}lP&@N%q0$Pay(L!hv1f*jN8@7%bke8ml>FWz)ulK zS%82jP|aKd#2OZQlXRNdF!DJ^Cnrgen7CgK=nqD|i?j&^NJx1JA#+DT?$7aj3*=@D zTig`P$MrwpI`?Lrk*A0YcNwwpIitBq1+_ zs{#Tkv%6vcX;1jD(XNA;s; zA!8mFWqt-BUI7g8d()eXPiQ0!&c|~Ih`-sKLB9wI+Dp(9`{gwh_+F$X-HF>wHz(y0 z#9*p(zlCQdaC5(hz(xcnaLG0KWyF$S#Vz+Yxc!I#e@5T~UCL~MN!kb$@FWZQnlz!6 zNm)os%D`==o0BpJF~~7xiF)Rz6shN&l%RSp$!);1>ug|DIJAo+Nh5qj*F83?y(F-1EBy2x&p`PeZPyONhB(e-x-&9E9nF9riryNB@fgW zuMU>E0@@-HfMXTnOVfGaVUSCE3u(_olA3XBA?ueRmbDUu#Wl=)l2Is+6MYp?C89#m zOG3THEx!Y&6Xqb;NOxWTR5v$)W$A-95#DLGqxZY=B;B~QT$UDgn#5a{q zcPClJ252SA7Q|G|4eu#a=w+D(3KRCX5af(Q03h@{F1ZDoCP{o0`#D`(?A8D29d7w$?)=dW(*=jbXe2)LV3{YafOrye&G` zcAf2R(Xp=6`R*1S>v|pcZPBrA&`I7F9qUG&;@_fU-J}U~i_T9`q~4-q-TW&Se-1ZP zL|EiCQg3sPII*9?ad+)RSVN(%*kj>stcY~2-!i`Jj!b==$Zi@pTSSg+mNfH<5^USW zOY1-`r{6~86s|ILu_AL8N^Y`Px+piWU}0d<%xQ}kqFd;MSqm4ju9Qz#hLHRS9^w5fVFE@%<)6;@eT$ZCzlh+I)fLj@i8=cvMr$dzkB3_C}x zn%c_s*n}7uV??eRqu`^%X&FZ3B|0s}<9wRlYZ_UhG{&v!;`(Y`g=;RN*|L`5-c6AE zEeLfbY*%vGl{~2mbyXQ$Q3UvP$O>02w;FqMxIH6s!#<>2<(TrVG%+%vE*>9CYKu$C zl+^Bi4p0%+3%Khd-$2MmJ}mSC?3}2^o}bG44GQtlKNTXsL!%K#Ak;3e_~EDUy9w^z z#B24Xc(IFhC|fQQL7>K}?Gv(4=#Fp1y4xq@a=}$iZlCZGakfv$6=Euo<>IyZYXh^K zy}VEwErf)~@3ZP?lQNw6%67zHl@teqfx1fUArZN{G|N&Oki=|TNy_f=1SBiwQ5I%m zycMh~#cF##2o4d2lO@o`fEOcrV6{-j)btBc>@A zY0AD9nZ?&Ti?J&eOcfKYk0-G3b_$8@6l7S&dzor|T_%`1<7>d|>uje$q7zk42%-m& z+YkIw-u*>{^(5jxQoe1qa+*VMI6MLnM3#XxC#H7O%IW4rWMbQg^GZn0Fv%Pey#2It zrfF*}rFujQVsv%%HW}f8;Xc3yoLyG$Hl5fZ*jzF`)Zo1OVkubO=RV#Tp?ZhB8x#h; z!lsH5>=Av-G4{s@CZm=V_h6uhYS6gc7NeCy48}%-SHhTsJ?1cMR9mP*^>H1*Ig*<`x){UsUow33|pVe99a&H-%}w>d-Dmd|VdRPgaJh zo@}#ni|``2x(XHon?fQVS2ZjE>M~sEK))x9{@2#%oM+9fDhXC|+19m^M&v2QL=h-d z;93PLZ-B}Ot`dI8KFZm1GxZ?vi^_u2xf46GN%2qv%m!RH0DEwhVT@L4B9#ue!e4@s zDUAQN!rubi>v25+{6HALa~R)Kvl(SPG2ji|kvED)AU=-kO(5S3Z}&ubsC~d><94sxF#U;BoI;$p;LYn2?gj_q9*Zw5Ka6oZfR?fkZ#~s0?_nW z1g;^$<|LiBo^VSqX$r2zwFfZMG0hsI-+Hd6p~W<$rYV>j0@X->Y+*?aiTAOj+i}CJ z*0mptiYx293I+<+K+YM4Ia(RJ8sPpF*NZ5k6J}+jR9#2Yz4;rGly;lRZ2K;YFd6&- z2%D+&A;uz_Mw2)W*3rzMRlIOraXq$|tie&MU>VMTjb)d-8gSzSfcq{=`4KMj!at!Z ziMJprkz5<8yKz5=oc(y33w$OCQ%AQFY9xk92pjU^^T+J%rCZYlyz122_Fc&EpyRSvjdel!*W%Tp=Fep2G zB1*0_m4%Z(2ZpOqE0sdQN%0OY$xD=zf;6RMmzr#MpXJlerBBt2$d{FlHG=oyMR*5; zT7Emm)v?3Wv7{x4YNa|trMeXHB&AhHNc}EECmup6=8JUS%^j;IKJ1GXYL$D5D)%x# zQz`DdP+AdN>*d7th~{LY1(~*mVoejNl*=Cx-^=*ji4P;1_9VjT-$bYhRMKBSpvzK} z!@p_Nw{&oS4X&|BjgE!!qQV6bQA(GIoTH?sl`fS^w*+Y?ndVcvTpQyn{ybx?M8+YD zdzXyJ!=e#rX~j*!&7gf33K~V9=xUmSN02g^ydMKd0tkui>j9wopNP2P|9wCy{(E1{ zk`ya{14022uJiOica37j8@4nj%`B@C`I2hBbnuhB8eJA7AL`*Y-!e8|5t4NKEN|96 zPRwba6>OhJ0XmuOGYRCjpmg&RZm{s$i0S*urqf2>Li|<6oi;j&ux=wzQg!ULjCDlw z*%*(NK`Es{nauuN4D2K%o11TBDV|%Xdh{aX<}i0nvmV`295_~s&1BWJfkkCFPKz)2 zM82XW0%`mL*H|zR?CDD6@03UYNm?TRXeJRS=1Am2O5{#}=1?LTF7rH4pdQpM8!)PIkJ#iJXX1oIO=0M?8Y~~Lb9i-^)MfMcdSE zu=et zGXbg$(RCnK#PUygQefH*thJ-$Zz29L<4&s{LRd+m1XPq1Zb3l(je(QK$sNzIR?s3u z$OJ6HGMUp(v6db{gdEDW5O;Vv>6bkWbnGIAch&Ps%&{9$v)RTRwP0)AvmG%Z=DG{E zGNdNRSg>7yMmLSZnOGP2SPQo5^9dDI#r2=qa%}>ds1aAsEO6+}@WW?3hCPD{OTCVZ z<%9qW#}?7@Vw{mz2k#A$B1mKyuCGA6t$^GU=3_-VKrX~}3}6v1EqBo8CQ!_zGer&{ zmf|v3;4Ljg9I20AX9f_4cOU7}a}q40ldvrUb|y*ZV+rBq3+(%34T?ap0#_=CcJaaK zdg>D9D##i@ZNaqykT0=#1!9P~jBs{gTVDjh)Ht5MD&-I7mCp;+nd<(Obmp+467 zzubB`;LlFhtLMz`oG++v)%Q7d7aL8*Z<}9I__KlmpAAnZ~b51s8jsw z|MDhHnCt&E*n0h6-uw#|e-79G!%L)ULElQ zxY%#y6|;}J0`yBHyx4Co<=(#tYZLBW#BI1oQ9e-&A^A=`!Ykro0!D(h*n~3>{Vss4 z71KEmw7I}M-fNU(Ikz5}SU~@Z; zD>x?<(h8B;quFsg^9I2Z#bpt&!kBB{z*tYNoS8Q;ElwY|GjC#th*QVy%$rHR*Ks76 zx6A}MO65Znc8}YcyEWnG9KyRj!n>K~65i`0e4j`7eur=d3G+pJpw$L+bFH9Eg$3uN z_n*kkTH<3(pTT%Crdd)#boZ_Mc8!|{G>aqQy=ejmNT4T+Gt2`dPLUk+I6p|xJ~VB% z`ShbGSADp{e1>0#ipS<~^Uzbsi^t}0^Si89d}yL!evif?9-G6>XZNZQa-VyMx!4?T zKEDlVMqJ1+52t~+7VDAWD7)`b6cC3m4E=yHqwoJR%3aRpuPB^;{ii^T{WEauH((a- z0~YfZzzu)>G~@}&uV4Q=kl6ob-1-gJL!f=UJ&7CtA}rQ4!nzQ}4`ki=fs#S3_uCuh z0HFjX`(UOySc!RnpqvLQ=?@T;UIO?VsK64G77Y}XS>G-|>AwJk`_JTU5^t%v^&3Dj z4)`W+eV+p0fd59w=zE;OpECdSwH(0jl6*w{kk3s@{ohvg?SWIc&?aB!FhqvdgO!-_ zDeVy%tQFc`WT>{+=X=I7Ux`&*$OecD*1$oot@riA@svQ{dsx@gG)~`x9|6#>KR4$0 zZ{Y1R-Z;PLJ0Hb=lnDO?%8m#}0Q}PtA#}xnEH9v!SW*(emrla(AnVqq8{!H!_lTHQb1J4^x_jleCe+< zNP}nn12x4+x`th20}(5Msbcr5LR-A1*vcx6D91O#N&|J(>o9-k=K{p9pDDw-0K3ai z;Pk!9nb)4BkVXGIB#oSg+c8kHi9~2xx1GI&&gu`%v8s4|R%%n#vWdcfqa=Mn#rZ#jqACrA(vA zO$X%!fS7~D1Xd~Br9B-T#qG9U2Tz{^l#6gZ#Hy-}o}LUxcnYq+Fo_+03@h!Xny)MS zrdUsL6FsgMcw8GPq>_hAnS?QygNmDC{?0Ri9vBVbXd1!>QgMx;k?{hOSp5QBH)S@I z0YM8JDR+Q^Bw;!sDw^E>Y zhTyY`#EjyHH7*BewEJNVEdnE2jfG|Evx>JX$>THQ9ua3}2tKPg1IVDVN_>k{d>_`( zSRmwlSVJ*{{Utss;YF8#xCz*i>SH8g*jFg$nreJV63g0RIHO%Hk=#vU7ff||IbLUB z4dz!s=*CwGB9)Xe6$xlXcF!foh><10=pGI3#i$t3o9X5PHR%R zwpCxYt_JVR(>1TrKBCUYWW>0;!!$1>3#6&HwTx#DG}4IS_DlXWLF^$EZS?S@Mxzq` zWXA(!x~G?^-e&TX9V$c6Bk?}j;Y8IQ6ToyvE@ua9x!%}g(g|@NDc>RwPZ=wM`2gvo zK9IS^WH>&St-94@CP}gRT9I%TVtBS$5!oIl@}MHp%a7uJD`euW5QQjz0SwfGYm7)s zMiSl;d2fZxOU!?Q59igRd8w(|vY#gvT^03yij~*z`uO#c?l7&oK6Q!}KPOai=hJm2 z_Sh)aYXI*uGR<^iYLAUM-Q0>yEsz;XAmGQ_USrNwJVHifzrP^x0L5p7wI1PtaGV^~ zb9bfSjo!L-{NCZSq7mlsSzL*i;2koc@ihS)X?~W$g-9B#)bly2V-=!<9Q{-Yw=%&| z$>&($J|ut*c7y~^ME=*iCSLEdx<^ssUvd2m7#Cp}zQAdMwWvIk$~6>N}B&-^1l>GiK+{hkh`I^?3kqt-bix89CCVJz{O%O zsPtOX6#No0qv4bcr*3=^iLc=j%YP2yBTSGrlGwIPtl>bnazqZIBRDY{c>@wgEkx+Y zpfPGC0{_F3Fw%N$05uHoLq=uT#woU`IwYXf6&RAb2k`45@WylrX2;=WR#Sy_cQEdr^f0$7CnbA0*Q&0@ECE4UH@P~4M;MQ zyW<{##J9-7or8frf>XnhHXMa9*#t3?o`agak3#t>Tih{%N6SY18aKGkr1vXaiR!yx z`nx8NkR#39J_fHYB4~s~BkDF~_#2wDL=oi4Vg3sMd!8~?iZF{Z`aY5pl?3Ndf|}-B z(maKnp*Ss&$4Epm=%+hMqWZZZa%hGkWKttG+&DSv^~7{`y$G4^OV?cIDoS!3GGaoo zB>mwOjWxzOo>B~ALR{#+pTH*{A3&M#o&K->iC8hr?inTR{&uYBf#2T77%eYsn{#_&jb@D5nD-md{Um{9@U5PM{ z649dQ;fkUsMG^OJEQpeMrubXF+ICgb+YZ${Cx&{ja+h?GDsfOe4`7{TfjlX65 zBXSpC+rY|3`6_~_+c=Jw($%e3H`dGbXFi*HX?M0+J-p`V;$xdFs0U0x_o&Rdz+f#eb z1dMYX>0VlFyxjoac%I1#AP^ziX zbp1Zn)v9psKjSeOyt_3y2R_8P>-5b#cL!KYa$BJ@^~rUb!uo`nkk%)k%c}LMtF}Ss zT&0gba zd=KOp>~n7Qd{c5(iLP|>k>x+4hOQQ#gY~%chWxAyc^mrZHr_dK@6KfCInS{S>$rM{ zf0i$(-R5oEZ|v9x66?qARN6OB;yPzHw^|qiym2|~4xg_Uyp^+5l>g7p=nZEbv$b4B z`8U;nm;H0Os(i+a1ugs=D8za8!+WpJvOBtx>!5ne8KKMKIdJ>V#8~|sut?{ogG=!| ziyyTG@Jw{^ycGWfr0Dk44s4P=Z>;rLtXHc##3qK*coZ#`8k?_}z@zy0s0I7G*A}f; zea?L?I=C{o5}?a(2VJIUuD0j)t=X9jNF9k^w`n`5UHx}c#q<37PQ3j4%(}v>d>`*5 zi8^MZ#@7V3Td3M);f3iow2w;wgP|9Q(OO$Sv&_zUZ0TVYQ4FiTNZtz1lz6oIMKXK zKheWv{zH}C>DA$A|44n(!aHf{KU9J97v90jmZvYAgQ&hSFxnf$n(eq#r&ilO-HP+< zvKx=H>V}W;Sqbdgi`7=5hqe+p9;8ROI3E(E=-O?EhVQT{W!xvMbJ&{cokYOLcl;-1 z`}(3#K#ZYoF6>V)F(R6+Hy}ct`W&dY9uD%_>oYhm;`WHPop;}Ehcn})nL>nBUU@PR z%}?ek1>vq7PerIVoweR{(KGDkM{z*FX;U84@vnrCPIna7%{wa{GaS%Nk9AJ*Dkge( zOkmHiqlPLc&dZ64>KYXt#WH$%MS6+7{o32dL4$=MLOk14pKwOgv-o~tDJaLVzeCZ1 zACT(!XT(Z?Y7T6z{(~GH0t&uf0T?4eYsv*4tacWMI0~#F6TK7NBuzyd>S-gsFyPn4 z;fOf0#WxG+S>iWM>HdxMv=JXzaG(_Z(b!oEfMh+!rnuf=s=r^j1*bWTp)2771RmS| zlSpH{YSLEY>l1#@b*#q+bH{l!yuNp#QwrE)4;}AAL3D!d?Grt*c$kxD9~gxr3;|De zFrA)1rMdOU^eFiG&+_WX(z6}@NdwWTz9{(5>C;SEr#XUkfTxpH3OCm^O*6dh=`-DT z>a%=YqJ-JLIw)0}<4?eEI67rG(#i8h!O%Qk9WfQi_eaUe{N~N-iEcp`$E5M=HVb`d zO5+Mr9l`344qQZ~V*4%flkA_8riAsWW2HzR~t0QQMiJv^m54DE_`Ryt!WB%jxbgN@aqna^=FVHquYU z)2&fHBtVUJkia}?Bs;l`A~(hB;~s@nzjos@GTNY|yM1(wr-G!I;gRrHg@(*&L>4s8 zL&8t6coF7}XL&51R*9W(f=5ydbfS+Kik#${hRJ?TiRm|C$~a{DY2)`*q6`)Uv*k;N z9hmC1swamWA9cV?Yu?AFt3D3)bE6D?(_)6hqa)~s2q?RT|li3|nPLb%V4MRU#g#Ow(zz)$F8R+_SgFLl#xftyAXE;b+ zZ^n1G+G`xTOTMq#z!JF(vj6&=B}Rw&a-p|$n0&Y+{G(u!uYV}njdZ)jC~Y!EyCx&q zr~L{t#qGf@ZHc8opLomiq1a`*{icd4S9{xCXET4rrO+WIw&d1xlINUcX`Uklv z{~$+=bh@^3>;3LJeuBW2G93Kos*7Js)Q)zn9`$;(9O!zsRuazeoU)*u9O!!M4vtkg z)%I#A!+Mby!HS%R($6EQ4REVDoL|>Gi|M-Xj(_LssuRUXsf%d-u^e}_bcUoM4wM>x zJ<^GuB0^`q2nP_1r&@ge8R~Gqy3j^pG-c;%rnMMFwW`A9*OTMX{+jtsoYG-u5!tW9 zB%SI=(y6E#F`14rQ!}OqUyPjOAj4Juu~b#x$f#A-)m_NL(Op1w_`jcmL({ktcOm`( zea4Ss!mb&(L|Uk>Hm!2}#PKOLB`NFhz2kQZW}tLxKLLRuw$-!N2KW*DWx zU`cKL7<@Dqe|7bxmDQW@~+Lt?YyaSWTEV8r(X2M$K~|n2UC`htymS1T?_5X z0vXjP3w9sDPf7|=2|i3?2J6zYVyUTV`ad|s2d)|613!0GURjY=Id(;RrWbw^du$U$;n74dPMb`4=W74wI($c0~UGZ;sVnKgLiij28+nx3x?R6HEHULv*(+gz}v$e zgttcWb}xQvHZN;_TEnOu**Udh`{)HSbSfoHb}7df%!T?|plO`@hCWGNzYSi?!E0TH z?3_36X&JIpQ_@s(Kh!})TF?`0{?uW!L3YWjxNjcV?2(d|lBMLitAjAQpN#&?77JFd z%}?LkaO=uRV@!$m?kN17B!9PeZpGhrCBG`%&(O+!GKPn?COr-;o$4+!mATb|B0HZc zBjF(h)(~Meq##KCa>(BY-9@>X51zh+w06e1wuggHMo@nK$kbjkb#sguF0U*ccU|Jr z!l^6PuU~(anbiB1Rk9(~><(7HqFB8e_s>+66{~|OCH2*{b>66)-V5CRG)7$Z)Q-_I z=D@}q%^^Jx?vkZNOwQgvE2QMtQH)>>dHahY4o=C-%ATBW zU$E>g)nPv0LE85y`TG#EtH^(8q88nMbr9B!K49%mA-*#yw70{_t%%IsFGR&-Te35j zDwX?P2a#JNS=)^+`=GpXcjojlPs=|Tjq>r$c8@3%G*(F(iXCIOo;l5$LuQ{-nJ*V~o2gYhW50g}>mL_= zb1wYkJy_@{B05SD-E4`((Wz7CXJx68d3OitLnq1GJ(kE^JMYTu!D(_s`MhNpDh{t{ zpTkeM5xtpmt0ug&eJ0w`j4gX}b^gGq9cob_os9h~f<9~N*a_3- z=VuREp%~oRK_pi|2LDLODXscK2Vtv-Z2c`lWL`FZefFSqd8J-L=nzWZQAV9c%INM$ zk(oQtoY+fgRfjRDWbKQQ;_zBKCF{0Tc*)XnmID&{gSRIjHrXvxUYW2yWNVYrQ9Shm z@^>UsWXta5e*X4$kgmCf{5{`QWUfhh^k8L5+USf?H_V$-q0C1|;qmBcc z_qaoLG4q63(A$3WgdLJG_Hlc;Tx}O3`i?m+*4|===4Q#xW{zOMOpdd^vu&<9uIF4c zHExdmyE*2pK6Yq_oo1%R+Sk~($p(9$IRIk(u!B@T{Q{@@_Hxu;{m=bMFFRW%o4Eoj zyK?bvieR}gSM|0l_)o@|>B7!}5c2G8_MUglF|l^3ody|95vXT+oc%Z?Fs$b?JO5() z+KbJL`q&jGPM9gNc9~s<`X4q2q5k~o(2lV9ZVY$ppBCD2)$$dn_bgw%SAEj;{xrH0 zf4ZkZ5017kF=zKRFORb;%!xhU z`QK|~gE>@~>lvTi)6V+Q%4_gCS%{x>kUBM{I(0IK#5tAfYX+5uT-x|=T(6>xPqtp4 zhF*6R$2NUDWOYi4%xV42p?#ENt6xE!`<3z{vi6}AJEqK=GS5uwC$Gpe$MW7JugEOw zP_N~T31Ic1u2__^ZwuFA3#2aZvu{L|2%y&Xh< za#-O;46br;`L*kpPhXx=A(QR$4)yM((cv+1^va5qtSMRY$A;ZQaoAB@s6*uNH(kWg zQL7Y(zyB09=l2~%b563(Puaq*4`P$%^h){VP5ZJ(XXoTEkZ&2s6|8SE#%*>E|)i&newk!XJRiyA)tNc@ zvTNSF$F|8|8e3)0JXQChr(|rNd>$IHWz%7~TO9D(d;91yJF>B)Rxd9&rrP|^9i^^0 zsOz6l*KL~`)2E@vk4A_g$t#p`{aXj&D~EiY zh!nDOIla%Qav92;FkEYAcjW2}W(cKLVVrS;h)4REq;o%14R_p$4zfNzq@4ju2v)hYV zQNN}(1sgBNIveJF@ipAE5#Ez2!6f!%9-R&$eO(C4ls^;+mXKfFRH(wKDx9jqnFxOq z>inFsyFLp8)z}QVp?>`d`~Hdhe2!Gr}b+hw=%v9h|}9)ex$yMOyjxi(YwE>z1r4N6TfZ>z1&<{eEtjn-@vjn^is z4UB0x|6>~eY^&&Y$ej&GX{$q(!P3&oS{+E6zI1N>3?A}G8J9XPH9aLGJ$+L8*fEow zY<>PCe3m6tMHNXYZOBL|FD=ujE}AJ3RNR57Dbu@UTcEf{U)+Nlc!{M?zCh(PD9{}B zdvg}l?>SRsXHU+4VDuPyyC`qm`HPiUJH}3#QrS5Z`+_rMlsR6=5Qc@VPhgcUXJQWY z=4bYl8=hJ|C2LCBq^Yx~eyc!!d818*`?ghn%^s-Sk*lywz5oet$npme-?Q-)BEOZX zJU;U;B<$GOh=da|bvYvUY}{?h4=U!%D2ynfLK)pC>r-WPp{#GjJtYkwJFx|8zKlkG zJ@WIgr6F%R&PmkP1Hh6Pm-or;hvns&<+3}by5+JSqd6v0sd7P~9Jwz|MrG#PL*`|y zkn0YoWy#w&ZJ)IRy-<^vU>DP!bu<5*UfynHuqMY}7~jfhiN*K9Cqo z_DgPJ?23JRWNfA^J1n0uPrWQhn2BQEHp`L^Y|I1B6SDk4L=J7NN94a{`Swssmfh1H za&W$#yKRr%8+i|J+=skh$y9~J-u;BklPSApVBgD_O&&j-JuhX?UfIVklQh^%qFblg^XwOX zu=($e^5Sxtc356yACqGb+du!ox8zQF>c!F7KU+U1TMj*pW~s=+`?r2%?CmRAU)WS`r@w1oBHx9B zr<4}=DtX0mHR|M15jNz#cW{&auQzqdLfO4>PIm3`?Xz-feu-_pnNWyK__&!T9+k_> z<%mqVqWs#orer5)Ew|%#nWOvQ1>={5op5{BiIl8mX(?z_`^a+n<*T=1bJ@Q8W%OaF z?7JAk*4E2=HXok4bZUMnw}PjqO-Y+Kb#iukNWP6RPTsX?x4cf+saf-;pzCC>L_OC} zn>c0p0xU5PK?M(hU_4f9zAE-Tnk^d|j8Jw&!2)y?>FFvi!vz-+pj(Ryy(yUd;~jlDT`~wl&-4 zX05SbxC8HccfdAa=)4*wR>(`tkwR4ofR9{lXUc~nAgOn*0P|~P+-_OZh$>`dg-T`s z=OK$_y?&Lh!Cn=6?J8Y^XIX;^c`4+fx&75ucBbOCKBw~FLG{9Kk;OseL`NP(>edF= z>Jh}T`xs?-im&B(;%;;B`zHvu94s1FVzBszz{Zt9Yu-l(dvw}cStnl0%pP>@(5xMg zR!-0Q*;|OLSTH0xeLt?RI#0{|T6tFJ2!#EMO_?x{$7K2P6{`?^aO0^Xh`#|-Cgk3Y zg-7m3>`^^j9NpM>d&(Zf_hZC}wtMW5q2pBAYc|>m`(#{wzBwXp#U;Qy23NkO{Oict zv#Id+NccT$uqpR%DwJ1ll}C)@N3imMKpgcRBA=dtx&4*QSE;kH{g(9`W^!g>sd>+a9uWQ3(Cjrigzj zU8?sfsu1xEfZnh1el1D&_KoCe*(n4wITFtxx0#vS=gaqvLOGBXe;n-v#soX=ak!I0 z?nB)h82#m@%zSfr+zO;UjIEraCoB_Y~_uckMEq1#3q9bjbvfVGLy5{Bht9}Ks%)2H*ch=dCxe7VI83F zZ(^NpP@eGnn=;)t*oLfHuuJjwW97<8A48TKg6puGt^MYvQy)>(O9spN`ewHD4-~Io z-;B0K?5K)8Z;YdCjMaNAM*)L`PFl$@Z;N?%VXI89PP_agN;`M6Q^!>9a^D}pcOZ;Xm#dUmrcB_@G zVwL}Cpx-CR>OvX2Rlb4V{}7gVerc3X!N|S2aQ~_JF$X>B?Gfkn0rh};f-{L z@vu{~(PpPMv3-AN9H&S3ad8)$BxCT{x=c?lmUV9`z4IWj4y;9chJCgs{)Q~-l zetOAqfbOCDFBii1*EasPji>UF{Ejkfw`@EvN1n3l^W~&{a^c}@*^T}1yR!V+()4RV z{UBUhQ$DY3+*2FT74u~G-J$%DEXO579x+MIS<>~k0{K_KY_$stjufe zd7!dwqd81u&C9a4U6YocWO9c(Y?8<=j{a53=3zxQ5P;0$l3HV|n?fFE0~#JfOd-CEiEy z$w7c8<}W!5Jj#3@@OXed%a1or%e*a_5a&(ucosNp_c)j*p8BR9gyoNLTV)V_2H<%L zd(j!f*8`phr13c;hyLua&M|L4^47P=Yp?7>#_vI^mJlc4s^?4ugJa0H1xZ$!K`_&eIECV0sGk5?mE{ron z>1})Ps{qeKaVcSVt()z^-vamqoJNU0L-?lv&qG@S!|S%X8@k3IL;Kl9`HO-ai;JO0-i?^4nb$nq#b?&c<$YQ?hN5A z8x_S~_b1K}ejMPrpZj~TA!ou*Dd4$R`b^>P06g~~pDFxvfagBkGwJ&YsOr0<5BOdF z_SODHqy_bBPrsAdaj?^}na_iy_TX0mo;yd+6#f>#b0_DS!aoIg?yMZ^ug7WlX(B&o z0Y3l(GoLkMO^)1s-?Noedtxq^HOq@DBUS%Wr>7BF;(R za6?$NA7^3vIJ7rI@r6F_$+{qnb9y_w1n}G?b*Avw0-l?U=7#B?-kv=Hcy9Rd^!@bw z90xqN-<%0NZG9XDAZ}e*>mP^dx1$~5zqjkV6Sn}k+zaxsA6Mg@*6%0oEx_gei4Xj8 zI78gS#KnGu0seBytBVhs?e$Z9WZB@GeW-?G&)SzK`5Xs$zH#5?FU$2)=Cm&#>QW7G z`68NOZ9BE^OWkDNN#qrW^PmTgPvYhmMp9(%*voh5q~& zktHXod;`>cz568Xd1!A-!M z*Ia)dePg)DF6|oR2mSeuOxn|zaou4LfJ1!$_?Lh<^NiU|!F&CA99z(Rs2MKIUchYk z!}$Cj)`@h|=QIVg{V)i-c{Rve-A>+{$eY(rUi#doU|x&71jJe17UYfg>oobE53a#G z>q?mYc}>CR{rJ=Md@4+19@Y~4G^Xo{=Yo_Avp>Hnh~)=QcC0(}j52b|IW)g1Snn^# zX%m-5&Vr`kwf_7zWPB7b$?cTm(lH7!7QinKx1*1a%S+8t^hrM*-yD)MdGq4dKD*ma zCy_VA&nLnz-GpUL!Q=isr?0uVF@>#tTsJQVd5L~pgcER)pBm&n;pexFI;F2@3L1Wx z7GV#n7x`fvJ#zVIME)LsKK0n4;liXBHw8az3FEW1s+`qL!LPK0X+h^r zz_co>1)VJ=Ens|gYESozdZAz91qn<{E1hCq`f2iVkOz=9@rsbwmM)TJ4f5LR7tGtz zMqXRIC!pK`z0q$0|6bTmz*q6vlD6=uA4`Z=-eTT>IX$L}s?Rh3DDvAHubF35w9p+# zx2XHf-;4YZ!v21r;N&H&fuDeNy>K4aaN#Rh&iF(8qO~p7PjEW}Wk$VAqqexJqRaqrprm?TO>u3RQC3@9TwhtDVM!%eTvxA&n1eNvIasRbaN>s41(ht7ftF8=TUd3@}%Qx@f3YjD?vmyP~A5roOVe zDo}!Q>%tgyl~WxA-j4VVloysjvszfcTZTgs~WR^>+yO@8V)nd_Jqg zHlLeCbG8m4?N=&)hkABLzA!H4Bi;e!lW0f=p!z$6yMtx%8LhGx^Wk7cdsxl*ajA!;P4v>oc%RzBydrm@53)5)jF4*#`RRvI8$mPJI+Qgi6G^4p`ucWNzZ z(T%KsfwIOxBK1YAm#h4^B=|U<{wc z^(h<4jZ;Yl%w42|xT`-(C>NJ1{NkO~3WQ0(jUyQTYp=Ge0MW9x{Z!fxgU>;@!nI{& z7%sQA470n10T{{0MT&eb@nF<-QPCq=8y5+i7JX zJi`}vQQUlFsXy{dlOT*m1kvK;UHwhMJqMRtJBpJ9yKw}=f9;9$5@m#s$S^h|ZSm8NycYl*1;y}5G@raZxfL+Cg&LAeG81W_1E4kB za@{zx!hh|_&8=k^5CKl2*y;FW$LnM))}4-#(;(O$!bL!c+iCd_&d8jchWI()CPK-< z84?+FQTBY&nh=)HmNLjin$uNiO3HD8r&>JfRd6vM z>vbw08+0lkE%?ty5~s)LlI!pw319DQM3mAbPkipwVVlp#RoLSn(RS-PpbgmWDs6|s zC-WgeK3alyMH)d+zgQ>RCw`=~TWAjQ{E9=^{wi&UdJaZd84`45hDYJj77huFBJ@;gJJgds0FvQD8`qE;%xE@|%*Sac zSfs>!VO+$A0~(E>`tu2t#5H7CwF%N5+e?wxl5KVc8dW~&Wp$#_{(s$l33#1VwRS=R z94-+8lo{j{3KS@%Lt2V}rnCi$MNX5FFrS8drCP9R2K@l9T{O`Ng-us)*cfODQvCn<(ezMP6d+oKS^{zd9!#UwehKcO)#v(NBnB;^^6Ag2;n=5f?Yk4pJ^j znA9Y8e<9sf#U$Yx@RV3CQZQ4g$?TNd4($}g$Qb-DDTxkOLVraqen92gk0t$(5a&z?CBKjI-lKYKMWYNXQ zIA>W?%0bpc)!)nAk^YPn&*9Sr{~-Oq&4h-(I1A9s1df!G$gb$*KaHqk#2Uy%xPlC1 z+CDb`7yRw}X9-W1;r9I#&WHS1sQOICs^e;<{R>VLBqJ=g{)H7ve zRXAep=^xKrlnl2_tZ7fuGWpduKPB>Is!D1qv;LZ@`It@d?Z>bI=NSW%mdUU7<{QC}@qO3u__%6wi6AYLU+v2ejNg>c`0@iDZt`n<`2qYfCbSdo6gDu{XfLx)9gjZfJzZ#s!2vO1dYayX;|N z3ZlJ;BmXH;FwY8^;CWLHskm?=KU>mz6-a)GO{2%;*#lc4SQq>~-VjoIRXA4o;H+9# z-K4`Jmr7cKl&iJvCwZRuHZc!PeU})J1{NBB*Gnxl+Jzyx*x@d*F`BzL1KTm71%_Cs z3vpu|^~(Lm;RE!ro9L5;A$h{^S#;039eFNS?&^yP&I_wRKJQmx;tWK#K*|l?DwC(9 zyV{tbr&F2q21Kh&{)dQ8nTdxHsZ9D7qE#mU7euGbM45qR_VM!Fex8M_9}#aqu;==s zK@&I$9r)yl%=(Q=^}>yEJOg1V_6#gLv@HzndoseCe4O^U45AfVm1UVPD|;cxCCbtm zT2qcSp$vl9#Rp8)Da~GB6QIX{!#ixV=@k!sts~03h6K5$B9vC6DaDqJ^64nL< z65o)B5~L)T3ENDfC({PDNMxX3K>$nh?=GEg<{Qy<%95y6x%5e(jWpMZ_JgQN3-B$F zqO!JtH{fR4N(angNT4a&5iLkv;7ftVS_}ZZ@X$^<`yvzLp{GR;ZoJnRo;{O9cSv5N zpubqJB!4oZ^GYUq5m^B#!MDof?TLi+I$s|72M}FFO#B#;6_Nf7(N;wMbwsBu33S^k zYz%%=M5n;Sd5Elylow$xlZjG;-Wp9{JE$$OI1oj z7$GHL$58{xuT@GiB5Z?247$W>wsfE=2hu2P?r4zDtJDGrT??J>b*{Z#nFdrXf%ZHR z5jOoTh>khOijR{TottJka+~SPtO8adDXNr&D;T7#beT#?STUp|&#CkkkUuEP z4*ywMd`j3FpVJY6J_(yMP3g+l#HM`1KT~yC|9#5RFMlL#BgtbbC1K038p&%aWd~?q z--btfY8kh-uZCy@5q)BT_m#z`1lsiwMC7t>YKA7sGRbaGyS7`or`qr&WZJa`uRqT5 zsA%x=6my=U1Bo@q`5?a_7heBqX&x;}_d;}!v6&#+EI@jgKa&Y|iJ7E5{!AtY5t&JP zE~2ZJiBkR84RS8Yq$a->tc3mWN#C{BqYtoEn`WKo`O<#_Pb1iTCK>3!(-46XM|7{U z)Z7FSQV1l};By8dR!S;VT8xCv zarmyOMz1Mn6q-xRI1x{xylswdo(VFj>`y^{r7S)rEX5}^pr``X_@js#yv7>*xqr|C zUal)kE%X%*qYLeZ%FENb{}4X7(E4?&j4tUp$Oun}EhT@bf^-t1^IRtA_bfnqj6ai! zc9Tik=g(wf7?CAOZ$)&~GEu4@<00pSAT{~5U|lfza+ive0Zu_;I1G2~wu%L=Y|Rz}^J86Gbt{;>Rh=;@?nq802PU?*O?|*@r-$QkIqW zOZ^T>YgEh{noy@o!vkPh4pMwdARJQ=;pl8#H~mJoez!_VSPo@a^h1?$_UJjI(Hl}D z@+7>5&#k3Ht{sk$4Zu^`Au~!6(>E?O!-#0-`ykrQoHsOA6ML&QHL@ntV_HSaxL&a0 zg^1zh%4ZPHdcLiIMrlAr#0MB#hj<;tc^xc1^r6rO-6ZnNpU;jy?L_{(pw?= zrfe3;v4|{A%C#E2tl3`>-YcF(dVi4(2|Wlrup{}jhzByH{VFC2*RT?@Aw;%Dda?5I zHs15Xd#zFTJ0cqr`b+R=jU=o(0@=%mtd{gu}RPCmCuk~EWB42bvs2iB=k)1I9-sOE8>9+=~q=uQl^G#*0fAi8%azJHKgaOn8eiJ zta*x2#p*>(X8RVhVI;3Xq-992Q$9mlQysK5b?=h=A)$P_1-p|xCgOn%=_V)!F-e&k zs#((`h_DBVsiB7SKct2x5>ta44MD~fk^YiGo4mYIw3G0j^^ug<6tHGhZv&qBIEYeg z1I0XA*lf5~tIQI=^zMDae5uPW;`@AlT| z$e}U-BRN&rMv}8sO2RW7z$Y;m6*RyPMcRZ<2`p}`bqd&cYpq7^Lx}WF(npk+FUE1P z_Cv55dCwvGoSULIMbtpT6A%u8BzhaE;b5?=$A<>X{87r_$3F30D9LRmQAG#A(@^3a z9bPS%@Ujy%GUt$BE{|8ciO8Koa!Be3hO^ zx~L>}$M~$Qra|3LP`@H~7|2{@?U2gtC*KU!MBNj$_!%H)DeJH4G!ZqBu&~eU!$@9( zDC-dDb;@T*{|l4ck2H0^gNPoJY=UgAHz1n}+q@OXfhuhP=}?yBLSdUpSSyOz?y=H` zrP795k#oP~I^SpRgOWR+WR0*nl3%ElR3`b+Ut#+wIZnY@R_Xg;uQy+4cNp8crAS;x$ z=#;Kz9F(-1t{N(CE@hjif*hdLkjzyn$>&r`qQ#q6gVmn%Hid+{AyR8P6Rv^iXNYuC z(w{4zA-xovmETFJ`#K`04>3xJDEkNWLw(jU-ftD|%(z41e5} z$&G;BSQK+#0l89HmibR*ZRc6}n~2yp{s&~GzqmHrgu7s}f1eges@L^MAKa-6cKf_zz7tGF$aJBX-(gg>B(ZeI;@ zok~gg^Oca2Jf>1xd<4mt5os3ESC!9@F4#C|7U~`@vLT`Kz+)DX9A#8VSczK12GN@ZRQ2-RDF$B(zL^HT8at zh-pCbq7)mbA!RY&?C{l^VW}4+yP3q4s`W}a7ha3(cM;id(myMoA>9eLy!`G+-7Rs? z1PGG!3EO-T$o0y81hO#-!AUm(*+E%rKoxbO1hi!8PW?k3tE-B2O+|$Buj*C?gzO{*;hc`5H|M#ND+5*^+$NgrNHJN{LXfMKJsRfvjhL*7*88)vY;>{=rHLjxTiNqLzM(9wx2FuS=_??F z(l-MSM=jk_Q{%NDHz>2;|8SJG6Dzk|8GG?kkHqOIoMLh<*-67DGRBiQrDV$&SSGr~5Lyrxoq zV`f{av|%@p-IXQTQ`j8IG+~=bPF1Px(}75yC8CC7(PO#->|=8eK+dq_HhOqCGe3eJ zP{X$g%!cS@>6jlPj!DhZNTd{2S4}1KSq|0sV5R=%(i^9^7M7xa`#MO+`X8}l6Ujzu zJd%vCO(b+X%zhG`NKGXBsg%U{q#ZTq`lgaQe+9+WEOD+iapq}qatIpq8 z+?a#jtILwxh3yIg|Na%m{sJP#K{K!%SQ3sK3M7+MN@6OtU4p_4)IUcM&dqa8Y>wmr zm3oyn+73dOl|HMwB{+ zR7~=L5tFhupKW}NWmuVM44ejEevL1GjLn~f>L$wo@N@-qs`44qnn4}-FW7nNlxZLHBMKFjV&Zu2%EyC_RC zR@fZL7gYLnkd-PO2GL5Jy-MA&nw7qbh&#{!1UX5{%xC}E8Aw_DHf33S1|p<(Aif*c z^PqIBDCVfRuPEkh=lhUjtkXvNdDSE__H`$w97@hdq@R;EDxV?E!D)zvt(Lk4$sZEB z7(AvA34cD6s-%~zn50Y%)vQTtX!O-k!y4|C8X8INMnuImq%WwLWC!h94JjM(O%V2S z7?Jn#NlhQz1o7q9`0`PS2I|j86pMg1DxV=eU3kwTgGhft^Q%eEQ9eU@yYNBXl_DFe zAiWI{-6jdEvLm>SD_tPP9USr*Quqycui7lqV?`EyG%>Y=?nIFdRgi}B>mi#d`B?No zXDOc{T_C*IZVS={kwt&!t|z3+^r=Rdv`_gA>4n03^-}kIkqrqglV43elaI|@!%C1L zM8ZdUvGVeO{5s*iwpJm1r{-6a-mQFwl+P*rc3zG2Ln71X505FIA$?qUuiiCC|4j3% zNq?byhV)m$d%7b?zbrC+^8UK=8PYd|_jK1H{jTO$lfJKfhV%pBJzY9bB_i!WTBUr3 zln>nfW20`Z$nbl1pgSXCk0%MMW+5Ak$ZARX6oalwx) zh)+#0Vv@YvqHZpLurMx(t)e_y5z+iC$ezOHNM@>(qsy%=kNe>~J3M8tSV^ie^hZHKw%NNxkkN)+~OsC=;hC&_Kz89BR1 zZf*~d@ye3yEo}3tAbL-}nfK&}RBGFK3AMa0HTjEE@#~`KY|Zxtexr)E>@YOq9y|XF zdH&H-_l{tV6x}PL29o!LZ6x_Xr6jD!w?$O&?kpk=Oj@OUhV(MwJ?EnCjv~V^Mu8rI zh^j~yhdqp-RG@M_t zA-aPo5P()GpCRSFdw*Tj-BDzK0CYb@*qY=35f5ZYzo=po-kfI*q~?Ku9WDQ7)f7b7 zA_wv%W&OF+MAUp7h~A}l3&1c$*C4Xfq`dhLK0|ty+?w|MhPvOA{2`$`BOje68EaHY z^?rFH$=8gSl(qTXsT#@6M3mbe#N0+d5^SSNyFqx%9{8UDvO-xqs)x~!XAm*wG06F( zWFP@7>GrL66v&yl?oBeNXZ+e9%V`F(8ts zMLdupWij74KY-|8h#1B}$eAU%Zt-{pIj>7@Q!R4#l3cfHGFQts@8EKqS&cU0P|QIz zACbc#ZB#x(`X}MNVNkayvLT^2fyay>xkbbS8B!MWO%#?|D%nHTq~{1fP(ym9k&~J# zYHSr;6|AvY?nXqPb-IwG7$Vm6Rb^L$=(7N~>{GE{a+{uXxz12)A&R@Xe<2S0ei*5{ zsmO+eGT+xD-a+4AM7cvjKC7%9!WbmSiKvN6pHg-q$QjD|6&pm<+~IN^UeveF@||^v&lpRNP*2n|w7+fs!gZKc(Uh zqS#Eu$;$HG(S4=H9Ep~3zH$O`e;|rY4}mWT7ItAB~Ku@VJX$TuIZK+Xe_+w?rh zN6O+;0x!q5kXHJuNw-oyL;5A*z43G+&1)sJzRYC%hQ^DmQ#6-KOO>_ltVD7Mk)tHN zSb2G+?8&O&D5-m!}rMUCDJD_N$)z(njQ519IK-dG= zhP528Y%2&mCxT7y1<`(z+rYQ%_-?lEuK7DiZYTxj z?+C)HFW=3*a_<&>0?W1EkkyHFUD+FDLcmv{E#LeIxHuz=ZeDdi~;CEZ7 z&u^&@f$Bk@FfB;;Bc9`vljWH3Fj3w>=V=X(ja^8}{}VLd zMf@}3KV2zYa>U3NL*9uO)yN+4$@hWb`5w=ShrRw?}hH4 z5Z^<5-&Zb9e$5v8Y;*(*6d&!`5GUtz$bAqmLu45n;^g~5htC9AHy^}W%j`c>v?>!XM-BHCcn%zr(F{7|g53lZB8 z7yH)TI5~CBM*J!wZ@bzMC%+jwwQm=eh%EmUq77@1$6J#X z8)JP$nKmJuZ{-_7cUQ!5h?5X)h?5@+os$q(A@X)4UpMB>LK{lu^FE;s_kn*F zkuM34q>%FtE6stGhcOZj8tqgEoZq!uiKQw;ypS z;-IfwoP0JaehZOjQQqjWAx_=~g@+Ks`ol(kIqLX@ubwzL>tP;s*)Hp2SsUho=jAuA zy?J$RL!6xTe2B;^&I+tQHpIz!?aJ%WBe4eY(%FUq$Sy>@&&Zk2%WE5w@>wr+ScY}{ zUkMWzwRaRTn4J;-g_|ymE899cT6_B^Pi%EV%;&qiip$#jDX6T-R%}2lN`%uPt2R+h zbiUD|w5lRsz`t)qHItB%dL;K!e6|>gmbwP1)kd;G#YFj^k4TCH^;DjX_Ib_Zg^6v2 z{(?yYB-peGQ2 z^@OXCVn2d`9z{P=kHVp{M4-3ON7P$7!J&XJ*jX?lHsmNK`*M{XEsL=>dP^2G$y=JB zqxR`&U%0rnb&7A@KvqSL^}to)e8Y}QbvUk1u~;CC=D3K(A?TB0u_d@@Af^L=Q8Dc{ z=mzzO*P$q_cwK{3v?|VS&S{<9ymJaeZ-f(;=x+L-csNHOIP2}T1k<#)mq z(|TY^O9cZleFLQxQ_kgRRmAiu#3W2>(I3Th9<-x@n4W}~glP+O;xM&%t${&33HePukkTeI!K|%4l5!ys07>F0&=Tf|GL#i}h)etA)b%Mj|zk`jK z6!AI>QSr)xi?+o%IEb7$ypSbxkTWl74h}=79HwWXt+gpIRUKk_0%{C~(Z=*tteJ`_ z&j``LIrt>*Q7NXk15+eIftc=R(5|ZdPo$z%aSk2?aT2CSpdOurM@1_^9mMooh)I|Z zLnjVXI|uKFPC2|*pa(hhCut6<4)Hn>H3q|IF~N4 zsi>)m7oP_t;q?vpkK)DCVl)u1A0j5IlTDbcU20!REKySj2eSswDI}| z?w2TD{|q){Qk-AAZe#IkMek{;U?5&+!_K;94kHycHSywuZN;k&DJ(Rh@D|d%C!~0N zJ(R0S;>EfYulRGz2y~M0dJ{V3@cI_|yEX-0s{am^hg7sJ zxsHQ439s#-Qw}e_cT%4MFV!Jl4d@SC+80I}uPGR~;`NHJ6^|ue!&r87eti#}p%sFG zcpVNqC(We?T*pD2gx6%~l*8*8bxJNB$(NBylTbzGvdp zsgq}LdzSS>=i1-cx-C_a&*wV}?OheCorKyoumpQY8&o;mwJqX)2jrYHUWnq(vS`W| z?EO_&Grv+&BJd8(+Ya|1mC(>%SMT1#;{H_(4ad+>;LN=kcq#6;BNglq)rtFRhkFN7 zaFkFW?(OKb;{J9hSChp3Dpaqy&qXTQmR$Wo9EUq=Iu7|h zU2&MI4l%{H73#CalqjJ(=-?w1ET*)vcuNT#79pm$U?S^WzKfzrcnp%i?`1J1Dzs)W z5Yw43pkjI*Qqcy9=>&L0((ZXQs#i=0f{mCIF?}7?vlmsBS$sy@BBsNLahTexKR%Ht zhZq0PBV8|UF5A1Hs>A-&NBT3`c%1+vC*k$|2^OzMV)5dS{wQA8NBRtb%)t{JUh!8G zPddDAV6jLbUK^TTl*4Ov3cUD4vK(I1&`x~{yi|vH!B8Rp8Ew2agTpIc-;3CW0?x0q zuqAQjuBv<%BM7eH)ZzSE0Xr*RS0M#MgaYT+N{3hc>9iZw>-^&Ej|SqkdkVZpQsBim zn9Jc+k9K-e;H5gW&(Y{zaO|Uv*W)ShI%-NXUgKbA#cMdyX9#qUb9l{w1jY!3w~*cg z&ZBsJKa{IU;x#7)UaUT8J$yU?FTM-TH$IZqL)BS7^J@@C%cS`=c3LuCk2<`rghhis zMoV*kedzFtpI;*mulV^@Oo3N61zvo^wcPw_LLI!=O~Olc*3bOn59=i1_2>PQ@yeqT zonPO=AWP#l9)}-2Z^Z9ke3LV2etjtgUR=wP=GO-ac=4^lmK1oY4)Gcv8T)9jhwr7p z>oGk1#q>dcerGI&gfDRa`lrJyetwn{>#Pi1W2V1;$hQ;|@D;S6u=Y`HM?n2S3 zxE}Ib_By`~LJGzR1(tlm;q^|m666!F$51rsJU}&p85IJ zrgE6_C%tp%OwyXEI&5nK8VU|=v@v~U3yW#|8SFX&l9WH~Z#cO5)USCB1U<$lchj<-=-u-*=x)QfNlJ>asW+&sd z6V5h@SNxg(Lx)%V9yb${Lh*Vv7B7CGFKG_0PJ!3_GI-hR)~e)vsnBRy~hDJ6gYS8cUYVs%GD%&es^cX=|}|!XZ^XojD1+s*O0IF$(DR$ zGit*zEEWi(ZNs(BhN}YY*aU4j_6Tc3-gDqe9SpSLXW)cM{>)!E(LE)8{TYY#Q2qI{ z!A4AqSRC)LDDwo<^t7rnixNoqg2n3#5SQC8E=T^_6zh-b(C7E{wW923<24MIP@gaJ zR@FR|te$HaS@9Myyp+GFo!Wyn~=S!hnyp?C(m90k(OvG_T!GfH2 z{)V%27O9|`wc)L0Y=}bg|4r0}<<}_B&g^+GjJD4ohBqkG=i~2VZ_sG%%=>Jm*QgEP zt7_+Opu<7?a0GJhP2ux9;|!o{)MtVNHz{JlXPdg`#;;M?6h6-tx7->o=<^S zeG0tz8*=6Fx)=Gm6nLo)@uC+6!)W7GjmyNO`PFo+#VdY(9Rxe;{JI<+E{)gQDe!vN z;T1o>_~Us=^Q$EVUi{yDn4zKw6HU0#PSN#0?xx?$* zv3T(p-IC61lW>OA_3*~X*eT%r+6LD$Nq7yWz>C*1ad_D?z{>S_26$+DJ9pyG0Jr2V z7WcuMFabh=Yh5pFn0N-jl)~`Ko&lygJMRw7;2gAP0RE~=oDJEvpP-j&!}5EIXXkp9 zLc_i=+CG2c4%W^izE(W;F|aKQt(|L)CwckgXy*@{o#XG5%uC_(zjQtyzee$!((3c$ z&~gw9_D7y&o1RuxW+8#$eZk^2Edj6RQEW|$HA;2pKQqxvus@@X*TFklyk7B*$YY6D zPtoGF6-F4{=ZM!uxZcru!TA;JGi&3S?M#PP{Ij;LQID=sgDe&aT%-73$dd3{odT~T z6YzQicxAESCf$cr9pbfjq`#w$*DgC*ysnG1MFH_TXNkq@evF{>8GGW^7O!EHD2>-W z4zKw80GB$vnov(P5U*<;Ub`X{ZHx13Bn4jl1%-0!;a@P?TnfBYhj{(_?*lyS@QObJ z9C(VwEB?*%r&HkdS_-`W=&=rVR&D#>*3i6c=4Y& z4yM3Mb=cNiv=a26(VkyF#bp<|gtqSTjmTp;zpn1Gc=0Nt^m^EZYb#w3uSbbcpY6Q@ zl!gh=7K~XUaLpWpXIn|F z9)}lKfM6Kyb@f$;*BQZvOp5F3et4Y#$78IAeB&I03$#wKIB20`AqL_Y& zL6*jJK?+Q(aR^Vs^d_tcNtm9S0#mMnNoRh(%@l{J-FH@F>>Tm>c=q?I!z+H@dFLF9 zSNy(nJ?@hv?K}II89RnsQ4ep>v6tU>Jo`M168Q6l(O#oY+Rgg&gT7WgmitcOJZqmx z=nSupf`R+aht59FAywL+k8%DSy(hsMdA(DY!k<@QJyd_bjm08?YZQMSHffC-N#W1) z6ENj3C9K5tMba9jI`sK}Kc+j6OU88Q%gLBNg?kt3^UuSgr7>+ufoWR`OzW^FBw@;5 zOiaR*x9yYGs8bR!T>wm5ux=z_sygcj(-%`<`p^Z*m|lGh}z|^sq-*-IwOklA<80~#$8(^XL*{%z;V-t^oDPCml!>92$yoLhzoi)xrS0WW$ zqo~e3<=GQZqTf82Jk)vxp=hrq_i`Q_p&*s{*$L$v| zREa=LCjf|~>y+0Vrt?bFqI2*Nhv{z63EGf_n{V)<>Ko1J}rOi?7zJ}=*5?Zfx$Hje1<96oNG z^;Ld^*L#OR~`1}-;ZfG%%ba0Jf?s7j>Ysnpi&ysXC03FhTb0x zoTsPlZs#c$z0ka{m|l9y6l6h#8(DgQrt(mWkZfhqrc zQry~W_nn@-b;PCC!_mg;9}chhedo!$Ene~a&MtdcyyEvA{+GeHvD4^(gg@)pvt_?8 z?AfOqr9x}?DDOKQQhMJRx2N^_=yi%+qpnDSDgVnr(%JY^xZkU5RQy@uvJ{xEPJ!tM z37GO971!cCmUNa-o%Mt1FH>OphaV(k`mn?FTTwqW^OXOI5N-`$ji09rQee6$1*Z3> zz;q-9rW-;hZtb=EPQ75taba+wFxqqQJbV=bCPG{B`_6ZNWbumMck<)y9E{(0b}2J< z4s%PusADg`MtSy`i&8-&jkbOG&z00Z@%zp`4_W(+z#~iVJ9j(#Tp#VT@#o zjR~0Ym-ttwz*KeC52l4F$@BE3A17n_H;3skEE@7dgXv8vFnuirrVUwpmWao6S4==X zAI8tq`V^QJ%3x~uowb4~@xstUVYKJq7IytV*=SJ*B0Lud-79=H4>UTEwc# zB@_jM-FGfcz;rG!ZNW8l(%D#b*cSf}Nid8yrtjg_hx&X+q%F2gOeX>~Tw0<(8w1mA zDGmf;`aI^T`g{iUM5`jExfGbbvzx{A%vel|DKH&Of$0ePq?oP%7Yr8DI}$K$1g5pP zzDUAUb!fxC2OC0av@tzty2bQ!Q6SJBF`f2Hi|KDLg3_3dOt6@~jS|tSi0K6>Fm2z{ zV#+(M(ZG3nuER8oRJ1L*=a2%^B`B(x4x*l5u$cZN0n?L+>7*2xiViX5|G5c<(Z=+) z{Vk>^M%rS_#I*J|7E}K3_|lj@h4lxJq0jH(Gg=ifeIf;>@8C?L_Z;{On9)E?uTO#L z>J*s1=`dXqX~<&wassC9u*f8wv6JSh>d-!1@q%HrG2P?zW+Om=^tQc z7%LRG_WlU>Ar;drkSdL7H8xY-PvNvN`{}b2ET&*e1Y*kfJd@6TBV{nPc>N;*uVuii zJ_TN?L%hz341Bcly3OGg|DSn(`-;VjC%CO6RwQ0c*e?{Xe;^gRw&a=`I{1SPv-pfoXP$rf-8WrVqh~sc={Vr7_mLL&FX`R)wDzv4 zfq%Gs9xSp3*F_Y zAviA9^cLcOpdQ^p?uWIa;eCiU#ZsfI%}XWQqW5M~crU+gq2AjQZ3vP*feyoFh*gy) zArk$t9zMl+_&^ujyKUdL)0~G}Oh*J|*5j+du%nIXEG!OsdcPpr7$n4W>pxjc>tO}B zSttVy4~xscR<+AraMZ(9?PEQh3`-mkL)65`;El+SH?=BN(G zR#9;;B=luZ=P^je1SCI)WaLnPskb3n8<23x&mQL2*$tA~!~K$aNa_QUV<71XNKS@i z(h>er=Tq`ozvOC2TAU<0Q*KB13mlva(V2oOLLvJ9zi5SM_%1}xxS8?;8ZHtzQ;vSu z&Xk2?@e8t%`OBHI411H#lsWK}po)F2cN>7B4^~xTyOC2Dh%a!3ti#f0&SOwTrJ=wT z@?q>Lxdg1++(Jo%U-Bd*BLT@vkPJ3j9lHXk zC(tu51bQgo3w8x?_NgQn40Z*`LM$VVc`J`Az*lD4z4B=_Y_KZXE5D0_Jvxg1+_^RW zA7DsBe>L%k*i+b(s>&ub8FgXK?zS!M?EWVVJ1J7nTaa7@ymWSNwrNBG0?uyU1*Jn& zRsI7FMcd-+Zh__K`sHSK4zrRzR8_fxW{L#P?r%b>=b!pWzMb8VxY;cy{KZ{`e&5`# zsCW;?%Pue|N4qQaZj8}RvN|I9Bqa3#NgX6SHF`dOC?s42J;`y9v;<0ZLDCbD@X6$$ zlUT3Q9?}{;s4-{|>vgUz@hF`g(ph|1uN!YlmpbWDmtJ=a_HJ|nZQVyvByitZGTwS! z0~(4h_1t&1!s$Z2t{@LAnc9zZHOTj+JS9BaKU6RiaK^t#RA2I_S;Ar)1l z*A15OI_oVjJ8v<2L2JNSDz1WIR$6qT&A9&)syejc`_Q5_yff zJJjNJQ4n;~B=P$G-WIPdVQwsip+LOOcX%yBkAj`D=Y$coz@@0%Ibo-5ET&j?WQ4)S zi0NI&SxiqY*=J(3qUj(X6FZ@*X6^0m(;@I8TuN zeF9U3YqabjBH132>@lWwk?cnaIPo!&90*ArIIq;qiUxY-tIjjg`4Vl>Ggrebv`kfH7N60f(KGo=V(OV| zpc4)B%o^yaXQJ~Z!ZE00*dVw;bl7f1QXTeZAJkHM2N{nX@s?8cm=%e0TKuPk4!hTf z{;WySAJt)hqBvL|8;$;)jp|3r^oM=E1UhT2jn0au0>@-F)Z4A6EAgNo4 z{D5RSzsG7M{l#TGb7ZVlKUVAfotHq(({R`cO6snUw9YH&VC%K()Os$``nX8z!;#jn zjkLbqI<68KBCTiFsrA{B*0+naeo3VD^CGQ}U#Hd&kF?Hbq`|TC z1QC!-Um0nA(mJ)iB+~jpk=E~zwEkkG^%?8bI{%+|U|iECMOwct()y1gt=F$p>+>S5 zXCtj&6=|LCqMJUO`F{91wcZwKeMY49dm^p#ea2wxxpiv2CDM9br1e#i)}L^#FX<|D z7Lo6*hl~1`40N4>D{-$LW1nMe50MiNA7_`TN8z|LfLXDpjDe2Wr;;27=?ivMueCia z@#eYFsk;E>w4V4KZ9R^wj@PNN{n7nvT7B}k_V2K`SK@FV3U>E&+*VynSTg+;uZL#r zgX`4#T5l8v_ou>b)g2y|Oy^sfLCddLr`9J##?E_1LCc>TX?>yBXEXMd>(n}zw?JFd zUJSQYcT89^{VuPEru8eCDUg| zTEA(XTF*u-|Ict+b<4t%=~KKu8_Ta+r`C6hwEkhZt-AiOWcs*B>-Voy>wJSI=yh*~ z+p22|OQ!!dV)@nU)cSUj*8dW2tF9+3nZ840>`$yy>tiCVuMM|V*AbRX-z?JlnssXZ zFJ3)n#;pmrRW~mznSN%(^3ShR>(!C5kA&N*J0UEY{$!-}k#%aF|C=l5b#I5;s#_A4 zOy9^^%$`^4osK=P`qm9IE8ny|MBl?-yR8u_=?0NMdNVT34I_rO2 zbt}sC$9OOAF~;^;b@u0PJgW|$*93#TLw2{N0c6xxGG`(v*_~}OfYs= zzcU-{`0t?8W5&h0@SKsQU_<BiVhsbmf$JDJuuz%%;Z zBCq~3fwmT~l)+R>b((rU`pDLEH6(V`Qk{Dsv8QE~{1+tuL{-FHB_pOD2#n-Ukl0;M zb*gc@+FsqDhxuY}Z&!CkHqgWEpkwc@XsO+Kle-r8@m&u;O-V)8CpiWZ&MHr*Uv#Kv z`*U7Ias?zKum(}mwyurn+y%)K$oEG2Q%H1o$eL0wL82!#D|ru+C4q5O;qmy3fqKS5 za!f#H8YC+Nk|X(09f4!h9v%-#&|5xFoq&BVgT&q+(^A(%@;ubMQ9J-iOQ5Z%S&s=? zY6KFycdO*xfKEQ&*3psAOBv7dmFNqDC)oy)F_3tz?IQ0f*)UW3FN>0^-CD-Lb_6-aF0EGTqzbhj3Qe)wKM zhuYO!;Qc62ujU;f@r|NY>Zzy?^l&fetO!WzA+h&=v_FlI*n4^^DL_I~8O!$;rRu{2 z_4Gr>-T~H9UxXw$2QP8VKWm!On|MA=2Rnyk@fn+eCGZPXzqUWo}iG)(y=MIth{dqYg z^kuKq`HVeMl0m%wTZVE^)hvX?&5BqU9B&z~C4@0sxpz|Cgs{=Z}gTz}mSPu_(_9O$( zZ0sl&d+O^(0;RU)B4)w{6?-Bzv8}jpU~#_CTj*L`?6b+9frY&H>eB69i@I%IVd28w z;!>OHXzwaoeo-&)*P0q(A-PyOU6wyI!1{5_2AldmJ;5n2P}^Z6rYHRR{bZCrqPjnv}S)_iYqaeH5X zu@^@Yj?hTkicxvzKu25qRNF;6+C0X)xUGE(OLp`v>7@$TLf;b6R@O>eJ9~`kfXB9W z6brri)_F71x2kYYVT_AZ!dJ9Y}>@CQ>RRxEx3o|DT>xwPRcLF2REGf8 zjHYj3p_P&~QW(Ta$r>rlVe2Jp>Rq&`&ol@sE1>%LYQE3tLlGUeLZmzrE~CKsH^UD$V8 zp~ti;ylHe%`?7(qzP?3$?KHBc+5)L%UaD_Vn^kM`*kv9oRXH7`d}>HJ&5rIue;WoX zZEB*;pIGK42D$=@O!`!rRAmlY9ig+k&2R&ra3CnnpOy!#OW8h^B(shwzP^~3FDT{l z`cZLNzQ1=l<|q*&;&Y4UyC|MN1+?u92~JyPr{ z^1)O@AumnK=ftUXlPAxp$a7uGcQ4A@)|VDK2GI7xLSGRb=VQ# zQ&!1*YdaQC4gweQ75UbV0v4V{%Wa>!mvX6x-7E4~>I}z`_d%V@t|{poKQcSpm%${_ zJnlwVyZi9by2Mmw5^4ji)rHg&0`bSikJQ zIbE$C-E@>gy=iaEfnuzuq1bTG;w3B z2T1um-sSf7)asy4)!`63+81{AEydmJd;z|$*Egc5*Y0_@?_$KQr*O~f#G!`QMs`Ei z4ERW~7jQABvv4wICoTi}mSb{v=CPsnVYyUuv8iPpzHZ^{0ZDs6vgDM$emVb{JltxR z($ce8a#m?9b{2Y;boUl#3bbKEl~gjggzYvL%ULhyW3g>^5htO}Vpl(*Mx94?w+(a{ z^k-5Qr<=!H#Qq_~JDPD|XFr~k{C1TwntRIzZ*&B1+&WXIBC(B1@@mH(UneZ!hvHVc zxUN;P=EMX9P~Ck2gwUThzgqy%_D*budeoAuVqA>nvDwoJaHb=)Sfdtq(QX1${NPz! zCXnsIEdT6*1JC5$#Cm!<4QjPyB-kN~*s~n)KF@D1wD)8GX_OP%QF7AM86f`C>grJg z$fjnduhblw`1wG$;%Zlhv_XdUqGq>UbOGf~W@UktXN?RPN$naKg cQT5Ln@NTxp&qBKdVe$9I=$3yAp4#;P0UUHOng9R* diff --git a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj index 71bac458b..7622293d2 100644 --- a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj +++ b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj @@ -168,8 +168,18 @@ + + + + + + AdvancedView.xaml + + + EncoderOptionsView.xaml + QueueSelectionView.xaml @@ -204,7 +214,7 @@ - + @@ -241,8 +251,8 @@ Code - - AdvancedView.xaml + + X264View.xaml AudioView.xaml @@ -331,6 +341,14 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -347,7 +365,7 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile diff --git a/win/CS/HandBrakeWPF/Startup/CastleBootstrapper.cs b/win/CS/HandBrakeWPF/Startup/CastleBootstrapper.cs index 97930ba4d..ae0369d7f 100644 --- a/win/CS/HandBrakeWPF/Startup/CastleBootstrapper.cs +++ b/win/CS/HandBrakeWPF/Startup/CastleBootstrapper.cs @@ -83,7 +83,9 @@ namespace HandBrakeWPF.Startup // Tab Components this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); + this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); + this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); this.windsorContainer.Register(Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)); diff --git a/win/CS/HandBrakeWPF/ViewModels/AdvancedViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/AdvancedViewModel.cs index 1c0f9de67..470a27be2 100644 --- a/win/CS/HandBrakeWPF/ViewModels/AdvancedViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/AdvancedViewModel.cs @@ -9,18 +9,10 @@ namespace HandBrakeWPF.ViewModels { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Parsing; using HandBrake.Interop.Model.Encoding; - using HandBrakeWPF.Commands.Interfaces; - using HandBrakeWPF.Helpers; - using HandBrakeWPF.Model; using HandBrakeWPF.ViewModels.Interfaces; ///

@@ -28,920 +20,68 @@ namespace HandBrakeWPF.ViewModels /// public class AdvancedViewModel : ViewModelBase, IAdvancedViewModel { - /// - /// The advanced encoder options command. - /// - private readonly IAdvancedEncoderOptionsCommand advancedEncoderOptionsCommand; - #region Constants and Fields - /// - /// AdvancedOptionsCache; - /// - private string optionsCache = string.Empty; - /// /// Backing field for displaying x264 options /// private bool? displayX264Options; /// - /// The adaptive b frames. - /// - private AdvancedChoice adaptiveBFrames; - - /// - /// The adaptive quantization strength. - /// - private double adaptiveQuantizationStrength; - - /// - /// The analysis. - /// - private AdvancedChoice analysis; - - /// - /// The b frames. - /// - private AdvancedChoice bFrames; - - /// - /// The cabac entropy coding. - /// - private bool cabacEntropyCoding; - - /// - /// The deblocking strength. - /// - private AdvancedChoice deblockingStrength; - - /// - /// The deblocking threshold. - /// - private AdvancedChoice deblockingThreshold; - - /// - /// The direct prediction. - /// - private AdvancedChoice directPrediction; - - /// - /// The eight by eight dct. + /// The show x 264 panel. /// - private bool eightByEightDct; - - /// - /// The motion estimation method. - /// - private AdvancedChoice motionEstimationMethod; - - /// - /// The motion estimation range. - /// - private int motionEstimationRange; - - /// - /// The no dct decimate. - /// - private bool noDctDecimate; - - /// - /// The psychovisual rate distortion. - /// - private double psychovisualRateDistortion; - - /// - /// The psychovisual trellis. - /// - private double psychovisualTrellis; - - /// - /// The pyramidal b frames. - /// - private AdvancedChoice pyramidalBFrames; - - /// - /// The reference frames. - /// - private AdvancedChoice referenceFrames; - - /// - /// The subpixel motion estimation. - /// - private AdvancedChoice subpixelMotionEstimation; - - /// - /// The trellis. - /// - private AdvancedChoice trellis; - - /// - /// X264 options that have UI elements that correspond to them. - /// - private HashSet uiOptions = new HashSet - { - "ref", - "bframes", - "b-adapt", - "direct", - "weightp", - "b-pyramid", - "me", - "subme", - "subq", - "merange", - "analyse", - "8x8dct", - "cabac", - "trellis", - "aq-strength", - "psy-rd", - "no-dct-decimate", - "deblock" - }; - - /// - /// The weighted p frames. - /// - private bool weightedPFrames; - - #endregion - - #region Constructors and Destructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// The advanced Encoder Options Command. - /// - public AdvancedViewModel(IAdvancedEncoderOptionsCommand advancedEncoderOptionsCommand) - { - this.advancedEncoderOptionsCommand = advancedEncoderOptionsCommand; - this.Task = new EncodeTask(); - this.UpdateUIFromAdvancedOptions(); - } - - /// - /// The task object property changed. - /// - /// - /// The sender. - /// - /// - /// The PropertyChangedEventArgs. - /// - private void Task_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) - { - if (e.PropertyName == UserSettingConstants.ShowAdvancedTab) - { - ShowX264AdvancedOptions = this.Task.ShowAdvancedTab; - this.NotifyOfPropertyChange(() => ShowX264AdvancedOptions); - } - } + private bool showX264Panel; #endregion #region Properties /// - /// Gets or sets a value indicating whether show x 264 advanced options. + /// Gets or sets the x 264 view model. /// - public bool ShowX264AdvancedOptions { get; set; } - - /// - /// Gets or sets a value indicating whether DisplayX264Options. - /// - public bool? DisplayX264Options - { - get - { - return this.displayX264Options; - } - set - { - this.displayX264Options = value; - - if (this.displayX264Options == false) - { - this.ShowX264AdvancedOptions = false; - } - - if (this.displayX264Options == true && this.Task.ShowAdvancedTab) - { - this.ShowX264AdvancedOptions = true; - } - - this.NotifyOfPropertyChange(() => this.DisplayX264Options); - this.NotifyOfPropertyChange(() => this.ShowX264AdvancedOptions); - } - } + public IX264ViewModel X264ViewModel { get; set; } /// - /// Gets or sets AdaptiveBFrames. + /// Gets or sets the encoder options view model. /// - public AdvancedChoice AdaptiveBFrames - { - get - { - return this.adaptiveBFrames; - } - - set - { - this.adaptiveBFrames = value; - this.NotifyOfPropertyChange(() => this.AdaptiveBFrames); - this.UpdateOptionsString(); - } - } + public IEncoderOptionsViewModel EncoderOptionsViewModel { get; set; } /// - /// Gets or sets AdaptiveQuantizationStrength. + /// Gets or sets a value indicating whether show x 264 panel. /// - public double AdaptiveQuantizationStrength + public bool ShowX264Panel { get { - return this.adaptiveQuantizationStrength; + return this.showX264Panel; } - set { - this.adaptiveQuantizationStrength = value; - this.NotifyOfPropertyChange(() => this.AdaptiveQuantizationStrength); - this.UpdateOptionsString(); + this.showX264Panel = value; + this.NotifyOfPropertyChange(() => this.ShowX264Panel); } } /// - /// Gets or sets AdvancedOptionsString. - /// - public string AdvancedOptionsString - { - get - { - return this.Task.AdvancedEncoderOptions; - } - - set - { - this.Task.AdvancedEncoderOptions = value; - this.UpdateUIFromAdvancedOptions(); - this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); - - // Reset the video tab if the user is using this tab. - if (!string.IsNullOrEmpty(this.Task.AdvancedEncoderOptions)) - { - this.advancedEncoderOptionsCommand.ExecuteClearVideo(); - } - } - } - - /// - /// Gets or sets Analysis. - /// - public AdvancedChoice Analysis - { - get - { - return this.analysis; - } - - set - { - this.analysis = value; - this.NotifyOfPropertyChange(() => this.Analysis); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets a value indicating whether AutomaticChange. - /// - public bool AutomaticChange { get; set; } - - /// - /// Gets or sets BFrames. - /// - public AdvancedChoice BFrames - { - get - { - return this.bFrames; - } - - set - { - this.bFrames = value; - this.NotifyOfPropertyChange(() => this.BFrames); - this.NotifyOfPropertyChange(() => this.BFramesOptionsVisible); - this.NotifyOfPropertyChange(() => this.PyramidalBFramesVisible); - this.UpdateOptionsString(); - } - } - - /// - /// Gets a value indicating whether BFramesOptionsVisible. - /// - public bool BFramesOptionsVisible - { - get - { - return this.BFrames.Value != "0"; - } - } - - /// - /// Gets or sets a value indicating whether CabacEntropyCoding. - /// - public bool CabacEntropyCoding - { - get - { - return this.cabacEntropyCoding; - } - - set - { - this.cabacEntropyCoding = value; - this.NotifyOfPropertyChange(() => this.CabacEntropyCoding); - this.NotifyOfPropertyChange(() => this.PsychovisualTrellisVisible); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets DeblockingStrength. - /// - public AdvancedChoice DeblockingStrength - { - get - { - return this.deblockingStrength; - } - - set - { - this.deblockingStrength = value; - this.NotifyOfPropertyChange(() => this.DeblockingStrength); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets DeblockingThreshold. - /// - public AdvancedChoice DeblockingThreshold - { - get - { - return this.deblockingThreshold; - } - - set - { - this.deblockingThreshold = value; - this.NotifyOfPropertyChange(() => this.DeblockingThreshold); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets DirectPrediction. - /// - public AdvancedChoice DirectPrediction - { - get - { - return this.directPrediction; - } - - set - { - this.directPrediction = value; - this.NotifyOfPropertyChange(() => this.DirectPrediction); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets a value indicating whether EightByEightDct. - /// - public bool EightByEightDct - { - get - { - return this.eightByEightDct; - } - - set - { - this.eightByEightDct = value; - this.NotifyOfPropertyChange(() => this.EightByEightDct); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets MotionEstimationMethod. - /// - public AdvancedChoice MotionEstimationMethod - { - get - { - return this.motionEstimationMethod; - } - - set - { - this.motionEstimationMethod = value; - this.NotifyOfPropertyChange(() => this.MotionEstimationMethod); - - if ((MotionEstimationMethod.Value == "hex" || MotionEstimationMethod.Value == "dia") && (motionEstimationRange > 16)) - { - this.motionEstimationRange = 16; - this.NotifyOfPropertyChange(() => this.MotionEstimationRange); - } - - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets MotionEstimationRange. - /// - public int MotionEstimationRange - { - get - { - return this.motionEstimationRange; - } - - set - { - if ((MotionEstimationMethod.Value == "hex" || MotionEstimationMethod.Value == "dia") && (value > 16)) - { - this.motionEstimationRange = 16; - } - else if (value < 4) - { - this.motionEstimationRange = 4; - } - else - { - this.motionEstimationRange = value; - } - - this.NotifyOfPropertyChange(() => this.MotionEstimationRange); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets a value indicating whether NoDctDecimate. - /// - public bool NoDctDecimate - { - get - { - return this.noDctDecimate; - } - - set - { - this.noDctDecimate = value; - this.NotifyOfPropertyChange(() => this.NoDctDecimate); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets PsychovisualRateDistortion. - /// - public double PsychovisualRateDistortion - { - get - { - return this.psychovisualRateDistortion; - } - - set - { - this.psychovisualRateDistortion = value; - this.NotifyOfPropertyChange(() => this.PsychovisualRateDistortion); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets PsychovisualTrellis. - /// - public double PsychovisualTrellis - { - get - { - return this.psychovisualTrellis; - } - - set - { - this.psychovisualTrellis = value; - this.NotifyOfPropertyChange(() => this.PsychovisualTrellis); - this.UpdateOptionsString(); - } - } - - /// - /// Gets a value indicating whether PsychovisualTrellisVisible. - /// - public bool PsychovisualTrellisVisible - { - get - { - return this.CabacEntropyCoding && this.Trellis.Value != "0"; - } - } - - /// - /// Gets a value indicating whether PsychovisualRateDistortionVisible. - /// - public bool PsychovisualRateDistortionVisible - { - get - { - int value; - int.TryParse(this.SubpixelMotionEstimation.Value.Trim(), out value); - return value >= 6; - } - } - - /// - /// Gets or sets PyramidalBFrames. - /// - public AdvancedChoice PyramidalBFrames - { - get - { - return this.pyramidalBFrames; - } - - set - { - this.pyramidalBFrames = value; - this.NotifyOfPropertyChange(() => this.PyramidalBFrames); - this.UpdateOptionsString(); - } - } - - /// - /// Gets a value indicating whether PyramidalBFramesVisible. - /// - public bool PyramidalBFramesVisible - { - get - { - return int.Parse(this.BFrames.Value) > 1; - } - } - - /// - /// Gets or sets ReferenceFrames. - /// - public AdvancedChoice ReferenceFrames - { - get - { - return this.referenceFrames; - } - - set - { - this.referenceFrames = value; - this.NotifyOfPropertyChange(() => this.ReferenceFrames); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets SubpixelMotionEstimation. - /// - public AdvancedChoice SubpixelMotionEstimation - { - get - { - return this.subpixelMotionEstimation; - } - - set - { - this.subpixelMotionEstimation = value; - this.NotifyOfPropertyChange(() => this.SubpixelMotionEstimation); - this.NotifyOfPropertyChange(() => this.PsychovisualRateDistortionVisible); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets Task. - /// - public EncodeTask Task { get; set; } - - /// - /// Gets or sets Trellis. - /// - public AdvancedChoice Trellis - { - get - { - return this.trellis; - } - - set - { - this.trellis = value; - this.NotifyOfPropertyChange(() => this.Trellis); - this.NotifyOfPropertyChange(() => this.PsychovisualTrellisVisible); - this.UpdateOptionsString(); - } - } - - /// - /// Gets or sets a value indicating whether WeightedPFrames. + /// Gets or sets a value indicating whether DisplayX264Options. /// - public bool WeightedPFrames + public bool? ShowSimplePanel { get { - return this.weightedPFrames; + return this.displayX264Options; } - set { - this.weightedPFrames = value; - this.NotifyOfPropertyChange(() => this.WeightedPFrames); - this.UpdateOptionsString(); - } - } - - #endregion - - #region Public Methods - - /// - /// The update ui from advanced options. - /// - public void UpdateUIFromAdvancedOptions() - { - this.AutomaticChange = true; - - // Reset UI to defaults, and re-apply options. - this.SetAdvancedToDefaults(); - - if (this.Task.AdvancedEncoderOptions == null) - { - this.AutomaticChange = false; - return; - } - - // Check the updated options string. Update UI for any recognized options. - string[] newOptionsSegments = this.Task.AdvancedEncoderOptions.Split(':'); - foreach (string newOptionsSegment in newOptionsSegments) - { - int equalsIndex = newOptionsSegment.IndexOf('='); - if (equalsIndex >= 0) - { - string optionName = newOptionsSegment.Substring(0, equalsIndex); - string optionValue = newOptionsSegment.Substring(equalsIndex + 1); - - if (optionName != string.Empty && optionValue != string.Empty) - { - AdvancedChoice newChoice; - int parseInt; - double parseDouble; - string[] subParts; - - switch (optionName) - { - case "ref": - if (int.TryParse(optionValue, out parseInt)) - { - newChoice = - AdvancedChoicesHelper.ReferenceFrames.SingleOrDefault( - choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); - if (newChoice != null) - { - this.ReferenceFrames = newChoice; - } - } - - break; - case "bframes": - if (int.TryParse(optionValue, out parseInt)) - { - newChoice = - AdvancedChoicesHelper.BFrames.SingleOrDefault( - choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); - if (newChoice != null) - { - this.BFrames = newChoice; - } - } - - break; - case "b-adapt": - newChoice = - AdvancedChoicesHelper.AdaptiveBFrames.SingleOrDefault( - choice => choice.Value == optionValue); - if (newChoice != null) - { - this.AdaptiveBFrames = newChoice; - } - - break; - case "direct": - newChoice = - AdvancedChoicesHelper.DirectPrediction.SingleOrDefault( - choice => choice.Value == optionValue); - if (newChoice != null) - { - this.DirectPrediction = newChoice; - } - - break; - case "weightp": - if (optionValue == "0") - { - this.WeightedPFrames = false; - } - else if (optionValue == "1") - { - this.WeightedPFrames = true; - } - - break; - case "b-pyramid": - newChoice = - AdvancedChoicesHelper.PyramidalBFrames.SingleOrDefault( - choice => choice.Value == optionValue); - if (newChoice != null) - { - this.PyramidalBFrames = newChoice; - } - - break; - case "me": - newChoice = - AdvancedChoicesHelper.MotionEstimationMethod.SingleOrDefault( - choice => choice.Value == optionValue); - if (newChoice != null) - { - this.MotionEstimationMethod = newChoice; - } - - break; - case "subme": - case "subq": - if (int.TryParse(optionValue, out parseInt)) - { - newChoice = - AdvancedChoicesHelper.SubpixelMotionEstimation.SingleOrDefault( - choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); - if (newChoice != null) - { - this.SubpixelMotionEstimation = newChoice; - } - } - - break; - case "merange": - if (int.TryParse(optionValue, out parseInt)) - { - this.MotionEstimationRange = parseInt; - } - - break; - case "analyse": - newChoice = - AdvancedChoicesHelper.Analysis.SingleOrDefault( - choice => choice.Value == optionValue); - if (newChoice != null) - { - this.Analysis = newChoice; - } - - break; - case "8x8dct": - if (optionValue == "0") - { - this.EightByEightDct = false; - } - else if (optionValue == "1") - { - this.EightByEightDct = true; - } - - break; - case "cabac": - if (optionValue == "0") - { - this.CabacEntropyCoding = false; - } - else if (optionValue == "1") - { - this.CabacEntropyCoding = true; - } - - break; - case "trellis": - if (int.TryParse(optionValue, out parseInt)) - { - newChoice = - AdvancedChoicesHelper.Trellis.SingleOrDefault( - choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); - if (newChoice != null) - { - this.Trellis = newChoice; - } - } - - break; - case "aq-strength": - if (double.TryParse(optionValue, NumberStyles.Any, CultureInfo.InvariantCulture, out parseDouble) && parseDouble >= 0.0 && - parseDouble <= 2.0) - { - this.AdaptiveQuantizationStrength = Math.Round(parseDouble, 1); - } - - break; - case "psy-rd": - subParts = optionValue.Split(','); - if (subParts.Length == 2) - { - double psyRD, psyTrellis; - if (double.TryParse(subParts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out psyRD) && - double.TryParse(subParts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out psyTrellis)) - { - if (psyRD >= 0.0 && psyRD <= 2.0 && psyTrellis >= 0.0 && psyTrellis <= 1.0) - { - this.PsychovisualRateDistortion = Math.Round(psyRD, 1); - this.PsychovisualTrellis = Math.Round(psyTrellis, 2); - } - } - } - - break; - case "no-dct-decimate": - if (optionValue == "0") - { - this.NoDctDecimate = false; - } - else if (optionValue == "1") - { - this.NoDctDecimate = true; - } - - break; - case "deblock": - subParts = optionValue.Split(','); - if (subParts.Length == 2) - { - int dbStrength, dbThreshold; - if (int.TryParse(subParts[0], out dbStrength) && - int.TryParse(subParts[1], out dbThreshold)) - { - newChoice = - AdvancedChoicesHelper.DeblockingStrength.SingleOrDefault( - choice => choice.Value == subParts[0]); - if (newChoice != null) - { - this.DeblockingStrength = newChoice; - } - - newChoice = - AdvancedChoicesHelper.DeblockingThreshold.SingleOrDefault( - choice => choice.Value == subParts[1]); - if (newChoice != null) - { - this.DeblockingThreshold = newChoice; - } - } - } - - break; - } - } - } + this.displayX264Options = value; + this.NotifyOfPropertyChange(() => this.ShowSimplePanel); } - - this.AutomaticChange = false; } #endregion #region Implemented Interfaces - #region IAdvancedViewModel - /// /// The set encoder. /// @@ -950,28 +90,19 @@ namespace HandBrakeWPF.ViewModels /// public void SetEncoder(VideoEncoder encoder) { - // If we are switching from x264, cache it's settings. - if (this.DisplayX264Options.HasValue && this.DisplayX264Options.Value ) - { - this.optionsCache = this.AdvancedOptionsString; - } + this.EncoderOptionsViewModel.SetEncoder(encoder); + this.X264ViewModel.SetEncoder(encoder); - // UI Set for new encoder. if (encoder == VideoEncoder.X264) { - this.AdvancedOptionsString = optionsCache; - this.DisplayX264Options = true; - } - else if (encoder == VideoEncoder.Theora) - { - this.AdvancedOptionsString = string.Empty; - this.DisplayX264Options = null; + this.ShowX264Panel = true; + this.ShowSimplePanel = false; } else { - this.AdvancedOptionsString = string.Empty; - this.DisplayX264Options = false; - } + this.ShowX264Panel = false; + this.ShowSimplePanel = true; + } } /// @@ -979,13 +110,10 @@ namespace HandBrakeWPF.ViewModels /// public void Clear() { - this.AdvancedOptionsString = string.Empty; + this.EncoderOptionsViewModel.Clear(); + this.X264ViewModel.Clear(); } - #endregion - - #region ITabInterface - /// /// Setup this tab for the specified preset. /// @@ -997,10 +125,8 @@ namespace HandBrakeWPF.ViewModels /// public void SetPreset(Preset preset, EncodeTask task) { - this.Task.PropertyChanged -= this.Task_PropertyChanged; - this.Task = task; - this.Task.PropertyChanged += this.Task_PropertyChanged; - this.AdvancedOptionsString = preset.Task.AdvancedEncoderOptions; + this.EncoderOptionsViewModel.SetPreset(preset, task); + this.X264ViewModel.SetPreset(preset, task); } /// @@ -1011,9 +137,10 @@ namespace HandBrakeWPF.ViewModels /// public void UpdateTask(EncodeTask task) { - this.Task = task; + this.EncoderOptionsViewModel.UpdateTask(task); + this.X264ViewModel.UpdateTask(task); + this.SetEncoder(task.VideoEncoder); - this.AdvancedOptionsString = task.AdvancedEncoderOptions; } /// @@ -1030,184 +157,8 @@ namespace HandBrakeWPF.ViewModels /// public void SetSource(Title title, Preset preset, EncodeTask task) { - this.Task = task; - this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); - } - - #endregion - - #endregion - - #region Methods - - /// - /// The set advanced to defaults. - /// - private void SetAdvancedToDefaults() - { - this.ReferenceFrames = AdvancedChoicesHelper.ReferenceFrames.SingleOrDefault(choice => choice.IsDefault); - this.BFrames = AdvancedChoicesHelper.BFrames.SingleOrDefault(choice => choice.IsDefault); - this.AdaptiveBFrames = AdvancedChoicesHelper.AdaptiveBFrames.SingleOrDefault(choice => choice.IsDefault); - this.DirectPrediction = AdvancedChoicesHelper.DirectPrediction.SingleOrDefault(choice => choice.IsDefault); - this.WeightedPFrames = true; - this.PyramidalBFrames = AdvancedChoicesHelper.PyramidalBFrames.SingleOrDefault(choice => choice.IsDefault); - this.MotionEstimationMethod = - AdvancedChoicesHelper.MotionEstimationMethod.SingleOrDefault(choice => choice.IsDefault); - this.SubpixelMotionEstimation = - AdvancedChoicesHelper.SubpixelMotionEstimation.SingleOrDefault(choice => choice.IsDefault); - this.MotionEstimationRange = 16; - this.Analysis = AdvancedChoicesHelper.Analysis.SingleOrDefault(choice => choice.IsDefault); - this.EightByEightDct = true; - this.CabacEntropyCoding = true; - this.Trellis = AdvancedChoicesHelper.Trellis.SingleOrDefault(choice => choice.IsDefault); - this.AdaptiveQuantizationStrength = 1.0; - this.PsychovisualRateDistortion = 1.0; - this.PsychovisualTrellis = 0.0; - this.DeblockingStrength = - AdvancedChoicesHelper.DeblockingStrength.SingleOrDefault(choice => choice.IsDefault); - this.DeblockingThreshold = - AdvancedChoicesHelper.DeblockingThreshold.SingleOrDefault(choice => choice.IsDefault); - this.NoDctDecimate = false; - } - - /// - /// Update the x264 options string from a UI change. - /// - private void UpdateOptionsString() - { - if (this.AutomaticChange) - { - return; - } - - List newOptions = new List(); - - // First add any parts of the options string that don't correspond to the UI - if (this.AdvancedOptionsString != null) - { - string[] existingSegments = this.AdvancedOptionsString.Split(':'); - foreach (string existingSegment in existingSegments) - { - string optionName = existingSegment; - int equalsIndex = existingSegment.IndexOf('='); - if (equalsIndex >= 0) - { - optionName = existingSegment.Substring(0, existingSegment.IndexOf("=", System.StringComparison.Ordinal)); - } - - if (!this.uiOptions.Contains(optionName) && optionName != string.Empty) - { - newOptions.Add(existingSegment); - } - } - } - - // Now add everything from the UI - if (!this.ReferenceFrames.IsDefault) - { - newOptions.Add("ref=" + this.ReferenceFrames.Value); - } - - if (!this.BFrames.IsDefault) - { - newOptions.Add("bframes=" + this.BFrames.Value); - } - - if (this.BFrames.Value != "0") - { - if (!this.AdaptiveBFrames.IsDefault) - { - newOptions.Add("b-adapt=" + this.AdaptiveBFrames.Value); - } - - if (!this.DirectPrediction.IsDefault) - { - newOptions.Add("direct=" + this.DirectPrediction.Value); - } - - if (this.BFrames.Value != "1" && !this.PyramidalBFrames.IsDefault) - { - newOptions.Add("b-pyramid=" + this.PyramidalBFrames.Value); - } - } - - if (!this.WeightedPFrames) - { - newOptions.Add("weightp=0"); - } - - if (!this.MotionEstimationMethod.IsDefault) - { - newOptions.Add("me=" + this.MotionEstimationMethod.Value); - } - - if (!this.SubpixelMotionEstimation.IsDefault) - { - newOptions.Add("subme=" + this.SubpixelMotionEstimation.Value); - } - - if (this.MotionEstimationRange != 16) - { - newOptions.Add("merange=" + this.MotionEstimationRange); - } - - if (!this.Analysis.IsDefault) - { - newOptions.Add("analyse=" + this.Analysis.Value); - } - - if (this.Analysis.Value != "none" && !this.EightByEightDct) - { - newOptions.Add("8x8dct=0"); - } - - if (!this.CabacEntropyCoding) - { - newOptions.Add("cabac=0"); - } - - if (!this.Trellis.IsDefault) - { - newOptions.Add("trellis=" + this.Trellis.Value); - } - - double psTrellis = 0.0; - if (this.CabacEntropyCoding && this.Trellis.Value != "0") - { - psTrellis = this.PsychovisualTrellis; - } - - if (this.AdaptiveQuantizationStrength != 1.0) - { - newOptions.Add( - "aq-strength=" + this.AdaptiveQuantizationStrength.ToString("F1", CultureInfo.InvariantCulture)); - } - - if (this.PsychovisualRateDistortion != 1.0 || psTrellis > 0.0) - { - newOptions.Add( - "psy-rd=" + this.PsychovisualRateDistortion.ToString("F1", CultureInfo.InvariantCulture) + "," + - psTrellis.ToString("F2", CultureInfo.InvariantCulture)); - } - - if (this.NoDctDecimate) - { - newOptions.Add("no-dct-decimate=1"); - } - - if (!this.DeblockingStrength.IsDefault || !this.DeblockingThreshold.IsDefault) - { - newOptions.Add("deblock=" + this.DeblockingStrength.Value + "," + this.DeblockingThreshold.Value); - } - - this.Task.AdvancedEncoderOptions = string.Join(":", newOptions); - this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); - - // Reset the video tab if the user is using this tab. - if (!string.IsNullOrEmpty(this.Task.AdvancedEncoderOptions)) - { - this.advancedEncoderOptionsCommand.ExecuteClearVideo(); - } + this.EncoderOptionsViewModel.SetSource(title, preset, task); + this.X264ViewModel.SetSource(title, preset, task); } #endregion diff --git a/win/CS/HandBrakeWPF/ViewModels/EncoderOptionsViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/EncoderOptionsViewModel.cs new file mode 100644 index 000000000..e578d9688 --- /dev/null +++ b/win/CS/HandBrakeWPF/ViewModels/EncoderOptionsViewModel.cs @@ -0,0 +1,114 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The Simple Encoder options screen +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.ViewModels +{ + using HandBrake.ApplicationServices.Model; + using HandBrake.ApplicationServices.Parsing; + using HandBrake.Interop.Model.Encoding; + + using HandBrakeWPF.ViewModels.Interfaces; + + /// + /// The Simple Encoder options screen + /// + public class EncoderOptionsViewModel : ViewModelBase, IEncoderOptionsViewModel, ITabInterface + { + /// + /// Initializes a new instance of the class. + /// + public EncoderOptionsViewModel() + { + this.Task = new EncodeTask(); + } + + /// + /// Gets or sets the task. + /// + public EncodeTask Task { get; set; } + + /// + /// Gets or sets the options string. + /// + public string AdvancedOptionsString + { + get + { + return this.Task.AdvancedEncoderOptions; + } + set + { + this.Task.AdvancedEncoderOptions = value; + this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); + } + } + + /// + /// The set source. + /// + /// + /// The selected title. + /// + /// + /// The current preset. + /// + /// + /// The task. + /// + public void SetSource(Title selectedTitle, Preset currentPreset, EncodeTask task) + { + this.Task = task; + this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); + } + + /// + /// The set preset. + /// + /// + /// The preset. + /// + /// + /// The task. + /// + public void SetPreset(Preset preset, EncodeTask task) + { + this.Task = task; + this.AdvancedOptionsString = preset.Task.AdvancedEncoderOptions; + } + + /// + /// The update task. + /// + /// + /// The task. + /// + public void UpdateTask(EncodeTask task) + { + this.Task = task; + this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); + } + + /// + /// The set encoder. + /// + /// + /// The encoder. + /// + public void SetEncoder(VideoEncoder encoder) + { + } + + /// + /// The clear. + /// + public void Clear() + { + } + } +} diff --git a/win/CS/HandBrakeWPF/ViewModels/Interfaces/IEncoderOptionsViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/Interfaces/IEncoderOptionsViewModel.cs new file mode 100644 index 000000000..10f1270d4 --- /dev/null +++ b/win/CS/HandBrakeWPF/ViewModels/Interfaces/IEncoderOptionsViewModel.cs @@ -0,0 +1,32 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The Simple Encoder Options Tab +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.ViewModels.Interfaces +{ + using HandBrake.Interop.Model.Encoding; + + /// + /// The Simple Encoder Options Tab + /// + public interface IEncoderOptionsViewModel : ITabInterface + { + /// + /// Set the currently selected encoder. + /// + /// + /// The Video Encoder. + /// + void SetEncoder(VideoEncoder encoder); + + /// + /// Clear out the settings. + /// + void Clear(); + } +} diff --git a/win/CS/HandBrakeWPF/ViewModels/Interfaces/IX264ViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/Interfaces/IX264ViewModel.cs new file mode 100644 index 000000000..70b72affe --- /dev/null +++ b/win/CS/HandBrakeWPF/ViewModels/Interfaces/IX264ViewModel.cs @@ -0,0 +1,32 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// Defines the IX264ViewModel type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.ViewModels.Interfaces +{ + using HandBrake.Interop.Model.Encoding; + + /// + /// The Advanced View Model Interface + /// + public interface IX264ViewModel : ITabInterface + { + /// + /// Set the currently selected encoder. + /// + /// + /// The Video Encoder. + /// + void SetEncoder(VideoEncoder encoder); + + /// + /// Clear out the settings. + /// + void Clear(); + } +} diff --git a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs index fe52f88ce..33f27c72b 100644 --- a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs @@ -706,6 +706,7 @@ namespace HandBrakeWPF.ViewModels } // Use the Path on the Title, or the Source Scan path if one doesn't exist. + this.SourceLabel = this.SourceName; this.CurrentTask.Source = !string.IsNullOrEmpty(this.selectedTitle.SourceName) ? this.selectedTitle.SourceName : this.ScannedSource.ScanPath; this.CurrentTask.Title = value.TitleNumber; this.NotifyOfPropertyChange(() => this.StartEndRangeItems); @@ -1619,16 +1620,7 @@ namespace HandBrakeWPF.ViewModels // Cleanup this.ShowStatusWindow = false; - - if (this.SelectedTitle != null && !string.IsNullOrEmpty(this.SelectedTitle.SourceName)) - { - this.SourceLabel = this.SelectedTitle.SourceName; - } - else - { - this.SourceLabel = this.SourceName; - } - + this.SourceLabel = this.SourceName; this.StatusLabel = "Scan Completed"; }); } @@ -1801,15 +1793,7 @@ namespace HandBrakeWPF.ViewModels this.ShowStatusWindow = false; if (e.Successful) { - if (this.SelectedTitle != null && !string.IsNullOrEmpty(this.SelectedTitle.SourceName)) - { - this.SourceLabel = this.SelectedTitle.SourceName; - } - else - { - this.SourceLabel = this.SourceName; - } - + this.SourceLabel = this.SourceName; this.StatusLabel = "Scan Completed"; } else if (!e.Successful && e.Exception == null) diff --git a/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs index 8e771fb66..b611e14d6 100644 --- a/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/OptionsViewModel.cs @@ -1614,6 +1614,12 @@ namespace HandBrakeWPF.ViewModels this.PreviewPicturesToScan.Add(20); this.PreviewPicturesToScan.Add(25); this.PreviewPicturesToScan.Add(30); + this.PreviewPicturesToScan.Add(35); + this.PreviewPicturesToScan.Add(40); + this.PreviewPicturesToScan.Add(45); + this.PreviewPicturesToScan.Add(50); + this.PreviewPicturesToScan.Add(55); + this.PreviewPicturesToScan.Add(60); this.SelectedPreviewCount = this.userSettingService.GetUserSetting(ASUserSettingConstants.PreviewScanCount); // x264 step diff --git a/win/CS/HandBrakeWPF/ViewModels/PreviewViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/PreviewViewModel.cs index 7ebb88619..39bc48090 100644 --- a/win/CS/HandBrakeWPF/ViewModels/PreviewViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/PreviewViewModel.cs @@ -22,6 +22,7 @@ namespace HandBrakeWPF.ViewModels using HandBrake.ApplicationServices.Model.Encoding; using HandBrake.ApplicationServices.Services.Interfaces; + using HandBrakeWPF.Services; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.ViewModels.Interfaces; @@ -35,7 +36,7 @@ namespace HandBrakeWPF.ViewModels /// /// Backing field for the encode service. /// - private readonly IEncode encodeService; + private readonly IEncodeServiceWrapper encodeService; /// /// The error service @@ -74,18 +75,17 @@ namespace HandBrakeWPF.ViewModels /// /// Initializes a new instance of the class. /// - /// - /// The encode Service. - /// /// /// The error Service. /// /// /// The user Setting Service. /// - public PreviewViewModel(IEncodeServiceWrapper encodeService, IErrorService errorService, IUserSettingService userSettingService) + public PreviewViewModel(IErrorService errorService, IUserSettingService userSettingService) { - this.encodeService = encodeService; + // Preview needs a seperate instance rather than the shared singleton. This could maybe do with being refactored at some point + this.encodeService = new EncodeServiceWrapper(userSettingService); + this.errorService = errorService; this.userSettingService = userSettingService; this.Title = "Preview"; @@ -93,6 +93,7 @@ namespace HandBrakeWPF.ViewModels this.PercentageValue = 0; this.StartAt = 1; this.Duration = 30; + this.CanPlay = true; UseSystemDefaultPlayer = userSettingService.GetUserSetting(UserSettingConstants.DefaultPlayer); this.Duration = userSettingService.GetUserSetting(UserSettingConstants.LastPreviewDuration); @@ -114,7 +115,7 @@ namespace HandBrakeWPF.ViewModels { get { - return new List { 5, 10, 30, 45, 60, 75, 90, 105, 120 }; + return new List { 5, 10, 30, 45, 60, 75, 90, 105, 120, 150, 180, 210, 240 }; } } @@ -210,6 +211,8 @@ namespace HandBrakeWPF.ViewModels set { this.isEncoding = value; + this.CanPlay = !value; + this.NotifyOfPropertyChange(() => this.CanPlay); this.NotifyOfPropertyChange(() => this.IsEncoding); } } @@ -219,6 +222,11 @@ namespace HandBrakeWPF.ViewModels /// public string CurrentlyPlaying { get; set; } + /// + /// Gets or sets a value indicating whether can play. + /// + public bool CanPlay { get; set; } + #endregion #region Public Methods @@ -279,7 +287,7 @@ namespace HandBrakeWPF.ViewModels } else { - string directory = Path.GetDirectoryName(encodeTask.Destination) ?? string.Empty; + string directory = Path.GetDirectoryName(encodeTask.Destination) ?? string.Empty; string filename = Path.GetFileNameWithoutExtension(encodeTask.Destination); string extension = Path.GetExtension(encodeTask.Destination); string previewFilename = string.Format("{0}_preview{1}", filename, extension); @@ -287,7 +295,7 @@ namespace HandBrakeWPF.ViewModels encodeTask.Destination = previewFullPath; this.CurrentlyPlaying = previewFullPath; } - + // Setup the encode task as a preview encode encodeTask.IsPreviewEncode = true; encodeTask.PreviewEncodeStartAt = this.StartAt.ToString(CultureInfo.InvariantCulture); @@ -415,6 +423,7 @@ namespace HandBrakeWPF.ViewModels { this.Percentage = "0.00%"; this.PercentageValue = 0; + this.IsEncoding = false; this.encodeService.EncodeCompleted -= this.encodeService_EncodeCompleted; this.encodeService.EncodeStatusChanged -= this.encodeService_EncodeStatusChanged; diff --git a/win/CS/HandBrakeWPF/ViewModels/X264ViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/X264ViewModel.cs new file mode 100644 index 000000000..e981b3e02 --- /dev/null +++ b/win/CS/HandBrakeWPF/ViewModels/X264ViewModel.cs @@ -0,0 +1,1155 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The X264 Advanced View Model +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.ViewModels +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + + using HandBrake.ApplicationServices.Model; + using HandBrake.ApplicationServices.Parsing; + using HandBrake.Interop.Model.Encoding; + + using HandBrakeWPF.Commands.Interfaces; + using HandBrakeWPF.Helpers; + using HandBrakeWPF.Model; + using HandBrakeWPF.ViewModels.Interfaces; + + /// + /// The Advanced View Model + /// + public class X264ViewModel : ViewModelBase, IX264ViewModel + { + /// + /// The advanced encoder options command. + /// + private readonly IAdvancedEncoderOptionsCommand advancedEncoderOptionsCommand; + + #region Constants and Fields + + /// + /// The adaptive b frames. + /// + private AdvancedChoice adaptiveBFrames; + + /// + /// The adaptive quantization strength. + /// + private double adaptiveQuantizationStrength; + + /// + /// The analysis. + /// + private AdvancedChoice analysis; + + /// + /// The b frames. + /// + private AdvancedChoice bFrames; + + /// + /// The cabac entropy coding. + /// + private bool cabacEntropyCoding; + + /// + /// The deblocking strength. + /// + private AdvancedChoice deblockingStrength; + + /// + /// The deblocking threshold. + /// + private AdvancedChoice deblockingThreshold; + + /// + /// The direct prediction. + /// + private AdvancedChoice directPrediction; + + /// + /// The eight by eight dct. + /// + private bool eightByEightDct; + + /// + /// The motion estimation method. + /// + private AdvancedChoice motionEstimationMethod; + + /// + /// The motion estimation range. + /// + private int motionEstimationRange; + + /// + /// The no dct decimate. + /// + private bool noDctDecimate; + + /// + /// The psychovisual rate distortion. + /// + private double psychovisualRateDistortion; + + /// + /// The psychovisual trellis. + /// + private double psychovisualTrellis; + + /// + /// The pyramidal b frames. + /// + private AdvancedChoice pyramidalBFrames; + + /// + /// The reference frames. + /// + private AdvancedChoice referenceFrames; + + /// + /// The subpixel motion estimation. + /// + private AdvancedChoice subpixelMotionEstimation; + + /// + /// The trellis. + /// + private AdvancedChoice trellis; + + /// + /// X264 options that have UI elements that correspond to them. + /// + private HashSet uiOptions = new HashSet + { + "ref", + "bframes", + "b-adapt", + "direct", + "weightp", + "b-pyramid", + "me", + "subme", + "subq", + "merange", + "analyse", + "8x8dct", + "cabac", + "trellis", + "aq-strength", + "psy-rd", + "no-dct-decimate", + "deblock" + }; + + /// + /// The weighted p frames. + /// + private bool weightedPFrames; + + #endregion + + #region Constructors and Destructors + + /// + /// Initializes a new instance of the class. + /// + /// + /// The advanced Encoder Options Command. + /// + public X264ViewModel(IAdvancedEncoderOptionsCommand advancedEncoderOptionsCommand) + { + this.advancedEncoderOptionsCommand = advancedEncoderOptionsCommand; + this.Task = new EncodeTask(); + this.UpdateUIFromAdvancedOptions(); + } + + /// + /// The task object property changed. + /// + /// + /// The sender. + /// + /// + /// The PropertyChangedEventArgs. + /// + private void Task_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == UserSettingConstants.ShowAdvancedTab) + { + ShowX264AdvancedOptions = this.Task.ShowAdvancedTab; + this.NotifyOfPropertyChange(() => ShowX264AdvancedOptions); + } + } + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether show x 264 advanced options. + /// + public bool ShowX264AdvancedOptions { get; set; } + + /// + /// Gets or sets AdaptiveBFrames. + /// + public AdvancedChoice AdaptiveBFrames + { + get + { + return this.adaptiveBFrames; + } + + set + { + this.adaptiveBFrames = value; + this.NotifyOfPropertyChange(() => this.AdaptiveBFrames); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets AdaptiveQuantizationStrength. + /// + public double AdaptiveQuantizationStrength + { + get + { + return this.adaptiveQuantizationStrength; + } + + set + { + this.adaptiveQuantizationStrength = value; + this.NotifyOfPropertyChange(() => this.AdaptiveQuantizationStrength); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets AdvancedOptionsString. + /// + public string AdvancedOptionsString + { + get + { + return this.Task.AdvancedEncoderOptions; + } + + set + { + this.Task.AdvancedEncoderOptions = value; + this.UpdateUIFromAdvancedOptions(); + this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); + + // Reset the video tab if the user is using this tab. + if (!string.IsNullOrEmpty(this.Task.AdvancedEncoderOptions)) + { + this.advancedEncoderOptionsCommand.ExecuteClearVideo(); + } + } + } + + /// + /// Gets or sets Analysis. + /// + public AdvancedChoice Analysis + { + get + { + return this.analysis; + } + + set + { + this.analysis = value; + this.NotifyOfPropertyChange(() => this.Analysis); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets a value indicating whether AutomaticChange. + /// + public bool AutomaticChange { get; set; } + + /// + /// Gets or sets BFrames. + /// + public AdvancedChoice BFrames + { + get + { + return this.bFrames; + } + + set + { + this.bFrames = value; + this.NotifyOfPropertyChange(() => this.BFrames); + this.NotifyOfPropertyChange(() => this.BFramesOptionsVisible); + this.NotifyOfPropertyChange(() => this.PyramidalBFramesVisible); + this.UpdateOptionsString(); + } + } + + /// + /// Gets a value indicating whether BFramesOptionsVisible. + /// + public bool BFramesOptionsVisible + { + get + { + return this.BFrames.Value != "0"; + } + } + + /// + /// Gets or sets a value indicating whether CabacEntropyCoding. + /// + public bool CabacEntropyCoding + { + get + { + return this.cabacEntropyCoding; + } + + set + { + this.cabacEntropyCoding = value; + this.NotifyOfPropertyChange(() => this.CabacEntropyCoding); + this.NotifyOfPropertyChange(() => this.PsychovisualTrellisVisible); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets DeblockingStrength. + /// + public AdvancedChoice DeblockingStrength + { + get + { + return this.deblockingStrength; + } + + set + { + this.deblockingStrength = value; + this.NotifyOfPropertyChange(() => this.DeblockingStrength); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets DeblockingThreshold. + /// + public AdvancedChoice DeblockingThreshold + { + get + { + return this.deblockingThreshold; + } + + set + { + this.deblockingThreshold = value; + this.NotifyOfPropertyChange(() => this.DeblockingThreshold); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets DirectPrediction. + /// + public AdvancedChoice DirectPrediction + { + get + { + return this.directPrediction; + } + + set + { + this.directPrediction = value; + this.NotifyOfPropertyChange(() => this.DirectPrediction); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets a value indicating whether EightByEightDct. + /// + public bool EightByEightDct + { + get + { + return this.eightByEightDct; + } + + set + { + this.eightByEightDct = value; + this.NotifyOfPropertyChange(() => this.EightByEightDct); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets MotionEstimationMethod. + /// + public AdvancedChoice MotionEstimationMethod + { + get + { + return this.motionEstimationMethod; + } + + set + { + this.motionEstimationMethod = value; + this.NotifyOfPropertyChange(() => this.MotionEstimationMethod); + + if ((MotionEstimationMethod.Value == "hex" || MotionEstimationMethod.Value == "dia") && (motionEstimationRange > 16)) + { + this.motionEstimationRange = 16; + this.NotifyOfPropertyChange(() => this.MotionEstimationRange); + } + + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets MotionEstimationRange. + /// + public int MotionEstimationRange + { + get + { + return this.motionEstimationRange; + } + + set + { + if ((MotionEstimationMethod.Value == "hex" || MotionEstimationMethod.Value == "dia") && (value > 16)) + { + this.motionEstimationRange = 16; + } + else if (value < 4) + { + this.motionEstimationRange = 4; + } + else + { + this.motionEstimationRange = value; + } + + this.NotifyOfPropertyChange(() => this.MotionEstimationRange); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets a value indicating whether NoDctDecimate. + /// + public bool NoDctDecimate + { + get + { + return this.noDctDecimate; + } + + set + { + this.noDctDecimate = value; + this.NotifyOfPropertyChange(() => this.NoDctDecimate); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets PsychovisualRateDistortion. + /// + public double PsychovisualRateDistortion + { + get + { + return this.psychovisualRateDistortion; + } + + set + { + this.psychovisualRateDistortion = value; + this.NotifyOfPropertyChange(() => this.PsychovisualRateDistortion); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets PsychovisualTrellis. + /// + public double PsychovisualTrellis + { + get + { + return this.psychovisualTrellis; + } + + set + { + this.psychovisualTrellis = value; + this.NotifyOfPropertyChange(() => this.PsychovisualTrellis); + this.UpdateOptionsString(); + } + } + + /// + /// Gets a value indicating whether PsychovisualTrellisVisible. + /// + public bool PsychovisualTrellisVisible + { + get + { + return this.CabacEntropyCoding && this.Trellis.Value != "0"; + } + } + + /// + /// Gets a value indicating whether PsychovisualRateDistortionVisible. + /// + public bool PsychovisualRateDistortionVisible + { + get + { + int value; + int.TryParse(this.SubpixelMotionEstimation.Value.Trim(), out value); + return value >= 6; + } + } + + /// + /// Gets or sets PyramidalBFrames. + /// + public AdvancedChoice PyramidalBFrames + { + get + { + return this.pyramidalBFrames; + } + + set + { + this.pyramidalBFrames = value; + this.NotifyOfPropertyChange(() => this.PyramidalBFrames); + this.UpdateOptionsString(); + } + } + + /// + /// Gets a value indicating whether PyramidalBFramesVisible. + /// + public bool PyramidalBFramesVisible + { + get + { + return int.Parse(this.BFrames.Value) > 1; + } + } + + /// + /// Gets or sets ReferenceFrames. + /// + public AdvancedChoice ReferenceFrames + { + get + { + return this.referenceFrames; + } + + set + { + this.referenceFrames = value; + this.NotifyOfPropertyChange(() => this.ReferenceFrames); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets SubpixelMotionEstimation. + /// + public AdvancedChoice SubpixelMotionEstimation + { + get + { + return this.subpixelMotionEstimation; + } + + set + { + this.subpixelMotionEstimation = value; + this.NotifyOfPropertyChange(() => this.SubpixelMotionEstimation); + this.NotifyOfPropertyChange(() => this.PsychovisualRateDistortionVisible); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets Task. + /// + public EncodeTask Task { get; set; } + + /// + /// Gets or sets Trellis. + /// + public AdvancedChoice Trellis + { + get + { + return this.trellis; + } + + set + { + this.trellis = value; + this.NotifyOfPropertyChange(() => this.Trellis); + this.NotifyOfPropertyChange(() => this.PsychovisualTrellisVisible); + this.UpdateOptionsString(); + } + } + + /// + /// Gets or sets a value indicating whether WeightedPFrames. + /// + public bool WeightedPFrames + { + get + { + return this.weightedPFrames; + } + + set + { + this.weightedPFrames = value; + this.NotifyOfPropertyChange(() => this.WeightedPFrames); + this.UpdateOptionsString(); + } + } + + #endregion + + #region Public Methods + + /// + /// The update ui from advanced options. + /// + public void UpdateUIFromAdvancedOptions() + { + this.AutomaticChange = true; + + // Reset UI to defaults, and re-apply options. + this.SetAdvancedToDefaults(); + + if (this.Task.AdvancedEncoderOptions == null) + { + this.AutomaticChange = false; + return; + } + + // Check the updated options string. Update UI for any recognized options. + string[] newOptionsSegments = this.Task.AdvancedEncoderOptions.Split(':'); + foreach (string newOptionsSegment in newOptionsSegments) + { + int equalsIndex = newOptionsSegment.IndexOf('='); + if (equalsIndex >= 0) + { + string optionName = newOptionsSegment.Substring(0, equalsIndex); + string optionValue = newOptionsSegment.Substring(equalsIndex + 1); + + if (optionName != string.Empty && optionValue != string.Empty) + { + AdvancedChoice newChoice; + int parseInt; + double parseDouble; + string[] subParts; + + switch (optionName) + { + case "ref": + if (int.TryParse(optionValue, out parseInt)) + { + newChoice = + AdvancedChoicesHelper.ReferenceFrames.SingleOrDefault( + choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); + if (newChoice != null) + { + this.ReferenceFrames = newChoice; + } + } + + break; + case "bframes": + if (int.TryParse(optionValue, out parseInt)) + { + newChoice = + AdvancedChoicesHelper.BFrames.SingleOrDefault( + choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); + if (newChoice != null) + { + this.BFrames = newChoice; + } + } + + break; + case "b-adapt": + newChoice = + AdvancedChoicesHelper.AdaptiveBFrames.SingleOrDefault( + choice => choice.Value == optionValue); + if (newChoice != null) + { + this.AdaptiveBFrames = newChoice; + } + + break; + case "direct": + newChoice = + AdvancedChoicesHelper.DirectPrediction.SingleOrDefault( + choice => choice.Value == optionValue); + if (newChoice != null) + { + this.DirectPrediction = newChoice; + } + + break; + case "weightp": + if (optionValue == "0") + { + this.WeightedPFrames = false; + } + else if (optionValue == "1") + { + this.WeightedPFrames = true; + } + + break; + case "b-pyramid": + newChoice = + AdvancedChoicesHelper.PyramidalBFrames.SingleOrDefault( + choice => choice.Value == optionValue); + if (newChoice != null) + { + this.PyramidalBFrames = newChoice; + } + + break; + case "me": + newChoice = + AdvancedChoicesHelper.MotionEstimationMethod.SingleOrDefault( + choice => choice.Value == optionValue); + if (newChoice != null) + { + this.MotionEstimationMethod = newChoice; + } + + break; + case "subme": + case "subq": + if (int.TryParse(optionValue, out parseInt)) + { + newChoice = + AdvancedChoicesHelper.SubpixelMotionEstimation.SingleOrDefault( + choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); + if (newChoice != null) + { + this.SubpixelMotionEstimation = newChoice; + } + } + + break; + case "merange": + if (int.TryParse(optionValue, out parseInt)) + { + this.MotionEstimationRange = parseInt; + } + + break; + case "analyse": + newChoice = + AdvancedChoicesHelper.Analysis.SingleOrDefault( + choice => choice.Value == optionValue); + if (newChoice != null) + { + this.Analysis = newChoice; + } + + break; + case "8x8dct": + if (optionValue == "0") + { + this.EightByEightDct = false; + } + else if (optionValue == "1") + { + this.EightByEightDct = true; + } + + break; + case "cabac": + if (optionValue == "0") + { + this.CabacEntropyCoding = false; + } + else if (optionValue == "1") + { + this.CabacEntropyCoding = true; + } + + break; + case "trellis": + if (int.TryParse(optionValue, out parseInt)) + { + newChoice = + AdvancedChoicesHelper.Trellis.SingleOrDefault( + choice => choice.Value == parseInt.ToString(CultureInfo.InvariantCulture)); + if (newChoice != null) + { + this.Trellis = newChoice; + } + } + + break; + case "aq-strength": + if (double.TryParse(optionValue, NumberStyles.Any, CultureInfo.InvariantCulture, out parseDouble) && parseDouble >= 0.0 && + parseDouble <= 2.0) + { + this.AdaptiveQuantizationStrength = Math.Round(parseDouble, 1); + } + + break; + case "psy-rd": + subParts = optionValue.Split(','); + if (subParts.Length == 2) + { + double psyRD, psyTrellis; + if (double.TryParse(subParts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out psyRD) && + double.TryParse(subParts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out psyTrellis)) + { + if (psyRD >= 0.0 && psyRD <= 2.0 && psyTrellis >= 0.0 && psyTrellis <= 1.0) + { + this.PsychovisualRateDistortion = Math.Round(psyRD, 1); + this.PsychovisualTrellis = Math.Round(psyTrellis, 2); + } + } + } + + break; + case "no-dct-decimate": + if (optionValue == "0") + { + this.NoDctDecimate = false; + } + else if (optionValue == "1") + { + this.NoDctDecimate = true; + } + + break; + case "deblock": + subParts = optionValue.Split(','); + if (subParts.Length == 2) + { + int dbStrength, dbThreshold; + if (int.TryParse(subParts[0], out dbStrength) && + int.TryParse(subParts[1], out dbThreshold)) + { + newChoice = + AdvancedChoicesHelper.DeblockingStrength.SingleOrDefault( + choice => choice.Value == subParts[0]); + if (newChoice != null) + { + this.DeblockingStrength = newChoice; + } + + newChoice = + AdvancedChoicesHelper.DeblockingThreshold.SingleOrDefault( + choice => choice.Value == subParts[1]); + if (newChoice != null) + { + this.DeblockingThreshold = newChoice; + } + } + } + + break; + } + } + } + } + + this.AutomaticChange = false; + } + + #endregion + + #region Implemented Interfaces + + #region IAdvancedViewModel + + /// + /// The set encoder. + /// + /// + /// The encoder. + /// + public void SetEncoder(VideoEncoder encoder) + { + } + + /// + /// The clear. + /// + public void Clear() + { + this.AdvancedOptionsString = string.Empty; + } + + #endregion + + #region ITabInterface + + /// + /// Setup this tab for the specified preset. + /// + /// + /// The preset. + /// + /// + /// The task. + /// + public void SetPreset(Preset preset, EncodeTask task) + { + this.Task.PropertyChanged -= this.Task_PropertyChanged; + this.Task = task; + this.Task.PropertyChanged += this.Task_PropertyChanged; + this.AdvancedOptionsString = preset.Task.AdvancedEncoderOptions; + } + + /// + /// Update all the UI controls based on the encode task passed in. + /// + /// + /// The task. + /// + public void UpdateTask(EncodeTask task) + { + this.Task = task; + this.SetEncoder(task.VideoEncoder); + this.AdvancedOptionsString = task.AdvancedEncoderOptions; + } + + /// + /// Setup this window for a new source + /// + /// + /// The title. + /// + /// + /// The preset. + /// + /// + /// The task. + /// + public void SetSource(Title title, Preset preset, EncodeTask task) + { + this.Task = task; + this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); + } + + #endregion + + #endregion + + #region Methods + + /// + /// The set advanced to defaults. + /// + private void SetAdvancedToDefaults() + { + this.ReferenceFrames = AdvancedChoicesHelper.ReferenceFrames.SingleOrDefault(choice => choice.IsDefault); + this.BFrames = AdvancedChoicesHelper.BFrames.SingleOrDefault(choice => choice.IsDefault); + this.AdaptiveBFrames = AdvancedChoicesHelper.AdaptiveBFrames.SingleOrDefault(choice => choice.IsDefault); + this.DirectPrediction = AdvancedChoicesHelper.DirectPrediction.SingleOrDefault(choice => choice.IsDefault); + this.WeightedPFrames = true; + this.PyramidalBFrames = AdvancedChoicesHelper.PyramidalBFrames.SingleOrDefault(choice => choice.IsDefault); + this.MotionEstimationMethod = + AdvancedChoicesHelper.MotionEstimationMethod.SingleOrDefault(choice => choice.IsDefault); + this.SubpixelMotionEstimation = + AdvancedChoicesHelper.SubpixelMotionEstimation.SingleOrDefault(choice => choice.IsDefault); + this.MotionEstimationRange = 16; + this.Analysis = AdvancedChoicesHelper.Analysis.SingleOrDefault(choice => choice.IsDefault); + this.EightByEightDct = true; + this.CabacEntropyCoding = true; + this.Trellis = AdvancedChoicesHelper.Trellis.SingleOrDefault(choice => choice.IsDefault); + this.AdaptiveQuantizationStrength = 1.0; + this.PsychovisualRateDistortion = 1.0; + this.PsychovisualTrellis = 0.0; + this.DeblockingStrength = + AdvancedChoicesHelper.DeblockingStrength.SingleOrDefault(choice => choice.IsDefault); + this.DeblockingThreshold = + AdvancedChoicesHelper.DeblockingThreshold.SingleOrDefault(choice => choice.IsDefault); + this.NoDctDecimate = false; + } + + /// + /// Update the x264 options string from a UI change. + /// + private void UpdateOptionsString() + { + if (this.AutomaticChange) + { + return; + } + + List newOptions = new List(); + + // First add any parts of the options string that don't correspond to the UI + if (this.AdvancedOptionsString != null) + { + string[] existingSegments = this.AdvancedOptionsString.Split(':'); + foreach (string existingSegment in existingSegments) + { + string optionName = existingSegment; + int equalsIndex = existingSegment.IndexOf('='); + if (equalsIndex >= 0) + { + optionName = existingSegment.Substring(0, existingSegment.IndexOf("=", System.StringComparison.Ordinal)); + } + + if (!this.uiOptions.Contains(optionName) && optionName != string.Empty) + { + newOptions.Add(existingSegment); + } + } + } + + // Now add everything from the UI + if (!this.ReferenceFrames.IsDefault) + { + newOptions.Add("ref=" + this.ReferenceFrames.Value); + } + + if (!this.BFrames.IsDefault) + { + newOptions.Add("bframes=" + this.BFrames.Value); + } + + if (this.BFrames.Value != "0") + { + if (!this.AdaptiveBFrames.IsDefault) + { + newOptions.Add("b-adapt=" + this.AdaptiveBFrames.Value); + } + + if (!this.DirectPrediction.IsDefault) + { + newOptions.Add("direct=" + this.DirectPrediction.Value); + } + + if (this.BFrames.Value != "1" && !this.PyramidalBFrames.IsDefault) + { + newOptions.Add("b-pyramid=" + this.PyramidalBFrames.Value); + } + } + + if (!this.WeightedPFrames) + { + newOptions.Add("weightp=0"); + } + + if (!this.MotionEstimationMethod.IsDefault) + { + newOptions.Add("me=" + this.MotionEstimationMethod.Value); + } + + if (!this.SubpixelMotionEstimation.IsDefault) + { + newOptions.Add("subme=" + this.SubpixelMotionEstimation.Value); + } + + if (this.MotionEstimationRange != 16) + { + newOptions.Add("merange=" + this.MotionEstimationRange); + } + + if (!this.Analysis.IsDefault) + { + newOptions.Add("analyse=" + this.Analysis.Value); + } + + if (this.Analysis.Value != "none" && !this.EightByEightDct) + { + newOptions.Add("8x8dct=0"); + } + + if (!this.CabacEntropyCoding) + { + newOptions.Add("cabac=0"); + } + + if (!this.Trellis.IsDefault) + { + newOptions.Add("trellis=" + this.Trellis.Value); + } + + double psTrellis = 0.0; + if (this.CabacEntropyCoding && this.Trellis.Value != "0") + { + psTrellis = this.PsychovisualTrellis; + } + + if (this.AdaptiveQuantizationStrength != 1.0) + { + newOptions.Add( + "aq-strength=" + this.AdaptiveQuantizationStrength.ToString("F1", CultureInfo.InvariantCulture)); + } + + if (this.PsychovisualRateDistortion != 1.0 || psTrellis > 0.0) + { + newOptions.Add( + "psy-rd=" + this.PsychovisualRateDistortion.ToString("F1", CultureInfo.InvariantCulture) + "," + + psTrellis.ToString("F2", CultureInfo.InvariantCulture)); + } + + if (this.NoDctDecimate) + { + newOptions.Add("no-dct-decimate=1"); + } + + if (!this.DeblockingStrength.IsDefault || !this.DeblockingThreshold.IsDefault) + { + newOptions.Add("deblock=" + this.DeblockingStrength.Value + "," + this.DeblockingThreshold.Value); + } + + this.Task.AdvancedEncoderOptions = string.Join(":", newOptions); + this.NotifyOfPropertyChange(() => this.AdvancedOptionsString); + + // Reset the video tab if the user is using this tab. + if (!string.IsNullOrEmpty(this.Task.AdvancedEncoderOptions)) + { + this.advancedEncoderOptionsCommand.ExecuteClearVideo(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Views/AdvancedView.xaml b/win/CS/HandBrakeWPF/Views/AdvancedView.xaml index b0b7698d2..99d05b099 100644 --- a/win/CS/HandBrakeWPF/Views/AdvancedView.xaml +++ b/win/CS/HandBrakeWPF/Views/AdvancedView.xaml @@ -3,29 +3,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Converters="clr-namespace:HandBrakeWPF.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:Helpers="clr-namespace:HandBrakeWPF.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:Properties="clr-namespace:HandBrakeWPF.Properties" - xmlns:controls="clr-namespace:HandBrakeWPF.Controls" x:Name="advancedView" mc:Ignorable="d" > - - - - - - @@ -33,533 +16,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml b/win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml new file mode 100644 index 000000000..c4e64d6a8 --- /dev/null +++ b/win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml.cs b/win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml.cs new file mode 100644 index 000000000..003d8274e --- /dev/null +++ b/win/CS/HandBrakeWPF/Views/EncoderOptionsView.xaml.cs @@ -0,0 +1,27 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// Interaction logic for EncoderOptionsView.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Views +{ + using System.Windows.Controls; + + /// + /// Interaction logic for AdvancedView.xaml + /// + public partial class EncoderOptionsView : UserControl + { + /// + /// Initializes a new instance of the class. + /// + public EncoderOptionsView() + { + InitializeComponent(); + } + } +} diff --git a/win/CS/HandBrakeWPF/Views/OptionsView.xaml b/win/CS/HandBrakeWPF/Views/OptionsView.xaml index 389a80791..83735e414 100644 --- a/win/CS/HandBrakeWPF/Views/OptionsView.xaml +++ b/win/CS/HandBrakeWPF/Views/OptionsView.xaml @@ -88,8 +88,6 @@ - - @@ -291,7 +289,6 @@ - diff --git a/win/CS/HandBrakeWPF/Views/X264View.xaml b/win/CS/HandBrakeWPF/Views/X264View.xaml new file mode 100644 index 000000000..b1c97e592 --- /dev/null +++ b/win/CS/HandBrakeWPF/Views/X264View.xaml @@ -0,0 +1,539 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Views/X264View.xaml.cs b/win/CS/HandBrakeWPF/Views/X264View.xaml.cs new file mode 100644 index 000000000..7c7f9bea3 --- /dev/null +++ b/win/CS/HandBrakeWPF/Views/X264View.xaml.cs @@ -0,0 +1,27 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// Interaction logic for AdvancedView.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Views +{ + using System.Windows.Controls; + + /// + /// Interaction logic for AdvancedView.xaml + /// + public partial class X264View : UserControl + { + /// + /// Initializes a new instance of the class. + /// + public X264View() + { + InitializeComponent(); + } + } +} -- 2.40.0